diff --git a/.gitignore b/.gitignore index 71c9194e8bd..a8cc1f2863c 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ src/test/data/sandbox/ # MacOS custom attributes files created by Finder .DS_Store docs/_site/ + +resources/view/puml diff --git a/README.md b/README.md index 13f5c77403f..f88f07e0c8c 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,23 @@ -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) +# InternEase + +InternEase is a **powerful and innovative desktop app designed to streamline the internship application process primarily for Computer Science undergraduates**. With its optimized **combination of a Command Line Interface (CLI) and Graphical User Interface (GUI)**, InternEase offers users the best of both worlds - the speed and efficiency of a CLI for those who can type quickly, and the user-friendly experience of a GUI for those who prefer a visual interface. Whether you’re a seasoned CLI user or a first-time applicant new to work environment, InternEase makes it easy to keep track of your progress, deadlines, and follow-up actions, so you can focus on landing your dream internship. + +[![CI Status](https://github.com/AY2223S2-CS2103T-W15-4/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2223S2-CS2103T-W15-4/tp/actions) +[![codecov](https://codecov.io/gh/AY2223S2-CS2103T-W15-4/tp/branch/master/graph/badge.svg?token=MVV9ABQAS8)](https://codecov.io/gh/AY2223S2-CS2103T-W15-4/tp) ![Ui](docs/images/Ui.png) -* This is **a sample project for Software Engineering (SE) students**.
- Example usages: - * as a starting point of a course project (as opposed to writing everything from scratch) - * as a case study -* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details. - * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big. - * It comes with a **reasonable level of user and developer documentation**. -* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...). -* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**. -* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info. +## About the project + +* This is **a software engineering project done by Team W15-4 from CS2103T (AY2223S2)**.
+* The aim of this project is to: + * help Computer Science students to keep track of their internship applications + * provide a flexible and efficient internship application management interface via **both CLI and GUI** +* It is named `InternEase` because it eases the internship application management process via both well-implemented CLI and GUI to help users focus on their internship preparations. +* For the detailed documentation of this project, see the **[InternEase Product Website](https://ay2223s2-cs2103t-w15-4.github.io/tp/)**. + +## Getting started +* View our [quick start guide](https://ay2223s2-cs2103t-w15-4.github.io/tp/UserGuide.html#quick-start) now to get started on an easier internship journey immediately. + +## Acknowledgements +* The project is based on an ongoing software project for a desktop application (called _AddressBook-Level3_) used for managing contact details by [SE-EDU initiative](https://se-education.org/addressbook-level3/). diff --git a/build.gradle b/build.gradle index 108397716bd..c48f16f7fd4 100644 --- a/build.gradle +++ b/build.gradle @@ -16,6 +16,10 @@ repositories { maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } } +run { + enableAssertions = true; +} + checkstyle { toolVersion = '10.2' } @@ -41,6 +45,7 @@ task coverage(type: JacocoReport) { } dependencies { + implementation 'org.jetbrains:annotations:23.0.0' String jUnitVersion = '5.4.0' String javaFxVersion = '11' @@ -65,8 +70,12 @@ dependencies { testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: jUnitVersion } +run { + enableAssertions = true +} + shadowJar { - archiveFileName = 'addressbook.jar' + archiveFileName = 'internEase.jar' } defaultTasks 'clean', 'test' diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000000..dffa17d0ea2 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,2 @@ +ignore: + - "src/main/java/seedu/address/ui" diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..b0e2cdb70f6 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -5,55 +5,61 @@ 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 `internease[at]comp.nus.edu.sg` ## Project team -### John Doe +### Lee Jia Sheng - + -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/jiasheng59)] +[[portfolio](team/jiasheng59.md)] -* Role: Project Advisor +* Role: Developer +* Responsibilities: `Statistics`, `Find` , `Sort` and `Help` feature -### Jane Doe +### Lai Hui Qi - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/laihuiqi)] +[[portfolio](team/laihuiqi.md)] -* Role: Team Lead -* Responsibilities: UI +* Role: Developer +* Responsibilities: `Clear`, `Clear_by`, `Delete`, `Exit`, `Revert`, `Revert_all` and `Task Package` related features -### Johnny Doe +### Benjamin Wee - + -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] +[[github](http://github.com/benjamin-wee)] [[portfolio](team/benjamin-wee.md)] * Role: Developer -* Responsibilities: Data +* Responsibilities: `list`, `add_date` and `remind` feature -### Jean Doe +### Lau Zhan Ming - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/zm-l)] +[[portfolio](team/zm-l.md)] * Role: Developer -* Responsibilities: Dev Ops + Threading +* Responsibilities: + * Implement feature to manage internship application: `add`, `edit` + * GUI for `add`, `edit`, `delete`, `archive`, `unarchive` -### James Doe +### Lok Jian Ming - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/jianminglok)] +[[portfolio](team/jianminglok.md)] * Role: Developer -* Responsibilities: UI +* Responsibilities: + * Contact management features: `add_contact`, `edit_contact`, `delete_contact` + * Document management features: `add_docs`, `edit_docs`, `delete_docs` + * Edit internship application status feature: `edit_status` + * Archiving features: `archive`, `unarchive`, `list_archived` diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 46eae8ee565..330025d0800 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -2,14 +2,75 @@ layout: page title: Developer Guide --- -* Table of Contents -{:toc} +## **Table of Contents** + - [Acknowledgements](#acknowledgements) + - [Setting up, getting started](#setting-up-getting-started) + - [Design](#design) + - [Architecture](#architecture) + - [UI Component](#ui-component) + - [Logic Component](#logic-component) + - [Model Component](#model-component) + - [Storage Component](#storage-component) + - [Common classes](#common-classes) + - [Implementation of features](#implementation) + - [Add](#add-feature) + - [Edit](#edit-feature) + - [Add contact](#add-contact-feature) + - [Edit contact](#edit-contact-feature) + - [Delete contact](#delete-contact-feature) + - [List](#list-feature) + - [Add documents](#add-documents-feature) + - [Edit documents](#edit-documents-feature) + - [Delete documents](#delete-documents-feature) + - [Edit status](#edit-status-feature) + - [Archive](#archive-feature) + - [Unarchive](#unarchive-feature) + - [List archived applications](#list-archived-applications-feature) + - [Find](#find-feature) + - [Sort](#sort-feature) + - [Clear](#clear-feature) + - [Clear by](#clear-by-feature) + - [Delete](#delete-feature) + - [Revert](#revert-feature) + - [Revert all](#revert-all-feature) + - [Remind](#remind-feature) + - [Exit](#exit-feature) + - [Add interview date](#add-interview-date-feature) + - [Side features](#side-features) + - [Task related features](#task-related-features) + - [Find task](#find-task-feature) + - [List task](#list-task-feature) + - [Todo related features](#todo-related-features) + - [Add todo](#add-todo-feature) + - [Clear todo](#clear-todo-feature) + - [Delete todo](#delete-todo-feature) + - [Edit deadline](#edit-deadline-feature) + - [Edit content](#edit-content-feature) + - [List todo](#list-todo-feature) + - [Note related features](#note-related-features) + - [Add note](#add-note-feature) + - [Clear note](#clear-note-feature) + - [Delete note](#delete-note-feature) + - [List note](#list-note-feature) + - [Documentation, logging, testing, configuration, dev-ops](#documentation-logging-testing-configuration-dev-ops) + - [Appendix: Requirements](#appendix-requirements) + - [Product Scope](#product-scope) + - [User stories](#user-stories) + - [Use cases](#use-cases) + - [Non functional requirements](#non-functional-requirements) + - [Glossary](#glossary) + - [Appendix: Instructions for manual testing](#appendix-instructions-for-manual-testing) + - [Launch and Shutdown](#launch-and-shutdown) + - [Appendix: Planned Enhancement](#appendix-planned-enhancement) -------------------------------------------------------------------------------------------------------------------- ## **Acknowledgements** -* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +* This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org). +* Code and documentations reused and adapted from [AB3 project](https://github.com/nus-cs2103-AY2223S2/tp) created by the [SE-EDU initiative](https://se-education.org/). + +[Go back to Table of Contents](#table-of-contents) -------------------------------------------------------------------------------------------------------------------- @@ -17,13 +78,15 @@ title: Developer Guide Refer to the guide [_Setting up and getting started_](SettingUp.md). +[Go back to Table of Contents](#table-of-contents) + -------------------------------------------------------------------------------------------------------------------- ## **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/AY2223S2-CS2103T-W15-4/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 +99,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/AY2223S2-CS2103T-W15-4/tp/tree/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/AY2223S2-CS2103T-W15-4/tp/tree/master/src/main/java/seedu/address/MainApp.java). It is responsible for, * At app launch: Initializes the components in the correct sequence, and connects them up with each other. * At shut down: Shuts down the components and invokes cleanup methods where necessary. @@ -67,35 +130,45 @@ For example, the `Logic` component defines its API in the `Logic.java` interface The sections below give more details of each component. +[Go back to Table of Contents](#table-of-contents) + ### 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/AY2223S2-CS2103T-W15-4/tp/blob/master/src/main/java/seedu/address/ui/Ui.java) -![Structure of the UI Component](images/UiClassDiagram.png) +![Structure of the UI Component](images/UiClassDiagram.png)
+The reference of the `panels` node is as shown below:
+![refPanels](images/PanelsRef.png)
+The detailed components of `MixedPanel` and `ApplicationListPanel` are as shown below:
+![refMixedPanel](images/MixedPanelRef.png) ![refApplicationPanel](images/ApplicationListPanelRef.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`, `MixedPanel`, `NoteListPanel`, `TodoListPanel` 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/AY2223S2-CS2103T-W15-4/tp/blob/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/AY2223S2-CS2103T-W15-4/tp/blob/master/src/main/resources/view/MainWindow.fxml) The `UI` component, * executes user commands using the `Logic` component. * listens for changes to `Model` data so that the UI can be updated with the modified data. * 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 `InternshipApplication` object residing in the `Model`. +* listens on each other in the `Ui` component, as `CommandBox` calls functions in `MainWindow` to `execute()` `Logic`. +* keeps a reference to other `Ui` component, as `MainWindow` keeps references of `MixedPanel`, `NoteListPanel`, `ApplicationListPanel` and `TodoListPanel` to implement the switching between each panel. + +[Go back to Table of Contents](#table-of-contents) ### 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/AY2223S2-CS2103T-W15-4/tp/tree/master/src/main/java/seedu/address/logic/Logic.java) Here's a (partial) class diagram of the `Logic` component: How the `Logic` component works: -1. When `Logic` is called upon to execute a command, it uses the `AddressBookParser` class to parse the user command. +1. When `Logic` is called upon to execute a command, it uses the `InternEaseParser` class to parse the user command. 1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddCommand`) which is executed by the `LogicManager`. -1. The command can communicate with the `Model` when it is executed (e.g. to add a person). +1. The command can communicate with the `Model` when it is executed (e.g. to add an internship application). 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. @@ -110,32 +183,29 @@ 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. +* When called upon to parse a user command, the `InternEaseParser` 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 `InternEaseParser` 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. +[Go back to Table of Contents](#table-of-contents) + ### 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/AY2223S2-CS2103T-W15-4/tp/tree/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 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 internship applications data i.e., all `InternshipApplication` objects (which are contained in a `UniqueApplicationList` object). +* stores the currently 'selected' `Application` 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.
- - - -
- +[Go back to Table of Contents](#table-of-contents) ### 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/AY2223S2-CS2103T-W15-4/tp/tree/master/src/main/java/seedu/address/storage/Storage.java) @@ -144,234 +214,1602 @@ The `Storage` component, * inherits from both `AddressBookStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed). * depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`) +[Go back to Table of Contents](#table-of-contents) + ### Common classes Classes used by multiple components are in the `seedu.addressbook.commons` package. +[Go back to Table of Contents](#table-of-contents) + -------------------------------------------------------------------------------------------------------------------- ## **Implementation** This section describes some noteworthy details on how certain features are implemented. -### \[Proposed\] Undo/redo feature +[Go back to Table of Contents](#table-of-contents) -#### Proposed Implementation +### Add feature -The proposed undo/redo mechanism is facilitated by `VersionedAddressBook`. It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. Additionally, it implements the following operations: +### How add feature is implemented -* `VersionedAddressBook#commit()` — Saves the current address book state in its history. -* `VersionedAddressBook#undo()` — Restores the previous address book state from its history. -* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history. +The `add` command allows users to add an internship application to the tracker. The add mechanism is facilitated by `AddCommand` class. It extends `Command`but overrides the `Command#execute` to add an internship application to the tracker. -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. +The sequence diagram below shows how the `AddCommand` object is created: +![AddSequenceDiagram](images/AddSequenceDiagram.png) -Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. +Step 1. Parsing -Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state. +The `AddCommandParser` checks that the user input is in the expected format, if not an exception will be thrown. -![UndoRedoState0](images/UndoRedoState0.png) +Step 2. Execution -Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state. +The `AddCommand#execute` calls `Model#addApplication`, causing an update to the internship list. -![UndoRedoState1](images/UndoRedoState1.png) +Step 3. Result -Step 3. The user executes `add n/David …​` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`. +The updated model is then saved. A `CommandResult` object with a message containing the execution result of the command is created and returned to `MainWindow#execute`. -![UndoRedoState2](images/UndoRedoState2.png) +[Go back to Table of Contents](#table-of-contents) -
: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`. +### Edit feature -
+### How edit feature is implemented -Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state. +The `edit` command allows users to edit an internship application of the tracker. The edit mechanism is facilitated by `EditCommand` class. It extends `Command`but overrides the `Command#execute` to edit an internship application of the tracker. -![UndoRedoState3](images/UndoRedoState3.png) +The sequence diagram below shows how the `EditCommand` object is created: +![EditSequenceDiagram](images/EditSequenceDiagram.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. +Step 1. Parsing -
+The `EditCommandParser` checks that the user input is in the expected format, if not an exception will be thrown. -The following sequence diagram shows how the undo operation works: +Step 2. Execution -![UndoSequenceDiagram](images/UndoSequenceDiagram.png) +The `EditCommand#execute` calls `Model#setApplication`, causing an update to the internship list. -
:information_source: **Note:** The lifeline for `UndoCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. +Step 3. Result -
+The updated model is then saved. A `CommandResult` object with a message containing the execution result of the command is created and returned to `MainWindow#execute`. -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. +[Go back to Table of Contents](#table-of-contents) -
: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. +### Add contact feature -
+#### How is the feature implemented -Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged. +The `add_contact` command allows users to add the contact of a company to an internship application. The implementation of the `add_contact` command is facilitated by the `AddContactCommand` class which is derived from the `Command` superclass, and overrides the `Command#execute` method. +The parsing process meanwhile involves the `InternEaseParser#parseCommand`, `ContactParser#parseContactCommand` and the `AddContactCommandParser#parse` methods. -![UndoRedoState4](images/UndoRedoState4.png) +The activity diagram below shows the workflow of the `add_contact` command during its execution. -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. +![AddContactActivityDiagram](images/AddContactActivityDiagram.png) -![UndoRedoState5](images/UndoRedoState5.png) +The constructor of the class `AddContactCommand` requires 2 arguments, a valid positive `Integer` index and a `Contact` object, both of which are obtained after the parsing process mentioned above. -The following activity diagram summarizes what happens when a user executes a new command: +The relevant operations from the `Model` interface are `Model#getSortedFilteredInternshipList` and `Model#setApplication`. - +A sequence diagram is shown here to illustrate the execution process of the `add_contact` command. -#### Design considerations: +![AddContactSequenceDiagram](images/AddContactSequenceDiagram.png) -**Aspect: How undo & redo executes:** +Given below is an explanation on the `add_contact` command's behaviours. -* **Alternative 1 (current choice):** Saves the entire address book. - * Pros: Easy to implement. - * Cons: May have performance issues in terms of memory usage. +Step 1. Parsing -* **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. +The `CommandBox#execute` method is invoked when the user's input in `CommandBox` is parsed, which results in the command word being parsed in the method `InternEaseParser#parseCommand` and subsequently the method `ContactParser#parseContactCommand`. +The method `AddContactCommandParser#parse` is invoked only if the command word matches `AddContactCommand.COMMAND_WORD`. -_{more aspects and alternatives to be added}_ +Step 2. Execution -### \[Proposed\] Data archiving +The `AddContactCommand#execute` method is invoked and calls are made to the `model` instance. The last shown list of internships are obtained by calling the method `Model#getSortedFilteredInternshipList`. +The internship application where the contact is to be added is then obtained by calling the `UniqueApplicationList#get` method with the specified index. As the InternshipApplication object is +immutable, a new `InternshipApplication` object is created with the contact details. The `Model#setApplication` method is then invoked to update the specified application in the list. -_{Explain here how the data archiving feature will be implemented}_ +Step 3. Result +The updated model is then saved. A `CommandResult` object with a message containing the execution result of the command is created and returned to `MainWindow#execute`. +The `ApplicationListPanel` is refreshed with a `ResultDialog` displaying the returned message for 5 seconds. --------------------------------------------------------------------------------------------------------------------- +>**NOTE:** +> Error handling: Any error message returned in the midst of execution will be displayed as a `ResultDialog` and the current command executed terminates immediately. -## **Documentation, logging, testing, configuration, dev-ops** +#### Why is it implemented this way -* [Documentation guide](Documentation.md) -* [Testing guide](Testing.md) -* [Logging guide](Logging.md) -* [Configuration guide](Configuration.md) -* [DevOps guide](DevOps.md) +The `AddContactCommand` provides enhancement to the existing `AddCommand` by separating the process of adding contact details of the company from the initial process of +adding a new internship application. This prevents the `AddCommand` from getting cluttered with large amount of arguments that may become difficult for the user to remember. --------------------------------------------------------------------------------------------------------------------- +**Aspect: Where to save the contact details:** -## **Appendix: Requirements** +* **Alternative 1 (current choice):** Separating it into a separate `Contact` class. + * Pros: Flexibility to add more details to the contact if needed in the future. + * Cons: More time required to implement. -### Product scope +* **Alternative 2:** Adding contact details as attributes in the `InternshipApplication` class. + * Pros: Easier than implement. + * Cons: More conflicts will occur if someone else is working on the `InternshipApplication` class at the same time. -**Target user profile**: +[Go back to Table of Contents](#table-of-contents) -* 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 -**Value proposition**: manage contacts faster than a typical mouse/GUI driven app +### Edit contact feature +#### How is the feature implemented -### User stories +The `edit_contact` command allows users to edit the contact of a company added to an internship application. The implementation of the `edit_contact` command is facilitated by the `EditContactCommand` class which is derived from the `Command` superclass, and overrides the `Command#execute` method. +The parsing process meanwhile involves the `InternEaseParser#parseCommand`, `ContactParser#parseContactCommand` and the `EditContactCommandParser#parse` methods. -Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` +The activity diagram below shows the workflow of the `edit_contact` command during its execution. -| 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 | +![EditContactActivityDiagram](images/EditContactActivityDiagram.png) -*{More to be added}* +The constructor of the class `EditContactCommand` requires 2 arguments, a valid positive `Integer` index and a `EditContactDescriptor` object, both of which are obtained after the parsing process mentioned above. -### Use cases +The relevant operations from the `Model` interface are `Model#getSortedFilteredInternshipList` and `Model#setApplication`. -(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise) +A sequence diagram is shown here to illustrate the execution process of the `edit_contact` command. -**Use case: Delete a person** +![EditContactSequenceDiagram](images/EditContactSequenceDiagram.png) -**MSS** +Given below is an explanation on the `edit_contact` command's behaviours. -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 +Step 1. Parsing - Use case ends. +The `CommandBox#execute` method is invoked when the user's input in `CommandBox` is parsed, which results in the command word being parsed in the method `InternEaseParser#parseCommand` and subsequently the method `ContactParser#parseContactCommand`. +The method `EditContactCommandParser#parse` is invoked only if the command word matches `EditContactCommand.COMMAND_WORD`. -**Extensions** +Step 2. Execution -* 2a. The list is empty. +The `EditContactCommand#execute` method is invoked and calls are made to the `model` instance. The last shown list of internships are obtained by calling the method `Model#getSortedFilteredInternshipList`. +The internship application where the contact is to be edited is then obtained by calling the `UniqueApplicationList#get` method with the specified index. As the InternshipApplication object is +immutable, a new `InternshipApplication` object is created with the edited contact details. The `Model#setApplication` method is then invoked to update the specified application in the list. - Use case ends. +Step 3. Result -* 3a. The given index is invalid. +The updated model is then saved. A `CommandResult` object with a message containing the execution result of the command is created and returned to `MainWindow#execute`. +The `ApplicationListPanel` is refreshed with a `ResultDialog` displaying the returned message for 5 seconds. - * 3a1. AddressBook shows an error message. +#### Why is it implemented this way - Use case resumes at step 2. +The `EditContactCommand` follows the design intuition behind the `AddContactCommand` by separating the process of editing contact details of the company from the process of +editing other attributes of an existing internship application. -*{More to be added}* +[Go back to Table of Contents](#table-of-contents) -### Non-Functional Requirements +### Delete contact feature -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. +#### How is the feature implemented -*{More to be added}* +The `delete_contact` command allows users to delete the contact of a company added to an internship application. The implementation of the `delete_contact` command is facilitated by the `DeleteContactCommand` class which is derived from the `Command` superclass, and overrides the `Command#execute` method. +The parsing process meanwhile involves the `InternEaseParser#parseCommand`, `ContactParser#parseContactCommand` and the `DeleteContactCommandParser#parse` methods. -### Glossary +The activity diagram below shows the workflow of the `delete_contact` command during its execution. -* **Mainstream OS**: Windows, Linux, Unix, OS-X -* **Private contact detail**: A contact detail that is not meant to be shared with others +![DeleteContactActivityDiagram](images/DeleteContactActivityDiagram.png) --------------------------------------------------------------------------------------------------------------------- +The constructor of the class `DeleteContactCommand` requires 1 argument, a valid positive `Integer` index, which is obtained after the parsing process mentioned above. -## **Appendix: Instructions for manual testing** +The relevant operations from the `Model` interface are `Model#getSortedFilteredInternshipList` and `Model#setApplication`. -Given below are instructions to test the app manually. +A sequence diagram is shown here to illustrate the execution process of the `delete_contact` command. -
:information_source: **Note:** These instructions only provide a starting point for testers to work on; -testers are expected to do more *exploratory* testing. +![DeleteContactSequenceDiagram](images/DeleteContactSequenceDiagram.png) -
+Given below is an explanation on the `delete_contact` command's behaviours. -### Launch and shutdown +Step 1. Parsing -1. Initial launch +The `CommandBox#execute` method is invoked when the user's input in `CommandBox` is parsed, which results in the command word being parsed in the method `InternEaseParser#parseCommand` and subsequently the method `ContactParser#parseContactCommand`. +The method `DeleteContactCommandParser#parse` is invoked only if the command word matches `DeleteContactCommand.COMMAND_WORD`. - 1. Download the jar file and copy into an empty folder +Step 2. Execution - 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. +The `DeleteContactCommand#execute` method is invoked and calls are made to the `model` instance. The last shown list of internships are obtained by calling the method `Model#getSortedFilteredInternshipList`. +The internship application where the contact is to be edited is then obtained by calling the `UniqueApplicationList#get` method with the specified index. As the InternshipApplication object is +immutable, a new `InternshipApplication` object is created with the contact details deleted. The `Model#setApplication` method is then invoked to update the specified application in the list. -1. Saving window preferences +Step 3. Result - 1. Resize the window to an optimum size. Move the window to a different location. Close the window. +The updated model is then saved. A `CommandResult` object with a message containing the execution result of the command is created and returned to `MainWindow#execute`. +The `ApplicationListPanel` is refreshed with a `ResultDialog` displaying the returned message for 5 seconds. - 1. Re-launch the app by double-clicking the jar file.
- Expected: The most recent window size and location is retained. +#### Why is it implemented this way + +The `DeleteContactCommand` follows the design intuition behind the `AddContactCommand` by separating the process of deleting contact details of the company from the process of +deleting other attributes of an existing internship application. + +[Go back to Table of Contents](#table-of-contents) + +### List feature + +#### How is the feature implemented + +The `list` command lists all ongoing internship applications, i.e. excluding archived applications. Once the list command is executed, +it updates the `predicate` of `FilteredInternshipList` in `model` to show only currently ongoing applications. All commands to be executed following this command +will follow the index of the latest list displayed in the internship application panel. + +The execution process of `list_archived` is demonstrated by the activity diagram below. + +![ListActivityDiagram](images/ListActivityDiagram.png) + +[Go back to Table of Contents](#table-of-contents) + +### Add documents feature + +#### How is the feature implemented + +The `add_docs` command allows users to add links of the documents submitted to a company to an internship application. The implementation of the `add_docs` command is facilitated by the `AddDocumentsCommand` class which is derived from the `Command` superclass, and overrides the `Command#execute` method. +The parsing process meanwhile involves the `InternEaseParser#parseCommand`, `DocumentsParser#parseDocumentsCommand` and the `AddDocumentsCommandParser#parse` methods. + +The activity diagram below shows the workflow of the `add_docs` command during its execution. + +![AddDocumentsActivityDiagram](images/AddDocumentsActivityDiagram.png) + +The constructor of the class `AddDocumentsCommand` requires 2 arguments, a valid positive `Integer` index and a `Documents` object, both of which are obtained after the parsing process mentioned above. + +The relevant operations from the `Model` interface are `Model#getSortedFilteredInternshipList` and `Model#setApplication`. + +A sequence diagram is shown here to illustrate the execution process of the `add_docs` command. + +![AddDocumentsSequenceDiagram](images/AddDocumentsSequenceDiagram.png) + +Given below is an explanation on the `add_docs` command's behaviours. + +Step 1. Parsing + +The `CommandBox#execute` method is invoked when the user's input in `CommandBox` is parsed, which results in the command word being parsed in the method `InternEaseParser#parseCommand` and subsequently the method `DocumentsParser#parseDocumentsCommand`. +The method `AddDocumentsCommandParser#parse` is invoked only if the command word matches `AddDocumentsCommand.COMMAND_WORD`. + +Step 2. Execution + +The `AddDocumentsCommand#execute` method is invoked and calls are made to the `model` instance. The last shown list of internships are obtained by calling the method `Model#getSortedFilteredInternshipList`. +The internship application where the document links are to be added is then obtained by calling the `UniqueApplicationList#get` method with the specified index. As the InternshipApplication object is +immutable, a new `InternshipApplication` object is created with the document links. The `Model#setApplication` method is then invoked to update the specified application in the list. + +Step 3. Result + +The updated model is then saved. A `CommandResult` object with a message containing the execution result of the command is created and returned to `MainWindow#execute`. +The `ApplicationListPanel` is refreshed with a `ResultDialog` displaying the returned message for 5 seconds. + +#### Why is it implemented this way + +The `AddDocumentsCommand` provides enhancement to the existing `AddCommand`, in a similar fashion to the `AddContactCommand`. + +[Go back to Table of Contents](#table-of-contents) + +### Edit documents feature + +#### How is the feature implemented + +The `edit_docs` command allows users to edit links of the documents submitted to a company to an internship application. The implementation of the `edit_docs` command is facilitated by the `EditDocumentsCommand` class which is derived from the `Command` superclass, and overrides the `Command#execute` method. +The parsing process meanwhile involves the `InternEaseParser#parseCommand`, `DocumentsParser#parseDocumentsCommand` and the `EditDocumentsCommandParser#parse` methods. + +The execution process of the `edit_docs` command can be demonstrated by the activity diagram of `edit_contact` by replacing `contact` related phrases or methods with `documents` related phrases or methods. + +> [Edit Contact Feature](#edit-contact-feature) + +[Go back to Table of Contents](#table-of-contents) + +### Delete documents feature + +#### How is the feature implemented + +The `delete_docs` command allows users to delete links of the documents added to an internship application. The implementation of the `edit_docs` command is facilitated by the `DeleteDocumentsCommand` class which is derived from the `Command` superclass, and overrides the `Command#execute` method. +The parsing process meanwhile involves the `InternEaseParser#parseCommand`, `DocumentsParser#parseDocumentsCommand` and the `DeleteDocumentsCommandParser#parse` methods. + +The execution process of the `delete_docs` command can be demonstrated by the activity diagram of `delete_contact` by replacing `contact` related phrases or methods with `documents` related phrases or methods. + +> [Delete Contact Feature](#delete-contact-feature) + +[Go back to Table of Contents](#table-of-contents) + +### Edit status feature + +#### How is the feature implemented + +The `edit_status` command allows users to edit the status of an internship application. The implementation of the `edit_status` command is facilitated by the `EditStatusCommand` class which is derived from the `Command` superclass, and overrides the `Command#execute` method. +The parsing process meanwhile involves the `InternEaseParser#parseCommand` and the `EditStatusCommandParser#parse` methods. + +The activity diagram below shows the workflow of the `edit_status` command during its execution. + +![EditStatusActivityDiagram](images/EditStatusActivityDiagram.png) + +The constructor of the class `EditStatusCommand` requires 2 arguments, a valid positive `Integer` index and a `InternshipStatus` object, both of which are obtained after the parsing process mentioned above. + +The relevant operations from the `Model` interface are `Model#getSortedFilteredInternshipList` and `Model#setApplication`. + +A sequence diagram is shown here to illustrate the execution process of the `edit_contact` command. + +![EditStatusSequenceDiagram](images/EditStatusSequenceDiagram.png) + +Given below is an explanation on the `edit_status` command's behaviours. + +Step 1. Parsing + +The `CommandBox#execute` method is invoked when the user's input in `CommandBox` is parsed, which results in the command word being parsed in the method `InternEaseParser#parseCommand`. +The method `EditStatusCommandParser#parse` is invoked only if the command word matches `EditStatusCommand.COMMAND_WORD`. + +Step 2. Execution + +The `EditContactCommand#execute` method is invoked and calls are made to the `model` instance. The last shown list of internships are obtained by calling the method `Model#getSortedFilteredInternshipList`. +The internship application where the status is to be edited is then obtained by calling the `UniqueApplicationList#get` method with the specified index. As the InternshipApplication object is +immutable, a new `InternshipApplication` object is created with the new status. The `Model#setApplication` method is then invoked to update the specified application in the list. + +Step 3. Result + +The updated model is then saved. A `CommandResult` object with a message containing the execution result of the command is created and returned to `MainWindow#execute`. +The `ApplicationListPanel` is refreshed with a `ResultDialog` displaying the returned message for 5 seconds. + +#### Why is it implemented this way + +The `EditStatusCommand` follows the design intuition behind the `EditContactCommand` by separating the process of editing status of the internship application from the process of +editing other attributes of an existing internship application. + +[Go back to Table of Contents](#table-of-contents) + +### Archive feature + +#### How is the feature implemented + +The `archive` command allows users to archive an internship application. The implementation of the `archive` command is facilitated by the `ArchiveCommand` class which is derived from the `Command` superclass, and overrides the `Command#execute` method. +The parsing process meanwhile involves the `InternEaseParser#parseCommand` and the `ArchiveCommandParser#parse` methods. + +The activity diagram below shows the workflow of the `archive` command during its execution. + +![ArchiveActivityDiagram](images/ArchiveActivityDiagram.png) + +The constructor of the class `ArchiveCommand` requires 1 argument, a valid positive `Integer` index, which is obtained after the parsing process mentioned above. + +The relevant operations from the `Model` interface are `Model#getSortedFilteredInternshipList` and `Model#setApplication`. + +A sequence diagram is shown here to illustrate the execution process of the `archive` command. + +![ArchiveSequenceDiagram](images/ArchiveSequenceDiagram.png) + +Given below is an explanation on the `archive` command's behaviours. + +Step 1. Parsing + +The `CommandBox#execute` method is invoked when the user's input in `CommandBox` is parsed, which results in the command word being parsed in the method `InternEaseParser#parseCommand`. +The method `ArchiveCommandParser#parse` is invoked only if the command word matches `ArchiveCommand.COMMAND_WORD`. + +Step 2. Execution + +The `ArchiveCommand#execute` method is invoked and calls are made to the `model` instance. The last shown list of internships are obtained by calling the method `Model#getSortedFilteredInternshipList`. +The internship application to be archived is then obtained by calling the `UniqueApplicationList#get` method with the specified index. As the InternshipApplication object is +immutable, a new archived `InternshipApplication` object is created. The `Model#setApplication` method is then invoked to update the specified application in the list. + +Step 3. Result + +The updated model is then saved. A `CommandResult` object with a message containing the execution result of the command is created and returned to `MainWindow#execute`. +The `ApplicationListPanel` is refreshed with a `ResultDialog` displaying the returned message for 5 seconds. + +#### Why is it implemented this way + +The `ArchiveCommand` allows an internship application to be archived in an easier way, as otherwise the user would have to remember a specific prefix +if it is implemented as part of the `EditCommand`. + +[Go back to Table of Contents](#table-of-contents) + +### Unarchive feature + +#### How is the feature implemented + +The `unarchive` command allows users to unarchive an internship application. The implementation of the `unarchive` command is facilitated by the `UnarchiveCommand` class which is derived from the `Command` superclass, and overrides the `Command#execute` method. +The parsing process meanwhile involves the `InternEaseParser#parseCommand` and the `UnarchiveCommandParser#parse` methods. + +The execution process of the `unarchive` command can be demonstrated by the activity diagram of `archive` by replacing `archive` related phrases or methods with `unarchive` related phrases or methods. + +> [Archive Feature](#archive-feature) + + +### List archived applications feature +#### How is the feature implemented + +The `list_archived` command lists all currently archived internship applications. The implementation of the `list_archived` command is facilitated by the `ListArchivedCommand` class which is derived from the `Command` superclass, and overrides the `Command#execute` method. +The execution of the `ListArchivedCommand` is similar to the `ListCommand`. + +The execution process of `list_archived` is demonstrated by the activity diagram below. + +![ListArchivedActivityDiagram](images/ListArchivedActivityDiagram.png) + +[Go back to Table of Contents](#table-of-contents) + +### Find feature + +The `find` command allows user to find all `InternshipApplication` whose +1. `CompanyName` and `JobTitle` contain the specified keyword, or +2. `Internship status` contain the specified status, or +3. `InterviewDate` is before, after, or between specified date(s). + +>**NOTE:** +> The matching of keyword in `CompanyName` and `JobTitle` is case-insensitive. As long as one word within CompanyName` +> and `JobTitle` matches any of the KEYWORDS, it will be shown in result. + +#### How is the feature implemented + +The sequence diagram below describes the interaction between classes when find command is entered. +![FindSequenceDiagram](./images/FindSequenceDiagram.png) + +Step 1. Parsing + +If the command word matches the word "find", `FindCommandParser#parse()` will be called to parse +the argument of find. The parsing logic is further divided into three cases below: +- Case 1: If the prefix `s/` is specified, the argument that follows the prefix immediately is +deemed as `InternshipStatus`. A `FindStatusCommand` with `StatusPredicate` is created to be executed. +- Case 2: If one of the following prefixes `before/`, `after/`, OR `from/` and `to/` is specified, the argument +that follows immediately is deemed as `InterviewDate`. A `FindDateCommand` with appropriate subclasses of +`DatePredicate` is created to be executed. +- Case 3: If none of the prefix among `s/`, `before/`, `after/`, `from/`, `to/` is specified +in the argument. `FindCommandParser` constructs a `FindCommand` object and treat all arguments that follow as `KEYWORD` + +Step 2. Execution + +The corresponding command is then invoked. Within each command's `execute` method, +`Model#updateFilteredInternshipList()` is invoked by passing in the predicate. For Cases 1 and 2, +only those `InternshipApplication`'s with matching `ApplicationStatus` or `InterviewDate` are added to the Model's +`filteredInternships` whereas for Case 3, if any word within the `CompanyName` or `JobTitle` matches one of the +`KEYWORD`(s), then that application is added to the Model's `filteredInternships`. + +Step 3. Result + +The updated model is then saved. A `CommandResult` object with a message containing the execution result of the command +is created and returned to `MainWindow#execute`. The `ApplicationListPanel` is refreshed with a `ResultDialog` +displaying the returned message for 5 seconds. + + +#### Why is it implemented this way + +The class diagram below shows current structure of classes related to `find` command. +![FindFeatureClassDiagram](./images/FindFeatureClassDiagram.png) + +It is designed and implemented in this way to make the find command more extensible to further enhancement to be made. +For example, developer may want to enable more prefixes that be searched using the search command. By the +use of inheritance, one can easily modify the behaviour of `find` command through overrding of the `execute` command +and rely on polymorphism. + +#### Alternatives considered + +**Syntax of `find` command** + +* **Alternative 1 (current choice):** Now to find attribute in `InternshipApplication`, the command syntax used is +by using its prefix, i.e. in this form `find s/PENDING`. + * Pros: shorter command + * Cons: Parser logic is harder to maintain as compared to Alternative 2 + +* **Alternative 2:** We can also make it in such format find_, e.g. find_status. + * Pros: Easy parser to implement + * Cons: Longer command which takes longer time to type + +[Go back to Table of Contents](#table-of-contents) + +### Sort feature + +The `sort` command allows user to sort all `InternshipApplication` by the order below: +1. `CompanyName` (alphabetical order) +2. `JobTitle` (alphabetical order) +3. `Internship status` (the default order of status) +4. `InterviewDate` (ascending order of the interview date, `InternshipApplication` having null values are placed at the end). + +>**NOTE:** +> The alphabetical order for comparing `CompanyName` and `JobTitle` is case-insensitive. To illustrate, the +> `InternshipApplication` with `CompanyName` "amazon" should appear before that with `CompanyName` "Google". + +#### How is the feature implemented + +The sequence diagram below describes the interaction between classes when sort command is entered. +![SortSequenceDiagram](./images/SortSequenceDiagram.png) + +Step 1. Parsing + +If the command word matches the word "sort", `SortCommandParser#parse()` will be called to parse +the argument of sort. Depending on the argument of sort, the corresponding comparator will be created. The sort command +is then created with appropriate comparator. + +Step 2. Execution + +The SortCommand#execute method is then invoked. The `Model#updateSortedFilteredInternshipList()` is invoked by passing in the comparator. +The underlying `SortedList` is the updated and sorted by using the Comparator being passed. + +Step 3. Result + +The updated model is then saved. A `CommandResult` object with a message containing the execution result of the command +is created and returned to `MainWindow#execute`. The `ApplicationListPanel` is refreshed with a `ResultDialog` +displaying the returned message for 5 seconds. + + +#### Why is it implemented this way + +It is designed and implemented in this way to make the sort command more extensible to further enhancement to be made. +For example, developer may want to provide more sorting order using the sort command. By the +use of inheritance, one can easily enhance the `sort` command by passing appropriate implementation of `Comparator` object +to sort the underlying list of `InternshipApplication`'s by polymorphism. + +[Go back to Table of Contents](#table-of-contents) + +### Clear feature +This section elaborated the `clear` feature by its functionality and the path of execution together with the `ClearCommand` implementation. Uml diagram is used to aid this description. + +#### How CLEAR Feature is implemented + +The `clear` feature enables user to clear all internship applications in the current internship application list. + +The execution process of `clear` is demonstrated by the sequence diagram below. +![ClearSequenceDiagram](images/ClearSequenceDiagram.png) + +Given below is a step-wise explanation on `clear` mechanism's behaviour. + +Step 1. Parsing
+The user input in the `CommandBox` will trigger `CommandBox#execute`, will result in the command word processing in `InternEaseParser#parse`. If the `COMMAND.WORD` matches `clear`. + +Step 2. Execution
+`ClearCommand#execute` is called with `model` instance. It attempts to get full list of `Internship Applications` by `Model#getSortedFilteredInternshipList`. +If the application list is currently not empty, `Model#setInternEase` empties the application list by replacing it with a new InternEase instance while `Model#addAllInternshipToCache` adds the entire list into the cacheList. + +Step 3. Result
+The result model is saved. A `CommandResult` with execution result message is returned until the `MainWindow#execute`. The `ApplicationListPanel` is refreshed with a `ResultDialog` displaying the execution message for 5 seconds. + +>**NOTE:**
+> Error handling: Any error message resulted in the midst of execution will be displayed as a `ResultDialog` and current execution terminates immediately. + +#### Why is it implemented this way + +The `ClearFeature` is an enhanced extension for the `DeleteFeature`. It provides an execution for a series operations of the `DeleteCommands` at once. Furthermore, it is made reversible by adding the entries into a cacheList immediately after clearing them. + +[Go back to Table of Contents](#table-of-contents) + +### Clear By feature +This section elaborated the `clear_by` feature by its functionality and the path of execution together with the `ClearByCommand` implementation. Uml diagrams are used to aid this description. + +#### How CLEAR_BY Feature is implemented + +The `clear_by` feature enables user to clear the internship applications in batch with the specific attribute and the specific keyword. There are 3 cases (attributes) available in this feature. +In `Logic` interface, `ClearByCommand` extends `Command` with a `ClearByCommand#execute` functionality. The parsing process is facilitated by both the `InternEaseParser#parse` and `ClearByCommandParser#parse`. + +The workflow of a `clear_by` command during its execution is shown by the activity diagrams below, the alternatives of the main diagrams are shown in 3 break-downs activity diagram below: +![ClearByActivityDiagram](images/ClearByActivityDiagram.png)
+Group - Company Name
+![GroupCompanyName](images/GroupCompanyName.png)
+Group - Job Title
+![GroupJobTitle](images/GroupJobTitle.png)
+Group - Status
+![GroupStatus](images/GroupStatus.png)
+ +There are 3 constructors `ClearByCommand::new` provided for 3 different cases stated below : + +* Case 1 : clear_by `COMPANY_NAME` + * `PREFIX` should be set to `n` + * Allows user to remove all internship applications with `ParamType=COMPANY_NAME` **fully match** with the entire provided keyword (case-sensitive). + +* Case 2 : clear_by `JOB_TITLE` + * `PREFIX` should be set to `j` + * Allows user to remove all internship applications with `ParamType=JOB_TITLE` **fully match** with the entire provided keyword (case-sensitive). + +* Case 3 : clear_by `STATUS` + * `PREFIX` should be set to `s`, the keywords accepted include `ACCEPTED, PENDING, RECEIVED, REJECTED, DECLINED`. + * Allows user to remove all internship applications with `ParamType=STATUS` **fully match** with the correct provided keyword (case-sensitive). + +>**Note:** +> The assignation of cases will be done by `ClearByCommandParser#parse`, each unavailable fields will be set to null. + +These operations are involved in the `Model` interface as `Model#getSortedFilteredInternshipList`, `Model#addInternshipToCache` and `Model#deleteInternship` + +The execution process of `Clear_by` is demonstrated by the sequence diagram below. +![ClearBySequenceDiagram](images/ClearBySequenceDiagram.png) + +Given below is a step-wise explanation on `clear_by` mechanism's behaviour. + +Step 1. Parsing
+ The user input in the `CommandBox` will trigger `CommandBox#execute`, will result in the command word processing in `InternEaseParser#parse`. If the `COMMAND.WORD` matches `clear_by`, it will then be passed to `ClearByCommandParser#parse`. + The `PREFIX` in the argument will then be investigated. Different constructor of `ClearByCommand` object will be using based on the `PREFIX`. + +Step 2. Execution
+ `ClearByCommand#execute` is called with `model` instance. It attempts to get full list of `Internship Applications` by `Model#getSortedFilteredInternshipList`. Then, the list is filtered by `ClearByCommand#getFilteredList` to filter out the applications to be cleared. + The size of the list-to-clear is checked before an iteration to `Model#deleteInternship` and `Model#addInternshipToCache`. The cleared items are stored in the cacheList to support `RevertCommand` in current InternEase session. + +Step 3. Result
+ The result model is saved. A `CommandResult` with execution result message is returned until the `MainWindow#execute`. The `ApplicationListPanel` is refreshed with a `ResultDialog` displaying the execution message for 5 seconds. + +>**NOTE:**
+> Error handling: Any error message resulted in the midst of execution will be displayed as a `ResultDialog` and current execution terminates immediately. + +#### Why is it implemented this way + +The `ClearByCommand` is an enhanced feature for both `DeleteCommand` and `ClearCommand`. It resolves user's request to perform customized batch deletion of internship applications. +Based on utility, the 3 fixed fields in an internship application are taken as the key attributes for this `clear_by` feature. The `PREFIX` for specifying the `clear_by` attribute is also the same as InternEase convention. +For the ease of implementation and avoid ambiguity, constructor `ClearByCommand::new` is overloaded, taking different fields. The usage of enum `ParamType` to specify the operating attribute type generalized the `ClearByCommand#execute`. +The other implementation aspects of `clear_by` feature follow the convention of `InternEase`. + +[Go back to Table of Contents](#table-of-contents) + +### Delete feature +This section elaborated the `delete` feature by its functionality and the path of execution together with the `DeleteCommand` implementation. Uml diagram is used to aid this description. + +#### How DELETE Feature is implemented + +The `delete` feature enables user to delete an internship applications with the specified index. +In `Logic` interface, `DeleteCommand` extends `Command` with a `DeleteCommand#execute` functionality. The parsing process is facilitated by both the `InternEaseParser#parse` and `DeleteCommandParser#parse`. + +All the delete operations should only have `INDEX` within the displayed Internship Application List. +The deleted application(s) in current session (after InternEase initialization, before exit) will be cached in a cacheList to enable the `revert` and `revert_all` features. + +These operations are involved in the `Model` interface as `Model#getSortedFilteredInternshipList`, `Model#addInternshipToCache` and `Model#deleteInternship` + +The execution process of `delete` is demonstrated by the sequence diagram below. +![DeleteSequenceDiagram](images/DeleteSequenceDiagram.png) + +Given below is a step-wise explanation on `delete` mechanism's behaviour. + +Step 1. Parsing
+The user input in the `CommandBox` will trigger `CommandBox#execute`, will result in the command word processing in `InternEaseParser#parse`. If the `COMMAND.WORD` matches `delete`, it will then be passed to `DeleteCommandParser#parse`. +The `INDEX` in the argument will then be investigated. + +Step 2. Execution
+`DeleteCommand#execute` is called with `model` instance. It attempts to get full list of `Internship Applications` by `Model#getSortedFilteredInternshipList`. The `INDEX` is checked against the size of the current Internship Application to ensure that it is within the desired range. +The `internshipToDelete` is retrieved from the filteredList and deleted from the model by `Model#deleteInternship`. The deleted item is stored in the cacheList to support `RevertCommand` and `RevertAllCommand` in the current InternEase session. + +Step 3. Result
+The result model is saved. A `CommandResult` with execution result message is returned until the `MainWindow#execute`. The `ApplicationListPanel` is refreshed with a `ResultDialog` displaying the execution message for 5 seconds. + +>**NOTE:**
+> Error handling: Any error message resulted in the midst of execution will be displayed as a `ResultDialog` and current execution terminates immediately. + +#### Why is it implemented this way + +The `DeleteCommand` is a common, must-have feature which helps to clean-up unwanted internship applications to enhance user experience. The cacheList provides an extra protection to the data to buffer the effect of this destructive operation. + +[Go back to Table of Contents](#table-of-contents) + +### Revert feature +This section elaborated the `revert` feature by its functionality and the path of execution together with the `RevertCommand` implementation. Uml diagram is used to aid this description. + +#### How REVERT Feature is implemented + +The `revert` feature enables user to revert the most recent deleted internship applications. +In `Logic` interface, `RevertCommand` extends `Command` with a `RevertCommand#execute` functionality. The parsing process is facilitated by the `InternEaseParser#parse`. + +All the revert operations can be operated when the cacheList for the current session is not empty. +The reverted application(s) in current session (after InternEase initialization, before exit) will be removed from the cacheList and be added to the end of the current internship application list. + +These operations are involved in the `Model` interface as `Model#getCachedInternshipList`, `Model#getAndRemoveCachedApplication` and `Model#addApplication`. + +The execution process of `revert` is demonstrated by the activity diagram below. +![RevertActivityDiagram](images/RevertActivityDiagram.png) + +Given below is a step-wise explanation on `revert` mechanism's behaviour. + +Step 1. Parsing
+The user input in the `CommandBox` will trigger `CommandBox#execute`, will result in the command word processing in `InternEaseParser#parse`. If the `COMMAND.WORD` matches `revert`. + +Step 2. Execution
+`RevertCommand#execute` is called with `model` instance. It attempts to get full list of `cached Internship Applications` by `Model#getCachedInternshipList`. +The `most recent cached Internship Application` is retrieved from the cacheList and deleted from it by `Model#getAndRemoveCachedApplication`. The retrieved item is added back to the end of the internship application list by `Model#addApplication`. + +Step 3. Result
+The result model is saved. A `CommandResult` with execution result message is returned until the `MainWindow#execute`. The `ApplicationListPanel` is refreshed with a `ResultDialog` displaying the execution message for 5 seconds. + +>**NOTE:**
+> Error handling: Any error message resulted in the midst of execution will be displayed as a `ResultDialog` and current execution terminates immediately. + +#### Why is it implemented this way + +The `RevertFeature` adds an extra cacheList provides an extra protection to the data to counter the destructive effect of the `ClearFeatures` and the `DeleteFeature`. The cacheList does not write in the memory space to provide +a temporary data-storing data structure that acts as a buffer for the current session's operations. + +#### Alternatives considered + +**Functionality of `revert` command** + +* **Alternative 1 (current choice):** Currently, only the revert of a single deleted / cleared internship application is available. + * Pros: Shorter command, easy to implement. + * Cons: Less efficient as compared to Alternative 2. + +* **Alternative 2:** We can also make it in such format `revert INDEX`, e.g. revert 3 (reverts 3 most recent deleted internship applications). + * Pros: More powerful feature. + * Cons: More complicate to implement. + +[Go back to Table of Contents](#table-of-contents) + +### Revert All feature +This section elaborated the `revert_all` feature by its functionality and the path of execution together with the `RevertAllCommand` implementation. Uml diagram is used to aid this description. + +#### How REVERT_ALL Feature is implemented + +The `revert_all` feature enables user to revert all deleted and cleared internship applications in the current session (after InternEase initialization, before exit). + +The execution process of `revert_all` is demonstrated by the sequence diagram below. +![RevertAllSequenceDiagram](images/RevertAllSequenceDiagram.png) + +Given below is a step-wise explanation on `revert_all` mechanism's behaviour. + +Step 1. Parsing
+The user input in the `CommandBox` will trigger `CommandBox#execute`, will result in the command word processing in `InternEaseParser#parse`. If the `COMMAND.WORD` matches `revert_all`. + +Step 2. Execution
+`RevertAllCommand#execute` is called with `model` instance. It attempts to get full list of `cached Internship Applications` by `Model#getCachedInternshipList`. +If the cacheList is currently not empty, `Model#setEmptyInternshipCacheList` empties the cacheList while `Model#addApplications` adds the entire list to the end of the current internship application list. + +Step 3. Result
+The result model is saved. A `CommandResult` with execution result message is returned until the `MainWindow#execute`. The `ApplicationListPanel` is refreshed with a `ResultDialog` displaying the execution message for 5 seconds. + +>**NOTE:**
+> Error handling: Any error message resulted in the midst of execution will be displayed as a `ResultDialog` and current execution terminates immediately. + +#### Why is it implemented this way + +The `RevertAllFeature` is an enhanced extension for the `RevertFeature`. It provides an execution for the series operations of the `RevertFeatures` at once. + +[Go back to Table of Contents](#table-of-contents) + +### Remind feature +This section elaborated the `remind` by its functionality and the path of execution together with the RemindCommand implementation. + +#### How Remind Feature is implemented +The `remind` feature enables users to view the details of the application with the most imminent interview date. + +Given below is a step-wise explanation on `remind` mechanism's behaviour. + +Step 1. Parsing + +The user input in the `CommandBox` will trigger `CommandBox#execute`, will result in the command word processing in `InternEaseParser#parse`. If the `COMMAND.WORD` matches `remind`. + +Step 2. Execution + +`RemindCommand#Execute` is called with a model instance. It directly returns a command result with `showReminder` set to `True`. + +Step 3. Result + +A `CommandResult` with execution result message is returned until the `MainWindow#execute`. The `ResultDialog` displays the execution message for 5 seconds. `MainWindow#handleReminder` handles the remind operation by showing or focusing on the reminder window. + +>**NOTE:**
+> Error handling: Any error message resulted in the midst of execution will be displayed as a `ResultDialog` and current execution terminates immediately. + +#### Why is it implemented this way + +The `RemindFeature` is a good to have feature. Existing reminder window is cleaned-up upon executing commands to prevent multiple windows from clogging up the screen. + +[Go back to Table of Contents](#table-of-contents) -1. _{ more test cases …​ }_ +### Exit feature +This section elaborated the `exit` feature by its functionality and the path of execution together with the `ExitCommand` implementation. Uml diagram is used to aid this description. -### Deleting a person +#### How Exit Feature is implemented -1. Deleting a person while all persons are being shown +The `exit` feature enables user to close InternEase using CLI command. - 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. +The execution process of `exit` is demonstrated by the sequence diagram below. +![ExitSequenceDiagram](images/ExitSequenceDiagram.png) - 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. +Given below is a step-wise explanation on `exit` mechanism's behaviour. - 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. +Step 1. Parsing
+The user input in the `CommandBox` will trigger `CommandBox#execute`, will result in the command word processing in `InternEaseParser#parse`. If the `COMMAND.WORD` matches `exit`. - 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
- Expected: Similar to previous. +Step 2. Execution
+`ExitCommand#execute` is called with `model` instance. It directly returns a command result with `exit` parameter being set to `True`. + +Step 3. Result
+A `CommandResult` with execution result message is returned until the `MainWindow#execute`. The `ResultDialog` displays the execution message for 5 seconds. +`MainWindow#handleExit` handle the exit operation by recording current gui settings and hiding all the opened windows. The InternEase software shuts down eventually. + +>**NOTE:**
+> Error handling: Any error message resulted in the midst of execution will be displayed as a `ResultDialog` and current execution terminates immediately. + +#### Why is it implemented this way + +The `ExitFeature` is a general, must-have feature. All the windows are cleaned-up upon exit to prevent illegal running of InternEase processes and the trigger of null-pointer exceptions. +The `ExitFeature` also acts as the termination point for cacheList usage in the current session. + +[Go back to Table of Contents](#table-of-contents) + +### Add Interview Date feature + +#### How is the feature implemented + +The `add_date` command allows users to add an interview date to an internship application. The implementation of the `add_date` command is facilitated by the `AddInterviewDateCommand` class which is derived from the `Command` superclass, and overrides the `Command#execute` method. +The parsing process meanwhile involves the `AddressBookParser#parse#` and the `AddInterviewDateCommandParser#parse` methods. + +The constructor of the class `AddInterviewDateCommand` requires 2 arguments, a valid positive `Integer` index and a `InterviewDate` object, both of which are obtained after the parsing process mentioned above. + +The relevant operations from the `Model` interface are `Model#getFilteredInternshipList`, `Model#setApplication` and `Model#updateFilteredInternshipList`. + +Given below is an explanation on the `add_date` command's behaviours. + +Step 1. Parsing + +The `CommandBox#execute` method is invoked when the user's input in `CommandBox` is parsed, which results in the command word being parsed in the method `InternEaseParser#parser`. +The method `AddInterviewDateCommandParser#parse` is invoked only if the command word matches `AddInterviewDateCommand.COMMAND_WORD`. + +Step 2. Execution + +The `AddInterviewDateCommand#execute` method is invoked and calls are made to the `model` instance. The last shown list of internships are obtained by calling the method `Model#getFilteredInternshipList`. +The internship application where the interview date is to be added is then obtained by calling the `UniqueApplicationList#get` method with the specified index. As the InternshipApplication object is +immutable, a new `InternshipApplication` object is created with the interview date. The `Model#setApplication` method is then invoked to update the specified application in the list. + +Step 3. Result + +The updated model is then saved. A `CommandResult` object with a message containing the execution result of the command is created and returned to `MainWindow#execute`. +The `ApplicationListPanel` is refreshed with a `ResultDialog` displaying the returned message for 5 seconds. + +>**NOTE:** +> Error handling: Any error message returned in the midst of execution will be displayed as a `ResultDialog` and the current command executed terminates immediately. + +#### Why is it implemented this way + +The `AddInterviewDateCommand` provides enhancement to the existing `AddCommand` by separating the process of adding the interview date for an application from the initial process of +adding a new internship application. This prevents the `AddCommand` from getting cluttered with large amount of arguments that may become difficult for the user to remember. + +**Aspect: Where to save the interview date details:** + +* **Alternative 1 (current choice):** Separating it into a separate `InterviewDate` class. + * Pros: Flexibility to add more details to the interview date if needed in the future. + * Cons: More time required to implement. + +* **Alternative 2:** Adding interview date as an attribute in the `InternshipApplication` class. + * Pros: Easier than implement. + * Cons: More conflicts will occur if someone else is working on the `InternshipApplication` class at the same time. + +[Go back to Table of Contents](#table-of-contents) + +### Side Features +**All the side features share similar execution paths as their respective main features execution logic, only minor changes are applied.** + +For example, the main differences in these features are on the specific functions used to carry out the execution and the specific lists used to store the relevant items.
+ - `Task` is a combination of `Todo` and `Note`.
+ - `TodoList` or `NoteList` are used instead of the list of InternshipApplications with their related methods.
+ - Methods with `Todo` or `Note` are used instead of `Application` or `Internship` (e.g., updateFiltered`Todo`List and updateFiltered`Note`List are used instead of updateFiltered`Internship`List).
+ - CacheList is not applicable here.
+ - All the commands (include main features) can be executed in any of the panels. It will automatically switch to the related panel and display the results after every execution.
+ - All commands here need to go through the `TaskParser` after being processed in the `InternEaseParser`.
+ - For GUI settings, `Todo` uses `TodoListPanel`, `Note` uses `NoteListPanel`, while `Task` uses `MixedPanel`.
+ +[Go back to Table of Contents](#table-of-contents) + +### Task related features +### Find Task feature +#### How is the feature implemented + +The `FindTaskFeature` provides searching function on `Todo` and `Note` with keywords. +The execution of `FindTaskCommand` is similar to `FindCommand` except it takes no prefix(purely keywords) and it searches on `InternshipTodo` company name and `Note` content. + +The execution process of `find_task` is demonstrated by the sequence diagram below.
+![FindTaskSequenceDiagram](images/FindTaskSequenceDiagram.png) + +#### Why is it implemented this way + +The `FindFeature` on `InternshipTodo` and `Note` are put together as `FindTaskFeature` because they have the same characteristic as a `Task`. It will enable the planning of internship +application to be more effective. + +[Go back to Table of Contents](#table-of-contents) + +### List Task feature +#### How is the feature implemented + +The `ListTaskFeature` lists both `TodoList` and `NoteList` all together in a single panel (mixed panel). +The execution of `ListTaskCommand` is similar to `ListCommand`. + +The execution process of `list_task` is demonstrated by the activity diagram below.
+![ListTaskActivityDiagram](images/ListTaskActivityDiagram.png) + +#### Why is it implemented this way + +By implementing the listing of both `TodoList` and `NoteList` together, user can have a quick overview of current available `Todo Tasks` and long-lasting reminders -- `Notes`. + +[Go back to Table of Contents](#table-of-contents) + +### Todo related features +### Add Todo feature +#### How is the feature implemented + +The `AddTodoFeature` enables the adding of new `InternshipTodo` instance into the current `TodoList`. +The execution of `AddTodoCommand` is similar to `AddCommand`, the main difference is `AddTodoCommand` comes with an extra mandatory attribute of `ApplicationDeadline`. + +The execution process of `add_todo` is demonstrated by the activity diagram below.
+![AddTodoActivityDiagram](images/AddTodoActivityDiagram.png) + +[Go back to Table of Contents](#table-of-contents) + +### Clear Todo feature +#### How is the feature implemented + +The `ClearTodoFeature` clears the entire `TodoList`. +The execution of `ClearTodoCommand` is similar to `ClearCommand`. However, cacheList is not available for `InternshipTodo` so this operation is irreversible. + +The execution process of `clear_todo` is demonstrated by the activity diagram below.
+![ClearTodoActivityDiagram](images/ClearTodoActivityDiagram.png) + +[Go back to Table of Contents](#table-of-contents) + +### Delete Todo feature +#### How is the feature implemented + +The `DeleteTodoFeature` deletes the specified `InternshipTodo` entry respective to the INDEX stated in the command. +The execution of `DeleteTodoCommand` is similar to `DeleteCommand`. However, cacheList is not available for `InternshipTodo` so this operation is irreversible. + +The execution process of `delete_todo` is demonstrated by the activity diagram below.
+![DeleteTodoActivityDiagram](images/DeleteTodoActivityDiagram.png) + +[Go back to Table of Contents](#table-of-contents) + +### Edit Deadline feature +#### How is the feature implemented + +The `EditDeadlineFeature` enables user to edit the deadline of an `InternshipTodo` with INDEX specified in the command. +The execution of `EditDeadlineCommand` is similar to `EditStatusCommand`, but it edits the `ApplicationDeadline` for the respective `InternshipTodo`. + +The execution process of `edit_deadline` is demonstrated by the sequence diagram below.
+![EditDeadlineSequenceDiagram](images/EditDeadlineSequenceDiagram.png) + +[Go back to Table of Contents](#table-of-contents) + +### Edit Content feature +#### How is the feature implemented + +The `EditContentFeature` enables user to edit the note content of an `InternshipTodo` with INDEX specified in the command. +The execution of `EditNoteContentCommand` is similar to `EditStatusCommand`, but it edits the `NoteContent` for the respective `InternshipTodo`. + +The execution process of `edit_content` is demonstrated by the sequence diagram below.
+![EditNoteContentSequenceDiagram](images/EditNoteContentSequenceDiagram.png) + +[Go back to Table of Contents](#table-of-contents) + +### List Todo feature +#### How is the feature implemented + +The `ListTodoFeature` lists current `TodoList` in the `TodoListPanel`. +The execution of `ListTodoCommand` is similar to `ListCommand`. + +The execution process of `list_todo` is demonstrated by the activity diagram below.
+![ListTodoActivityDiagram](images/ListTodoActivityDiagram.png) + +[Go back to Table of Contents](#table-of-contents) + +### Note related features + +An overview of `Note` package is shown below.
+![NoteOverviewDiagram](images/NoteOverviewDiagram.png) + + +### Add Note feature +#### How is the feature implemented + +The `AddNoteFeature` enables the adding of new `Note` instance into the current `NoteList`. +The execution of `AddNoteCommand` is similar to `AddCommand`, the main difference is `AddNoteCommand` only has a mandatory attribute `NOTE_CONTENT`. + +The execution process of `add_note` can be demonstrated by the activity diagram of `add_todo` by replacing `todo` related phrases or methods to `note` related phrases or methods. +> [Add Todo Feature](#add-todo-feature) + +[Go back to Table of Contents](#table-of-contents) + +### Clear Note feature +#### How is the feature implemented + +The `ClearNoteFeature` clears the entire `NoteList`. +The execution of `ClearNoteCommand` is similar to `ClearCommand`. However, cacheList is not available for `Note` so this operation is irreversible. + +The execution process of `clear_note` can be demonstrated by the activity diagram of `clear_todo` by replacing `todo` related phrases or methods to `note` related phrases or methods. +> [Clear Todo Feature](#clear-todo-feature) + +[Go back to Table of Contents](#table-of-contents) + +### Delete Note feature +#### How is the feature implemented + +The `DeleteNoteFeature` deletes the specified `Note` entry respective to the INDEX stated in the command. +The execution of `DeleteNoteCommand` is similar to `DeleteCommand`. However, cacheList is not available for `Note` so this operation is irreversible. + +The execution process of `delete_note` can be demonstrated by the activity diagram of `delete_todo` by replacing `todo` related phrases or methods to `note` related phrases or methods. +> [Delete Todo Feature](#delete-todo-feature) + +[Go back to Table of Contents](#table-of-contents) + +### List Note feature +#### How is the feature implemented + +The `ListNoteFeature` lists current `NoteList` in the `NoteListPanel`. +The execution of `ListNoteCommand` is similar to `ListCommand`. + +The execution process of `list_note` can be demonstrated by the activity diagram of `list_todo` by replacing `todo` related phrases or methods to `note` related phrases or methods. +> [List Todo Feature](#list-todo-feature) + +[Go back to Table of Contents](#table-of-contents) + +-------------------------------------------------------------------------------------------------------------------- + +## **Documentation, logging, testing, configuration, dev-ops** + +* [Documentation guide](Documentation.md) +* [Testing guide](Testing.md) +* [Logging guide](Logging.md) +* [Configuration guide](Configuration.md) +* [DevOps guide](DevOps.md) + +[Go back to Table of Contents](#table-of-contents) + +-------------------------------------------------------------------------------------------------------------------- + +## **Appendix: Requirements** + +### Product scope + +**Target user profile**: + +* Computer Science undergraduate +* has a need to manage a number of internship applications +* prefer desktop apps over other types +* able to type fast +* prefers typing to mouse interactions +* is reasonably comfortable using CLI apps + +**Value proposition**: manage internship applications faster and more efficiently than a typical mouse/GUI driven app + +[Go back to Table of Contents](#table-of-contents) + +### 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 internship application entry | | +| `* * *` | internship applicant | manage the contact details of the company I am applying to | conveninently contact the company for queries or setting up interviews | +| `* * *` | internship applicant | note down links to the the documents submmited to the company I am applying to | conveninently retrieve the version of the resume or cover letter submitted to the company | +| `* * *` | internship applicant | archive my internship application entry | view a list of ongoing internship applications which do not get cluttered over time | +| `* * *` | internship applicant | delete my submission | remove wrong entries or application that I no longer need | +| `* * *` | internship applicant | view a list of my internship applications submitted | prevent repeated applications to the same company | +| `* *` | internship applicant | delete all my applications | start fresh | +| `* * *` | internship applicant | update the status of my application as it progresses | identify which stage of the application I am in | + +[Go back to Table of Contents](#table-of-contents) + +### Use cases + +(For all use cases below, the **System** is `InternEase` and the **Actor** is the `user`, unless specified otherwise) + +**Use case: UC01 Add an internship application entry** + +**MSS** + +1. User requests to add an internship applications. +2. InternEase adds the internship application and displays a success message. + + Use case ends. + +**Extensions** + +* 1a. The command format is invalid. + * 1a1. InternEase shows an error message and gives a specific suggestion on the correct command format. + + Use case ends. + +**Use case: UC02 Add contact details of a company to an internship application** + +**MSS** + +1. User requests to view the list of internship applications. +2. InternEase shows the internship application list with their indexes specified. +3. User requests to add the contact details of a company to a specific internship application in the list by specifying its respective index and details of the contact. +4. InternEase adds the contact details of the company to the internship application and displays a success message. + + Use case ends. + +**Extensions** + +* 2a. The list is empty. + * 2a1. InternEase shows an alert message that there is no internship application in the list. + + Use case ends. + +* 3a. The provided index is invalid. + + * 3a1. InternEase shows an error message and gives a specific suggestion on the index's range. + * 3a2. User enters a new internship application index. + + Steps 3a1 to 3a2 are repeated until a valid index is provided. Use case resumes at step 4. + +* 3b. The provided contact is invalid. + * 3b1. InternEase shows an error message and gives a specific suggestion on the format of a valid contact. + * 3b2. User enters a new contact. + + Steps 3b1 to 3b2 are repeated until a valid contact is provided. Use case resumes at step 4. + +* 3c. The command format is invalid. + * 3c1. InternEase shows an error message and gives a specific suggestion on the correct command format. + * 3c2. User enters a new command. + + Steps 3c1 to 3c2 are repeated until a valid command is entered. Use case resumes at step 4. + +**Use case: UC03 Edit contact details of the company for an internship application** + +**MSS** + +1. User requests to view the list of internship applications. +2. InternEase shows the internship application list with their indexes specified. +3. User requests to edit the contact details of the company for the specific internship application in the list by specifying its respective index and details of the updated contact. +4. InternEase edits the contact details of the company for the internship application and displays a success message. + + Use case ends. + +**Extensions** + +* 2a. The list is empty. + * 2a1. InternEase shows an alert message that there is no internship application in the list. + + Use case ends. + +* 3a. The provided index is invalid. + + * 3a1. InternEase shows an error message and gives a specific suggestion on the index's range. + * 3a2. User enters a new internship application index. + + Steps 3a1 to 3a2 are repeated until a valid index is provided. Use case resumes at step 4. + +* 3b. The provided contact is invalid. + * 3b1. InternEase shows an error message and gives a specific suggestion on the format of a valid contact. + * 3b2. User enters a new contact. + + Steps 3b1 to 3b2 are repeated until a valid contact is provided. Use case resumes at step 4. + +* 3c. The command format is invalid. + * 3c1. InternEase shows an error message and gives a specific suggestion on the correct command format. + * 3c2. User enters a new command. + + Steps 3c1 to 3c2 are repeated until a valid command is entered. Use case resumes at step 4. + +**Use case: UC04 Delete contact details of the company for an internship application** + +**MSS** + +1. User requests to view the list of internship applications. +2. InternEase shows the internship application list with their indexes specified. +3. User requests to delete the contact details of the company for the specific internship application in the list by specifying its respective index. +4. InternEase delete the contact details of the company for the internship application and displays a success message. + + Use case ends. + +**Extensions** + +* 2a. The list is empty. + * 2a1. InternEase shows an alert message that there is no internship application in the list. + + Use case ends. + +* 3a. The provided index is invalid. + + * 3a1. InternEase shows an error message and gives a specific suggestion on the index's range. + * 3a2. User enters a new internship application index. + + Steps 3a1 to 3a2 are repeated until a valid index is provided. Use case resumes at step 4. + +* 3b. The command format is invalid. + * 3b1. InternEase shows an error message and gives a specific suggestion on the correct command format. + * 3b2. User enters a new command. + + Steps 3b1 to 3b2 are repeated until a valid command is entered. Use case resumes at step 4. + +**Use case: UC05 Add links for documents submitted to the company for an internship application** + +Similar to `UC02 Add contact details of a company to an internship application` except that the documents link submitted to a company for an internship application is added. + +**Use case: UC06 Edit links for documents submitted to the company for an internship application** + +Similar to `UC03 Edit contact details of the company for an internship application` except that the documents link submitted to a company for an internship application is edited. + +**Use case: UC07 Delete contact details of the company for an internship application** + +Similar to `UC04 Delete contact details of the company for an internship application` except that the documents link submitted to a company for an internship application is deleted. + +**Use case: UC08 Revert a recent deleted internship application entry** + +**MSS** + +1. User accidentally deletes an internship application entry. +2. User requests to revert the recent execution of delete command. +3. InterEase retrieves the internship application from the CacheList and add it back to the end of the current internship application list. +4. InternEase displays a success message. + + Use case ends. + +**Extensions** + +* 3a. The CacheList is empty. + * 3a1. InternEase shows an alert message that there is no deleted internship application can be restored. + + Use case ends. + + +**Use case: UC09 Revert all deleted internship application entries in current session** + +**MSS** + +1. User accidentally deletes or clears some internship application entries. +2. User requests to restore all the deleted or cleared entries. +3. InterEase adds all the cached internship applications back to the end of the current internship application list. +4. InternEase displays a success message. + + Use case ends. + +**Extensions** + +* 3a. The CacheList is empty. + * 3a1. InternEase shows an alert message that there is no deleted internship application can be restored. + + Use case ends. + + +**Use case: UC10 Delete an internship application entry** + +**MSS** + +1. User requests to view the list internship applications. +2. InternEase shows the internship applications list with their indexes specified. +3. User requests to delete a specific internship application in the list by specifying its respective index. +4. InterEase deletes the internship application from the list and displays a success message. + + Use case ends. + +**Extensions** + +* 2a. The list is empty. + * 2a1. InternEase shows an alert message that there is no internship application in the list. + + Use case ends. + +* 3a. The given index is invalid. + * 3a1. InternEase shows an error message and gives specific suggestion on the index's range. + + * 3a2. User enters new internship application index. + + Steps 3a1 to 3a2 are repeated until a valid index is provided. + Use case resumes at step 4. + +* 3b. The command format is incorrect. + + * 3b1. InternEase shows an error message and gives specific suggestion on the command format. + + * 3b2. User enters new command. + + Steps 3b1 to 3b2 are repeated until a valid command is provided. + Use case resumes at step 4. + +**Use case: UC11 Sort internship applications** + +**MSS** + +1. User specifies the order, either company name, job title, status or interview date for the list of internship +applications to be sorted. +2. InternEase shows the sorted list of application ordered by the attribute specified. + Use case ends. + +**Extensions** + +* 1a. User specifies more than one attribute. + * 1a1. InternEase notices user that the command format is invalid. + + Use case ends. + +**Use case: UC12 Clear all internship application entries** + +**MSS** + +1. User requests to clear all the data in the application. +2. InternEase clears all the internship application entries, shows an empty list of internship application data and displays a success message. + + Use case ends. + +**Use case: UC13 Edit the status of an internship application** + +**MSS** + +1. User requests to view the list of internship applications. +2. InternEase shows the internship application list with their indexes specified. +3. User requests to edit the application status of a specific internship application in the list by specifying its respective index and the updated status. +4. InternEase updates the application status of the internship application and displays a success message. + + Use case ends. + +**Extensions** + +* 2a. The list is empty. + + * 2a1. InternEase shows an alert message that there is no internship application in the list. + + Use case ends. + +* 3a. The provided index is invalid. + + * 3a1. InternEase shows an error message and gives a specific suggestion on the index's range. + * 3a2. User enters a new internship application index. + + Steps 3a1 to 3a2 are repeated until a valid index is provided. Use case resumes at step 4. + +* 3b. The provided status is invalid. + + * 3b1. InternEase shows an error message and gives a specific suggestion on the possible statuses. + * 3b2. User enters a new internship application status. + + Steps 3b1 to 3a2 are repeated until a valid status is provided. Use case resumes at step 4. + +* 3c. The command format is invalid. + * 3c1. InternEase shows an error message and gives a specific suggestion on the correct command format. + * 3c2. User enters a new command. + + Steps 3c1 to 3c2 are repeated until a valid command is entered. Use case resumes at step 4. + +**Use case: UC14 Help** + +**MSS** + +1. User requests for help. +2. InternEase shows a list of available commands to the user. + +**Use case: UC15 List** + +**MSS** + +1. User requests to view the list of internship applications. +2. InternEase shows all the ongoing internship applications as a list with their indexes specified. + + Use case ends. + +**Extensions** +* 1a. The list is empty. + * 1a1. InternEase shows an alert message that there is no internship application in the list. + + Use case ends. + +**Use case: UC16 Clear relevant internship application entries by keyword** + +**MSS** + +1. User requests to view the list of internship applications. +2. InternEase shows the internship application list with their attributes specified. +3. User requests to clear all the relevant entries with specific keyword and its attribute. +4. InternEase requests confirmation from user. +5. InternEase updates the application status of the internship application and displays a success message. + + Use case ends. + +**Extensions** +* 1a. The list is empty. + * 1a1. InternEase shows an alert message that there is no internship application in the list. + + Use case ends. + +* 2a. The provided attribute is invalid. + * 2a1. InternEase shows an error message and gives a specific suggestion on the correct command format. + * 2a2. User enters a new command. + + Steps 2a1 to 2a2 are repeated until a valid attribute is provided. Use case resumes at step 4. + +**Use case: UC17 Archive an internship application** + +**MSS** + +1. User requests to view the list of internship applications. +2. InternEase shows the internship application list with their indexes specified. +3. User requests to archive a specific internship application in the list by specifying its respective index. +4. InternEase archives the internship application and displays a success message. + + Use case ends. + +**Extensions** + +* 2a. The list is empty. + + * 2a1. InternEase shows an alert message that there is no internship application in the list. + + Use case ends. + +* 3a. The provided index is invalid. + + * 3a1. InternEase shows an error message and gives a specific suggestion on the index's range. + * 3a2. User enters a new internship application index. + + Steps 3a1 to 3a2 are repeated until a valid index is provided. Use case resumes at step 4. + +* 3b. The command format is invalid. + * 3b1. InternEase shows an error message and gives a specific suggestion on the correct command format. + * 3b2. User enters a new command. + + Steps 3b1 to 3b2 are repeated until a valid command is entered. Use case resumes at step 4. + +* 3c. The specified internship application is already archived. + + * 3a1. InternEase shows an error message. + + Use case ends. + +**Use case: UC18 Unarchive an internship application** + +Similar to `UC17 Archive an internship application` except that the internship application is unarchived. + +**Use case: UC19 List archived internship applications** + +Similar to `UC15 List` except that only archived internship applications are shown. + +**Use case: UC20 Find an application by its company name and job title** + +**MSS** + +1. User specifies keyword for matching company name and job title for the internship application. +2. InternEase shows a list of application whose company name or job title fulfill the matching keyword. + Use case ends. + +**Extensions** + +* 1a. The list is empty. + * 1a1. InternEase notices user that there is no internship application in the list. + + Use case ends. + +**Use case: UC21 Find an application by its status** + +Similar to `UC20 Find an application by its company name and job title` except it's now finding by status. + +**Use case: UC22 Find an application by the range of interview date** + +Similar to `UC20 Find an application by its company name and job title` except user specifies a range of date when the interview date falls within. + +### Side features + +**Use case: UC23 Add a todo task entry** + +Similar to `UC01 Add an internship application entry` except todo task is added instead of an internship application. + +**Use case: UC24 List todo** + +Similar to `UC15 List` except todo tasks are listed instead of internship applications. + +**Use case: UC25 Edit the note content of a todo task** + +Similar to `UC13 Edit the status of an internship application` except the note content of a todo task is edited. + +**Use case: UC26 Edit the deadline of a todo task** + +Similar to `UC13 Edit the status of an internship application` except the deadline of a todo task is edited. + +**Use case: UC27 Delete a todo task entry** + +Similar to `UC10 Delete an internship application entry` except the specified todo task is deleted. + +**Use case: UC28 Clear all todo task entries** + +Similar to `UC12 Clear all internship application entries` except all the todo task entries are cleared instead of all the internship application entries. + +**Use case: UC29 Add a note** + +Similar to `UC01 Add an internship application entry` except a note entry is added instead of an internship application. + +**Use case: UC30 List note** + +Similar to `UC15 List` except note entries are listed instead of internship applications. + +**Use case: UC31 Delete a note entry** + +Similar to `UC10 Delete an internship application entry` except the specified note entry is deleted. + +**Use case: UC32 Clear all note entries** + +Similar to `UC12 Clear all internship application entries` except all the notes entries are cleared instead of all the internship application entries. + +**Use case: UC33 List task** + +Similar to `UC15 List` except todo task entries and note entries are listed instead of internship applications. + +**Use case: UC34 Find a task by its field** + +Similar to `UC20 Find an application by its company name and job title` except todo task entries and note entries which match the specified keyword are filtered out and listed. + +**Use case: UC35 Edit an internship application** + +**MSS** + +1. User requests to view the list of internship applications. +2. InternEase shows the internship application list with their indexes specified. +3. User requests to edit the details of specific internship application in the list by specifying its index. +4. InternEase edits the details of the internship application and displays a success message. + + Use case ends. + +**Extensions** + +* 2a. The list is empty. + * 2a1. InternEase shows an alert message that there is no internship application in the list. + + Use case ends. + +* 3a. The provided index is invalid. + + * 3a1. InternEase shows an error message and gives a specific suggestion on the index's range. + * 3a2. User enters a new internship application index. + + Steps 3a1 to 3a2 are repeated until a valid index is provided. Use case resumes at step 4. + +* 3b. The provided details is invalid. + * 3b1. InternEase shows an error message and gives a specific suggestion on the format of valid details. + * 3b2. User enters new details. + + Steps 3b1 to 3b2 are repeated until valid details is provided. Use case resumes at step 4. + +* 3c. The command format is invalid. + * 3c1. InternEase shows an error message and gives a specific suggestion on the correct command format. + * 3c2. User enters a new command. + + Steps 3c1 to 3c2 are repeated until a valid command is entered. Use case resumes at step 4. + +[Go back to Table of Contents](#table-of-contents) + +### 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 internship applications without a noticeable increase in 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 typing commands than using the mouse. +4. InternEase doesn't support storing the actual file for the resume and cover letter. User can only include links to their resume and cover letter used for a particular application. +5. InternEase is unable to remind user through any platform outside of the application. + +[Go back to Table of Contents](#table-of-contents) + +### Glossary + +* **CLI**: Command line interface +* **GUI**: Graphical User interface +* **Mainstream OS**: Windows, Linux, Unix, OS-X +* **InternshipTodo**: Internships that are planned to apply +* **Note**: Act as long-lasting reminders for future internship applications and interviews + +[Go back to Table of Contents](#table-of-contents) + +-------------------------------------------------------------------------------------------------------------------- + +## **Appendix: Instructions for manual testing** + +Given below are instructions to test the app manually. + +
:information_source: **Note:** These instructions only provide a starting point for testers to work on; +testers are expected to do more *exploratory* testing. + +
+ +[Go back to Table of Contents](#table-of-contents) + +### Launch and shutdown + +1. Initial launch of InternEase + + 1. Download the jar file and copy into an empty folder + 2. Launch the application by: + 1. Double-click the jar file.
+ or + 2. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar InternEase.jar` command to run the program.
+ Expected: Shows the GUI with a set of sample internship applications. The window size may not be optimal. + +2. Saving window preferences + + 1. Resize the window to an optimal size. Move the window to a different location. Close the window. + + 2. Re-launch the app by double-clicking the jar file.
+ Expected: The most recent window size and location is retained. -1. _{ more test cases …​ }_ +3. Shutting down InternEase + 1. Shut down InternEase + 1. Using `exit` command.
+ or + 2. Close the window using the 'X' button on top-right of the window frame. + 2. All prior activities will be saved. + 3. Re-launch InternEase by [Step 1(ii)](#Launch-and-shutdown).
Expected: All the saved data will be loaded and displayed. -### Saving data +[Go back to Table of Contents](#table-of-contents) -1. Dealing with missing/corrupted data files +## **Appendix: Planned Enhancement** - 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ +1. The current display duration may not suit everyone and the dialog content looks messy on showing all particulars. The display duration could be customized (can be decided by user) and the dialog content could be enhanced to show important particulars only in further enhancement. +2. All commands are executable on any panel (e.g., command `delete_note 2` can delete the 2nd note even though the panel is showing the todo list only). We plan to have some enhancement on it by implementing some custom restrictions (can be decided by user) to limit the command executions according to the displaying GUI. +3. The current method of displaying the details of an internship application by clicking the card on the left panel to display it on the right panel is less ideal to users who prefer to perform operations solely via CLI. We plan to enhance their user experience by including a `view` command in future iterations, allowing them to + show the details of an internship application on the right panel by including an `INDEX` when typing in the command. +4. The current method for managing documents used for an internship application only supports `HTTP` and `HTTPS` links to those documents. We plan to enhance this feature by allowing users to upload the files directly to our app, so that they can view the file immediately without an Internet connection. -1. _{ more test cases …​ }_ +[Go back to Table of Contents](#table-of-contents) diff --git a/docs/SettingUp.md b/docs/SettingUp.md index 275445bd551..53d9110f486 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 [InternEase’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 e7df68b01ea..061f35e4d81 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -3,39 +3,111 @@ layout: page title: User Guide --- -AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps. - -* Table of Contents -{:toc} +InternEase is a powerful and innovative desktop app designed to streamline the internship application process primarily +for Computer Science undergraduates. With its optimized combination of a Command Line Interface (CLI) +and Graphical User Interface (GUI), InternEase offers users the best of both worlds - the speed and efficiency of a CLI +for those who can type quickly, and the user-friendly experience of a GUI for those who prefer a visual interface. +Whether you're a seasoned CLI user or a first-time applicant new to work environment, InternEase makes it easy +to keep track of your progress, deadlines, and follow-up actions, so you can focus on landing your dream internship. + +## Features Menu +- [Quick start](#quick-start) + +- [Features](#features) + +- [Main Features](#main-features-tracking-applied-internships) + - [View guide : `help`](#view-help--help) + - [Manage an internship application](#add-an-internship-application--add) + - [Add an internship application : `add`](#add-an-internship-application--add) + - [Edit an internship application : `edit`](#edit-an-internship-application--edit) + - [List currently ongoing internship applications : `list`](#display-a-list-of-ongoing-internship-applications--list) + - [Sort all internship applications : `sort`](#sort-all-internship-applications--sort) + - [Find internship applications by the company name, job title, status, or interview date : `find`](#find-internship-applications-by-the-company-name-job-title-status-or-interview-date--find) + - [Add an interview date : `add_date`](#add-an-interview-date--add_date) + - [Manage company contact for an internship application](#add-contact-details--add_contact) + - [Add a company contact : `add_contact`](#add-contact-details--add_contact) + - [Edit a company contact : `edit_contact`](#edit-contact-details--edit_contact) + - [Delete a company contact : `delete_contact`](#delete-contact-details--delete_contact) + - [Edit the status of an internship application : `edit_status`](#edit-application-status--edit_status) + - [Manage documents for an internship application](#add-documents--add_docs) + - [Add documents : `add_docs`](#add-documents--add_docs) + - [Edit documents : `edit_docs`](#edit-documents--edit_docs) + - [Delete documents : `delete_docs`](#delete-documents--delete_docs) + - [Archive and unarchive an internship application](#archive-an-internship-application--archive) + - [Archive an application : `archive`](#archive-an-internship-application--archive) + - [Unarchive an application : `unarchive`](#unarchive-an-internship-application--unarchive) + - [List all archived applications : `list_archived`](#display-a-list-of-archived-internship-applications--list_archived) + - [Displaying reminders : `remind`](#displaying-the-internship-application-with-the-most-imminent-interview--remind) + - [Remove entry(entries)](#delete-an-internship-application--delete) + - [Delete an internship application : `delete`](#delete-an-internship-application--delete) + - [Clear all internship applications : `clear`](#clear-all-internship-application-entries--clear) + - [Clear specific internship applications : `clear_by`](#clear-internship-application-entries-with-keyword--clear_by) + - [Revert delete or clear](#revert-a-recently-deleted-internship-application--revert) + - [Revert the most recent delete command : `revert`](#revert-a-recently-deleted-internship-application--revert) + - [Revert all delete and clear commands : `revert_all`](#revert-all-recently-deleted-or-cleared-internship-applications--revert_all) + - [Exit InternEase : `exit`](#exit-the-program--exit) + +- [Side Features](#side-features-planning-to-apply-internships) + - [Task (todo and notes)](#display-lists-of-tasks-todos-and-notes--list_task) + - [List current available tasks : `list_task`](#display-lists-of-tasks-todos-and-notes--list_task) + - [Find specific tasks : `find_task`](#search-for-a-task-todo-and-notes--find_task) + - [Todo](#display-a-list-of-todo-internship-applications--list_todo) + - [List current available todo internship applications : `list_todo`](#display-a-list-of-todo-internship-applications--list_todo) + - [Add a todo internship application : `add_todo`](#add-a-todo-application--add_todo) + - [Edit the deadline of a todo task : `edit_deadline`](#edit-todo-application-deadline--edit_deadline) + - [Edit the note content of a todo task : `edit_content`](#edit-todo-note-content--edit_content) + - [Delete a todo task : `delete_todo`](#delete-a-todo-application--delete_todo) + - [Clear all todo tasks : `clear_todo`](#clear-all-todo-application-entries--clear_todo) + - [Note](#display-list-of-short-note--list_note) + - [List current saved notes : `list_note`](#display-list-of-short-note--list_note) + - [Add a note : `add_note`](#add-a-note-add_note) + - [Delete a note : `delete_note`](#delete-a-note--delete_note) + - [Clear all notes : `clear_note`](#clear-all-notes---clear_note) + +- [FAQ](#faq) + +- [Command Summary](#command-summary) -------------------------------------------------------------------------------------------------------------------- ## Quick start -1. Ensure you have Java `11` or above installed in your Computer. +> **Note**
+> This is a desktop app. + +1. Have Java `11` or above installed in local laptop or Computer. -1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases). +2. Download the latest version (InternEase v1.4) of `internease.jar` from [here](https://github.com/AY2223S2-CS2103T-W15-4/tp/releases).
-1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook. +3. Copy the file to the folder you want to use as the _home folder_ for your InternEaseApp. -1. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar addressbook.jar` command to run the application.
- A GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
+4. Start the app by: + - Opening a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar internease.jar` command to run the program.
+ or + - Double-clicking the downloaded `internease.jar` file.
+ > **Note**
+ > You should see the app is running now. A GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
![Ui](images/Ui.png) -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.
+5. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
Some example commands you can try: - * `list` : Lists all contacts. + * `add n/TechCompany j/Software Engineer` : Adds an application for the `Software Engineer` role at `TechCompany`. + + * `list` : Displays all the internships that the user has applied for. + + * `delete 2` : Deletes the second internship application in the list of applications. - * `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. + * `find TechCompany` : Searches for all application with `COMPANY_NAME` and/or `JOB_TITLE` as `Google` - * `delete 3` : Deletes the 3rd contact shown in the current list. + * `edit_status 2 s/PENDING` : Changes the status of the 2nd application in the applications list to `Pending offer`. - * `clear` : Deletes all contacts. + * `exit` : Exits the application. - * `exit` : Exits the app. +6. Refer to the [Features](#features) below for details of each command. -1. Refer to the [Features](#features) below for details of each command. +[↑ Back to Top of Section](#quick-start)
+[↑ Back to Features Menu](#features-menu) -------------------------------------------------------------------------------------------------------------------- @@ -46,137 +118,601 @@ AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized fo **:information_source: Notes about the command format:**
* Words in `UPPER_CASE` are the parameters to be supplied by the user.
- e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`. + e.g. in `add n/COMPANY_NAME`, `COMPANY_NAME` is a parameter which can be used as `add n/LinkedIn`. * Items in square brackets are optional.
- e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`. + e.g `n/COMPANY_NAME [l/LOCATION]` can be used as `n/LinkedIn l/Clementi` or as `n/LinkedIn`. * 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. + e.g. `[r/REVIEW]…​` can be used as ` ` (i.e. 0 times), `r/Close to MRT`, `r/Close to MRT t/Kind, inclusive bosses and colleagues` etc. * Parameters can be in any order.
- e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable. + e.g. if the command specifies `n/COMPANY_NAME j/JOB_TITLE`, `j/JOB_TITLE n/COMPANY_NAME` is also acceptable. * If a parameter is expected only once in the command but you specified it multiple times, only the last occurrence of the parameter will be taken.
- e.g. if you specify `p/12341234 p/56785678`, only `p/56785678` will be taken. + e.g. if you specify `l/Clementi l/Changi`, only `l/Changi` will be taken. -* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
+* Note that the following are reserved keyword in InternEase, and including these characters in the company name, job title, + review, and other fields may lead to unspecified behaviour: + * `n/`, `j/`, `r/`, `p/`, `q/`, `l/`, `s/`, `note/`, `rate/`, `reflect/`, `e/`, `by/`, `c/`, `d/`, `rs/`, `cl/`, `before/`, `after/`, `from/`, `to/`, + +* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit`,`clear`, and `remind`) will be ignored.
e.g. if the command specifies `help 123`, it will be interpreted as `help`. +* >**Note:**
+ > InternEase has 4 window interfaces which include the internship application list, the todo list, the note list and the task list. + > All the commands can be used in any interface. If the command for a different interface is executed in current interface, the current interface will switch to the respective interface and display the result of the command. + +GUI Breakdown: +![GuiOverview](images/ui/GuiOverview.png) + -### Viewing help : `help` +[↑ Back to Top of Section](#features)
+[↑ Back to Features Menu](#features-menu) -Shows a message explaning how to access the help page. +## Main features: Tracking applied internships -![help message](images/helpMessage.png) +### View help : `help` +Shows user the link to user guide. Format: `help` +[↑ Back to Features Menu](#features-menu) -### Adding a person: `add` +### Add an internship application : `add` -Adds a person to the address book. +Adds an internship application to the tracker -Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` +Format: `add n/COMPANY_NAME j/JOB_TITLE [l/LOCATION] [s/SALARY] [rate/RATING] [q/QUALIFICATION]... [p/PROGRAMMINGLANGUAGE]... [r/REVIEW]... [note/NOTE]... [reflect/REFLECTION]...` +- `SALARY` should be in the form of amount followed by a space and then the currency in upper case. -
:bulb: **Tip:** -A person can have any number of tags (including 0) -
+Examples: +* `add n/Facebook j/Product Manager` adds an application for the Product Manager role at Facebook. +* `add n/LinkedIn j/Software Engineer s/2000 SGD` adds an application for the Software Engineer role at LinkedIn with salary 2000 SGD. + +GUI alternative: +1. Click the Add Internship button to add a new internship application. +2. A popup window similar to the image below will appear.
+Add Internship Popup +3. Fields that are marked with red asterisk are compulsory fields while others are optional fields. +4. You can fill in the fields accordingly and press the `Add` button to execute the `add` command. + +[↑ Back to Features Menu](#features-menu) + +### Edit an internship application : `edit` + +Edits the internship . + +Format: `edit INDEX [n/COMPANY_NAME] [j/JOB_TITLE] [l/LOCATION] [s/SALARY] [rate/RATING] [q/QUALIFICATION]... [p/PROGRAMMINGLANGUAGE]... [r/REVIEW]... [note/NOTE]... [reflect/REFLECTION]...` + +- Edits the internship application at the specified `INDEX`. +- The index refers to the index number shown in the displayed internship list. +- The index must be a positive integer 1, 2, 3, …​ +- If `COMPANY_NAME` or `JOB_TITLE` is empty in the form, they will retain the former value, but the `COMPANY_NAME` or `JOB_TITLE` in CLI command cannot be empty. +- Other attribute can be left as empty string. 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` +* `edit 1 q/Singapore citizen q/Pursuing CS degree` updates the qualification of the internship with first index to `Singapore citizen` and `Pursuing CS degree`. +* `edit 2 n/LinkedIn j/Data Engineer` updates the second internship company name to `LinkedIn` and job title to `Data Engineer`. + +GUI alternative: +1. Click the Edit Internship button to edit an internship application. +2. A popup window similar to the image below will appear.
+Edit Internship Popup +3. You can fill in the fields accordingly and press the `Edit` button to execute the `edit` command. -### Listing all persons : `list` +[↑ Back to Features Menu](#features-menu) -Shows a list of all persons in the address book. +### Display a list of ongoing internship applications : `list` + +Displays a list of applied internships which are ongoing Format: `list` -### Editing a person : `edit` +Examples: + +* `list` shows all the ongoing internship applications for with 1 indexing. +* If there are no ongoing internship applications at the moment, + "No applications at the moment" will be shown. + +[↑ Back to Features Menu](#features-menu) + +### Sort all internship applications : `sort` + +Sorts internship applications according to either company name, job title, status or interview date in ascending order. + +Format: `sort PREFIX` +1. Sort by company name: `sort n/` +2. Sort by job title: `sort j/` +3. Sort by status: `sort s/` +4. Sort by interview date `sort d/` + +Example: +* `sort d/` sorts all applications with their interview date in ascending order, those without interview date available + yet will be placed at the end of the list. + +[↑ Back to Features Menu](#features-menu) + +### Find internship applications by the company name, job title, status, or interview date : `find` + +Find all internship applications (including those that have been archived) by its company name, job title, +status and/or interview date. + +Format: +There are three use cases for the `find` command: +1. Find by the application's company name and/or job title: `find KEYWORD [MORE KEYWORDS]` +2. Find by the current status of the application: `find s/STATUS` +3. Find by upcoming interview date: `find before/DATE`, `find after/DATE_TIME`, `find from/DATE_TIME1 to/DATE_TIME2` + +- The search for company name, job title, and status are case-insensitive. +- The order of KEYWORD doesn't matter. +- In use case 1, as long as a single word in company name and/or job title matches one of the KEYWORD's, it + will be shown to user. E.g. `JP Morgan` and `goldman Sachs` matches the keyword in `find JP Morgan Goldman Sachs`. +- Only full word will be matched. E.g. `goldman Sachs` won't match `find GOLD`. + +Examples: +* `find Google` searches for all application with `COMPANY_NAME` and/or `JOB_TITLE` as Google. +* `find s/PENDING` searches for all application that are pending. +* `find after/2023-12-02 12:30 PM` searches for all application that are having interview after + 2023-12-02 12:30 PM (inclusive). + +[↑ Back to Features Menu](#features-menu) + +### Add an interview date : `add_date` + +Adds an interview date and time to an internship application. + +Format: `add_date INDEX d/DATE_TIME` + +- Adds an interview date to the internship application at the specified `INDEX`. +- The index refers to the index number shown in the displayed internship list. +- The index must be a positive integer 1, 2, 3, …​ +- `DATE_TIME` should be a valid date time of the format `yyyy-MM-dd hh:mm a`, where `a` is either `AM` or `PM`, and the date and time must be after the current date and time. +- If the internship application at the specified `INDEX` already has an interview date, the previous interview date will be overwritten by the new one. + +Examples: +* `add_date 1 d/2023-05-02 11:30 AM` adds the date and time 2023-05-02 11:30 AM to the first application in the list of applications. +* `add_date 2 d/2023-07-03 12:30 PM` adds the date and time 2023-07-03 12:30 PM to the second application in the list of applications. + +[↑ Back to Features Menu](#features-menu) + +### Add contact details : `add_contact` + +Adds the contact details of a company to a specified application. + +Format: `add_contact INDEX p/PHONE_NUMBER e/EMAIL` + +- Adds contact details to the internship application at the specified `INDEX`. +- The index refers to the index number shown in the displayed internship list. +- The index must be a positive integer 1, 2, 3, …​ +- **Both** the phone number and email must be provided. +- `PHONE_NUMBER` should be a valid phone number of at least 3 digits. +- `EMAIL` should be a valid email of the format `username@domain.com`. + +Examples: +* `add_contact 1 p/87654321 e/abc@gmail.com` adds the contact number `87654321` and email `abc@gmail.com` to the 1st application in the list of applications. +* `add_contact 2 p/65432100 e/someemail@gmail.com` adds the contact number `65432100` and the email `someemail@gmail.com` to the 2nd application in the list of applications. + +[↑ Back to Features Menu](#features-menu) + +### Edit contact details : `edit_contact` + +Edits the contact details of a company previously added to a specified application. + +Format: `edit_contact INDEX [p/PHONE_NUMBER] [e/EMAIL]` + +- Edits contact details of the internship application at the specified `INDEX`. +- The index refers to the index number shown in the displayed internship list. +- The index must be a positive integer 1, 2, 3, …​ +- **At least one** field should be provided. +- `PHONE_NUMBER` should be a valid phone number of at least 3 digits. +- `EMAIL` should be a valid email of the format `username@domain.com`. -Edits an existing person in the address book. +Examples: +* `edit_contact 1 p/87654321 e/abc@gmail.com` updates the contact number and email of the company to `87654321` and `abc@gmail.com` respectively for the 1st application in the list of applications. +* `edit_contact 2 e/someemail@gmail.com` updates the email of the company to `someemail@gmail.com` for the 2nd application in the list of applications. +* `edit_contact 3 p/12345678` updates the contact number of the company to `12345678` for the 3rd application in the list of applications. + +[↑ Back to Features Menu](#features-menu) + +### Delete contact details : `delete_contact` + +Deletes the contact details of a company previously added to a specified application. + +Format: `delete_contact INDEX` + +- Deletes contact details added to the internship application at the specified `INDEX`. +- The index refers to the index number shown in the displayed internship list. +- The index must be a positive integer 1, 2, 3, …​ + +Examples: +* `delete_contact 1` deletes the contact number and email of the company for the 1st application in the list of applications. + +[↑ Back to Features Menu](#features-menu) + +### Edit application status : `edit_status` + +Edits the application status. + +Format: `edit_status INDEX s/STATUS` +- Edits the status of the specified `INDEX` to the specified `STATUS`. +- The index refers to the index number shown in the displayed internship list. +- The index must be a positive integer 1, 2, 3, …​ +- Available status: PENDING, RECEIVED, ACCEPTED, DECLINED, REJECTED + - PENDING: Internship application submitted, outcome has not been released. + - RECEIVED: Offer received. + - ACCEPTED: Offer accepted. + - DECLINED: Offer received and declined. + - REJECTED: Application rejected. + +Examples: +* `edit_status 2 s/PENDING` Changes the status of the 2nd application in the applications list to `PENDING` (Internship application submitted, outcome has not been released). + +[↑ Back to Features Menu](#features-menu) + +### Add documents : `add_docs` + +Adds documents including a resume link and a cover letter link to a specified application. + +Format: `add_docs INDEX rs/RESUME_LINK cl/COVER_LETTER_LINK` + +- The index refers to the index number shown in the displayed internship list. +- The index must be a positive integer 1, 2, 3, …​ +- **Both** the resume link and the cover letter link must be provided. +- `RESUME_LINK` must be a valid URL in the format `http://domain/path` or `https://domain/path`. +- `COVER_LETTER_LINK` must be a valid URL in the format `http://domain/path` or `https://domain/path`. + +Examples: +* `add_docs 1 rs/https://www.example.com/resume cl/https://www.example.com/coverletter` adds the resume link `https://www.example.com/resume` + and cover letter link `https://www.example.com/coverletter` to the 1st application in the list of applications. +* `add_docs 2 rs/https://www.goodresume.com/myresume cl/https://www.goodcoverletter.com/mycoverletter` adds the resume link `https://www.goodresume.com/myresume` + and cover letter link `https://www.goodcoverletter.com/mycoverletter` to the 2nd application in the list of applications. + +[↑ Back to Features Menu](#features-menu) + +### Edit documents : `edit_docs` + +Edits the documents which include the resume link and cover letter link previously added to a specified application. + +Format: `edit_docs INDEX [rs/RESUME_LINK] [cl/COVER_LETTER_LINK]` -Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` +- The index refers to the index number shown in the displayed internship list. +- The index must be a positive integer 1, 2, 3, …​ +- **At least one** field should be provided. +- `RESUME_LINK` must be a valid URL in the format `http://domain/path` or `https://domain/path`. +- `COVER_LETTER_LINK` must be a valid URL in the format `http://domain/path` or `https://domain/path`. -* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …​ -* At least one of the optional fields must be provided. -* Existing values will be updated to the input values. -* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative. -* You can remove all the person’s tags by typing `t/` without - specifying any tags after it. +Examples: +* `edit_docs 1 rs/https://www.example.com/resume cl/https://www.example.com/coverletter` updates the resume link and cover letter link to `https://www.example.com/resume` + and `https://www.example.com/coverletter` respectively for the 1st application in the list of applications. +* `edit_docs 2 rs/https://www.example.com/resume` updates the resume link to `https://www.example.com/resume` for the 2nd application in the list of applications. +* `edit_docs 3 cl/https://www.example.com/coverletter` updates the cover letter link to `https://www.example.com/coverletter` for the 3rd application in the list of applications. + +[↑ Back to Features Menu](#features-menu) + +### Delete documents : `delete_docs` + +Deletes the documents previously added to a specified application. + +Format: `delete_docs INDEX` + +- The index refers to the index number shown in the displayed internship list. +- The index must be a positive integer 1, 2, 3, …​ 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. +* `delete_docs 1` deletes the documents for the 1st application in the list of applications. + +[↑ Back to Features Menu](#features-menu) -### Locating persons by name: `find` +### Archive an internship application : `archive` -Finds persons whose names contain any of the given keywords. +Archives a specified application so that it would be hidden from the list of ongoing applications. -Format: `find KEYWORD [MORE_KEYWORDS]` +Format: `archive INDEX` -* 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` +- The index refers to the index number shown in the displayed internship list. +- The index must be a positive integer 1, 2, 3, …​ Examples: -* `find John` returns `john` and `John Doe` -* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png) +* `archive 1` archives the 1st application in the list of applications. + +GUI alternative: +1. Click the Archive Internship button to archive an internship application that is not archived. + +[↑ Back to Features Menu](#features-menu) + +### Unarchive an internship application : `unarchive` + +Unarchives a specified application that was previously archived so that it would be shown in the list of ongoing applications. + +Format: `unarchive INDEX` + +- The index refers to the index number shown in the displayed internship list. +- The index must be a positive integer 1, 2, 3, …​ + +Examples: +* `unarchive 1` unarchives the 1st application in the list of archived applications. + +GUI alternative: +1. Click the Archive Internship button to unarchive an internship application that is archived. + +[↑ Back to Features Menu](#features-menu) + +### Display a list of archived internship applications : `list_archived` + +Displays a list of archived internship applications. + +Format: `list_archived` + +- If there are no archived internship applications at the moment, "No archived applications at the moment" will be shown. + +Examples: + +* `list_archived` shows all the archived internship applications with 1 indexing. + +[↑ Back to Features Menu](#features-menu) -### Deleting a person : `delete` +### Delete an internship application : `delete` -Deletes the specified person from the address book. +Deletes the specified internship application from the list of internships applied. Format: `delete INDEX` -* Deletes the person at the specified `INDEX`. -* The index refers to the index number shown in the displayed person list. +* Deletes the application of internship at the specified `INDEX`. +* The index refers to the index number shown in the displayed internship list. * The index **must be a positive integer** 1, 2, 3, …​ Examples: -* `list` followed by `delete 2` deletes the 2nd person in the address book. -* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command. +* `delete 2` Deletes the 2nd internship application in the list of applications. -### Clearing all entries : `clear` +GUI alternative: +1. Click the Delete Internship button to delete an internship application. -Clears all entries from the address book. +[↑ Back to Features Menu](#features-menu) + +### Clear internship application entries with keyword : `clear_by` + +Clear all relevant internship application entries from the internship tracker with specific keyword. + +Format: `clear_by n/COMPANY_NAME` OR `clear_by j/JOB_TITLE` OR `clear_by s/STATUS` + +* Clears all internship applications with the specified keyword - `COMPANY_NAME`, `JOB_TITLE` or `STATUS`. +* As a protective approach, only internship applications with desired particulars that are **fully matched** with the entire, case-sensitive keyword will be cleared. +* Three types of clear_by features are provided, they can only be executed independently. + +Examples: +* `clear_by n/Meta` Clears all application with COMPANY_NAME as Meta. +* `clear_by j/Software engineer` Clears all application with JOB_TITLE as Software Engineer. +* `clear_by s/REJECTED` Clears all rejected application (with STATUS as REJECTED). + +[↑ Back to Features Menu](#features-menu) + +### Displaying the internship application with the most imminent interview : `remind` + +Displays the details of an internship application with the earliest date in a pop up window, with reference to the +current date and time. + +Format: `remind` + +[↑ Back to Features Menu](#features-menu) + +### Clear all internship application entries : `clear` + +Clears all internship application entries from the internship tracker. Format: `clear` -### Exiting the program : `exit` +[↑ Back to Features Menu](#features-menu) + +### Revert a recently deleted internship application : `revert` + +Reverts the most recent delete command and restores the relevant data to the end of the current internship applications list. + +Format: `revert` + +Examples: +1. Assume the most recent delete command was `delete 2` which has data `n/Tech j/Job`, the data was removed from the applications list. +2. Command `revert` restores the internship application entry at the back the application list, which has an effect similar to `add n/Tech j/Job`. + +**This command is only able to restore current session's data, all the deleted / cleared data will be permanently deleted if command `exit` is executed.** + +[↑ Back to Features Menu](#features-menu) + +### Revert all recently deleted or cleared internship applications : `revert_all` + +Reverts all recent delete commands or clear commands and restores the affected data back to the end of the current internship applications list. + +Format: `revert_all` + +**This command is only able to restore current session's data, all the deleted / cleared data will be permanently deleted if command `exit` is executed.** + +[↑ Back to Features Menu](#features-menu) + +### Exit the program : `exit` Exits the program. Format: `exit` -### Saving the data +[↑ Back to Features Menu](#features-menu) -AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. +## Side features: Planning to apply internships -### Editing the data file +### Display lists of tasks (todos and notes) : `list_task` -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. +Displays the todo-list and the note list together. -
: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. -
+Format: `list_task` + +Examples: + +* `list_task` shows all the todos and notes in one window. +* If there are no todo and note at the moment, `No task (todo and note) at the moment` will be shown in the result dialog. + +[↑ Back to Features Menu](#features-menu) + +### Search for a task (todo and notes) : `find_task` + +Searches the recorded lists of todos and notes by keyword (company name in todos and note content in notes). + +Format: `find_task KEYWORD` + +Searches for the todos or notes with the specified case-insensitive `KEYWORD`. +The keyword refers to the company name in todos or the note content in notes that the user intends to look for. + +Examples: +`find_task test week` searches for all todos with `COMPANY_NAME` or all notes with `NOTE_CONTENT` that contain `test` or `week`. + +[↑ Back to Features Menu](#features-menu) + +### Display a list of todo internship applications : `list_todo` + +Displays a list of todo applications (todo internship application). + +Format: `list_todo` + +Examples: + +* `list_todo` shows all the todo applications that the user has recorded. +* If there are no todo applications for at the moment, `No todo at the moment` will be shown. + +[↑ Back to Features Menu](#features-menu) + +### Add a todo application : `add_todo` + +Adds a todo internship application to the todo list. + +Format: `add_todo n/COMPANY_NAME j/JOB_TITLE by/DEADLINE` +- `DEADLINE` should be in the format yyyy-mm-dd. +- `DEADLINE` should not be earlier than the date when the todo application is created. + +Examples: +* `add_todo n/Facebook j/Product Manager by/2023-06-07` adds a todo application for the Product Manager role at Facebook. The internship should be applied by 7 June 2023. +* `add_todo n/LinkedIn j/Software Engineer by/2023-10-04` adds a todo application for the Software Engineer role at LinkedIn.The internship should be applied by 4 October 2023. + +[↑ Back to Features Menu](#features-menu) + +### Edit todo application deadline : `edit_deadline` -### Archiving data files `[coming in v2.0]` +Edits the deadline of the specified todo task from current available todo liss. -_Details coming soon ..._ +Format: `edit_deadline INDEX by/DEADLINE` +- Edits the deadline of the specified `INDEX` to the specified `DEADLINE`. +- The index refers to the index number shown in the displayed todo list. +- The index must be a positive integer 1, 2, 3, …​ +- `DEADLINE` should be in the format yyyy-mm-dd. +- `DEADLINE` should not be earlier than the date when the todo application is created. + +Examples: +* `edit_deadline 2 by/2023-07-06` Changes the deadline of the 2nd todo application in the todo list to `2023-07-06` (6 July 2023). + +[↑ Back to Features Menu](#features-menu) + +### Edit todo note content : `edit_content` + +Edits the note content of the specified todo task from current available todo list. + +Format: `edit_content INDEX c/NOTE_CONTENT` +- Edits the note content of the specified `INDEX` to the specified `NOTE_CONTENT`. +- The index refers to the index number shown in the displayed todo list. +- The index must be a positive integer 1, 2, 3, …​ +- **Note content is an optional field for todo applications** +- `NOTE_CONTENT` is empty (null) in default. +- `NOTE_CONTENT` can take 1 to 55 characters. + +Examples: +* `edit_content 2 c/Venue changed` Changes the note content of the 2nd todo application in the todo list to `Venue changed`. + +[↑ Back to Features Menu](#features-menu) + +### Delete a todo application : `delete_todo` + +Deletes the specified todo application from the todo list + +Format: `delete_todo INDEX` + +* Deletes the todo application at the specified `INDEX`. +* The index refers to the index number shown in the displayed todo list. +* The index **must be a positive integer** 1, 2, 3, …​ +* **This action is irreversible** + +Examples: +* `delete_todo 2` Deletes the 2nd todo application in the todo list. + +[↑ Back to Features Menu](#features-menu) + +### Clear all todo application entries : `clear_todo` + +Clears all todo application entries from the todo applications list. + +Format: `clear_todo` + +**This action is irreversible** + +[↑ Back to Features Menu](#features-menu) + +### Display list of short note : `list_note` + +Displays a list of saved notes. + +Format: `list_note` + +Examples: +* `list_note` shows all the notes that the user has written. +* If there are no internships applied for at the moment, `No note at the moment` will be shown. + +[↑ Back to Features Menu](#features-menu) + +### Add a note: `add_note` + +Adds a note to the note list. + +Format: `add_note c/NOTE_CONTENT` +- `NOTE_CONTENT` can take 1 to 55 characters. + +Examples: +* `add_note c/Focus on software engineering jobs!` adds a note with content `Focus on software engineering jobs!` into the note list. + +[↑ Back to Features Menu](#features-menu) + +### Delete a note : `delete_note` + +Deletes the specified note from the list of notes. + +Format: `delete_note INDEX` + +* Deletes the note at the specified `INDEX`. +* The index refers to the index number shown in the displayed note list. +* The index **must be a positive integer** 1, 2, 3, …​ +* **This action is irreversible** + +Examples: +* `delete_note 2` Deletes the 2nd note in the list of notes. + +[↑ Back to Features Menu](#features-menu) + +### Clear all notes : `clear_note` + +Clears all notes from the note. + +Format: `clear_note` + +**This action is irreversible** + +[↑ Back to Features Menu](#features-menu) -------------------------------------------------------------------------------------------------------------------- ## FAQ **Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder. +**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous InternEase home folder. + +**Q**: How do I update the statistics at bottom right corner?
+**A**: It's automatically update after you execute every command / action via either CLI or GUI. + +[↑ Back to Features Menu](#features-menu) -------------------------------------------------------------------------------------------------------------------- @@ -184,10 +720,42 @@ _Details coming soon ..._ 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` +**Add** | `add n/COMPANY_NAME j/JOB_TITLE [l/LOCATION] [s/SALARY] [rate/RATING] [q/QUALIFICATION]... [p/PROGRAMMINGLANGUAGE]... [r/REVIEW]... [note/NOTE]... [reflect/REFLECTION]...`
e.g., `add n/LinkedIn j/Software Engineer s/2000 SGD` +**Add Contact** | `add_contact INDEX p/PHONE_NUMBER e/EMAIL`
e.g., `add_contact 1 p/87654321 e/abc@gmail.com` +**Add Documents** | `add_docs INDEX rs/RESUME_LINK cl/COVER_LETTER_LINK`
e.g., `add_docs 1 rs/https://www.example.com/resume cl/https://www.example.com/coverletter` +**Add Interview Date** | `add_date INDEX d/DATE_TIME`
e.g., `add_date 1 d/2023-05-02 11:30 AM` +**Add Note** |`add_note c/NOTE_CONTENT`
e.g., `add_note c/The tasks are planned to be done by tomorrow!` +**Add Todo** |`add_todo n/COMPANY_NAME J/JOB_TITLE by/DEADLINE`
e.g., `add_todo n/company j/Manager d/2023-09-08` +**Archive** | `archive INDEX`
e.g., `archive 2` +**Clear** | `clear` +**Clear_by** | `clear_by n/COMPANY_NAME`
`clear_by j/JOB_TITLE`
`clear_by s/STATUS` +**Clear Note** |`clear_note` +**Clear Todo** |`clear_todo` +**Delete** | `delete INDEX`
e.g., `delete 2` +**Delete Contact** | `delete_contact INDEX`
e.g., `delete_contact 2` +**Delete Documents** | `delete_docs INDEX`
e.g., `delete_docs 2` +**Delete Note** |`delete_note INDEX`
e.g., `delete_note 2` +**Delete Todo** |`delete_todo INDEX`
e.g., `delete_todo 2` +**Edit** | `edit INDEX [n/COMPANY_NAME] [j/JOB_TITLE] [l/LOCATION] [s/SALARY] [rate/RATING] [q/QUALIFICATION]... [p/PROGRAMMINGLANGUAGE]... [r/REVIEW]... [note/NOTE]... [reflect/REFLECTION]...`
e.g., `edit 1 q/Singapore citizen q/Pursuing CS degree` +**Edit Contact** | `edit_contact INDEX [p/PHONE_NUMBER] [e/EMAIL]`
e.g., `edit_contact 3 p/98765432 e/def@gmail.com` +**Edit Documents** | `edit_docs INDEX [rs/RESUME_LINK] [cl/COVER_LETTER_LINK]`
e.g., `edit_docs 2 rs/https://www.goodresume.com/myresume cl/https://www.goodcoverletter.com/mycoverletter` +**Edit Deadline** |`edit_deadline INDEX by/DEADLINE`
e.g., `edit_deadline 2 by/2023-06-05` +**Edit Note Content** |`edit_content c/NOTE_CONTENT`
e.g., `edit_content 2 c/Venue changed` +**Edit Status** | `edit_status INDEX s/STATUS`
e.g., `edit_status 2 s/PENDING` +**Exit** | `exit` +**Find Applications** | `find KEYWORD [MORE KEYWORDS]`
e.g., `find Google`
`find s/STATUS`
e.g., `find s/PENDING`
`find before/DATE`, `find after/DATE_TIME`, `find from/DATE_TIME1 to/DATE_TIME2`
e.g., `find before/2023-01-31 12:45 PM` +**Find Task** |`find_task KEYWORD`
e.g., `find_task test` **Help** | `help` +**List** |`list` +**List Archived Applications** |`list_archived` +**List Note** |`list_note` +**List Task** |`list_task` +**List Todo** |`list_todo` +**Reminder** | `remind` +**Revert** | `revert` +**Revert All** | `revert_all` +**Sort Applications** | `sort n/`
`sort j/`
`sort s/`
`sort d/` +**Unarchive** | `unarchive INDEX`
e.g., `unarchive 2` + +[↑ Back to Top of Section](#command-summary)
+[↑ Back to Features Menu](#features-menu) diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..4522e32bd98 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: "AB-3" +title: "InternEase" theme: minima header_pages: @@ -8,7 +8,7 @@ header_pages: markdown: kramdown -repository: "se-edu/addressbook-level3" +repository: "AY2223S2-CS2103T-W15-4/tp" github_icon: "images/github-icon.png" plugins: diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss index 0d3f6e80ced..22aa2bf198b 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: "InternEase"; font-size: 32px; } } diff --git a/docs/diagrams/AddContactActivityDiagram.puml b/docs/diagrams/AddContactActivityDiagram.puml new file mode 100644 index 00000000000..29219124caf --- /dev/null +++ b/docs/diagrams/AddContactActivityDiagram.puml @@ -0,0 +1,22 @@ +@startuml +start +:User enters command add_contact; +if () then ([PREFIX p/ and e/ are present]) + :Parse command arguments; + if () then ([Phone number and email are valid]) + :Generate new AddContactCommand; + if () then ([Provided index is valid]) + :Add contact details to the specified application; + :Generate success message; + else ([else]) + :Generate error message; + endif + else ([else]) + :Generate error message; + endif + else ([else]) + :Generate error message; +endif +:Display result message to user; +stop +@enduml diff --git a/docs/diagrams/AddContactSequenceDiagram.puml b/docs/diagrams/AddContactSequenceDiagram.puml new file mode 100644 index 00000000000..f6eaa19a7e3 --- /dev/null +++ b/docs/diagrams/AddContactSequenceDiagram.puml @@ -0,0 +1,75 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternEaseParser" as InternEaseParser LOGIC_COLOR +participant ":ContactParser" as ContactParser LOGIC_COLOR +participant ":AddContactCommandParser" as AddContactCommandParser LOGIC_COLOR +participant "a:AddContactCommand" as AddContactCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute(add_contact) +activate LogicManager + +LogicManager -> InternEaseParser : parseCommand(add_contact) +activate InternEaseParser + +create ContactParser +InternEaseParser -> ContactParser : parseContactCommand(add_contact, args) +activate ContactParser + +create AddContactCommandParser +ContactParser -> AddContactCommandParser +activate AddContactCommandParser + +AddContactCommandParser --> ContactParser +deactivate AddContactCommandParser + +ContactParser -> AddContactCommandParser : parse(args) +activate AddContactCommandParser + +create AddContactCommand +AddContactCommandParser -> AddContactCommand +activate AddContactCommand + +AddContactCommand --> AddContactCommandParser : a +deactivate AddContactCommand + +AddContactCommandParser --> ContactParser : a +deactivate AddContactCommandParser +AddContactCommandParser -[hidden]-> InternEaseParser : result +destroy AddContactCommandParser + +ContactParser --> InternEaseParser : a +deactivate ContactParser + +InternEaseParser --> LogicManager : a +deactivate InternEaseParser + +LogicManager -> AddContactCommand : execute() +activate AddContactCommand + +AddContactCommand -> Model : setApplication(original, contactAdded) +activate Model + +Model --> AddContactCommand +deactivate Model + +create CommandResult +AddContactCommand -> CommandResult +activate CommandResult + +CommandResult --> AddContactCommand : r +deactivate CommandResult + +AddContactCommand --> LogicManager : r +deactivate AddContactCommand + +[<--LogicManager : r +deactivate LogicManager +@enduml diff --git a/docs/diagrams/AddDocumentsActivityDiagram.puml b/docs/diagrams/AddDocumentsActivityDiagram.puml new file mode 100644 index 00000000000..19ec02b3180 --- /dev/null +++ b/docs/diagrams/AddDocumentsActivityDiagram.puml @@ -0,0 +1,22 @@ +@startuml +start +:User enters command add_docs; +if () then ([PREFIX rs/ and cl/ are present]) + :Parse command arguments; + if () then ([Resume and cover letter links are valid]) + :Generate new AddDocumentsCommand; + if () then ([Provided index is valid]) + :Add document links to the specified application; + :Generate success message; + else ([else]) + :Generate error message; + endif + else ([else]) + :Generate error message; + endif + else ([else]) + :Generate error message; +endif +:Display result message to user; +stop +@enduml diff --git a/docs/diagrams/AddDocumentsSequenceDiagram.puml b/docs/diagrams/AddDocumentsSequenceDiagram.puml new file mode 100644 index 00000000000..eef1c1b8c8c --- /dev/null +++ b/docs/diagrams/AddDocumentsSequenceDiagram.puml @@ -0,0 +1,75 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternEaseParser" as InternEaseParser LOGIC_COLOR +participant ":DocumentsParser" as DocumentsParser LOGIC_COLOR +participant ":AddDocumentsCommandParser" as AddDocumentsCommandParser LOGIC_COLOR +participant "a:AddDocumentsCommand" as AddDocumentsCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute(add_docs) +activate LogicManager + +LogicManager -> InternEaseParser : parseCommand(add_docs) +activate InternEaseParser + +create DocumentsParser +InternEaseParser -> DocumentsParser : parseDocumentsCommand(add_docs, args) +activate DocumentsParser + +create AddDocumentsCommandParser +DocumentsParser -> AddDocumentsCommandParser +activate AddDocumentsCommandParser + +AddDocumentsCommandParser --> DocumentsParser +deactivate AddDocumentsCommandParser + +DocumentsParser -> AddDocumentsCommandParser : parse(args) +activate AddDocumentsCommandParser + +create AddDocumentsCommand +AddDocumentsCommandParser -> AddDocumentsCommand +activate AddDocumentsCommand + +AddDocumentsCommand --> AddDocumentsCommandParser : a +deactivate AddDocumentsCommand + +AddDocumentsCommandParser --> DocumentsParser : a +deactivate AddDocumentsCommandParser +AddDocumentsCommandParser -[hidden]-> InternEaseParser : result +destroy AddDocumentsCommandParser + +DocumentsParser --> InternEaseParser : a +deactivate DocumentsParser + +InternEaseParser --> LogicManager : a +deactivate InternEaseParser + +LogicManager -> AddDocumentsCommand : execute() +activate AddDocumentsCommand + +AddDocumentsCommand -> Model : setApplication(original, documentsAdded) +activate Model + +Model --> AddDocumentsCommand +deactivate Model + +create CommandResult +AddDocumentsCommand -> CommandResult +activate CommandResult + +CommandResult --> AddDocumentsCommand : r +deactivate CommandResult + +AddDocumentsCommand --> LogicManager : r +deactivate AddDocumentsCommand + +[<--LogicManager : r +deactivate LogicManager +@enduml diff --git a/docs/diagrams/AddSequenceDiagram.puml b/docs/diagrams/AddSequenceDiagram.puml new file mode 100644 index 00000000000..93d7821acad --- /dev/null +++ b/docs/diagrams/AddSequenceDiagram.puml @@ -0,0 +1,61 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternEaseParser" as InternEaseParser LOGIC_COLOR +participant ":AddCommandParser" as AddCommandParser LOGIC_COLOR +participant "a:AddCommand" as AddCommand LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute(add) +activate LogicManager + +LogicManager -> InternEaseParser: parseCommand(add) +activate InternEaseParser + +create AddCommandParser +InternEaseParser -> AddCommandParser : AddCommandParser() +activate AddCommandParser + +AddCommandParser --> InternEaseParser +deactivate AddCommandParser + +InternEaseParser -> AddCommandParser : parse(arguments) +activate AddCommandParser +create AddCommand +AddCommandParser -> AddCommand : AddCommand(internship) +activate AddCommand + +AddCommand --> AddCommandParser +deactivate AddCommand + +AddCommandParser --> InternEaseParser : a +deactivate AddCommandParser +AddCommandParser -[hidden]--> InternEaseParser : a +destroy AddCommandParser + +InternEaseParser --> LogicManager : a +deactivate InternEaseParser + +LogicManager -> AddCommand : execute() +activate AddCommand + +AddCommand -> Model : addApplication(internship) +activate Model + +Model --> AddCommand +deactivate Model + +AddCommand --> LogicManager : result +deactivate AddCommand +AddCommand -[hidden]--> LogicManager +destroy AddCommand + +<-- LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/AddTodoActivityDiagram.puml b/docs/diagrams/AddTodoActivityDiagram.puml new file mode 100644 index 00000000000..51882101016 --- /dev/null +++ b/docs/diagrams/AddTodoActivityDiagram.puml @@ -0,0 +1,16 @@ +@startuml +start +:User enters AddTodoCommand; + if () then ([Input Argument is valid]) + :Generate new AddTodoCommand; + if () then ([Todo to add already exists in the current TodoList]) + :Generate duplicate Todo alert; + else ([else]) + :Add Todo into TodoList; + :Generate success message; + endif + else ([else]) + :Generate error message; + endif +stop +@enduml diff --git a/docs/diagrams/ApplicationListPanelRef.puml b/docs/diagrams/ApplicationListPanelRef.puml new file mode 100644 index 00000000000..8b04226904f --- /dev/null +++ b/docs/diagrams/ApplicationListPanelRef.puml @@ -0,0 +1,37 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor UI_COLOR_T4 +skinparam classBackgroundColor UI_COLOR + +package UI <>{ +Class "{abstract}\nUiPart" as UiPart +Class ControlBox +Class PopupAddInternship +Class PopupEditInternship +Class ApplicationListPanel #CF2765 +Class ApplicationCard +} + +package Model <> { +Class HiddenModel #FFFFFF +} + +ApplicationListPanel --|> UiPart +ControlBox --|> UiPart +PopupAddInternship --|> UiPart +PopupEditInternship --|> UiPart +ApplicationCard --|> UiPart + +ApplicationListPanel -down-> ControlBox +ApplicationListPanel -down-> ApplicationCard +ApplicationCard -down-> PopupEditInternship +ControlBox -down-> PopupAddInternship + +ApplicationCard .right.> Model +PopupAddInternship .right.> Model +PopupEditInternship .right.> Model + +ApplicationCard -[hidden]right- ControlBox + +@enduml diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml index ef81d18c337..8a2382fd5d5 100644 --- a/docs/diagrams/ArchitectureSequenceDiagram.puml +++ b/docs/diagrams/ArchitectureSequenceDiagram.puml @@ -13,7 +13,7 @@ activate ui UI_COLOR ui -[UI_COLOR]> logic : execute("delete 1") activate logic LOGIC_COLOR -logic -[LOGIC_COLOR]> model : deletePerson(p) +logic -[LOGIC_COLOR]> model : deleteInternship(i) activate model MODEL_COLOR model -[MODEL_COLOR]-> logic diff --git a/docs/diagrams/ArchiveActivityDiagram.puml b/docs/diagrams/ArchiveActivityDiagram.puml new file mode 100644 index 00000000000..cc8780ed8c6 --- /dev/null +++ b/docs/diagrams/ArchiveActivityDiagram.puml @@ -0,0 +1,17 @@ +@startuml +start +:User enters command archive; +:Generate new ArchiveCommand; +if () then ([Provided index is valid]) + if () then ([Internship application is not archived]) + :Archive the specified application; + :Generate success message; + else ([else]) + :Generate error message; + endif + else ([else]) + :Generate error message; + endif +:Display result message to user; +stop +@enduml diff --git a/docs/diagrams/ArchiveSequenceDiagram.puml b/docs/diagrams/ArchiveSequenceDiagram.puml new file mode 100644 index 00000000000..57894cea8bd --- /dev/null +++ b/docs/diagrams/ArchiveSequenceDiagram.puml @@ -0,0 +1,67 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternEaseParser" as InternEaseParser LOGIC_COLOR +participant ":ArchiveCommandParser" as ArchiveCommandParser LOGIC_COLOR +participant "a:ArchiveCommand" as ArchiveCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute(archive) +activate LogicManager + +LogicManager -> InternEaseParser : parseCommand(archive) +activate InternEaseParser + +create ArchiveCommandParser +InternEaseParser -> ArchiveCommandParser +activate ArchiveCommandParser + +ArchiveCommandParser --> InternEaseParser +deactivate ArchiveCommandParser + +InternEaseParser -> ArchiveCommandParser : parse(args) +activate ArchiveCommandParser + +create ArchiveCommand +ArchiveCommandParser -> ArchiveCommand +activate ArchiveCommand + +ArchiveCommand --> ArchiveCommandParser : a +deactivate ArchiveCommand + +ArchiveCommandParser --> InternEaseParser : a +deactivate ArchiveCommandParser +ArchiveCommandParser -[hidden]-> InternEaseParser : result +destroy ArchiveCommandParser + +InternEaseParser --> LogicManager : a +deactivate InternEaseParser + +LogicManager -> ArchiveCommand : execute() +activate ArchiveCommand + +ArchiveCommand -> Model : setApplication(original, archived) +activate Model + +Model --> ArchiveCommand +deactivate Model + +create CommandResult +ArchiveCommand -> CommandResult +activate CommandResult + +CommandResult --> ArchiveCommand : r +deactivate CommandResult + +ArchiveCommand --> LogicManager : r +deactivate ArchiveCommand + +[<--LogicManager : r +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ClearByActivityDiagram.puml b/docs/diagrams/ClearByActivityDiagram.puml new file mode 100644 index 00000000000..29c3bd32561 --- /dev/null +++ b/docs/diagrams/ClearByActivityDiagram.puml @@ -0,0 +1,34 @@ +@startuml +start +:User enters ClearByCommand; +if () then ([PREFIX is provided]) + :Parse command arguments; + if () then ([PREFIX is n]) + :Generate new ClearByCommand of CompanyName; + : + Refer to activity in Group Company Name + ; + else ([else]) + if () then ([PREFIX is j]) + :Generate new ClearByCommand of JobTitle; + : + Refer to activity in Group Job Title + ; + else ([else]) + if () then ([PREFIX is s]) + :Generate new ClearByCommand of Status; + : + Refer to activity in Group Status + ; + else ([else]) + :Generate error message; + endif + endif + endif + else ([else]) + :Generate error message; +endif +:Display result message to user; +stop + +@enduml diff --git a/docs/diagrams/ClearBySequenceDiagram.puml b/docs/diagrams/ClearBySequenceDiagram.puml new file mode 100644 index 00000000000..e3e3fc85574 --- /dev/null +++ b/docs/diagrams/ClearBySequenceDiagram.puml @@ -0,0 +1,79 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternEaseParser" as InternEaseParser LOGIC_COLOR +participant ":ClearByCommandParser" as ClearByCommandParser LOGIC_COLOR +participant "c:ClearByCommand" as ClearByCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant "model:Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute("clear_by pf/ipt") +activate LogicManager + +note right: pf = prefix; ipt = input +LogicManager -> InternEaseParser : parseCommand("clear_by pf/ipt") +activate InternEaseParser + +create ClearByCommandParser +InternEaseParser -> ClearByCommandParser +activate ClearByCommandParser + +ClearByCommandParser --> InternEaseParser +deactivate ClearByCommandParser + +InternEaseParser -> ClearByCommandParser : parse("pf/ipt") +activate ClearByCommandParser + +create ClearByCommand +ClearByCommandParser -> ClearByCommand +activate ClearByCommand + +ClearByCommand --> ClearByCommandParser : c +deactivate ClearByCommand + +ClearByCommandParser --> InternEaseParser : c +deactivate ClearByCommandParser +ClearByCommandParser -[hidden]-> InternEaseParser : result +destroy ClearByCommandParser + +InternEaseParser --> LogicManager : c +deactivate InternEaseParser + +LogicManager -> ClearByCommand : execute(model) +activate ClearByCommand + +loop size of filteredList + +ClearByCommand -> Model : addInternshipToCache(application) +activate Model + +Model --> ClearByCommand +deactivate Model + +ClearByCommand -> Model : deleteInternship(application) +activate Model + +Model --> ClearByCommand +deactivate Model + +end + +create CommandResult +ClearByCommand -> CommandResult +activate CommandResult + +CommandResult --> ClearByCommand : r +deactivate CommandResult + +ClearByCommand --> LogicManager : r +deactivate ClearByCommand + + +[<--LogicManager : r +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ClearSequenceDiagram.puml b/docs/diagrams/ClearSequenceDiagram.puml new file mode 100644 index 00000000000..fdb268da8c1 --- /dev/null +++ b/docs/diagrams/ClearSequenceDiagram.puml @@ -0,0 +1,64 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternEaseParser" as InternEaseParser LOGIC_COLOR +participant "c:ClearCommand" as ClearCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant "model:Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute("clear") +activate LogicManager + +LogicManager -> InternEaseParser : parseCommand("clear") +activate InternEaseParser + +create ClearCommand +InternEaseParser -> ClearCommand +activate ClearCommand + +ClearCommand --> InternEaseParser : c +deactivate ClearCommand + +InternEaseParser --> LogicManager : c +deactivate InternEaseParser + +LogicManager -> ClearCommand : execute(model) +activate ClearCommand + +ClearCommand -> Model : getSortedFilteredInternshipList() +activate Model + +Model --> ClearCommand : lastShownList +deactivate Model + +ClearCommand -> Model : addAllInternshipToCache(lastShownList) +activate Model + +Model --> ClearCommand +deactivate Model + +ClearCommand -> Model : setInternEase(new) +activate Model + +Model --> ClearCommand +deactivate Model + +create CommandResult +ClearCommand -> CommandResult +activate CommandResult + +CommandResult --> ClearCommand : r +deactivate CommandResult + +ClearCommand --> LogicManager : r +deactivate ClearCommand + + +[<--LogicManager : r +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ClearTodoActivityDiagram.puml b/docs/diagrams/ClearTodoActivityDiagram.puml new file mode 100644 index 00000000000..de710a6595d --- /dev/null +++ b/docs/diagrams/ClearTodoActivityDiagram.puml @@ -0,0 +1,12 @@ +@startuml +start +:User enters ClearTodoCommand; +:Generate new ClearTodoCommand; + if () then ([TodoList is empty]) + :Generate null message; + else ([else]) + :Clear the TodoList and replace it with a new null TodoList; + :Generate success message; + endif +stop +@enduml diff --git a/docs/diagrams/DeleteContactActivityDiagram.puml b/docs/diagrams/DeleteContactActivityDiagram.puml new file mode 100644 index 00000000000..a821efa18a8 --- /dev/null +++ b/docs/diagrams/DeleteContactActivityDiagram.puml @@ -0,0 +1,13 @@ +@startuml +start +:User enters command delete_contact; +:Generate new DeleteContactCommand; +if () then ([Provided index is valid]) + :Delete contact details of the specified application; + :Generate success message; + else ([else]) + :Generate error message; + endif +:Display result message to user; +stop +@enduml diff --git a/docs/diagrams/DeleteContactSequenceDiagram.puml b/docs/diagrams/DeleteContactSequenceDiagram.puml new file mode 100644 index 00000000000..bef0847ce8f --- /dev/null +++ b/docs/diagrams/DeleteContactSequenceDiagram.puml @@ -0,0 +1,75 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternEaseParser" as InternEaseParser LOGIC_COLOR +participant ":ContactParser" as ContactParser LOGIC_COLOR +participant ":DeleteContactCommandParser" as DeleteContactCommandParser LOGIC_COLOR +participant "a:DeleteContactCommand" as DeleteContactCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute(delete_contact) +activate LogicManager + +LogicManager -> InternEaseParser : parseCommand(delete_contact) +activate InternEaseParser + +create ContactParser +InternEaseParser -> ContactParser : parseContactCommand(delete_contact, args) +activate ContactParser + +create DeleteContactCommandParser +ContactParser -> DeleteContactCommandParser +activate DeleteContactCommandParser + +DeleteContactCommandParser --> ContactParser +deactivate DeleteContactCommandParser + +ContactParser -> DeleteContactCommandParser : parse(args) +activate DeleteContactCommandParser + +create DeleteContactCommand +DeleteContactCommandParser -> DeleteContactCommand +activate DeleteContactCommand + +DeleteContactCommand --> DeleteContactCommandParser : d +deactivate DeleteContactCommand + +DeleteContactCommandParser --> ContactParser : d +deactivate DeleteContactCommandParser +DeleteContactCommandParser -[hidden]-> InternEaseParser : result +destroy DeleteContactCommandParser + +ContactParser --> InternEaseParser : d +deactivate ContactParser + +InternEaseParser --> LogicManager : d +deactivate InternEaseParser + +LogicManager -> DeleteContactCommand : execute() +activate DeleteContactCommand + +DeleteContactCommand -> Model : setApplication(original, contactDeleted) +activate Model + +Model --> DeleteContactCommand +deactivate Model + +create CommandResult +DeleteContactCommand -> CommandResult +activate CommandResult + +CommandResult --> DeleteContactCommand : r +deactivate CommandResult + +DeleteContactCommand --> LogicManager : r +deactivate DeleteContactCommand + +[<--LogicManager : r +deactivate LogicManager +@enduml diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml index 1dc2311b245..20e1f7b2134 100644 --- a/docs/diagrams/DeleteSequenceDiagram.puml +++ b/docs/diagrams/DeleteSequenceDiagram.puml @@ -3,52 +3,56 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":InternEaseParser" as InternEaseParser LOGIC_COLOR participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR -participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR -participant ":CommandResult" as CommandResult LOGIC_COLOR +participant "c:DeleteCommand" as DeleteCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR end box box Model MODEL_COLOR_T1 -participant ":Model" as Model MODEL_COLOR +participant "model:Model" as Model MODEL_COLOR end box - -[-> LogicManager : execute("delete 1") +[-> LogicManager : execute("delete INDEX") activate LogicManager -LogicManager -> AddressBookParser : parseCommand("delete 1") -activate AddressBookParser +LogicManager -> InternEaseParser : parseCommand("delete INDEX") +activate InternEaseParser create DeleteCommandParser -AddressBookParser -> DeleteCommandParser +InternEaseParser -> DeleteCommandParser activate DeleteCommandParser -DeleteCommandParser --> AddressBookParser +DeleteCommandParser --> InternEaseParser deactivate DeleteCommandParser -AddressBookParser -> DeleteCommandParser : parse("1") +InternEaseParser -> DeleteCommandParser : parse("INDEX") activate DeleteCommandParser create DeleteCommand DeleteCommandParser -> DeleteCommand activate DeleteCommand -DeleteCommand --> DeleteCommandParser : d +DeleteCommand --> DeleteCommandParser : c deactivate DeleteCommand -DeleteCommandParser --> AddressBookParser : d +DeleteCommandParser --> InternEaseParser : c deactivate DeleteCommandParser -'Hidden arrow to position the destroy marker below the end of the activation bar. -DeleteCommandParser -[hidden]-> AddressBookParser +DeleteCommandParser -[hidden]-> InternEaseParser : result destroy DeleteCommandParser -AddressBookParser --> LogicManager : d -deactivate AddressBookParser +InternEaseParser --> LogicManager : c +deactivate InternEaseParser -LogicManager -> DeleteCommand : execute() +LogicManager -> DeleteCommand : execute(model) activate DeleteCommand -DeleteCommand -> Model : deletePerson(1) +DeleteCommand -> Model : deleteInternship(internshipToDelete) +activate Model + +Model --> DeleteCommand +deactivate Model + +DeleteCommand -> Model : addInternshipToCache(internshipToDelete) activate Model Model --> DeleteCommand @@ -58,12 +62,13 @@ create CommandResult DeleteCommand -> CommandResult activate CommandResult -CommandResult --> DeleteCommand +CommandResult --> DeleteCommand : r deactivate CommandResult -DeleteCommand --> LogicManager : result +DeleteCommand --> LogicManager : r deactivate DeleteCommand -[<--LogicManager + +[<--LogicManager : r deactivate LogicManager @enduml diff --git a/docs/diagrams/DeleteTodoActivityDiagram.puml b/docs/diagrams/DeleteTodoActivityDiagram.puml new file mode 100644 index 00000000000..9edd18a98a7 --- /dev/null +++ b/docs/diagrams/DeleteTodoActivityDiagram.puml @@ -0,0 +1,12 @@ +@startuml +start +:User enters DeleteTodoCommand; +:Generate new DeleteTodoCommand; + if () then ([INDEX is valid]) + :Delete the Todo-task at the specified position; + :Generate success message; + else ([else]) + :Generate invalid Index alert message; + endif +stop +@enduml diff --git a/docs/diagrams/EditContactActivityDiagram.puml b/docs/diagrams/EditContactActivityDiagram.puml new file mode 100644 index 00000000000..de1ae309516 --- /dev/null +++ b/docs/diagrams/EditContactActivityDiagram.puml @@ -0,0 +1,22 @@ +@startuml +start +:User enters command edit_contact; +if () then ([PREFIX p/ and/or e/ is present]) + :Parse command arguments; + if () then ([Provided argument(s) is/are valid]) + :Generate new EditContactCommand; + if () then ([Provided index is valid]) + :Edit contact details of the specified application; + :Generate success message; + else ([else]) + :Generate error message; + endif + else ([else]) + :Generate error message; + endif + else ([else]) + :Generate error message; +endif +:Display result message to user; +stop +@enduml diff --git a/docs/diagrams/EditContactSequenceDiagram.puml b/docs/diagrams/EditContactSequenceDiagram.puml new file mode 100644 index 00000000000..fa1c848160c --- /dev/null +++ b/docs/diagrams/EditContactSequenceDiagram.puml @@ -0,0 +1,75 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternEaseParser" as InternEaseParser LOGIC_COLOR +participant ":ContactParser" as ContactParser LOGIC_COLOR +participant ":EditContactCommandParser" as EditContactCommandParser LOGIC_COLOR +participant "a:EditContactCommand" as EditContactCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute(edit_contact) +activate LogicManager + +LogicManager -> InternEaseParser : parseCommand(edit_contact) +activate InternEaseParser + +create ContactParser +InternEaseParser -> ContactParser : parseContactCommand(edit_contact, args) +activate ContactParser + +create EditContactCommandParser +ContactParser -> EditContactCommandParser +activate EditContactCommandParser + +EditContactCommandParser --> ContactParser +deactivate EditContactCommandParser + +ContactParser -> EditContactCommandParser : parse(args) +activate EditContactCommandParser + +create EditContactCommand +EditContactCommandParser -> EditContactCommand +activate EditContactCommand + +EditContactCommand --> EditContactCommandParser : e +deactivate EditContactCommand + +EditContactCommandParser --> ContactParser : e +deactivate EditContactCommandParser +EditContactCommandParser -[hidden]-> InternEaseParser : result +destroy EditContactCommandParser + +ContactParser --> InternEaseParser : e +deactivate ContactParser + +InternEaseParser --> LogicManager : e +deactivate InternEaseParser + +LogicManager -> EditContactCommand : execute() +activate EditContactCommand + +EditContactCommand -> Model : setApplication(original, contactEdited) +activate Model + +Model --> EditContactCommand +deactivate Model + +create CommandResult +EditContactCommand -> CommandResult +activate CommandResult + +CommandResult --> EditContactCommand : r +deactivate CommandResult + +EditContactCommand --> LogicManager : r +deactivate EditContactCommand + +[<--LogicManager : r +deactivate LogicManager +@enduml diff --git a/docs/diagrams/EditDeadlineSequenceDiagram.puml b/docs/diagrams/EditDeadlineSequenceDiagram.puml new file mode 100644 index 00000000000..2f1709f2da3 --- /dev/null +++ b/docs/diagrams/EditDeadlineSequenceDiagram.puml @@ -0,0 +1,83 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternEaseParser" as InternEaseParser LOGIC_COLOR +participant ":TaskParser" as TaskParser LOGIC_COLOR +participant ":EditDeadlineCommandParser" as EditDeadlineCommandParser LOGIC_COLOR +participant "c:EditDeadlineCommand" as EditDeadlineCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant "model:Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute(input) +activate LogicManager + +note right: input = "edit_deadline 1 by/newDate" \n cmd = "edit_deadline" \n args = "1 by/dt" +LogicManager -> InternEaseParser : parseCommand(input) +activate InternEaseParser + +InternEaseParser -> TaskParser : parseTaskCommand(cmd, args) +activate TaskParser + +create EditDeadlineCommandParser +TaskParser -> EditDeadlineCommandParser +activate EditDeadlineCommandParser + +EditDeadlineCommandParser --> TaskParser +deactivate EditDeadlineCommandParser + +TaskParser -> EditDeadlineCommandParser : parse(1, newDate) +activate EditDeadlineCommandParser + +create EditDeadlineCommand +EditDeadlineCommandParser -> EditDeadlineCommand +activate EditDeadlineCommand + +EditDeadlineCommand --> EditDeadlineCommandParser : c +deactivate EditDeadlineCommand + +EditDeadlineCommandParser --> TaskParser : c +deactivate EditDeadlineCommandParser +EditDeadlineCommandParser -[hidden]-> InternEaseParser : result +destroy EditDeadlineCommandParser + +TaskParser --> InternEaseParser : c +deactivate TaskParser + +InternEaseParser --> LogicManager : c +deactivate InternEaseParser + +LogicManager -> EditDeadlineCommand : execute(model) +activate EditDeadlineCommand + +EditDeadlineCommand -> Model : setTodo(original, updated) +activate Model + +Model --> EditDeadlineCommand +deactivate Model + +EditDeadlineCommand -> Model : updateFilteredTodoList() +activate Model + +Model --> EditDeadlineCommand +deactivate Model + +create CommandResult +EditDeadlineCommand -> CommandResult +activate CommandResult + +CommandResult --> EditDeadlineCommand : r +deactivate CommandResult + +EditDeadlineCommand --> LogicManager : r +deactivate EditDeadlineCommand + + +[<--LogicManager : r +deactivate LogicManager +@enduml diff --git a/docs/diagrams/EditNoteContentSequenceDiagram.puml b/docs/diagrams/EditNoteContentSequenceDiagram.puml new file mode 100644 index 00000000000..895d66bf6e6 --- /dev/null +++ b/docs/diagrams/EditNoteContentSequenceDiagram.puml @@ -0,0 +1,82 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternEaseParser" as InternEaseParser LOGIC_COLOR +participant ":TaskParser" as TaskParser LOGIC_COLOR +participant ":EditNoteContentCommandParser" as EditNoteContentCommandParser LOGIC_COLOR +participant "c:EditNoteContentCommand" as EditNoteContentCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant "model:Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute(input) +activate LogicManager + +note right: nc = new content \n input = "edit_content 1 c/nc" \n cmd = "edit_content" , args = "1 c/nc" +LogicManager -> InternEaseParser : parseCommand(input) +activate InternEaseParser + +InternEaseParser -> TaskParser : parseTaskCommand(cmd, args) +activate TaskParser + +create EditNoteContentCommandParser +TaskParser -> EditNoteContentCommandParser +activate EditNoteContentCommandParser + +EditNoteContentCommandParser --> TaskParser +deactivate EditNoteContentCommandParser + +TaskParser -> EditNoteContentCommandParser : parse(1, nc) +activate EditNoteContentCommandParser + +create EditNoteContentCommand +EditNoteContentCommandParser -> EditNoteContentCommand +activate EditNoteContentCommand + +EditNoteContentCommand --> EditNoteContentCommandParser : c +deactivate EditNoteContentCommand + +EditNoteContentCommandParser --> TaskParser : c +deactivate EditNoteContentCommandParser +EditNoteContentCommandParser -[hidden]-> InternEaseParser : result +destroy EditNoteContentCommandParser + +TaskParser --> InternEaseParser : c +deactivate TaskParser + +InternEaseParser --> LogicManager : c +deactivate InternEaseParser + +LogicManager -> EditNoteContentCommand : execute(model) +activate EditNoteContentCommand +EditNoteContentCommand -> Model : setTodo(original, updated) +activate Model + +Model --> EditNoteContentCommand +deactivate Model + +EditNoteContentCommand -> Model : updateFilteredTodoList() +activate Model + +Model --> EditNoteContentCommand +deactivate Model + +create CommandResult +EditNoteContentCommand -> CommandResult +activate CommandResult + +CommandResult --> EditNoteContentCommand : r +deactivate CommandResult + +EditNoteContentCommand --> LogicManager : r +deactivate EditNoteContentCommand + + +[<--LogicManager : r +deactivate LogicManager +@enduml diff --git a/docs/diagrams/EditSequenceDiagram.puml b/docs/diagrams/EditSequenceDiagram.puml new file mode 100644 index 00000000000..48f779dc15b --- /dev/null +++ b/docs/diagrams/EditSequenceDiagram.puml @@ -0,0 +1,62 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternEaseParser" as InternEaseParser LOGIC_COLOR +participant ":EditCommandParser" as EditCommandParser LOGIC_COLOR +participant "e:EditCommand" as EditCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute(edit) +activate LogicManager + +LogicManager -> InternEaseParser: parseCommand(edit) +activate InternEaseParser + +create EditCommandParser +InternEaseParser -> EditCommandParser : EditCommandParser() +activate EditCommandParser + +EditCommandParser --> InternEaseParser +deactivate EditCommandParser + +InternEaseParser -> EditCommandParser : parse(args) +activate EditCommandParser +create EditCommand +EditCommandParser -> EditCommand : EditCommand(index, editInternshipDescriptor) +activate EditCommand + +EditCommand --> EditCommandParser +deactivate EditCommand + +EditCommandParser --> InternEaseParser : e +deactivate EditCommandParser +EditCommandParser -[hidden]--> InternEaseParser : e +destroy EditCommandParser + +InternEaseParser --> LogicManager : e +deactivate InternEaseParser + +LogicManager -> EditCommand : execute() +activate EditCommand + +EditCommand -> Model : setApplication(internshipToEdit, editedInternship) +activate Model + +Model --> EditCommand +deactivate Model + +EditCommand --> LogicManager : result +deactivate EditCommand +EditCommand -[hidden]--> LogicManager +destroy EditCommand + +<-- LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/EditStatusActivityDiagram.puml b/docs/diagrams/EditStatusActivityDiagram.puml new file mode 100644 index 00000000000..a96e06ef036 --- /dev/null +++ b/docs/diagrams/EditStatusActivityDiagram.puml @@ -0,0 +1,22 @@ +@startuml +start +:User enters command edit_status; +if () then ([PREFIX s/ is present]) + :Parse command arguments; + if () then ([Provided status is valid]) + :Generate new EditStatusCommand; + if () then ([Provided index is valid]) + :Edit status of the specified application; + :Generate success message; + else ([else]) + :Generate error message; + endif + else ([else]) + :Generate error message; + endif + else ([else]) + :Generate error message; +endif +:Display result message to user; +stop +@enduml diff --git a/docs/diagrams/EditStatusSequenceDiagram.puml b/docs/diagrams/EditStatusSequenceDiagram.puml new file mode 100644 index 00000000000..81d3ddeb68f --- /dev/null +++ b/docs/diagrams/EditStatusSequenceDiagram.puml @@ -0,0 +1,67 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternEaseParser" as InternEaseParser LOGIC_COLOR +participant ":EditStatusCommandParser" as EditStatusCommandParser LOGIC_COLOR +participant "a:EditStatusCommand" as EditStatusCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute(edit_contact) +activate LogicManager + +LogicManager -> InternEaseParser : parseCommand(edit_contact) +activate InternEaseParser + +create EditStatusCommandParser +InternEaseParser -> EditStatusCommandParser +activate EditStatusCommandParser + +EditStatusCommandParser --> InternEaseParser +deactivate EditStatusCommandParser + +InternEaseParser -> EditStatusCommandParser : parse(args) +activate EditStatusCommandParser + +create EditStatusCommand +EditStatusCommandParser -> EditStatusCommand +activate EditStatusCommand + +EditStatusCommand --> EditStatusCommandParser : e +deactivate EditStatusCommand + +EditStatusCommandParser --> InternEaseParser : e +deactivate EditStatusCommandParser +EditStatusCommandParser -[hidden]-> InternEaseParser : result +destroy EditStatusCommandParser + +InternEaseParser --> LogicManager : e +deactivate InternEaseParser + +LogicManager -> EditStatusCommand : execute() +activate EditStatusCommand + +EditStatusCommand -> Model : setApplication(original, statusEdited) +activate Model + +Model --> EditStatusCommand +deactivate Model + +create CommandResult +EditStatusCommand -> CommandResult +activate CommandResult + +CommandResult --> EditStatusCommand : r +deactivate CommandResult + +EditStatusCommand --> LogicManager : r +deactivate EditStatusCommand + +[<--LogicManager : r +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ExitSequenceDiagram.puml b/docs/diagrams/ExitSequenceDiagram.puml new file mode 100644 index 00000000000..7ad6939b72b --- /dev/null +++ b/docs/diagrams/ExitSequenceDiagram.puml @@ -0,0 +1,42 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternEaseParser" as InternEaseParser LOGIC_COLOR +participant "c:ExitCommand" as ExitCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR +end box + +[-> LogicManager : execute("exit") +activate LogicManager + +LogicManager -> InternEaseParser : parseCommand("exit") +activate InternEaseParser + +create ExitCommand +InternEaseParser -> ExitCommand +activate ExitCommand + +ExitCommand --> InternEaseParser : c +deactivate ExitCommand + +InternEaseParser --> LogicManager : c +deactivate InternEaseParser + +LogicManager -> ExitCommand : execute(model) +activate ExitCommand + +create CommandResult +ExitCommand -> CommandResult +activate CommandResult + +CommandResult --> ExitCommand : r +deactivate CommandResult + +ExitCommand --> LogicManager : r +deactivate ExitCommand + +[<--LogicManager : r +deactivate LogicManager +@enduml diff --git a/docs/diagrams/FindFeatureClassDiagram.puml b/docs/diagrams/FindFeatureClassDiagram.puml new file mode 100644 index 00000000000..a43aaa6c5f5 --- /dev/null +++ b/docs/diagrams/FindFeatureClassDiagram.puml @@ -0,0 +1,42 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor LOGIC_COLOR_T4 +skinparam classBackgroundColor LOGIC_COLOR + +package Logic { + +Class FindCommandParser +Class FindCommand +Class FindStatusCommand +Class FindDateCommand +Class "{abstract}\nCommand" as Command +} + +package Model{ + +Class NameContainsKeywordsPredicate MODEL_COLOR +Class StatusPredicate MODEL_COLOR +Class "{abstract}\nDatePredicate" as DatePredicate MODEL_COLOR +Class BeforeDatePredicate MODEL_COLOR +Class AfterDatePredicate MODEL_COLOR +Class BetweenDatePredicate MODEL_COLOR +} + +FindCommand -up-|> Command +FindDateCommand -up[LOGIC_COLOR_T4]-|> FindCommand +FindStatusCommand -up[LOGIC_COLOR_T4]-|> FindCommand +FindCommandParser .up.> FindCommand +FindCommandParser .up.> FindDateCommand +FindCommandParser .up.> FindStatusCommand + +BeforeDatePredicate -up[MODEL_COLOR]--|> DatePredicate +AfterDatePredicate -up[MODEL_COLOR]--|> DatePredicate +BetweenDatePredicate --up[MODEL_COLOR]--|> DatePredicate + +FindCommand -left->"1" NameContainsKeywordsPredicate +FindDateCommand -left->"1" DatePredicate +FindStatusCommand -left->"1" StatusPredicate + +Command ..> Model +@enduml diff --git a/docs/diagrams/FindSequenceDiagram.puml b/docs/diagrams/FindSequenceDiagram.puml new file mode 100644 index 00000000000..9e689c8eb6f --- /dev/null +++ b/docs/diagrams/FindSequenceDiagram.puml @@ -0,0 +1,77 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":FindCommandParser" as FindCommandParser LOGIC_COLOR +participant "p:NameContainsKeywordsPredicate" as NameContainsKeywordsPredicate LOGIC_COLOR +participant "f:FindCommand" as FindCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("find google") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("find google") +activate AddressBookParser + +create FindCommandParser +AddressBookParser -> FindCommandParser +activate FindCommandParser + +FindCommandParser --> AddressBookParser +deactivate FindCommandParser + +AddressBookParser -> FindCommandParser : parse("google") +activate FindCommandParser + +create NameContainsKeywordsPredicate +FindCommandParser -> NameContainsKeywordsPredicate +activate NameContainsKeywordsPredicate + +NameContainsKeywordsPredicate --> FindCommandParser +deactivate NameContainsKeywordsPredicate + +create FindCommand +FindCommandParser -> FindCommand : FindCommand(p) +activate FindCommand + +FindCommand --> FindCommandParser : f +deactivate FindCommand + +FindCommandParser --> AddressBookParser : f +deactivate FindCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +FindCommandParser -[hidden]-> AddressBookParser +destroy FindCommandParser + +AddressBookParser --> LogicManager : f +deactivate AddressBookParser + +LogicManager -> FindCommand : execute() +activate FindCommand + +FindCommand -> Model : updateFilteredInternshipList(p) +activate Model + +Model --> FindCommand +deactivate Model + +create CommandResult +FindCommand -> CommandResult +activate CommandResult + +CommandResult --> FindCommand +deactivate CommandResult + +FindCommand --> LogicManager : result +deactivate FindCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/FindTaskSequenceDiagram.puml b/docs/diagrams/FindTaskSequenceDiagram.puml new file mode 100644 index 00000000000..85596d38937 --- /dev/null +++ b/docs/diagrams/FindTaskSequenceDiagram.puml @@ -0,0 +1,83 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternEaseParser" as InternEaseParser LOGIC_COLOR +participant ":TaskParser" as TaskParser LOGIC_COLOR +participant ":FindTaskCommandParser" as FindTaskCommandParser LOGIC_COLOR +participant "c:FindTaskCommand" as FindTaskCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant "model:Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute(input) +activate LogicManager + +note right: kw = keyword \n input = "find_task kw" \n cmd = "find_task" +LogicManager -> InternEaseParser : parseCommand(input) +activate InternEaseParser + +InternEaseParser -> TaskParser : parseTaskCommand(cmd, kw) +activate TaskParser + +create FindTaskCommandParser +TaskParser -> FindTaskCommandParser +activate FindTaskCommandParser + +FindTaskCommandParser --> TaskParser +deactivate FindTaskCommandParser + +TaskParser -> FindTaskCommandParser : parse("kw") +activate FindTaskCommandParser + +create FindTaskCommand +FindTaskCommandParser -> FindTaskCommand +activate FindTaskCommand + +FindTaskCommand --> FindTaskCommandParser : c +deactivate FindTaskCommand + +FindTaskCommandParser --> TaskParser : c +deactivate FindTaskCommandParser +FindTaskCommandParser -[hidden]-> InternEaseParser : result +destroy FindTaskCommandParser + +TaskParser --> InternEaseParser : c +deactivate TaskParser + +InternEaseParser --> LogicManager : c +deactivate InternEaseParser + +LogicManager -> FindTaskCommand : execute(model) +activate FindTaskCommand + +FindTaskCommand -> Model : updateFilteredTodoList() +activate Model + +Model --> FindTaskCommand +deactivate Model + +FindTaskCommand -> Model : updateFilteredNoteList() +activate Model + +Model --> FindTaskCommand +deactivate Model + +create CommandResult +FindTaskCommand -> CommandResult +activate CommandResult + +CommandResult --> FindTaskCommand : r +deactivate CommandResult + +FindTaskCommand --> LogicManager : r +deactivate FindTaskCommand + + +[<--LogicManager : r +deactivate LogicManager +@enduml diff --git a/docs/diagrams/GroupCompanyName.puml b/docs/diagrams/GroupCompanyName.puml new file mode 100644 index 00000000000..d558d082a92 --- /dev/null +++ b/docs/diagrams/GroupCompanyName.puml @@ -0,0 +1,10 @@ +@startuml +start +if () then ([Internship application list is empty]) +:Generate null list message; +else ([else]) +:Clear all internship applications with keyword for COMPANY_NAME; +:Generate success message; +endif +stop +@enduml diff --git a/docs/diagrams/GroupJobTitle.puml b/docs/diagrams/GroupJobTitle.puml new file mode 100644 index 00000000000..3a5b6cf414a --- /dev/null +++ b/docs/diagrams/GroupJobTitle.puml @@ -0,0 +1,10 @@ +@startuml +start +if () then ([Internship application list is empty]) +:Generate null list message; +else ([else]) +:Clear all internship applications with keyword for JOB_TITLE; +:Generate success message; +endif +stop +@enduml diff --git a/docs/diagrams/GroupStatus.puml b/docs/diagrams/GroupStatus.puml new file mode 100644 index 00000000000..c0cf0b8280c --- /dev/null +++ b/docs/diagrams/GroupStatus.puml @@ -0,0 +1,10 @@ +@startuml +start +if () then ([Internship application list is empty]) +:Generate null list message; +else ([else]) +:Clear all internship applications with keyword for STATUS; +:Generate success message; +endif +stop +@enduml diff --git a/docs/diagrams/ListActivityDiagram.puml b/docs/diagrams/ListActivityDiagram.puml new file mode 100644 index 00000000000..627fe8df166 --- /dev/null +++ b/docs/diagrams/ListActivityDiagram.puml @@ -0,0 +1,13 @@ +@startuml +start +:User enters command list; +:Generate new ListCommand; +:Update the list filteredInternshipList to show all ongoing applications; + if () then ([filteredInternshipList is not empty]) + :Generate success message; + else ([else]) + :Generate no active applications message; + endif +stop + +@enduml diff --git a/docs/diagrams/ListArchivedActivityDiagram.puml b/docs/diagrams/ListArchivedActivityDiagram.puml new file mode 100644 index 00000000000..533fdd8ed8b --- /dev/null +++ b/docs/diagrams/ListArchivedActivityDiagram.puml @@ -0,0 +1,13 @@ +@startuml +start +:User enters command list_archived; +:Generate new ListArchivedCommand; +:Update the list filteredInternships to show only archived entries; + if () then ([filteredInternships is not empty]) + :Generate success message; + else ([else]) + :Generate error message; + endif +stop + +@enduml diff --git a/docs/diagrams/ListTaskActivityDiagram.puml b/docs/diagrams/ListTaskActivityDiagram.puml new file mode 100644 index 00000000000..c50c5c9a4fc --- /dev/null +++ b/docs/diagrams/ListTaskActivityDiagram.puml @@ -0,0 +1,13 @@ +@startuml +start +:User enters ListTaskCommand; +:Generate new ListTaskCommand; +:Update filtered TodoList and filtered NoteList to show all entries; + if () then ([TodoList and NoteList are both empty]) + :Generate null message; + else ([else]) + :Generate success message; + endif +stop + +@enduml diff --git a/docs/diagrams/ListTodoActivityDiagram.puml b/docs/diagrams/ListTodoActivityDiagram.puml new file mode 100644 index 00000000000..05ad20efede --- /dev/null +++ b/docs/diagrams/ListTodoActivityDiagram.puml @@ -0,0 +1,13 @@ +@startuml +start +:User enters ListTodoCommand; +:Generate new ListTodoCommand; +:Update filtered TodoList to show all entries; + if () then ([TodoList is empty]) + :Generate null message; + else ([else]) + :Generate success message; + endif +stop + +@enduml diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml index d4193173e18..05e1971b618 100644 --- a/docs/diagrams/LogicClassDiagram.puml +++ b/docs/diagrams/LogicClassDiagram.puml @@ -6,7 +6,7 @@ skinparam classBackgroundColor LOGIC_COLOR package Logic { -Class AddressBookParser +Class InternEaseParser Class XYZCommand Class CommandResult Class "{abstract}\nCommand" as Command @@ -27,8 +27,8 @@ Class HiddenOutside #FFFFFF HiddenOutside ..> Logic LogicManager .right.|> Logic -LogicManager -right->"1" AddressBookParser -AddressBookParser ..> XYZCommand : creates > +LogicManager -right->"1" InternEaseParser +InternEaseParser ..> XYZCommand : creates > XYZCommand -up-|> Command LogicManager .left.> Command : executes > diff --git a/docs/diagrams/MixedPanelRef.puml b/docs/diagrams/MixedPanelRef.puml new file mode 100644 index 00000000000..ae96c8afd18 --- /dev/null +++ b/docs/diagrams/MixedPanelRef.puml @@ -0,0 +1,35 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor UI_COLOR_T4 +skinparam classBackgroundColor UI_COLOR + +package UI <>{ +Class "{abstract}\nUiPart" as UiPart +Class TodoListPanel +Class NoteListPanel +Class MixedPanel #CF2765 +Class TodoCard +Class NoteCard +} + +package Model <> { +Class HiddenModel #FFFFFF +} + +MixedPanel --|> UiPart +TodoListPanel --|> UiPart +NoteListPanel --|> UiPart +TodoCard --|> UiPart +NoteCard --|> UiPart + +MixedPanel -down-> "1" TodoListPanel +MixedPanel -down-> "1" NoteListPanel +TodoListPanel -down-> TodoCard +NoteListPanel -down-> NoteCard + +TodoCard .right.> Model +NoteCard .right.> Model +NoteCard -[hidden]right- TodoCard + +@enduml diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 4439108973a..aedb39a9b79 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -4,7 +4,7 @@ skinparam arrowThickness 1.1 skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR -Package Model <>{ +Package "Model classes" <>{ Class "<>\nReadOnlyAddressBook" as ReadOnlyAddressBook Class "<>\nReadOnlyUserPrefs" as ReadOnlyUserPrefs Class "<>\nModel" as Model @@ -12,13 +12,22 @@ Class AddressBook Class ModelManager Class UserPrefs -Class UniquePersonList -Class Person -Class Address -Class Email -Class Name -Class Phone -Class Tag +Class UniqueApplicationList +Class InternshipApplication +Class CompanyName +Class JobTitle +Class Review +Class ProgrammingLanguage +Class Qualification +Class Location +Class Salary +Class Note +Class Rating +Class Reflection +Class InterviewDate +Class InternshipStatus +Class Contact +Class Documents } @@ -34,17 +43,22 @@ ModelManager -left-> "1" AddressBook ModelManager -right-> "1" UserPrefs UserPrefs .up.|> ReadOnlyUserPrefs -AddressBook *--> "1" UniquePersonList -UniquePersonList --> "~* all" Person -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address -Person *--> "*" Tag +AddressBook *--> "1" UniqueApplicationList +UniqueApplicationList --> "~* all" InternshipApplication +InternshipApplication *--> CompanyName +InternshipApplication *--> JobTitle +InternshipApplication *--> "*" Review +InternshipApplication *--> "*" ProgrammingLanguage +InternshipApplication *--> "*" Qualification +InternshipApplication *--> Location +InternshipApplication *--> Salary +InternshipApplication *--> "*" Note +InternshipApplication *--> Rating +InternshipApplication *--> "*" Reflection +InternshipApplication *--> InterviewDate +InternshipApplication *--> InternshipStatus +InternshipApplication *--> Contact +InternshipApplication *--> Documents -Name -[hidden]right-> Phone -Phone -[hidden]right-> Address -Address -[hidden]right-> Email - -ModelManager -->"~* filtered" Person +ModelManager -->"~* filtered" InternshipApplication @enduml diff --git a/docs/diagrams/NoteOverviewDiagram.puml b/docs/diagrams/NoteOverviewDiagram.puml new file mode 100644 index 00000000000..59b16b290dd --- /dev/null +++ b/docs/diagrams/NoteOverviewDiagram.puml @@ -0,0 +1,49 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor LOGIC_COLOR_T4 +skinparam classBackgroundColor LOGIC_COLOR + +package Logic { + +Class AddNoteCommandParser +Class DeleteNoteCommandParser +Class AddNoteCommand +Class ClearNoteCommand +Class DeleteNoteCommand +Class ListNoteCommand +Class "{abstract}\nCommand" as Command +} + +package Model{ + +Class NoteList MODEL_COLOR +Class UniqueNoteList MODEL_COLOR +Class "{abstract}\nReadOnlyNoteList" as ReadOnlyNoteList MODEL_COLOR +Class Note MODEL_COLOR +Class NoteContent MODEL_COLOR +Class "<>\nTaskType" as TaskType MODEL_COLOR +} + +AddNoteCommand -up-|> Command +ClearNoteCommand -up-|> Command +DeleteNoteCommand -up-|> Command +ListNoteCommand -up-|> Command +AddNoteCommandParser .up.> AddNoteCommand +DeleteNoteCommandParser .up.> DeleteNoteCommand + +AddNoteCommand .up.> NoteList +ClearNoteCommand .up.> NoteList +DeleteNoteCommand .up.> NoteList +ListNoteCommand .up.> NoteList + +NoteList -up-|> ReadOnlyNoteList +NoteList *--> UniqueNoteList +NoteList *--> Note +Note *--> NoteContent +Note *--> TaskType + +AddNoteCommand -left->"1" Note + +Command ..> Model +@enduml diff --git a/docs/diagrams/PanelsRef.puml b/docs/diagrams/PanelsRef.puml new file mode 100644 index 00000000000..f8facf5e17c --- /dev/null +++ b/docs/diagrams/PanelsRef.puml @@ -0,0 +1,36 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor UI_COLOR_T4 +skinparam classBackgroundColor UI_COLOR + +package Model <> { +Class HiddenModel #FFFFFF +} + +package Panels <> { +Class "{abstract}\nUiPart" as UiPart +Class MixedPanel #CF2765 +Class ApplicationListPanel #CF2765 +Class StatsInformationListPanel #CF2765 +Class StatsInformationCard #CF2765 +Class ViewContentPanel #CF2765 +Class MainWindow +} + +MainWindow *-down-> "1" ApplicationListPanel +MainWindow *-down-> "1" StatsInformationListPanel +MainWindow *-down-> "1" MixedPanel +MainWindow *-down-> "1" ViewContentPanel + +MixedPanel --|> UiPart +ApplicationListPanel --|> UiPart +StatsInformationListPanel --|> UiPart +StatsInformationCard --|> UiPart +ViewContentPanel --|> UiPart + +StatsInformationListPanel -down-> StatsInformationCard + +StatsInformationCard .right.> Model + +@enduml diff --git a/docs/diagrams/ParserClasses.puml b/docs/diagrams/ParserClasses.puml index 0c7424de6e0..79e6f70aa39 100644 --- a/docs/diagrams/ParserClasses.puml +++ b/docs/diagrams/ParserClasses.puml @@ -9,7 +9,7 @@ Class XYZCommand package "Parser classes"{ Class "<>\nParser" as Parser -Class AddressBookParser +Class InternEaseParser Class XYZCommandParser Class CliSyntax Class ParserUtil @@ -19,12 +19,12 @@ Class Prefix } Class HiddenOutside #FFFFFF -HiddenOutside ..> AddressBookParser +HiddenOutside ..> InternEaseParser -AddressBookParser .down.> XYZCommandParser: creates > +InternEaseParser .down.> XYZCommandParser: creates > XYZCommandParser ..> XYZCommand : creates > -AddressBookParser ..> Command : returns > +InternEaseParser ..> Command : returns > XYZCommandParser .up.|> Parser XYZCommandParser ..> ArgumentMultimap XYZCommandParser ..> ArgumentTokenizer diff --git a/docs/diagrams/RevertActivityDiagram.puml b/docs/diagrams/RevertActivityDiagram.puml new file mode 100644 index 00000000000..f8019ed257d --- /dev/null +++ b/docs/diagrams/RevertActivityDiagram.puml @@ -0,0 +1,17 @@ +@startuml +start +:User enters RevertCommand; +:Generate new RevertCommand; + if () then ([CacheList is not empty]) + :Remove the most recent deleted application from the cacheList; + if () then ([The retrieved application is not in current application list]) + :Add the retrieved application to the end of the current application list; + else ([else]) + :Generate duplicate internship applications alert; + endif + else ([else]) + :Generate null cacheList message; + endif +stop + +@enduml diff --git a/docs/diagrams/RevertAllSequenceDiagram.puml b/docs/diagrams/RevertAllSequenceDiagram.puml new file mode 100644 index 00000000000..dc38a56c440 --- /dev/null +++ b/docs/diagrams/RevertAllSequenceDiagram.puml @@ -0,0 +1,64 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternEaseParser" as InternEaseParser LOGIC_COLOR +participant "c:RevertAllCommand" as RevertAllCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant "model:Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("clear") +activate LogicManager + +LogicManager -> InternEaseParser : parseCommand("clear") +activate InternEaseParser + +create RevertAllCommand +InternEaseParser -> RevertAllCommand +activate RevertAllCommand + +RevertAllCommand --> InternEaseParser : c +deactivate RevertAllCommand + +InternEaseParser --> LogicManager : c +deactivate InternEaseParser + +LogicManager -> RevertAllCommand : execute(model) +activate RevertAllCommand + +RevertAllCommand -> Model : getCachedInternshipList() +activate Model + +Model --> RevertAllCommand : cacheList +deactivate Model + +RevertAllCommand -> Model : setEmptyInternshipCacheList() +activate Model + +Model --> RevertAllCommand +deactivate Model + +RevertAllCommand -> Model : addApplications(cacheList) +activate Model + +Model --> RevertAllCommand +deactivate Model + +create CommandResult +RevertAllCommand -> CommandResult +activate CommandResult + +CommandResult --> RevertAllCommand : r +deactivate CommandResult + +RevertAllCommand --> LogicManager : r +deactivate RevertAllCommand + +[<--LogicManager : r +deactivate LogicManager +@enduml diff --git a/docs/diagrams/SortSequenceDiagram.puml b/docs/diagrams/SortSequenceDiagram.puml new file mode 100644 index 00000000000..031177fccce --- /dev/null +++ b/docs/diagrams/SortSequenceDiagram.puml @@ -0,0 +1,82 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":SortCommandParser" as SortCommandParser LOGIC_COLOR +participant "cmp:InterviewDateComparator" as InterviewDateComparator LOGIC_COLOR +participant "s:SortCommand" as SortCommand LOGIC_COLOR +participant "result:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("sort d/") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("sort d/") +activate AddressBookParser + +create SortCommandParser +AddressBookParser -> SortCommandParser +activate SortCommandParser + +SortCommandParser --> AddressBookParser +deactivate SortCommandParser + +AddressBookParser -> SortCommandParser : parse("d/") +activate SortCommandParser + +create InterviewDateComparator +SortCommandParser -> InterviewDateComparator +activate InterviewDateComparator + +InterviewDateComparator --> SortCommandParser +deactivate InterviewDateComparator + +create SortCommand +SortCommandParser -> SortCommand : SortCommand(cmp) +activate SortCommand + +SortCommand --> SortCommandParser : s +deactivate SortCommand + +SortCommandParser --> AddressBookParser : s +deactivate SortCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +SortCommandParser -[hidden]-> AddressBookParser +destroy SortCommandParser + +AddressBookParser --> LogicManager : s +deactivate AddressBookParser + +LogicManager -> SortCommand : execute() +activate SortCommand + +SortCommand -> Model : updateSortedFilteredInternshipList(cmp) +activate Model + +Model --> SortCommand +deactivate Model + +create CommandResult +SortCommand -> CommandResult +activate CommandResult + +CommandResult --> SortCommand +deactivate CommandResult + +SortCommand --> LogicManager : result +deactivate SortCommand + +'Hidden arrow to position the destroy marker below the end of the activation bar. +SortCommand -[hidden]-> LogicManager +destroy SortCommand + + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml index 760305e0e58..ad6371432ca 100644 --- a/docs/diagrams/StorageClassDiagram.puml +++ b/docs/diagrams/StorageClassDiagram.puml @@ -4,7 +4,7 @@ skinparam arrowThickness 1.1 skinparam arrowColor STORAGE_COLOR skinparam classBackgroundColor STORAGE_COLOR -package Storage{ +package "Storage classes" { package "UserPrefs Storage" #F4F6F6{ Class "<>\nUserPrefsStorage" as UserPrefsStorage @@ -18,8 +18,7 @@ package "AddressBook Storage" #F4F6F6{ Class "<>\nAddressBookStorage" as AddressBookStorage Class JsonAddressBookStorage Class JsonSerializableAddressBook -Class JsonAdaptedPerson -Class JsonAdaptedTag +Class JsonAdaptedInternshipApplication } } @@ -37,7 +36,6 @@ Storage -right-|> AddressBookStorage JsonUserPrefsStorage .up.|> UserPrefsStorage JsonAddressBookStorage .up.|> AddressBookStorage JsonAddressBookStorage ..> JsonSerializableAddressBook -JsonSerializableAddressBook --> "*" JsonAdaptedPerson -JsonAdaptedPerson --> "*" JsonAdaptedTag +JsonSerializableAddressBook --> "*" JsonAdaptedInternshipApplication @enduml diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml index 95473d5aa19..b2561abb166 100644 --- a/docs/diagrams/UiClassDiagram.puml +++ b/docs/diagrams/UiClassDiagram.puml @@ -10,15 +10,13 @@ Class "{abstract}\nUiPart" as UiPart Class UiManager Class MainWindow Class HelpWindow -Class ResultDisplay -Class PersonListPanel -Class PersonCard -Class StatusBarFooter +Class ReminderWindow Class CommandBox -} - -package Model <> { -Class HiddenModel #FFFFFF +Class ResultDialog +Class Panels #CF2765 +Class QuickAccessToolbar +Class PopupAddInternship +Class PopupEditInternship } package Logic <> { @@ -31,30 +29,31 @@ HiddenOutside ..> Ui UiManager .left.|> Ui UiManager -down-> "1" MainWindow MainWindow *-down-> "1" CommandBox -MainWindow *-down-> "1" ResultDisplay -MainWindow *-down-> "1" PersonListPanel -MainWindow *-down-> "1" StatusBarFooter -MainWindow --> "0..1" HelpWindow - -PersonListPanel -down-> "*" PersonCard - -MainWindow -left-|> UiPart - -ResultDisplay --|> UiPart +MainWindow *-down-> HelpWindow +MainWindow *-down-> ReminderWindow +MainWindow --> "0..1" ResultDialog +MainWindow *-down-> "1" Panels +MainWindow *-down-> "1" QuickAccessToolbar +MainWindow *-down-> "1" PopupAddInternship +MainWindow *-down-> "*" PopupEditInternship + +MainWindow --|> UiPart CommandBox --|> UiPart -PersonListPanel --|> UiPart -PersonCard --|> UiPart -StatusBarFooter --|> UiPart HelpWindow --|> UiPart +ReminderWindow --|> UiPart +Panels --|> UiPart +QuickAccessToolbar --|> UiPart +PopupAddInternship --|> UiPart +PopupEditInternship --|> UiPart -PersonCard ..> Model -UiManager -right-> Logic +UiManager -left-> Logic MainWindow -left-> Logic -PersonListPanel -[hidden]left- HelpWindow -HelpWindow -[hidden]left- CommandBox -CommandBox -[hidden]left- ResultDisplay -ResultDisplay -[hidden]left- StatusBarFooter +HelpWindow -[hidden]right- ResultDialog +ResultDialog -[hidden]right- CommandBox +CommandBox -[hidden]right- Panels +MainWindow -[hidden]right- QuickAccessToolbar +UiManager -[hidden]down- Logic MainWindow -[hidden]-|> UiPart @enduml diff --git a/docs/diagrams/UndoRedoState0.puml b/docs/diagrams/UndoRedoState0.puml index 96e30744d24..34885420931 100644 --- a/docs/diagrams/UndoRedoState0.puml +++ b/docs/diagrams/UndoRedoState0.puml @@ -15,6 +15,6 @@ State2 -[hidden]right-> State3 hide State2 hide State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State1 @end diff --git a/docs/diagrams/UndoRedoState2.puml b/docs/diagrams/UndoRedoState2.puml index bccc230a5d1..0ce7073e187 100644 --- a/docs/diagrams/UndoRedoState2.puml +++ b/docs/diagrams/UndoRedoState2.puml @@ -14,7 +14,7 @@ package States <> { State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State3 @end diff --git a/docs/diagrams/UndoSequenceDiagram.puml b/docs/diagrams/UndoSequenceDiagram.puml deleted file mode 100644 index 410aab4e412..00000000000 --- a/docs/diagrams/UndoSequenceDiagram.puml +++ /dev/null @@ -1,53 +0,0 @@ -@startuml -!include style.puml - -box Logic LOGIC_COLOR_T1 -participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR -participant "u:UndoCommand" as UndoCommand LOGIC_COLOR -end box - -box Model MODEL_COLOR_T1 -participant ":Model" as Model MODEL_COLOR -participant ":VersionedAddressBook" as VersionedAddressBook MODEL_COLOR -end box -[-> LogicManager : execute(undo) -activate LogicManager - -LogicManager -> AddressBookParser : parseCommand(undo) -activate AddressBookParser - -create UndoCommand -AddressBookParser -> UndoCommand -activate UndoCommand - -UndoCommand --> AddressBookParser -deactivate UndoCommand - -AddressBookParser --> LogicManager : u -deactivate AddressBookParser - -LogicManager -> UndoCommand : execute() -activate UndoCommand - -UndoCommand -> Model : undoAddressBook() -activate Model - -Model -> VersionedAddressBook : undo() -activate VersionedAddressBook - -VersionedAddressBook -> VersionedAddressBook :resetData(ReadOnlyAddressBook) -VersionedAddressBook --> Model : -deactivate VersionedAddressBook - -Model --> UndoCommand -deactivate Model - -UndoCommand --> LogicManager : result -deactivate UndoCommand -UndoCommand -[hidden]-> LogicManager : result -destroy UndoCommand - -[<--LogicManager -deactivate LogicManager -@enduml diff --git a/docs/images/AddContactActivityDiagram.png b/docs/images/AddContactActivityDiagram.png new file mode 100644 index 00000000000..442d0d04956 Binary files /dev/null and b/docs/images/AddContactActivityDiagram.png differ diff --git a/docs/images/AddContactSequenceDiagram.png b/docs/images/AddContactSequenceDiagram.png new file mode 100644 index 00000000000..89292794f95 Binary files /dev/null and b/docs/images/AddContactSequenceDiagram.png differ diff --git a/docs/images/AddDocumentsActivityDiagram.png b/docs/images/AddDocumentsActivityDiagram.png new file mode 100644 index 00000000000..b343c869a11 Binary files /dev/null and b/docs/images/AddDocumentsActivityDiagram.png differ diff --git a/docs/images/AddDocumentsSequenceDiagram.png b/docs/images/AddDocumentsSequenceDiagram.png new file mode 100644 index 00000000000..fa5bf23f640 Binary files /dev/null and b/docs/images/AddDocumentsSequenceDiagram.png differ diff --git a/docs/images/AddSequenceDiagram.png b/docs/images/AddSequenceDiagram.png new file mode 100644 index 00000000000..53cdad1eed3 Binary files /dev/null and b/docs/images/AddSequenceDiagram.png differ diff --git a/docs/images/AddTodoActivityDiagram.png b/docs/images/AddTodoActivityDiagram.png new file mode 100644 index 00000000000..0256286c99a Binary files /dev/null and b/docs/images/AddTodoActivityDiagram.png differ diff --git a/docs/images/ApplicationListPanelRef.png b/docs/images/ApplicationListPanelRef.png new file mode 100644 index 00000000000..dc8c9d9d35b Binary files /dev/null and b/docs/images/ApplicationListPanelRef.png differ diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png index 2f1346869d0..4a47cb2ec2b 100644 Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ diff --git a/docs/images/ArchiveActivityDiagram.png b/docs/images/ArchiveActivityDiagram.png new file mode 100644 index 00000000000..2596e4db053 Binary files /dev/null and b/docs/images/ArchiveActivityDiagram.png differ diff --git a/docs/images/ArchiveSequenceDiagram.png b/docs/images/ArchiveSequenceDiagram.png new file mode 100644 index 00000000000..c6a03b35019 Binary files /dev/null and b/docs/images/ArchiveSequenceDiagram.png differ diff --git a/docs/images/ClearByActivityDiagram.png b/docs/images/ClearByActivityDiagram.png new file mode 100644 index 00000000000..9e39fc93675 Binary files /dev/null and b/docs/images/ClearByActivityDiagram.png differ diff --git a/docs/images/ClearBySequenceDiagram.png b/docs/images/ClearBySequenceDiagram.png new file mode 100644 index 00000000000..b9e3c54f888 Binary files /dev/null and b/docs/images/ClearBySequenceDiagram.png differ diff --git a/docs/images/ClearSequenceDiagram.png b/docs/images/ClearSequenceDiagram.png new file mode 100644 index 00000000000..9a2b9575b45 Binary files /dev/null and b/docs/images/ClearSequenceDiagram.png differ diff --git a/docs/images/ClearTodoActivityDiagram.png b/docs/images/ClearTodoActivityDiagram.png new file mode 100644 index 00000000000..c654dea169f Binary files /dev/null and b/docs/images/ClearTodoActivityDiagram.png differ diff --git a/docs/images/DeleteContactActivityDiagram.png b/docs/images/DeleteContactActivityDiagram.png new file mode 100644 index 00000000000..1c446ef2209 Binary files /dev/null and b/docs/images/DeleteContactActivityDiagram.png differ diff --git a/docs/images/DeleteContactSequenceDiagram.png b/docs/images/DeleteContactSequenceDiagram.png new file mode 100644 index 00000000000..eeb4c4fd680 Binary files /dev/null and b/docs/images/DeleteContactSequenceDiagram.png differ diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png index fa327b39618..affbe8d9dab 100644 Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ diff --git a/docs/images/DeleteTodoActivityDiagram.png b/docs/images/DeleteTodoActivityDiagram.png new file mode 100644 index 00000000000..44cb22a608e Binary files /dev/null and b/docs/images/DeleteTodoActivityDiagram.png differ diff --git a/docs/images/EditContactActivityDiagram.png b/docs/images/EditContactActivityDiagram.png new file mode 100644 index 00000000000..9adefd799e9 Binary files /dev/null and b/docs/images/EditContactActivityDiagram.png differ diff --git a/docs/images/EditContactSequenceDiagram.png b/docs/images/EditContactSequenceDiagram.png new file mode 100644 index 00000000000..44be7a8cdb5 Binary files /dev/null and b/docs/images/EditContactSequenceDiagram.png differ diff --git a/docs/images/EditDeadlineSequenceDiagram.png b/docs/images/EditDeadlineSequenceDiagram.png new file mode 100644 index 00000000000..ce55b2b1f22 Binary files /dev/null and b/docs/images/EditDeadlineSequenceDiagram.png differ diff --git a/docs/images/EditNoteContentSequenceDiagram.png b/docs/images/EditNoteContentSequenceDiagram.png new file mode 100644 index 00000000000..8b37971494e Binary files /dev/null and b/docs/images/EditNoteContentSequenceDiagram.png differ diff --git a/docs/images/EditSequenceDiagram.png b/docs/images/EditSequenceDiagram.png new file mode 100644 index 00000000000..0414e0fb469 Binary files /dev/null and b/docs/images/EditSequenceDiagram.png differ diff --git a/docs/images/EditStatusActivityDiagram.png b/docs/images/EditStatusActivityDiagram.png new file mode 100644 index 00000000000..17cde8f1cc7 Binary files /dev/null and b/docs/images/EditStatusActivityDiagram.png differ diff --git a/docs/images/EditStatusSequenceDiagram.png b/docs/images/EditStatusSequenceDiagram.png new file mode 100644 index 00000000000..d33b3c00dce Binary files /dev/null and b/docs/images/EditStatusSequenceDiagram.png differ diff --git a/docs/images/ExitSequenceDiagram.png b/docs/images/ExitSequenceDiagram.png new file mode 100644 index 00000000000..b51245fb414 Binary files /dev/null and b/docs/images/ExitSequenceDiagram.png differ diff --git a/docs/images/FindFeatureClassDiagram.png b/docs/images/FindFeatureClassDiagram.png new file mode 100644 index 00000000000..d2503276cde Binary files /dev/null and b/docs/images/FindFeatureClassDiagram.png differ diff --git a/docs/images/FindSequenceDiagram.png b/docs/images/FindSequenceDiagram.png new file mode 100644 index 00000000000..da882014681 Binary files /dev/null and b/docs/images/FindSequenceDiagram.png differ diff --git a/docs/images/FindTaskSequenceDiagram.png b/docs/images/FindTaskSequenceDiagram.png new file mode 100644 index 00000000000..48b71706871 Binary files /dev/null and b/docs/images/FindTaskSequenceDiagram.png differ diff --git a/docs/images/GroupCompanyName.png b/docs/images/GroupCompanyName.png new file mode 100644 index 00000000000..0581acd4dfc Binary files /dev/null and b/docs/images/GroupCompanyName.png differ diff --git a/docs/images/GroupJobTitle.png b/docs/images/GroupJobTitle.png new file mode 100644 index 00000000000..13d3d41b5dc Binary files /dev/null and b/docs/images/GroupJobTitle.png differ diff --git a/docs/images/GroupStatus.png b/docs/images/GroupStatus.png new file mode 100644 index 00000000000..fd64ce92604 Binary files /dev/null and b/docs/images/GroupStatus.png differ diff --git a/docs/images/ListActivityDiagram.png b/docs/images/ListActivityDiagram.png new file mode 100644 index 00000000000..67da8b3266e Binary files /dev/null and b/docs/images/ListActivityDiagram.png differ diff --git a/docs/images/ListArchivedActivityDiagram.png b/docs/images/ListArchivedActivityDiagram.png new file mode 100644 index 00000000000..bba81898259 Binary files /dev/null and b/docs/images/ListArchivedActivityDiagram.png differ diff --git a/docs/images/ListTaskActivityDiagram.png b/docs/images/ListTaskActivityDiagram.png new file mode 100644 index 00000000000..15f0d896afd Binary files /dev/null and b/docs/images/ListTaskActivityDiagram.png differ diff --git a/docs/images/ListTodoActivityDiagram.png b/docs/images/ListTodoActivityDiagram.png new file mode 100644 index 00000000000..63535e8bcfe Binary files /dev/null and b/docs/images/ListTodoActivityDiagram.png differ diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png index 9e9ba9f79e5..da3a3aede4e 100644 Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ diff --git a/docs/images/MixedPanelRef.png b/docs/images/MixedPanelRef.png new file mode 100644 index 00000000000..31583d8e73b Binary files /dev/null and b/docs/images/MixedPanelRef.png differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index 04070af60d8..9a834a4ea73 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/NoteOverviewDiagram.png b/docs/images/NoteOverviewDiagram.png new file mode 100644 index 00000000000..5eb6a684c0e Binary files /dev/null and b/docs/images/NoteOverviewDiagram.png differ diff --git a/docs/images/PanelsRef.png b/docs/images/PanelsRef.png new file mode 100644 index 00000000000..60168566323 Binary files /dev/null and b/docs/images/PanelsRef.png differ diff --git a/docs/images/ParserClasses.png b/docs/images/ParserClasses.png index e7b4c8880cd..a411bb7a2dd 100644 Binary files a/docs/images/ParserClasses.png and b/docs/images/ParserClasses.png differ diff --git a/docs/images/RevertActivityDiagram.png b/docs/images/RevertActivityDiagram.png new file mode 100644 index 00000000000..d084fcd10f0 Binary files /dev/null and b/docs/images/RevertActivityDiagram.png differ diff --git a/docs/images/RevertAllSequenceDiagram.png b/docs/images/RevertAllSequenceDiagram.png new file mode 100644 index 00000000000..3fa0bf9c9f4 Binary files /dev/null and b/docs/images/RevertAllSequenceDiagram.png differ diff --git a/docs/images/SortSequenceDiagram.png b/docs/images/SortSequenceDiagram.png new file mode 100644 index 00000000000..71846857e24 Binary files /dev/null and b/docs/images/SortSequenceDiagram.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png index 2533a5c1af0..8e3f5541345 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..1f0fe290758 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..021c4e4446b 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/benjamin-wee.png b/docs/images/benjamin-wee.png new file mode 100644 index 00000000000..0a12937ed17 Binary files /dev/null and b/docs/images/benjamin-wee.png differ diff --git a/docs/images/jianminglok.png b/docs/images/jianminglok.png new file mode 100644 index 00000000000..0292476d1ad Binary files /dev/null and b/docs/images/jianminglok.png differ diff --git a/docs/images/jiasheng59.png b/docs/images/jiasheng59.png new file mode 100644 index 00000000000..ca62d99082e Binary files /dev/null and b/docs/images/jiasheng59.png differ diff --git a/docs/images/laihuiqi.png b/docs/images/laihuiqi.png new file mode 100644 index 00000000000..b8e6f4fa623 Binary files /dev/null and b/docs/images/laihuiqi.png differ diff --git a/docs/images/ui/GuiOverview.png b/docs/images/ui/GuiOverview.png new file mode 100644 index 00000000000..8e2d10e4b04 Binary files /dev/null and b/docs/images/ui/GuiOverview.png differ diff --git a/docs/images/ui/buttons/add-internship-button.png b/docs/images/ui/buttons/add-internship-button.png new file mode 100644 index 00000000000..dac068103a2 Binary files /dev/null and b/docs/images/ui/buttons/add-internship-button.png differ diff --git a/docs/images/ui/buttons/archive-internship-button.png b/docs/images/ui/buttons/archive-internship-button.png new file mode 100644 index 00000000000..fa2f2705bc6 Binary files /dev/null and b/docs/images/ui/buttons/archive-internship-button.png differ diff --git a/docs/images/ui/buttons/delete-internship-button.png b/docs/images/ui/buttons/delete-internship-button.png new file mode 100644 index 00000000000..b191188066d Binary files /dev/null and b/docs/images/ui/buttons/delete-internship-button.png differ diff --git a/docs/images/ui/buttons/edit-internship-button.png b/docs/images/ui/buttons/edit-internship-button.png new file mode 100644 index 00000000000..b6f615dbdcc Binary files /dev/null and b/docs/images/ui/buttons/edit-internship-button.png differ diff --git a/docs/images/ui/popups/add-internship-popup.png b/docs/images/ui/popups/add-internship-popup.png new file mode 100644 index 00000000000..b283aa2c5a2 Binary files /dev/null and b/docs/images/ui/popups/add-internship-popup.png differ diff --git a/docs/images/ui/popups/edit-internship-popup.png b/docs/images/ui/popups/edit-internship-popup.png new file mode 100644 index 00000000000..d98273e7fd9 Binary files /dev/null and b/docs/images/ui/popups/edit-internship-popup.png differ diff --git a/docs/images/zm-l.png b/docs/images/zm-l.png new file mode 100644 index 00000000000..82a784e0ac9 Binary files /dev/null and b/docs/images/zm-l.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..8d5e1023820 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,17 +1,17 @@ --- layout: page -title: AddressBook Level-3 +title: InternEase --- -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) -[![codecov](https://codecov.io/gh/se-edu/addressbook-level3/branch/master/graph/badge.svg)](https://codecov.io/gh/se-edu/addressbook-level3) +[![CI Status](https://github.com/AY2223S2-CS2103T-W15-4/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2223S2-CS2103T-W15-4/tp/actions) +[![codecov](https://codecov.io/gh/AY2223S2-CS2103T-W15-4/tp/branch/master/graph/badge.svg?token=MVV9ABQAS8)](https://codecov.io/gh/AY2223S2-CS2103T-W15-4/tp) ![Ui](images/Ui.png) -**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). +**InternEase is a desktop application for managing your internship applications.** 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 InternEase, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). +* If you are interested about developing InternEase, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. **Acknowledgements** diff --git a/docs/team/benjamin-wee.md b/docs/team/benjamin-wee.md new file mode 100644 index 00000000000..99f4d681beb --- /dev/null +++ b/docs/team/benjamin-wee.md @@ -0,0 +1,47 @@ +--- +layout: page +title: Benjamin Wee's Project Portfolio Page +--- + +### Project: InternEase + +InternEase is a **powerful and innovative desktop app designed to streamline the internship application process** for **Computer Science undergraduates**. With its optimized **combination of a Command Line Interface (CLI) and Graphical User Interface (GUI)**, InternEase offers users the best of both worlds - the speed and efficiency of a CLI for those who can type quickly, and the user-friendly experience of a GUI for those who prefer a visual interface. InternEase is a good choice for you who wants to concentrate on your internship preparation by helping to manage your internship applications' data. + +Listed below are my contributions to the project. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=benjamin-wee&breakdown=true&sort=groupTitle&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + +* **New Features**: + * Manage the interview date of an internship application + * What it does: Users are able to add an interview date to an internship application + * Justification: This feature allows the user, an internship applicant in this case to quickly retrieve the interview date of an application. + * Highlights: A Date Time validation feature had to be implemented to ensure that the date and time provided by the user is valid. + * Credits: Some parts of the code was reused and adapted from the [AB3 project](https://github.com/nus-cs2103-AY2223S2/tp) created by the [SE-EDU initiative](https://se-education.org/). + * Remind the user of the internship application with the most imminent interview date + * What it does: Users are able to view the details of the internship application with the interview that is the nearest to the current date and time in a pop-up window every time they launch the application/execute the `remind` command. + * This feature allows users to quickly identify the application with an interview date that is closest to the current date and time, so that they will not miss the interview. + * Credits: Some parts of the code was reused and adapted from the [AB3 project](https://github.com/nus-cs2103-AY2223S2/tp) created by the [SE-EDU initiative](https://se-education.org/). + + +* **Project management**: + * Managed release `v1.2` on GitHub. + +* **Enhancements to existing features**: + * Enhanced `list` feature according to the InternEase model in [#122](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/122) + * Added implementation of the reminder button as part of our GUI enhancement. + +* **Documentation**: + * User Guide: + * Added documentation for the features below in [#208](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/208), [#68](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/68) + * `add_date`, `remind` + * `list` + * Developer Guide: + * Added table of contents in [#306](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/306) + * Added implementation details for `add_date` in [#150](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/150) + * Added implementation details for `remind` in [#314](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/314) + +* **Community**: + * Reported bugs and suggestions for other teams in the class : [Team CS2103T-F12-2](https://github.com/benjamin-wee/ped/issues) + +* **Tools**: + * Java 11 and JavaFX diff --git a/docs/team/jianminglok.md b/docs/team/jianminglok.md new file mode 100644 index 00000000000..d25b1e29c77 --- /dev/null +++ b/docs/team/jianminglok.md @@ -0,0 +1,60 @@ +--- +layout: page +title: Lok Jian Ming's Project Portfolio Page +--- + +### Project: InternEase + +InternEase is a **powerful and innovative desktop app designed to streamline the internship application process** for **Computer Science undergraduates**. With its optimized **combination of a Command Line Interface (CLI) and Graphical User Interface (GUI)**, InternEase offers users the best of both worlds - the speed and efficiency of a CLI for those who can type quickly, and the user-friendly experience of a GUI for those who prefer a visual interface. InternEase is a good choice for you who wants to concentrate on your internship preparation by helping to manage your internship applications' data. + +Given below are my contributions to the project. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=jianminglok&breakdown=true) + +* **New Features**: + * Manage the company contact of an internship application + * What it does: Users are able to add a company contact, which consists of both a phone and an email to an internship application, and subsequently edit and remove it. + * Justification: This feature allows the user, an internship applicant in this case to quickly retrieve the contact details of an application, so that he can make an enquiry or ask for an update on his/her application status, making his/her internship application journey even easier. + * Highlights: The regular expression used for validating the email from the user input was updated, which was challenging in the sense that those available on the Internet were not always correct and had to be carefully modified and adapted. + * Credits: Some parts of the code was reused and adapted from the [AB3 project](https://github.com/nus-cs2103-AY2223S2/tp) created by the [SE-EDU initiative](https://se-education.org/). + * Manage the documents of an internship application + * What it does: Users are able to add links to documents consisting of a resume and a cover letter to an internship application, and edit and remove them afterwards. + * Justification: This features allows users to retrieve the specific version of their resume and cover letter submitted for a particular application for reference before an interview. + * Highlights: A URL validation feature had to be implemented to ensure that the links to the documents provided by the user are valid. + * Edit the status of an internship application + * What it does: Users are able to update the status of their application to one of the following: `PENDING`, `RECEIVED`, `ACCEPTED`, `DECLINED`, `REJECTED`. + * Justification: This feature allows users to quickly identify the status of an application, so that they will not need to repeatedly check their email for the latest response, if any from the company. + * Highlights: Enumerations are used to ensure that the status provided is valid. + * Archive an internship application + * What is does: Users are able to archive and unarchive an internship application. They can also view a list of archived applications. + * Justification: This features allow users to focus on the currently ongoing internship applications, without having their list of applications getting cluttered up over time. + * Highlights: New predicates were created to allow ongoing and archived internship applications to be listed separately. +* **Project management**: + * Setup and added Codecov label for the team project + +* **Enhancements to existing features**: + * Updated the `list` feature to only show ongoing internship applications, separating out the archived ones in [#138](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/138). + * Added implementation of the `ViewContentPanel` class as part of our GUI enhancement in [#165](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/165). + * Updated the RegEx (regular expression) for validating email addresses in [#266](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/266). + * Credits: Some parts of the code was reused and adapted from the [AB3 project](https://github.com/nus-cs2103-AY2223S2/tp) created by the [SE-EDU initiative](https://se-education.org/). + +* **Documentation**: + * User Guide: + * Added documentation for the features below in [#45](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/45), [#217](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/217) and [#267](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/267). + * `add_contact`, `edit_contact`, `delete_contact` + * `add_docs`, `edit_docs`, `delete_docs` + * `archive`, `unarchive`, `list_archived` + * `edit_status` + * Developer Guide: + * Added use cases, implementation details and UML diagrams for the features below in [#61](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/61), [#287](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/287) and [#288](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/288). + * `add_contact`, `edit_contact`, `delete_contact` + * `add_docs`, `edit_docs`, `delete_docs` + * `archive`, `unarchive`, `list_archived` + * `edit_status` + +* **Community**: + * PRs reviewed (with non-trivial review comments): [#141](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/141), [#160](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/160) + * Reported bugs and suggestions for other teams in the class: [Team CS2103T-W11-3](https://github.com/jianminglok/ped/issues) + +* **Tools**: + * Java 11 and JavaFX diff --git a/docs/team/jiasheng59.md b/docs/team/jiasheng59.md new file mode 100644 index 00000000000..67ebc398483 --- /dev/null +++ b/docs/team/jiasheng59.md @@ -0,0 +1,57 @@ +--- +layout: page +title: Lee Jia Sheng's Project Portfolio Page +--- + +### Project: InternEase + +InternEase is a powerful and innovative desktop app designed to streamline the internship application process for Computer Science undergraduates. +With its optimized combination of a Command Line Interface (CLI) and Graphical User Interface (GUI), +InternEase offers users the best of both worlds - the speed and efficiency +of a CLI for those who can type quickly, and the user-friendly experience of a GUI for those who prefer a visual interface. +If you are new to internship application, InternEase will definitely be your best buddy that gets you started smoothly. + +Given below are my contributions to the project. + +* **New Feature**: Added a summary panel to show statistics of internship applications made. + * What it does: allows the user to have an overview to current status of internship applications. + * Justification: This feature improves the product in terms of visualising data into a more intuitive form and helps user to better manage those applications which have yet to received reply. + * Highlights: This enhancement not only support summarising existing status of internship applications implemented but also will support any further internship status to be implemented in future automatically. It's complete in the sense that it's complete and not hard-coded so by introducing new `status`, it won't require any changes in this portion. + * Disclaimer: The GUI design of the summary panel references the design of `ListView` panel of AB3 which is the upstream repo InternEase forked from. + +* **New Feature**: Added a sort command that allows the user to sort all applications by company name, job title, application status and interview dates. + * What it does: allows the user to have a sorted view of the list of internship applications by above-mentioned attributes. + * Justification: This feature makes the product more user-friendly as the user is able to better manage large volume of applications made and view the relatively more urgent internship applications using sort by interview date command. + * Highlights: This enhancement is challenging to implement as it requires changing the underlying representation of the internship application list without breaking the abstraction barrier, so that other commands relying on the initial representation don't break after the change. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=jiasheng59&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + +* **Project management**: + * Set up the GitHub team org and repo + * Lead the discussion in weekly project meeting + * Encourage teammates to brainstorm for Unique Selling Point of InternEase at the end of v1.2 iteration + * Managed releases `v1.3` - `v1.4` (2 releases) on GitHub + +* **Enhancements to existing features**: Enhanced the find command that allows the user to find internship application by the company name, job title, application status and interview date(s). + * What it does: allows the user to find an internship application by matching keyword for the company name and job title, by a specified application status, and by specifying a range of dates where the interview date falls within. + * Justification: This feature makes the product more user-friendly as the user is able to search through the list with huge number of applications efficiently and makes subsequent changes. + * Highlights: This enhancement is challenging to implement as to make the command format as `defensive` as it could be, the checking in parser is somewhat exponential. + +* **Enhancements to existing features: Others**: + * Updated the help command link to current UG + * Wrote additional tests for new and existing features to increase coverage from 36.91% to 39.83% (Pull requests [\#290](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/290)) + +* **Documentation**: + * User Guide: + * Added documentation for the features `list`, `help`, `sort` and `find`: [\#200](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/200), [\#210](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/210), [\#212](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/212) + * Did cosmetic tweaks to existing documentation of features: [\#291](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/291) + * Developer Guide: + * Added implementation details of the `list`, `sort`, and `find` feature and corresponding use cases + * Drew related UML diagrams for better illustration + +* **Community**: + * PRs reviewed (with non-trivial review comments): [\#106](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/106), [\#129](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/129), +[\#130](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/130), [\#131](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/131), [\#132](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/132), +[\#133](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/133), [\#147](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/147), [\#155](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/155), [\#262](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/262) + * Contributed to forum discussions (examples: [1](https://github.com/nus-cs2103-AY2223S2/forum/issues/239#issuecomment-1454813105), [2](https://github.com/nus-cs2103-AY2223S2/forum/issues/294#issuecomment-1489103419), [3](https://github.com/nus-cs2103-AY2223S2/forum/issues/294), [4](https://github.com/nus-cs2103-AY2223S2/forum/issues/247#issuecomment-1460008541)) + * Reported bugs and suggestions for other teams in the class (examples: [1](https://github.com/AY2223S2-CS2103T-W13-3/tp/issues/161#event-8953814014)) 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/laihuiqi.md b/docs/team/laihuiqi.md new file mode 100644 index 00000000000..8d80495d82a --- /dev/null +++ b/docs/team/laihuiqi.md @@ -0,0 +1,53 @@ +--- +layout: page +title: Lai Hui Qi's Project Portfolio Page +--- +### Project: InternEase + +InternEase is a **powerful and innovative desktop app designed to streamline the internship application process** for **Computer Science undergraduates**. It is a good choice for you to concentrate on your internship preparation as it helps to manage your internship applications' data. + +Given below are my contributions to the project.
+**Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=laihuiqi&breakdown=true&sort=groupTitle&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+**Credits**: Some code reused and adapted from [AB3 project](https://github.com/nus-cs2103-AY2223S2/tp) created by the [SE-EDU initiative](https://se-education.org/). + +**New Feature**: Adds the ability to plan an internship application by introducing a `task package` with subpackages `todo` and `note`. +* Allows user to plan future internship applications and record long-lasting reminders to enhance future internship applications and interviews. +* `task` package : `find_task` and `list_task`. +* `todo` package : `add_todo`, `clear_todo`, `delete_todo`, `edit_deadline`, `edit_content` and `list_todo`. +* `note` package : `add_note`, `clear_note`, `delete_note` and `list_note`. +* Justification: Provides functional spaces for planning internships and preparing interviews. +* Highlights: An independent structure of logic, model, storage and ui is implemented for this side features package to avoid ambiguity. +* **Credits**: Some code reused and adapted from [AB3 project](https://github.com/nus-cs2103-AY2223S2/tp) created by the [SE-EDU initiative](https://se-education.org/). + +**New Feature**: Clears the internship applications with specified attributes and keywords +* Allows user to delete internship applications at once by `company name`, `job title` or `status` with keywords specified (`clear_by`). +* Justification: Enable more efficient cleaned-up for unwanted internship applications. +* Highlights: The implementation of different class constructors and their respective parse functions is slightly challenging. +* **Credits**: Some code reused and adapted from [AB3 project](https://github.com/nus-cs2103-AY2223S2/tp) created by the [SE-EDU initiative](https://se-education.org/). + +**New Feature**: Reverts the recent deletion of internship application +* Allows user to restore the most recent internship application from the CacheList. +* Justification: Resolves the situation when the user accidentally deletes an internship application with many important particulars. +* Highlights: CacheList is implemented to be the data structure to hold the deleted or cleared internship applications temporarily. +* **Credits**: Some code reused and adapted from [AB3 project](https://github.com/nus-cs2103-AY2223S2/tp) created by the [SE-EDU initiative](https://se-education.org/). + +**Project management (Team-based tasks)**: +* Added labels and modified label descriptions for issues, updated README.md and index.md. [\#59](https://github.com/AY2223S2-CS2103T-W15-4/tp/issues/59) +* Modified documentation for Quick Start and added table of contents for User Guide. [\#47](https://github.com/AY2223S2-CS2103T-W15-4/tp/issues/47), [\#161](https://github.com/AY2223S2-CS2103T-W15-4/tp/issues/161) + +**Enhancements implemented**: +* Enhanced `clear`, `delete` and `exit` features according to the InternEase model. +* Enhanced GUI interface for `MainWindow`, `Result Dialog` and `task package` related GUI parts. [\#128](https://github.com/AY2223S2-CS2103T-W15-4/tp/issues/128), [\#162](https://github.com/AY2223S2-CS2103T-W15-4/tp/issues/162) +* Enhanced test cases for assigned features, increased related class test coverage to at least 80%.[\#272](https://github.com/AY2223S2-CS2103T-W15-4/tp/issues/272) +* **Credits**: Some code reused and adapted from [AB3 project](https://github.com/nus-cs2103-AY2223S2/tp) created by the [SE-EDU initiative](https://se-education.org/). + +**Documentation**: +* User Guide: + * Updated documentation for main features and side features stated above [\#47](https://github.com/AY2223S2-CS2103T-W15-4/tp/issues/47), [\#161](https://github.com/AY2223S2-CS2103T-W15-4/tp/issues/161) +* Developer Guide: + * Added use cases, implementation details and UML diagrams for all features stated above and `Ui component`. [\#52](https://github.com/AY2223S2-CS2103T-W15-4/tp/issues/52), [\#274](https://github.com/AY2223S2-CS2103T-W15-4/tp/issues/274) +* **Credits**: Some code reused and adapted from [AB3 project](https://github.com/nus-cs2103-AY2223S2/tp) created by the [SE-EDU initiative](https://se-education.org/). + +**Community**: +* PRs reviewed (with non-trivial review comments): [\#145](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/145), [\#157](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/157), [\#173](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/173), [\#266](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/266) +* Reported bugs and suggestions for other teams in PE-D: [PE-D Group W16-1](https://github.com/laihuiqi/ped/issues) diff --git a/docs/team/zm-l.md b/docs/team/zm-l.md new file mode 100644 index 00000000000..bf33e7ba0b9 --- /dev/null +++ b/docs/team/zm-l.md @@ -0,0 +1,42 @@ +--- +layout: page +title: Lau Zhan Ming's Project Portfolio Page +--- + +### Project: InternEase + +InternEase is a **powerful and innovative desktop app designed to streamline the internship application process** for **Computer Science undergraduates**. With its optimized **combination of a Command Line Interface (CLI) and Graphical User Interface (GUI)**, InternEase offers users the best of both worlds - the speed and efficiency of a CLI for those who can type quickly, and the user-friendly experience of a GUI for those who prefer a visual interface. InternEase is a good choice for you who wants to concentrate on your internship preparation by helping to manage your internship applications' data. + +Given below are my contributions to the project. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=zm-l&breakdown=true) + +* **New Feature**: + * Manage an internship application + * What it does: Users are able to add and edit internship application + * Justification: This feature allows the user, an internship applicant in this case to add and edit an internship application they have. + * Highlights: Regular expressions are used to ensure user are typing in correct informations. Some fields are left as optional to give user more freedom to insert relevant details. + * Credits: Some parts of the code was reused and adapted from the [AB3 project](https://github.com/nus-cs2103-AY2223S2/tp) created by the [SE-EDU initiative] + +* **Project management**: + * Managed release `v1.1` on GitHub. + +* **Enhancements to existing features**: + * Improved UI/UX by designing and implementing a form for user to type in details when using the `add` and `edit` command. + * Implemented buttons for `add`, `edit`, `delete`, `archive` and `unarchive` command. + * Wrote additional tests for existing features to increase coverage from 49% to 69% (Pull requests [#326](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/326), [#330](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/330), [#331](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/331), [#333](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/333)) + +* **Documentation**: + * User Guide: + * Added documentation for the features `add` and `edit` command in [#211](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/211). + * Developer Guide: + * Added use cases for `add` command in [#143](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/143) + * Added use cases for `edit` command in [#298](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/298) + +* **Community**: + * PRs reviewed (with non-trivial review comments): [262](https://github.com/AY2223S2-CS2103T-W15-4/tp/pull/262) + * Contributed to forum discussions (examples: [1](https://github.com/nus-cs2103-AY2223S2/forum/issues/360)) + * Reported bugs and suggestions for other teams in the class: [CS2103-W17-4](https://github.com/zm-l/ped/issues) + +* **Tools**: + * Java 11 and Java FX diff --git a/docs/tutorials/AddRemark.md b/docs/tutorials/AddRemark.md index 880c701042f..715cc336e0d 100644 --- a/docs/tutorials/AddRemark.md +++ b/docs/tutorials/AddRemark.md @@ -229,7 +229,7 @@ Now that we have all the information that we need, let’s lay the groundwork fo ### Add a new `Remark` class -Create a new `Remark` in `seedu.address.model.person`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code. +Create a new `Remark` in `seedu.address.model.application`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code. A copy-paste and search-replace later, you should have something like [this](https://github.com/se-edu/addressbook-level3/commit/4516e099699baa9e2d51801bd26f016d812dedcc#diff-41bb13c581e280c686198251ad6cc337cd5e27032772f06ed9bf7f1440995ece). Note how `Remark` has no constrains and thus does not require input validation. @@ -242,7 +242,7 @@ Let’s change `RemarkCommand` and `RemarkCommandParser` to use the new `Remark` Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each person. -Simply add the following to [`seedu.address.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688). +Simply add the following to [`seedu.address.ui.ApplicationCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688). **`PersonCard.java`:** diff --git a/docs/tutorials/RemovingFields.md b/docs/tutorials/RemovingFields.md index f29169bc924..d89e990a1d6 100644 --- a/docs/tutorials/RemovingFields.md +++ b/docs/tutorials/RemovingFields.md @@ -28,7 +28,7 @@ IntelliJ IDEA provides a refactoring tool that can identify *most* parts of a re ### Assisted refactoring -The `address` field in `Person` is actually an instance of the `seedu.address.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu. +The `address` field in `Person` is actually an instance of the `seedu.address.model.application.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu. * :bulb: To make things simpler, you can unselect the options `Search in comments and strings` and `Search for text occurrences` ![Usages detected](../images/remove/UnsafeDelete.png) diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java index 4133aaa0151..a9843f7607b 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/address/MainApp.java @@ -18,9 +18,14 @@ import seedu.address.model.AddressBook; import seedu.address.model.Model; import seedu.address.model.ModelManager; +import seedu.address.model.NoteList; import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyNote; +import seedu.address.model.ReadOnlyTodoList; import seedu.address.model.ReadOnlyUserPrefs; +import seedu.address.model.TodoList; import seedu.address.model.UserPrefs; +import seedu.address.model.statstics.StatsManager; import seedu.address.model.util.SampleDataUtil; import seedu.address.storage.AddressBookStorage; import seedu.address.storage.JsonAddressBookStorage; @@ -28,6 +33,10 @@ import seedu.address.storage.Storage; import seedu.address.storage.StorageManager; import seedu.address.storage.UserPrefsStorage; +import seedu.address.storage.task.note.JsonNoteListStorage; +import seedu.address.storage.task.note.NoteStorage; +import seedu.address.storage.task.todo.JsonTodoListStorage; +import seedu.address.storage.task.todo.TodoListStorage; import seedu.address.ui.Ui; import seedu.address.ui.UiManager; @@ -36,7 +45,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, 4, 0, true); private static final Logger logger = LogsCenter.getLogger(MainApp.class); @@ -44,6 +53,7 @@ public class MainApp extends Application { protected Logic logic; protected Storage storage; protected Model model; + protected StatsManager statsManager; protected Config config; @Override @@ -57,13 +67,19 @@ 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); + TodoListStorage todoListStorage = new JsonTodoListStorage(userPrefs.getTodoListFilePath()); + NoteStorage noteStorage = new JsonNoteListStorage(userPrefs.getNoteListFilePath()); + + storage = new StorageManager(addressBookStorage, userPrefsStorage, todoListStorage, noteStorage); + initLogging(config); model = initModelManager(storage, userPrefs); - logic = new LogicManager(model, storage); + statsManager = new StatsManager(model); + + logic = new LogicManager(model, storage, statsManager); ui = new UiManager(logic); } @@ -74,6 +90,14 @@ public void init() throws Exception { * or an empty address book will be used instead if errors occur when reading {@code storage}'s address book. */ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { + ReadOnlyAddressBook initialAddressBook = initModelAddressBook(storage); + ReadOnlyTodoList initialTodoList = initModelTodoList(storage); + ReadOnlyNote initialNoteList = initModelNoteList(storage); + + return new ModelManager(initialAddressBook, userPrefs, initialTodoList, initialNoteList); + } + + private ReadOnlyAddressBook initModelAddressBook(Storage storage) { Optional addressBookOptional; ReadOnlyAddressBook initialData; try { @@ -90,7 +114,47 @@ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { initialData = new AddressBook(); } - return new ModelManager(initialData, userPrefs); + return initialData; + } + + private ReadOnlyTodoList initModelTodoList(Storage storage) { + Optional todoListOptional; + ReadOnlyTodoList initialData; + try { + todoListOptional = storage.readTodoList(); + if (!todoListOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample todolist"); + } + initialData = todoListOptional.orElseGet(SampleDataUtil::getSampleTodoList); + } catch (DataConversionException e) { + logger.warning("Data file not in the correct format. Will be starting with an empty todolist"); + initialData = new TodoList(); + } catch (IOException e) { + logger.warning("Problem while reading from the file. Will be starting with an empty todolist"); + initialData = new TodoList(); + } + + return initialData; + } + + private ReadOnlyNote initModelNoteList(Storage storage) { + Optional noteListOptional; + ReadOnlyNote initialData; + try { + noteListOptional = storage.readNoteList(); + if (!noteListOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample note list"); + } + initialData = noteListOptional.orElseGet(SampleDataUtil::getSampleNoteList); + } catch (DataConversionException e) { + logger.warning("Data file not in the correct format. Will be starting with an empty note list"); + initialData = new NoteList(); + } catch (IOException e) { + logger.warning("Problem while reading from the file. Will be starting with an empty note list"); + initialData = new NoteList(); + } + + return initialData; } private void initLogging(Config config) { diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/address/commons/core/GuiSettings.java index ba33653be67..6bf49afc827 100644 --- a/src/main/java/seedu/address/commons/core/GuiSettings.java +++ b/src/main/java/seedu/address/commons/core/GuiSettings.java @@ -10,8 +10,8 @@ */ public class GuiSettings implements Serializable { - private static final double DEFAULT_HEIGHT = 600; - private static final double DEFAULT_WIDTH = 740; + private static final double DEFAULT_HEIGHT = 700; + private static final double DEFAULT_WIDTH = 1285; private final double windowWidth; private final double windowHeight; diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java index 1deb3a1e469..581b3dbd499 100644 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ b/src/main/java/seedu/address/commons/core/Messages.java @@ -7,7 +7,13 @@ 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_APPLICATION_DISPLAYED_INDEX = + "The application index provided is invalid"; + public static final String MESSAGE_INVALID_TODO_DISPLAYED_INDEX = + "The todo index provided is invalid"; + public static final String MESSAGE_INVALID_NOTE_DISPLAYED_INDEX = + "The note index provided is invalid"; + public static final String MESSAGE_APPLICATION_LISTED_OVERVIEW = "%1$d applications listed!"; + public static final String MESSAGE_INVALID_DISPLAYED_INDEX = "This provided index is invalid!"; + public static final String MESSAGE_RESULT_LISTED_OVERVIEW = "%1$d tasks listed!"; } diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java index 92cd8fa605a..7861fa2d271 100644 --- a/src/main/java/seedu/address/logic/Logic.java +++ b/src/main/java/seedu/address/logic/Logic.java @@ -8,7 +8,10 @@ import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.statstics.StatsManager; +import seedu.address.model.task.InternshipTodo; +import seedu.address.model.task.Note; /** * API of the Logic component @@ -30,14 +33,41 @@ public interface Logic { */ ReadOnlyAddressBook getAddressBook(); - /** Returns an unmodifiable view of the filtered list of persons */ - ObservableList getFilteredPersonList(); + /** + * Returns a StatsManager. + */ + StatsManager getStatsManager(); + + /** Returns an unmodifiable view of the filtered list of internship applications. */ + ObservableList getFilteredInternshipList(); + + /** Returns an unmodifiable view of the sorted filtered list of internship applications */ + ObservableList getSortedFilteredInternshipList(); + + /** Returns an unmodifiable view of the filtered list of todos. */ + ObservableList getFilteredTodoList(); + + /** Returns an unmodifiable view of the filtered list of notes. */ + ObservableList getFilteredNoteList(); + + /** Returns the InternshipApplication with the most imminent interview*/ + InternshipApplication getReminderApplication(); /** * Returns the user prefs' address book file path. */ Path getAddressBookFilePath(); + /** + * Returns the user prefs' todo list file path. + */ + Path getTodoListFilePath(); + + /** + * Returns the user prefs' note list file path. + */ + Path getNoteListFilePath(); + /** * Returns the user prefs' GUI settings. */ diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java index 9d9c6d15bdc..2f75ffbe576 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/address/logic/LogicManager.java @@ -10,11 +10,15 @@ import seedu.address.logic.commands.Command; import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.AddressBookParser; +import seedu.address.logic.parser.InternEaseParser; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.Model; import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.statstics.StatsManager; +import seedu.address.model.tag.TaskType; +import seedu.address.model.task.InternshipTodo; +import seedu.address.model.task.Note; import seedu.address.storage.Storage; /** @@ -26,15 +30,17 @@ public class LogicManager implements Logic { private final Model model; private final Storage storage; - private final AddressBookParser addressBookParser; + private final StatsManager statsManager; + private final InternEaseParser internEaseParser; /** * Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}. */ - public LogicManager(Model model, Storage storage) { + public LogicManager(Model model, Storage storage, StatsManager statsManager) { this.model = model; this.storage = storage; - addressBookParser = new AddressBookParser(); + this.statsManager = statsManager; + internEaseParser = new InternEaseParser(); } @Override @@ -42,11 +48,19 @@ public CommandResult execute(String commandText) throws CommandException, ParseE logger.info("----------------[USER COMMAND][" + commandText + "]"); CommandResult commandResult; - Command command = addressBookParser.parseCommand(commandText); + Command command = internEaseParser.parseCommand(commandText); commandResult = command.execute(model); + statsManager.updateAllStatsInformation(); try { - storage.saveAddressBook(model.getAddressBook()); + if (commandResult.getType() == TaskType.NONE) { + storage.saveAddressBook(model.getAddressBook()); + } else if (commandResult.getType() == TaskType.TODO) { + storage.saveTodoList(model.getTodoList()); + } else if (commandResult.getType() == TaskType.NOTE) { + storage.saveNoteList(model.getNoteList()); + } + } catch (IOException ioe) { throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe); } @@ -60,15 +74,50 @@ public ReadOnlyAddressBook getAddressBook() { } @Override - public ObservableList getFilteredPersonList() { - return model.getFilteredPersonList(); + public StatsManager getStatsManager() { + return statsManager; + } + + @Override + public ObservableList getFilteredInternshipList() { + return model.getSortedFilteredInternshipList(); } + @Override + public ObservableList getSortedFilteredInternshipList() { + return model.getSortedFilteredInternshipList(); + } + + @Override + public ObservableList getFilteredTodoList() { + return model.getFilteredTodoList(); + } + + @Override + public ObservableList getFilteredNoteList() { + return model.getFilteredNoteList(); + } + + @Override + public InternshipApplication getReminderApplication() { + model.updateReminder(); + return model.getReminder(); + } @Override public Path getAddressBookFilePath() { return model.getAddressBookFilePath(); } + @Override + public Path getTodoListFilePath() { + return model.getTodoListFilePath(); + } + + @Override + public Path getNoteListFilePath() { + return model.getNoteListFilePath(); + } + @Override public GuiSettings getGuiSettings() { return model.getGuiSettings(); diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java index 71656d7c5c8..99b52b1c349 100644 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddCommand.java @@ -1,60 +1,70 @@ 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.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_JOB_TITLE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_LOCATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PROGRAMMING_LANGUAGE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_QUALIFICATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RATING; +import static seedu.address.logic.parser.CliSyntax.PREFIX_REFLECTION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_REVIEW; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.address.model.application.InternshipApplication; /** - * Adds a person to the address book. + * Adds an application to the internship tracker. */ public class AddCommand extends Command { public static final String COMMAND_WORD = "add"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. " - + "Parameters: " - + PREFIX_NAME + "NAME " - + PREFIX_PHONE + "PHONE " - + PREFIX_EMAIL + "EMAIL " - + PREFIX_ADDRESS + "ADDRESS " - + "[" + PREFIX_TAG + "TAG]...\n" + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an internship application to the tracker.\n" + + "Compulsory Parameters: " + + PREFIX_COMPANY_NAME + "COMPANY_NAME " + + PREFIX_JOB_TITLE + "JOB_TITLE\n" + + "Optional Parameters: " + + PREFIX_REVIEW + "REVIEW " + + PREFIX_LOCATION + "LOCATION " + + PREFIX_PROGRAMMING_LANGUAGE + "PROGRAMMING_LANGUAGE " + + PREFIX_QUALIFICATION + "QUALIFICATION " + + PREFIX_SALARY + "SALARY " + + PREFIX_RATING + "RATING " + + PREFIX_NOTE + "NOTE " + + PREFIX_REFLECTION + "REFLECTION\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"; + + PREFIX_COMPANY_NAME + "LinkedIn " + + PREFIX_JOB_TITLE + "Software Engineer " + + PREFIX_REVIEW + "high learning curve, fast paced environment " + + PREFIX_LOCATION + "Clementi"; - public static final String MESSAGE_SUCCESS = "New person added: %1$s"; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book"; + public static final String MESSAGE_SUCCESS = "New internship application added: %1$s"; + public static final String MESSAGE_DUPLICATE_INTERNSHIP = "This internship application " + + "already exists in the address book"; - private final Person toAdd; + private final InternshipApplication toAdd; /** - * Creates an AddCommand to add the specified {@code Person} + * Creates an AddCommand to add the specified {@code InternshipApplication} */ - public AddCommand(Person person) { - requireNonNull(person); - toAdd = person; + public AddCommand(InternshipApplication application) { + requireNonNull(application); + toAdd = application; } @Override public CommandResult execute(Model model) throws CommandException { requireNonNull(model); - if (model.hasPerson(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); + if (model.hasApplication(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_INTERNSHIP); } - model.addPerson(toAdd); + model.addApplication(toAdd); return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); } diff --git a/src/main/java/seedu/address/logic/commands/AddInterviewDateCommand.java b/src/main/java/seedu/address/logic/commands/AddInterviewDateCommand.java new file mode 100644 index 00000000000..319914058c7 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddInterviewDateCommand.java @@ -0,0 +1,109 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; + +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.application.JobTitle; +import seedu.address.model.application.Location; +import seedu.address.model.application.Note; +import seedu.address.model.application.ProgrammingLanguage; +import seedu.address.model.application.Qualification; +import seedu.address.model.application.Rating; +import seedu.address.model.application.Reflection; +import seedu.address.model.application.Review; +import seedu.address.model.application.Salary; +import seedu.address.model.contact.Contact; +import seedu.address.model.documents.Documents; + +/** + * Adds an interview date to an application identified using it's displayed index from the list of internship + * applications. + */ +public class AddInterviewDateCommand extends Command { + + public static final String COMMAND_WORD = "add_date"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Adds interview date to the specified application from the list of internships applied.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_DATE + "DATE] (DATE must be in 'yyyy-MM-dd hh:mm a' format, where a can be AM or PM)" + + "\nExample: " + COMMAND_WORD + " 1 " + + PREFIX_DATE + "2023-06-07 12:00 PM"; + + + public static final String MESSAGE_ADD_INTERVIEW_DATE_SUCCESS = "Interview date added to application: %1$s"; + + private final Index targetIndex; + private final InterviewDate toAdd; + + /** + * @param targetIndex of the internship application to add interview date + * @param interviewDate InterviewDate to add + */ + public AddInterviewDateCommand(Index targetIndex, InterviewDate interviewDate) { + requireNonNull(targetIndex); + this.targetIndex = targetIndex; + toAdd = interviewDate; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getSortedFilteredInternshipList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + InternshipApplication internshipToAddInterviewDate = lastShownList.get(targetIndex.getZeroBased()); + InternshipApplication internshipWithInterviewDate = createInternshipWithInterviewDate( + internshipToAddInterviewDate, toAdd); + + model.setApplication(internshipToAddInterviewDate, internshipWithInterviewDate); + return new CommandResult(String.format(MESSAGE_ADD_INTERVIEW_DATE_SUCCESS, internshipToAddInterviewDate + + "\n" + toAdd)); + } + + private static InternshipApplication createInternshipWithInterviewDate( + InternshipApplication internshipToAddInterviewDate, InterviewDate interviewDate) { + assert internshipToAddInterviewDate != null; + + CompanyName companyName = internshipToAddInterviewDate.getCompanyName(); + JobTitle jobTitle = internshipToAddInterviewDate.getJobTitle(); + Set reviews = internshipToAddInterviewDate.getReviews(); + Set programmingLanguages = internshipToAddInterviewDate.getProgrammingLanguages(); + Set qualifications = internshipToAddInterviewDate.getQualifications(); + Location location = internshipToAddInterviewDate.getLocation(); + Salary salary = internshipToAddInterviewDate.getSalary(); + Set notes = internshipToAddInterviewDate.getNotes(); + Rating rating = internshipToAddInterviewDate.getRating(); + Set reflections = internshipToAddInterviewDate.getReflections(); + Contact contact = internshipToAddInterviewDate.getContact(); + boolean isArchived = internshipToAddInterviewDate.isArchived(); + InternshipStatus status = internshipToAddInterviewDate.getStatus(); + Documents documents = internshipToAddInterviewDate.getDocuments(); + + return new InternshipApplication(companyName, jobTitle, reviews, programmingLanguages, qualifications, location, + salary, notes, rating, reflections, contact, status, isArchived, interviewDate, documents); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddInterviewDateCommand // instanceof handles nulls + && targetIndex.equals(((AddInterviewDateCommand) other).targetIndex) + && toAdd.equals(((AddInterviewDateCommand) other).toAdd)); // state check + } + +} diff --git a/src/main/java/seedu/address/logic/commands/ArchiveCommand.java b/src/main/java/seedu/address/logic/commands/ArchiveCommand.java new file mode 100644 index 00000000000..f6fd07f096c --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ArchiveCommand.java @@ -0,0 +1,113 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ARCHIVED_APPLICATIONS; + +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.application.JobTitle; +import seedu.address.model.application.Location; +import seedu.address.model.application.Note; +import seedu.address.model.application.ProgrammingLanguage; +import seedu.address.model.application.Qualification; +import seedu.address.model.application.Rating; +import seedu.address.model.application.Reflection; +import seedu.address.model.application.Review; +import seedu.address.model.application.Salary; +import seedu.address.model.contact.Contact; +import seedu.address.model.documents.Documents; + +/** + * Archives an internship application identified using it's displayed index from the list of internship applications. + */ +public class ArchiveCommand extends Command { + + public static final String COMMAND_WORD = "archive"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Archives the specified application from the list of internships applied.\n" + + "Archives the application of internship at the specified INDEX.\n" + + "The index refers to the index number shown in the displayed internship list.\n" + + "Parameters: INDEX (must be a positive integer 1, 2, 3, ...)\n" + + "Example: " + COMMAND_WORD + " 2"; + + public static final String MESSAGE_ARCHIVE_APPLICATION_SUCCESS = "Archived Application: %1$s"; + public static final String MESSAGE_APPLICATION_ALREADY_ARCHIVED = "Application is already archived!"; + + private final Index targetIndex; + + /** + * Creates an ArchiveCommand to archive the specified {@code targetIndex} internship + * + * @param targetIndex of the internship application to archive + */ + public ArchiveCommand(Index targetIndex) { + requireNonNull(targetIndex); + + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getSortedFilteredInternshipList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + InternshipApplication internshipToArchive = lastShownList.get(targetIndex.getZeroBased()); + + if (internshipToArchive.isArchived()) { + throw new CommandException(MESSAGE_APPLICATION_ALREADY_ARCHIVED); + } + + InternshipApplication archivedApplication = createdArchivedApplication(internshipToArchive); + + model.setApplication(internshipToArchive, archivedApplication); + model.updateFilteredInternshipList(PREDICATE_SHOW_ARCHIVED_APPLICATIONS); + return new CommandResult(String.format(MESSAGE_ARCHIVE_APPLICATION_SUCCESS, archivedApplication)); + } + + /** + * Creates and returns an archived {@code InternshipApplication} + */ + private static InternshipApplication createdArchivedApplication(InternshipApplication internshipApplication) { + assert internshipApplication != null; + + CompanyName companyName = internshipApplication.getCompanyName(); + JobTitle jobTitle = internshipApplication.getJobTitle(); + Set reviews = internshipApplication.getReviews(); + Set programmingLanguages = internshipApplication.getProgrammingLanguages(); + Set qualifications = internshipApplication.getQualifications(); + Location location = internshipApplication.getLocation(); + Salary salary = internshipApplication.getSalary(); + Set notes = internshipApplication.getNotes(); + Rating rating = internshipApplication.getRating(); + Set reflections = internshipApplication.getReflections(); + Contact contact = internshipApplication.getContact(); + InternshipStatus status = internshipApplication.getStatus(); + InterviewDate interviewDate = internshipApplication.getInterviewDate(); + Documents documents = internshipApplication.getDocuments(); + + return new InternshipApplication(companyName, jobTitle, reviews, programmingLanguages, qualifications, location, + salary, notes, rating, reflections, contact, status, true, interviewDate, + documents); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ArchiveCommand // instanceof handles nulls + && targetIndex.equals(((ArchiveCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/ClearByCommand.java b/src/main/java/seedu/address/logic/commands/ClearByCommand.java new file mode 100644 index 00000000000..e1b8f6920cc --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ClearByCommand.java @@ -0,0 +1,178 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_JOB_TITLE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_STATUS; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.JobTitle; + +/** + * Clears the internship application entries by parameter specified. + */ +public class ClearByCommand extends Command { + /** + * An enum class to specify the clear_by types. + */ + enum ParamType { + JOBTITLE("Job title"), COMPANYNAME("Company name"), STATUS("Status"); + + private String name; + + /** + * Creates a string representation {@code name} for the respective ParamType. + */ + ParamType(String name) { + this.name = name; + } + + /** + * Getter for the string representation of the respective ParamType enum object. + */ + public String getName() { + return name; + } + } + + public static final String COMMAND_WORD = "clear_by"; + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Clear all relevant entries from the internship tracker with specific keyword.\n" + + "Clears all application with the specified keyword - COMPANY_NAME, JOB_TITLE or STATUS.\n" + + "Three types of clear_by features are provided, but can only execute independently.\n" + + "Examples: \n" + + COMMAND_WORD + " " + PREFIX_COMPANY_NAME + "Meta Clears all application with COMPANY_NAME as Meta.\n" + + COMMAND_WORD + " " + PREFIX_JOB_TITLE + "Software engineer Clears all application with JOB_TITLE as " + + "Software Engineer.\n" + + COMMAND_WORD + " " + PREFIX_STATUS + + "REJECTED Clears all rejected application (with STATUS as REJECTED)."; + + public static final String MESSAGE_INVALID_PARAMETER = "Invalid param!"; + public static final String MESSAGE_NO_PARAMETER = "Please provide clear_by parameter!"; + public static final String MESSAGE_CLEAR_SUCCESS = "All internship application with %s : %s has been cleared!"; + public static final String MESSAGE_NULL = "There is nothing to clear!"; + public static final String MESSAGE_FAILED = "Clear_by command cannot be executed!"; + public static final String MESSAGE_EMPTY_FILTERED_LIST = "There is no %s with keyword \"%s\"!"; + + private CompanyName companyName = null; + private JobTitle jobTitle = null; + private InternshipStatus status = null; + private String param; + + private ParamType paramType; + + /** + * Creates a ClearByCommand to delete all the relevant applications that have company names match with + * {@code companyName}. + */ + public ClearByCommand(CompanyName companyName) { + this.companyName = companyName; + param = companyName.fullName; + paramType = ParamType.COMPANYNAME; + } + + /** + * Creates a ClearByCommand to delete all the relevant applications that have jobTitles match with + * {@code jobTitle}. + */ + public ClearByCommand(JobTitle jobTitle) { + this.jobTitle = jobTitle; + param = jobTitle.fullName; + paramType = ParamType.JOBTITLE; + } + + /** + * Creates a ClearByCommand to delete all the relevant applications that have statuses match with + * {@code status}. + */ + public ClearByCommand(InternshipStatus status) { + this.status = status; + param = status.toString(); + paramType = ParamType.STATUS; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getSortedFilteredInternshipList(); + + if (lastShownList.size() == 0) { + return new CommandResult(MESSAGE_NULL); + } + + try { + ParamType.valueOf(String.valueOf(paramType)); + List filteredList = getFilteredList(lastShownList); + + if (filteredList.size() == 0) { + return new CommandResult(String.format(MESSAGE_EMPTY_FILTERED_LIST, paramType.getName(), param)); + } + + for (InternshipApplication application : filteredList) { + model.addInternshipToCache(application); + model.deleteInternship(application); + } + + return new CommandResult(String.format(MESSAGE_CLEAR_SUCCESS, paramType.getName(), param)); + + } catch (IllegalArgumentException e) { + return new CommandResult(MESSAGE_FAILED); + } + + } + + private List getFilteredList(List lastShownList) { + List filteredList; + switch (paramType) { + case COMPANYNAME: + filteredList = lastShownList + .stream() + .filter(a -> (a.getCompanyName()).equals(companyName)) + .collect(Collectors.toList()); + break; + case JOBTITLE: + filteredList = lastShownList + .stream() + .filter(a -> (a.getJobTitle().equals(jobTitle))) + .collect(Collectors.toList()); + break; + case STATUS: + filteredList = lastShownList + .stream() + .filter(a -> (a.getStatus().equals(status))) + .collect(Collectors.toList()); + break; + default: + filteredList = new ArrayList<>(); + } + return filteredList; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } else if (!(other instanceof ClearByCommand)) { + return false; + } + + switch (paramType) { + case COMPANYNAME: + return companyName.equals(((ClearByCommand) other).companyName); + case JOBTITLE: + return jobTitle.equals(((ClearByCommand) other).jobTitle); + case STATUS: + return status.equals(((ClearByCommand) other).status); + default: + return false; + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java index 9c86b1fa6e4..8c460e39204 100644 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java @@ -2,22 +2,34 @@ import static java.util.Objects.requireNonNull; +import java.util.List; + import seedu.address.model.AddressBook; import seedu.address.model.Model; +import seedu.address.model.application.InternshipApplication; /** - * Clears the address book. + * Clears the internship applications list. */ public class ClearCommand extends Command { public static final String COMMAND_WORD = "clear"; - public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; - + public static final String MESSAGE_SUCCESS = "All internship application has been cleared!"; + public static final String MESSAGE_NULL = "There is nothing to clear!"; @Override public CommandResult execute(Model model) { requireNonNull(model); - model.setAddressBook(new AddressBook()); + + List lastShownList = model.getSortedFilteredInternshipList(); + + if (lastShownList.size() == 0) { + return new CommandResult(MESSAGE_NULL); + } + + model.addAllInternshipToCache(lastShownList); + model.setInternEase(new AddressBook()); + return new CommandResult(MESSAGE_SUCCESS); } } diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java index 92f900b7916..63479c5e64a 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.model.tag.TaskType; + /** * Represents the result of a command execution. */ @@ -17,13 +19,21 @@ public class CommandResult { /** The application should exit. */ private final boolean exit; + + /** Reminder should be shown to user. */ + private final boolean showReminder; + + private TaskType type = TaskType.NONE; + + /** * Constructs a {@code CommandResult} with the specified fields. */ - public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { + public CommandResult(String feedbackToUser, boolean showHelp, boolean exit, boolean showReminder) { this.feedbackToUser = requireNonNull(feedbackToUser); this.showHelp = showHelp; this.exit = exit; + this.showReminder = showReminder; } /** @@ -31,7 +41,17 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { * and other fields set to their default value. */ public CommandResult(String feedbackToUser) { - this(feedbackToUser, false, false); + this(feedbackToUser, false, false, false); + } + + /** + * Constructs a {@code CommandResult} with the specified {@code feedbackToUser}, + * and set type to the command type. + */ + + public CommandResult(String feedbackToUser, TaskType type) { + this(feedbackToUser, false, false, false); + this.type = type; } public String getFeedbackToUser() { @@ -42,10 +62,18 @@ public boolean isShowHelp() { return showHelp; } + public boolean isRemind() { + return showReminder; + } + public boolean isExit() { return exit; } + public TaskType getType() { + return type; + } + @Override public boolean equals(Object other) { if (other == this) { @@ -60,12 +88,18 @@ public boolean equals(Object other) { CommandResult otherCommandResult = (CommandResult) other; return feedbackToUser.equals(otherCommandResult.feedbackToUser) && showHelp == otherCommandResult.showHelp - && exit == otherCommandResult.exit; + && exit == otherCommandResult.exit + && showReminder == otherCommandResult.showReminder; } @Override public int hashCode() { - return Objects.hash(feedbackToUser, showHelp, exit); + return Objects.hash(feedbackToUser, showHelp, exit, showReminder); + } + + @Override + public String toString() { + return this.feedbackToUser; } } diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java index 02fd256acba..9d34f8dd407 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ b/src/main/java/seedu/address/logic/commands/DeleteCommand.java @@ -8,24 +8,29 @@ import seedu.address.commons.core.index.Index; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.address.model.application.InternshipApplication; /** - * Deletes a person identified using it's displayed index from the address book. + * Deletes an internship application identified by its displayed index from the internship applications list. */ public class DeleteCommand extends Command { public static final String COMMAND_WORD = "delete"; public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Deletes the person identified by the index number used in the displayed person list.\n" - + "Parameters: INDEX (must be a positive integer)\n" - + "Example: " + COMMAND_WORD + " 1"; + + ": Deletes the specified application from the list of internships applied.\n" + + "Deletes the application of internship at the specified INDEX.\n" + + "The index refers to the index number shown in the displayed internship list.\n" + + "Parameters: INDEX (must be a positive integer 1, 2, 3, ...)\n" + + "Example: " + COMMAND_WORD + " 2"; - public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s"; + public static final String MESSAGE_DELETE_APPLICATION_SUCCESS = "Deleted Application: %1$s"; private final Index targetIndex; + /** + * Creates a DeleteCommand to delete the specified {@code targetIndex} internship applications. + */ public DeleteCommand(Index targetIndex) { this.targetIndex = targetIndex; } @@ -33,15 +38,18 @@ public DeleteCommand(Index targetIndex) { @Override public CommandResult execute(Model model) throws CommandException { requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); + List lastShownList = model.getSortedFilteredInternshipList(); if (targetIndex.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + throw new CommandException(Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); } - Person personToDelete = lastShownList.get(targetIndex.getZeroBased()); - model.deletePerson(personToDelete); - return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete)); + InternshipApplication internshipToDelete = lastShownList.get(targetIndex.getZeroBased()); + + model.deleteInternship(internshipToDelete); + model.addInternshipToCache(internshipToDelete); + + return new CommandResult(String.format(MESSAGE_DELETE_APPLICATION_SUCCESS, internshipToDelete)); } @Override diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java index 7e36114902f..c0ab4fbc549 100644 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ b/src/main/java/seedu/address/logic/commands/EditCommand.java @@ -1,12 +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 static seedu.address.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_JOB_TITLE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_LOCATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PROGRAMMING_LANGUAGE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_QUALIFICATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_REFLECTION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_REVIEW; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY; import java.util.Collections; import java.util.HashSet; @@ -19,87 +22,117 @@ 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; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.application.JobTitle; +import seedu.address.model.application.Location; +import seedu.address.model.application.Note; +import seedu.address.model.application.ProgrammingLanguage; +import seedu.address.model.application.Qualification; +import seedu.address.model.application.Rating; +import seedu.address.model.application.Reflection; +import seedu.address.model.application.Review; +import seedu.address.model.application.Salary; +import seedu.address.model.contact.Contact; +import seedu.address.model.documents.Documents; /** - * Edits the details of an existing person in the address book. + * Edits the details of an existing internship in the tracker. */ public class EditCommand extends Command { public static final String COMMAND_WORD = "edit"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified " - + "by the index number used in the displayed person list. " + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the internship identified " + + "by the index number used in the displayed internship 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" + + "[" + PREFIX_COMPANY_NAME + "COMPANY_NAME] " + + "[" + PREFIX_JOB_TITLE + "JOB_TITLE] " + + "[" + PREFIX_LOCATION + "LOCATION] " + + "[" + PREFIX_SALARY + "SALARY]" + + "[" + PREFIX_QUALIFICATION + "QUALIFICATION]... " + + "[" + PREFIX_PROGRAMMING_LANGUAGE + "PROGRAMMING_LANGUAGE]... " + + "[" + PREFIX_REVIEW + "REVIEW]... " + + "[" + PREFIX_NOTE + "NOTE]... " + + "[" + PREFIX_REFLECTION + "REFLECTION]... " + "Example: " + COMMAND_WORD + " 1 " - + PREFIX_PHONE + "91234567 " - + PREFIX_EMAIL + "johndoe@example.com"; + + PREFIX_SALARY + "2000 SGD " + + PREFIX_NOTE + "JC senior working there"; - public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s"; + public static final String MESSAGE_EDIT_INTERNSHIP_SUCCESS = "Edited Internship: %1$s"; public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book."; + public static final String MESSAGE_DUPLICATE_INTERNSHIP = "This internship already exists in the tracker."; private final Index index; - private final EditPersonDescriptor editPersonDescriptor; + private final EditInternshipDescriptor editInternshipDescriptor; /** - * @param index of the person in the filtered person list to edit - * @param editPersonDescriptor details to edit the person with + * @param index of the internship in the filtered internship list to edit + * @param editInternshipDescriptor details to edit the internship with */ - public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) { + public EditCommand(Index index, EditInternshipDescriptor editInternshipDescriptor) { requireNonNull(index); - requireNonNull(editPersonDescriptor); + requireNonNull(editInternshipDescriptor); this.index = index; - this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor); + this.editInternshipDescriptor = new EditInternshipDescriptor(editInternshipDescriptor); } @Override public CommandResult execute(Model model) throws CommandException { requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); + List lastShownList = model.getSortedFilteredInternshipList(); if (index.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + throw new CommandException(Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); } - Person personToEdit = lastShownList.get(index.getZeroBased()); - Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor); + InternshipApplication internshipToEdit = lastShownList.get(index.getZeroBased()); + InternshipApplication editedInternship = createEditedInternship(internshipToEdit, editInternshipDescriptor); - if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); + if (!internshipToEdit.isSameApplication(editedInternship) && model.hasApplication(editedInternship)) { + throw new CommandException(MESSAGE_DUPLICATE_INTERNSHIP); } - model.setPerson(personToEdit, editedPerson); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson)); + model.setApplication(internshipToEdit, editedInternship); + return new CommandResult(String.format(MESSAGE_EDIT_INTERNSHIP_SUCCESS, editedInternship)); } /** - * Creates and returns a {@code Person} with the details of {@code personToEdit} - * edited with {@code editPersonDescriptor}. + * Creates and returns a {@code InternshipApplication} with the details of {@code personToEdit} + * edited with {@code editInternshipDescriptor}. */ - 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); + private static InternshipApplication createEditedInternship(InternshipApplication internshipToEdit, + EditInternshipDescriptor editInternshipDescriptor) { + assert internshipToEdit != null; + + CompanyName updatedCompanyName = editInternshipDescriptor.getCompanyName() + .orElse(internshipToEdit.getCompanyName()); + JobTitle updatedJobTitle = editInternshipDescriptor.getJobTitle().orElse(internshipToEdit.getJobTitle()); + Location updatedLocation = editInternshipDescriptor.getLocation().orElse(internshipToEdit.getLocation()); + Salary updatedSalary = editInternshipDescriptor.getSalary().orElse(internshipToEdit.getSalary()); + Rating updatedRating = editInternshipDescriptor.getRating().orElse(internshipToEdit.getRating()); + Set updatedQualifications = editInternshipDescriptor.getQualifications() + .orElse(internshipToEdit.getQualifications()); + Set updatedProgrammingLanguages = editInternshipDescriptor.getProgrammingLanguages() + .orElse(internshipToEdit.getProgrammingLanguages()); + Set updatedReviews = editInternshipDescriptor.getReviews().orElse(internshipToEdit.getReviews()); + Set updatedNotes = editInternshipDescriptor.getNotes().orElse(internshipToEdit.getNotes()); + Set updatedReflections = editInternshipDescriptor.getReflections() + .orElse(internshipToEdit.getReflections()); + Contact contact = internshipToEdit.getContact(); + InternshipStatus status = internshipToEdit.getStatus(); + InterviewDate interviewDate = internshipToEdit.getInterviewDate(); + Documents documents = internshipToEdit.getDocuments(); + boolean isArchived = internshipToEdit.isArchived(); + + return new InternshipApplication(updatedCompanyName, updatedJobTitle, updatedReviews, + updatedProgrammingLanguages, updatedQualifications, updatedLocation, updatedSalary, + updatedNotes, updatedRating, updatedReflections, contact, status, isArchived, interviewDate, + documents); } @Override @@ -117,88 +150,177 @@ public boolean equals(Object other) { // state check EditCommand e = (EditCommand) other; return index.equals(e.index) - && editPersonDescriptor.equals(e.editPersonDescriptor); + && editInternshipDescriptor.equals(e.editInternshipDescriptor); } /** - * Stores the details to edit the person with. Each non-empty field value will replace the - * corresponding field value of the person. + * Stores the details to edit the internship with. Each non-empty field value will replace the + * corresponding field value of the internship. */ - public static class EditPersonDescriptor { - private Name name; - private Phone phone; - private Email email; - private Address address; - private Set tags; - - public EditPersonDescriptor() {} + public static class EditInternshipDescriptor { + private CompanyName companyName; + private JobTitle jobTitle; + private Location location; + private Salary salary; + private Rating rating; + private Set qualifications; + private Set programmingLanguages; + private Set reviews; + private Set notes; + private Set reflections; + + public EditInternshipDescriptor() {} /** * 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); + public EditInternshipDescriptor(EditInternshipDescriptor toCopy) { + setCompanyName(toCopy.companyName); + setJobTitle(toCopy.jobTitle); + setLocation(toCopy.location); + setSalary(toCopy.salary); + setRating(toCopy.rating); + setQualifications(toCopy.qualifications); + setProgrammingLanguages(toCopy.programmingLanguages); + setReviews(toCopy.reviews); + setNotes(toCopy.notes); + setReflections(toCopy.reflections); } /** * Returns true if at least one field is edited. */ public boolean isAnyFieldEdited() { - return CollectionUtil.isAnyNonNull(name, phone, email, address, tags); + return CollectionUtil.isAnyNonNull(companyName, jobTitle, location, salary, rating, qualifications, + programmingLanguages, reviews, notes, reflections); + } + + public void setCompanyName(CompanyName companyName) { + this.companyName = companyName; + } + + public Optional getCompanyName() { + return Optional.ofNullable(companyName); } - public void setName(Name name) { - this.name = name; + public void setJobTitle(JobTitle jobTitle) { + this.jobTitle = jobTitle; } - public Optional getName() { - return Optional.ofNullable(name); + public Optional getJobTitle() { + return Optional.ofNullable(jobTitle); } - public void setPhone(Phone phone) { - this.phone = phone; + public void setLocation(Location location) { + this.location = location; } - public Optional getPhone() { - return Optional.ofNullable(phone); + public Optional getLocation() { + return Optional.ofNullable(location); } - public void setEmail(Email email) { - this.email = email; + public void setSalary(Salary salary) { + this.salary = salary; } - public Optional getEmail() { - return Optional.ofNullable(email); + public Optional getSalary() { + return Optional.ofNullable(salary); } - public void setAddress(Address address) { - this.address = address; + public void setRating(Rating rating) { + this.rating = rating; } - public Optional
getAddress() { - return Optional.ofNullable(address); + public Optional getRating() { + return Optional.ofNullable(rating); } /** - * Sets {@code tags} to this object's {@code tags}. - * A defensive copy of {@code tags} is used internally. + * Sets {@code qualifications} to this object's {@code qualifications}. + * A defensive copy of {@code qualifications} is used internally. */ - public void setTags(Set tags) { - this.tags = (tags != null) ? new HashSet<>(tags) : null; + public void setQualifications(Set qualifications) { + this.qualifications = (qualifications != null) ? new HashSet<>(qualifications) : null; } /** - * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException} + * Returns an unmodifiable qualification set, which throws {@code UnsupportedOperationException} * if modification is attempted. - * Returns {@code Optional#empty()} if {@code tags} is null. + * Returns {@code Optional#empty()} if {@code qualifications} is null. + */ + public Optional> getQualifications() { + return (qualifications != null) ? Optional.of(Collections.unmodifiableSet(qualifications)) + : Optional.empty(); + } + + /** + * Sets {@code programmingLanguages} to this object's {@code programmingLanguages}. + * A defensive copy of {@code programmingLanguages} is used internally. */ - public Optional> getTags() { - return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); + public void setProgrammingLanguages(Set programmingLanguages) { + this.programmingLanguages = (programmingLanguages != null) ? new HashSet<>(programmingLanguages) : null; + } + + /** + * Returns an unmodifiable programmingLanguage set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + * Returns {@code Optional#empty()} if {@code programmingLanguages} is null. + */ + public Optional> getProgrammingLanguages() { + return (programmingLanguages != null) ? Optional.of(Collections.unmodifiableSet(programmingLanguages)) + : Optional.empty(); + } + + /** + * Sets {@code reviews} to this object's {@code reviews}. + * A defensive copy of {@code reviews} is used internally. + */ + public void setReviews(Set reviews) { + this.reviews = (reviews != null) ? new HashSet<>(reviews) : null; + } + + /** + * Returns an unmodifiable review set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + * Returns {@code Optional#empty()} if {@code reviews} is null. + */ + public Optional> getReviews() { + return (reviews != null) ? Optional.of(Collections.unmodifiableSet(reviews)) : Optional.empty(); + } + + /** + * Sets {@code notes} to this object's {@code notes}. + * A defensive copy of {@code notes} is used internally. + */ + public void setNotes(Set notes) { + this.notes = (notes != null) ? new HashSet<>(notes) : null; + } + + /** + * Returns an unmodifiable note set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + * Returns {@code Optional#empty()} if {@code notes} is null. + */ + public Optional> getNotes() { + return (notes != null) ? Optional.of(Collections.unmodifiableSet(notes)) : Optional.empty(); + } + + /** + * Sets {@code reflections} to this object's {@code reflections}. + * A defensive copy of {@code reflections} is used internally. + */ + public void setReflections(Set reflections) { + this.reflections = (reflections != null) ? new HashSet<>(reflections) : null; + } + + /** + * Returns an unmodifiable reflection set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + * Returns {@code Optional#empty()} if {@code reflections} is null. + */ + public Optional> getReflections() { + return (reflections != null) ? Optional.of(Collections.unmodifiableSet(reflections)) : Optional.empty(); } @Override @@ -209,18 +331,23 @@ public boolean equals(Object other) { } // instanceof handles nulls - if (!(other instanceof EditPersonDescriptor)) { + if (!(other instanceof EditInternshipDescriptor)) { 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()); + EditInternshipDescriptor e = (EditInternshipDescriptor) other; + + return getCompanyName().equals(e.getCompanyName()) + && getJobTitle().equals(e.getJobTitle()) + && getLocation().equals(e.getLocation()) + && getSalary().equals(e.getSalary()) + && getRating().equals(e.getRating()) + && getQualifications().equals(e.getQualifications()) + && getProgrammingLanguages().equals(e.getProgrammingLanguages()) + && getReviews().equals(e.getReviews()) + && getNotes().equals(e.getNotes()) + && getReflections().equals(e.getReflections()); } } } diff --git a/src/main/java/seedu/address/logic/commands/EditStatusCommand.java b/src/main/java/seedu/address/logic/commands/EditStatusCommand.java new file mode 100644 index 00000000000..fb885fc60f2 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/EditStatusCommand.java @@ -0,0 +1,109 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_STATUS; + +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.application.JobTitle; +import seedu.address.model.application.Location; +import seedu.address.model.application.Note; +import seedu.address.model.application.ProgrammingLanguage; +import seedu.address.model.application.Qualification; +import seedu.address.model.application.Rating; +import seedu.address.model.application.Reflection; +import seedu.address.model.application.Review; +import seedu.address.model.application.Salary; +import seedu.address.model.contact.Contact; +import seedu.address.model.documents.Documents; + +/** + * Edits the status of an application identified using it's displayed index from the list of internship applications. + */ +public class EditStatusCommand extends Command { + + public static final String COMMAND_WORD = "edit_status"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Edits the status the specified application from the list of internships applied.\n" + + "Parameters: INDEX (must be a positive integer) " + + PREFIX_STATUS + "STATUS (must be one of PENDING, RECEIVED, ACCEPTED, DECLINED or REJECTED)\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_STATUS + "NA"; + + public static final String MESSAGE_UPDATE_STATUS_SUCCESS = "Status of application updated: %1$s"; + + private final Index targetIndex; + + private final InternshipStatus toUpdate; + + /** + * @param targetIndex of the internship application to update status + * @param internshipStatus Internship status to update + */ + public EditStatusCommand(Index targetIndex, InternshipStatus internshipStatus) { + requireNonNull(targetIndex); + + this.targetIndex = targetIndex; + toUpdate = internshipStatus; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getSortedFilteredInternshipList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + InternshipApplication internshipToUpdateStatus = lastShownList.get(targetIndex.getZeroBased()); + InternshipApplication updatedApplication = createdUpdatedApplication(internshipToUpdateStatus, toUpdate); + + model.setApplication(internshipToUpdateStatus, updatedApplication); + return new CommandResult(String.format(MESSAGE_UPDATE_STATUS_SUCCESS, updatedApplication)); + } + + /** + * Creates and returns a {@code InternshipApplication} with the status of {@code InternshipStatus} + */ + private static InternshipApplication createdUpdatedApplication(InternshipApplication internshipApplication, + InternshipStatus status) { + assert internshipApplication != null; + + CompanyName companyName = internshipApplication.getCompanyName(); + JobTitle jobTitle = internshipApplication.getJobTitle(); + Set reviews = internshipApplication.getReviews(); + Set programmingLanguages = internshipApplication.getProgrammingLanguages(); + Set qualifications = internshipApplication.getQualifications(); + Location location = internshipApplication.getLocation(); + Salary salary = internshipApplication.getSalary(); + Set notes = internshipApplication.getNotes(); + Rating rating = internshipApplication.getRating(); + Set reflections = internshipApplication.getReflections(); + Contact contact = internshipApplication.getContact(); + boolean isArchived = internshipApplication.isArchived(); + InterviewDate interviewDate = internshipApplication.getInterviewDate(); + Documents documents = internshipApplication.getDocuments(); + + return new InternshipApplication(companyName, jobTitle, reviews, programmingLanguages, qualifications, location, + salary, notes, rating, reflections, contact, status, isArchived, interviewDate, documents); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EditStatusCommand // instanceof handles nulls + && targetIndex.equals(((EditStatusCommand) other).targetIndex) + && toUpdate.equals(((EditStatusCommand) other).toUpdate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java index 3dd85a8ba90..2c970162daf 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java @@ -3,17 +3,17 @@ import seedu.address.model.Model; /** - * Terminates the program. + * Terminates InternEase. */ public class ExitCommand extends Command { public static final String COMMAND_WORD = "exit"; - public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; + public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting InternEase as requested ..."; @Override public CommandResult execute(Model model) { - return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); + return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, false); } } diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java index d6b19b0a0de..7a3cd9a1d8d 100644 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ b/src/main/java/seedu/address/logic/commands/FindCommand.java @@ -1,26 +1,51 @@ package seedu.address.logic.commands; import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE_AFTER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE_BEFORE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE_FROM; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE_TO; +import static seedu.address.logic.parser.CliSyntax.PREFIX_STATUS; import seedu.address.commons.core.Messages; import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.application.NameContainsKeywordsPredicate; /** - * Finds and lists all persons in address book whose name contains any of the argument keywords. + * Finds and lists all internship application in record whose CompanyName + * and/or JobTitle contains any of the argument keywords. * Keyword matching is case insensitive. */ public class FindCommand extends Command { public static final String COMMAND_WORD = "find"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of " - + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": (1) Finds all internship applications whose " + + "CompanyName and/or JobTitle contain any of the specified keywords (case-insensitive) " + + "and displays them as a list with index numbers.\n" + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" - + "Example: " + COMMAND_WORD + " alice bob charlie"; + + "Example: " + COMMAND_WORD + " google software engineer intern\n" + + "(2) Finds all internship applications whose status matches the specified status (case-insensitive) " + + "and displays them as a list with index numbers.\n" + + "Parameters: " + "[" + PREFIX_STATUS + "STATUS]\n" + + "Example: " + COMMAND_WORD + " " + PREFIX_STATUS + "RECEIVED\n" + + "(3) Finds all internship applications whose interview date is before, after " + + "or between specified date(s)\n" + + "(i) Parameters: " + PREFIX_DATE_BEFORE + "DATE\n" + + "Example: " + COMMAND_WORD + " " + PREFIX_DATE_BEFORE + "12/31/2023 at 12:00 PM\n" + + "(ii) Parameters: " + PREFIX_DATE_AFTER + "DATE\n" + + "Example: " + COMMAND_WORD + " " + PREFIX_DATE_AFTER + "12/31/2023 at 12:00 PM\n" + + "(iii) Parameters: " + PREFIX_DATE_FROM + "START_DATE " + PREFIX_DATE_TO + "END_DATE\n" + + "Example: " + COMMAND_WORD + " " + PREFIX_DATE_FROM + "12/1/2023 at 1:00 PM " + + PREFIX_DATE_TO + "12/31/2023 at 7:00 PM"; private final NameContainsKeywordsPredicate predicate; + public FindCommand() { + this.predicate = null; + } + public FindCommand(NameContainsKeywordsPredicate predicate) { this.predicate = predicate; } @@ -28,9 +53,10 @@ public FindCommand(NameContainsKeywordsPredicate predicate) { @Override public CommandResult execute(Model model) { requireNonNull(model); - model.updateFilteredPersonList(predicate); + model.updateFilteredInternshipList(predicate); return new CommandResult( - String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); + String.format(Messages.MESSAGE_APPLICATION_LISTED_OVERVIEW, + model.getSortedFilteredInternshipList().size())); } @Override diff --git a/src/main/java/seedu/address/logic/commands/FindDateCommand.java b/src/main/java/seedu/address/logic/commands/FindDateCommand.java new file mode 100644 index 00000000000..c272414bdc3 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/FindDateCommand.java @@ -0,0 +1,42 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.commons.core.Messages; +import seedu.address.model.Model; +import seedu.address.model.application.DatePredicate; + +/** + * Finds and lists all internship application in record whose {@code InterviewDate} + * is before, after or between specified date(s). + * The date specified is inclusive. + */ +public class FindDateCommand extends FindCommand { + private final DatePredicate predicate; + + /** + * Creates an FindDateCommand to find the internship application with {@code InternshipDate} + * matches the given predicate. + * + * @param predicate The predicate containing date condition to be matched + */ + public FindDateCommand(DatePredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredInternshipList(predicate); + return new CommandResult( + String.format(Messages.MESSAGE_APPLICATION_LISTED_OVERVIEW, + model.getSortedFilteredInternshipList().size())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FindDateCommand // instanceof handles nulls + && predicate.equals(((FindDateCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/FindStatusCommand.java b/src/main/java/seedu/address/logic/commands/FindStatusCommand.java new file mode 100644 index 00000000000..b99b0477634 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/FindStatusCommand.java @@ -0,0 +1,41 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.commons.core.Messages; +import seedu.address.model.Model; +import seedu.address.model.application.StatusPredicate; + +/** + * Finds and lists all internship application in record whose status + * matches the specified keyword. + * Keyword matching is case insensitive. + */ +public class FindStatusCommand extends FindCommand { + private final StatusPredicate predicate; + + /** + * Creates an FindStatusCommand to find the specified {@code InternshipStatus}. + * + * @param predicate The predicate containing status to be matched + */ + public FindStatusCommand(StatusPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredInternshipList(predicate); + return new CommandResult( + String.format(Messages.MESSAGE_APPLICATION_LISTED_OVERVIEW, + model.getSortedFilteredInternshipList().size())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FindStatusCommand // instanceof handles nulls + && predicate.equals(((FindStatusCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java index bf824f91bd0..6a62830801a 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java @@ -6,7 +6,6 @@ * Format full help instructions for every command for display. */ public class HelpCommand extends Command { - public static final String COMMAND_WORD = "help"; public static final String MESSAGE_USAGE = COMMAND_WORD + ": Shows program usage instructions.\n" @@ -16,6 +15,6 @@ public class HelpCommand extends Command { @Override public CommandResult execute(Model model) { - return new CommandResult(SHOWING_HELP_MESSAGE, true, false); + return new CommandResult(SHOWING_HELP_MESSAGE, true, false, false); } } diff --git a/src/main/java/seedu/address/logic/commands/ListArchivedCommand.java b/src/main/java/seedu/address/logic/commands/ListArchivedCommand.java new file mode 100644 index 00000000000..89aca9448f1 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ListArchivedCommand.java @@ -0,0 +1,33 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ARCHIVED_APPLICATIONS; + +import java.util.List; + +import seedu.address.model.Model; +import seedu.address.model.application.InternshipApplication; + +/** + * Lists all internship applications which are archived in the list of internship applications to the user. + */ +public class ListArchivedCommand extends Command { + + public static final String COMMAND_WORD = "list_archived"; + + public static final String MESSAGE_SUCCESS = "Listed all archived applications"; + public static final String MESSAGE_NO_APPLICATIONS = "No archived applications at the moment"; + + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredInternshipList(PREDICATE_SHOW_ARCHIVED_APPLICATIONS); + List lastShownList = model.getSortedFilteredInternshipList(); + if (lastShownList.size() > 0) { + return new CommandResult(MESSAGE_SUCCESS); + } else { + return new CommandResult(MESSAGE_NO_APPLICATIONS); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java index 84be6ad2596..76453983c29 100644 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ b/src/main/java/seedu/address/logic/commands/ListCommand.java @@ -1,24 +1,33 @@ package seedu.address.logic.commands; import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import static seedu.address.model.Model.PREDICATE_SHOW_ONGOING_APPLICATIONS; + +import java.util.List; import seedu.address.model.Model; +import seedu.address.model.application.InternshipApplication; /** - * Lists all persons in the address book to the user. + * Lists all internship applications in the address book to the user. */ public class ListCommand extends Command { public static final String COMMAND_WORD = "list"; - public static final String MESSAGE_SUCCESS = "Listed all persons"; + public static final String MESSAGE_SUCCESS = "Listed all applications"; + public static final String MESSAGE_NO_APPLICATIONS = "No active applications at the moment"; @Override public CommandResult execute(Model model) { requireNonNull(model); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(MESSAGE_SUCCESS); + model.updateFilteredInternshipList(PREDICATE_SHOW_ONGOING_APPLICATIONS); + List lastShownList = model.getSortedFilteredInternshipList(); + if (lastShownList.size() > 0) { + return new CommandResult(MESSAGE_SUCCESS); + } else { + return new CommandResult(MESSAGE_NO_APPLICATIONS); + } } } diff --git a/src/main/java/seedu/address/logic/commands/RemindCommand.java b/src/main/java/seedu/address/logic/commands/RemindCommand.java new file mode 100644 index 00000000000..d6b095d07ae --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/RemindCommand.java @@ -0,0 +1,17 @@ +package seedu.address.logic.commands; + +import seedu.address.model.Model; + +/** + * Displays reminder for upcoming interview. + */ +public class RemindCommand extends Command { + public static final String COMMAND_WORD = "remind"; + + public static final String SHOWING_REMINDER_MESSAGE = "Opened reminder window."; + + @Override + public CommandResult execute(Model model) { + return new CommandResult(SHOWING_REMINDER_MESSAGE, false, false, true); + } +} diff --git a/src/main/java/seedu/address/logic/commands/RevertAllCommand.java b/src/main/java/seedu/address/logic/commands/RevertAllCommand.java new file mode 100644 index 00000000000..6994c14a4ac --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/RevertAllCommand.java @@ -0,0 +1,35 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.address.model.Model; +import seedu.address.model.application.InternshipApplication; + +/** + * Reverts all deleted or cleared internship applications that are stored in the cache list for current session only. + */ +public class RevertAllCommand extends Command { + + public static final String COMMAND_WORD = "revert_all"; + + public static final String MESSAGE_SUCCESS = "All cleared internship applications are reverted!"; + public static final String MESSAGE_NO_APPLICATIONS = "No applications at the moment"; + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + + List cacheList = model.getCachedInternshipList(); + + if (cacheList.size() > 0) { + model.setEmptyInternshipCacheList(); + model.addApplications(cacheList); + return new CommandResult(MESSAGE_SUCCESS); + } else { + return new CommandResult(MESSAGE_NO_APPLICATIONS); + } + } + +} diff --git a/src/main/java/seedu/address/logic/commands/RevertCommand.java b/src/main/java/seedu/address/logic/commands/RevertCommand.java new file mode 100644 index 00000000000..a1d2ef6b49d --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/RevertCommand.java @@ -0,0 +1,44 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.address.model.Model; +import seedu.address.model.application.InternshipApplication; + +/** + * Reverts a most recently deleted or cleared internship application stored in the cache list for current session only. + */ +public class RevertCommand extends Command { + + public static final String COMMAND_WORD = "revert"; + + public static final String MESSAGE_SUCCESS = "Previous deletion of internship application is reverted: %1$s"; + public static final String MESSAGE_NO_APPLICATIONS = "No applications at the moment"; + public static final String MESSAGE_DUPLICATE_APPLICATION = "This internship application " + + "already exists in the tracker"; + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + + List cacheList = model.getCachedInternshipList(); + + if (cacheList.size() > 0) { + InternshipApplication application = model.getAndRemoveCachedApplication(); + return hasApplication(model, application); + } else { + return new CommandResult(MESSAGE_NO_APPLICATIONS); + } + } + + private CommandResult hasApplication(Model model, InternshipApplication application) { + if (model.hasApplication(application)) { + return new CommandResult(MESSAGE_DUPLICATE_APPLICATION); + } else { + model.addApplication(application); + return new CommandResult(String.format(MESSAGE_SUCCESS, application)); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/SortCommand.java b/src/main/java/seedu/address/logic/commands/SortCommand.java new file mode 100644 index 00000000000..4d6d23c821c --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/SortCommand.java @@ -0,0 +1,66 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_JOB_TITLE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_STATUS; + +import java.util.Comparator; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.parser.Prefix; +import seedu.address.model.Model; +import seedu.address.model.application.InternshipApplication; + +/** + * Sorts and lists all internship application by any one of the following attributes: CompanyName, + * JobTitle, Status, InterviewDate in ascending order. + */ +public class SortCommand extends Command { + + public static final String COMMAND_WORD = "sort"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Sorts the list of internship applications" + + "by ascending order. Exactly one parameter should be specified. " + + "If the value is not present, it will be placed at the end of the list.\n" + + "Parameters: " + + "[" + PREFIX_COMPANY_NAME + "COMPANY_NAME] " + + "[" + PREFIX_JOB_TITLE + "JOB_TITLE] " + + "[" + PREFIX_STATUS + "STATUS] " + + "[" + PREFIX_DATE + "INTERVIEW_DATE]\n" + + "Example: " + COMMAND_WORD + " " + PREFIX_DATE; + + public static final Prefix[] PREFIXES_SUPPORTED = { + PREFIX_COMPANY_NAME, PREFIX_JOB_TITLE, PREFIX_STATUS, PREFIX_DATE + }; + + private final Comparator comparator; + + /** + * Creates a SortCommand with comparator to sort the list of internship application. + * + * @param comparator Comparator used to sort the list + */ + public SortCommand(Comparator comparator) { + requireNonNull(comparator); + this.comparator = comparator; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateSortedFilteredInternshipList(comparator); + return new CommandResult( + String.format(Messages.MESSAGE_APPLICATION_LISTED_OVERVIEW, + model.getSortedFilteredInternshipList().size())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof SortCommand // instanceof handles nulls + && comparator.equals(((SortCommand) other).comparator)); // state check + } +} + diff --git a/src/main/java/seedu/address/logic/commands/UnarchiveCommand.java b/src/main/java/seedu/address/logic/commands/UnarchiveCommand.java new file mode 100644 index 00000000000..9f0e993c4eb --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/UnarchiveCommand.java @@ -0,0 +1,113 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ONGOING_APPLICATIONS; + +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.application.JobTitle; +import seedu.address.model.application.Location; +import seedu.address.model.application.Note; +import seedu.address.model.application.ProgrammingLanguage; +import seedu.address.model.application.Qualification; +import seedu.address.model.application.Rating; +import seedu.address.model.application.Reflection; +import seedu.address.model.application.Review; +import seedu.address.model.application.Salary; +import seedu.address.model.contact.Contact; +import seedu.address.model.documents.Documents; + +/** + * Unarchives an internship application identified using it's displayed index from the list of internship applications. + */ +public class UnarchiveCommand extends Command { + + public static final String COMMAND_WORD = "unarchive"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Unarchives the specified application from the list of internships applied.\n" + + "Unarchives the application of internship at the specified INDEX.\n" + + "The index refers to the index number shown in the displayed internship list.\n" + + "Parameters: INDEX (must be a positive integer 1, 2, 3, ...)\n" + + "Example: " + COMMAND_WORD + " 2"; + + public static final String MESSAGE_UNARCHIVE_APPLICATION_SUCCESS = "Unarchived Application: %1$s"; + public static final String MESSAGE_APPLICATION_NOT_ARCHIVED = "Application is not currently archived!"; + + private final Index targetIndex; + + /** + * Creates an UnarchiveCommand to unarchive the specified {@code targetIndex} internship + * + * @param targetIndex of the internship application to unarchive + */ + public UnarchiveCommand(Index targetIndex) { + requireNonNull(targetIndex); + + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getSortedFilteredInternshipList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + InternshipApplication internshipToUnarchive = lastShownList.get(targetIndex.getZeroBased()); + + if (!internshipToUnarchive.isArchived()) { + throw new CommandException(MESSAGE_APPLICATION_NOT_ARCHIVED); + } + + InternshipApplication archivedApplication = createdUnarchivedApplication(internshipToUnarchive); + + model.setApplication(internshipToUnarchive, archivedApplication); + model.updateFilteredInternshipList(PREDICATE_SHOW_ONGOING_APPLICATIONS); + return new CommandResult(String.format(MESSAGE_UNARCHIVE_APPLICATION_SUCCESS, archivedApplication)); + } + + /** + * Creates and returns an archived {@code InternshipApplication} + */ + private static InternshipApplication createdUnarchivedApplication(InternshipApplication internshipApplication) { + assert internshipApplication != null; + + CompanyName companyName = internshipApplication.getCompanyName(); + JobTitle jobTitle = internshipApplication.getJobTitle(); + Set reviews = internshipApplication.getReviews(); + Set programmingLanguages = internshipApplication.getProgrammingLanguages(); + Set qualifications = internshipApplication.getQualifications(); + Location location = internshipApplication.getLocation(); + Salary salary = internshipApplication.getSalary(); + Set notes = internshipApplication.getNotes(); + Rating rating = internshipApplication.getRating(); + Set reflections = internshipApplication.getReflections(); + Contact contact = internshipApplication.getContact(); + InternshipStatus status = internshipApplication.getStatus(); + InterviewDate interviewDate = internshipApplication.getInterviewDate(); + Documents documents = internshipApplication.getDocuments(); + + return new InternshipApplication(companyName, jobTitle, reviews, programmingLanguages, qualifications, location, + salary, notes, rating, reflections, contact, status, false, interviewDate, + documents); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UnarchiveCommand // instanceof handles nulls + && targetIndex.equals(((UnarchiveCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/contact/AddContactCommand.java b/src/main/java/seedu/address/logic/commands/contact/AddContactCommand.java new file mode 100644 index 00000000000..2c9f02788e4 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/contact/AddContactCommand.java @@ -0,0 +1,115 @@ +package seedu.address.logic.commands.contact; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; + +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.application.JobTitle; +import seedu.address.model.application.Location; +import seedu.address.model.application.Note; +import seedu.address.model.application.ProgrammingLanguage; +import seedu.address.model.application.Qualification; +import seedu.address.model.application.Rating; +import seedu.address.model.application.Reflection; +import seedu.address.model.application.Review; +import seedu.address.model.application.Salary; +import seedu.address.model.contact.Contact; +import seedu.address.model.documents.Documents; + +/** + * Adds a contact to an application identified using it's displayed index from the list of internship applications. + */ +public class AddContactCommand extends Command { + + public static final String COMMAND_WORD = "add_contact"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Adds contact details to the specified application from the list of internships applied.\n" + + "Parameters: INDEX (must be a positive integer) " + + PREFIX_PHONE + "PHONE " + + PREFIX_EMAIL + "EMAIL " + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_PHONE + "98765432 " + + PREFIX_EMAIL + "johnd@example.com "; + + public static final String MESSAGE_ADD_CONTACT_SUCCESS = "Contact details added to application: %1$s"; + + private final Index targetIndex; + + private final Contact toAdd; + + /** + * @param targetIndex of the internship application to add contact details + * @param contact Contact to add + */ + public AddContactCommand(Index targetIndex, Contact contact) { + requireNonNull(targetIndex); + + this.targetIndex = targetIndex; + toAdd = contact; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getSortedFilteredInternshipList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + InternshipApplication internshipToAddContact = lastShownList.get(targetIndex.getZeroBased()); + InternshipApplication internshipWithContact = createInternshipWithContact(internshipToAddContact, toAdd); + + model.setApplication(internshipToAddContact, internshipWithContact); + return new CommandResult(String.format(MESSAGE_ADD_CONTACT_SUCCESS, internshipToAddContact + "\n" + toAdd)); + } + + /** + * Creates and returns a {@code InternshipApplication} with the details of {@code internshipToAddContact} + * added with the contact {@code toAdd}. + */ + private static InternshipApplication createInternshipWithContact(InternshipApplication internshipToAddContact, + Contact contact) { + assert internshipToAddContact != null; + + CompanyName companyName = internshipToAddContact.getCompanyName(); + JobTitle jobTitle = internshipToAddContact.getJobTitle(); + Set reviews = internshipToAddContact.getReviews(); + Set programmingLanguages = internshipToAddContact.getProgrammingLanguages(); + Set qualifications = internshipToAddContact.getQualifications(); + Location location = internshipToAddContact.getLocation(); + Salary salary = internshipToAddContact.getSalary(); + Set notes = internshipToAddContact.getNotes(); + Rating rating = internshipToAddContact.getRating(); + Set reflections = internshipToAddContact.getReflections(); + InternshipStatus status = internshipToAddContact.getStatus(); + boolean isArchived = internshipToAddContact.isArchived(); + InterviewDate interviewDate = internshipToAddContact.getInterviewDate(); + Documents documents = internshipToAddContact.getDocuments(); + + return new InternshipApplication(companyName, jobTitle, reviews, programmingLanguages, qualifications, location, + salary, notes, rating, reflections, contact, status, isArchived, interviewDate, documents); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddContactCommand // instanceof handles nulls + && targetIndex.equals(((AddContactCommand) other).targetIndex) + && toAdd.equals(((AddContactCommand) other).toAdd)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/contact/DeleteContactCommand.java b/src/main/java/seedu/address/logic/commands/contact/DeleteContactCommand.java new file mode 100644 index 00000000000..72de20f4cf9 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/contact/DeleteContactCommand.java @@ -0,0 +1,113 @@ +package seedu.address.logic.commands.contact; + +import static java.util.Objects.requireNonNull; + +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.application.JobTitle; +import seedu.address.model.application.Location; +import seedu.address.model.application.Note; +import seedu.address.model.application.ProgrammingLanguage; +import seedu.address.model.application.Qualification; +import seedu.address.model.application.Rating; +import seedu.address.model.application.Reflection; +import seedu.address.model.application.Review; +import seedu.address.model.application.Salary; +import seedu.address.model.documents.Documents; + +/** + * Delete the contact details of an application identified using it's displayed index + * from the list of internship applications. + */ +public class DeleteContactCommand extends Command { + + public static final String COMMAND_WORD = "delete_contact"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the contact details of of the specified application from the " + + "list of internships applied.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1 "; + + public static final String MESSAGE_CONTACT_NOT_FOUND = + "No contact added"; + + public static final String MESSAGE_DELETE_CONTACT_SUCCESS = + "Contact deleted from application: %1$s"; + + private final Index targetIndex; + + /** + * @param targetIndex of the internship application to delete contact + */ + public DeleteContactCommand(Index targetIndex) { + requireNonNull(targetIndex); + + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getSortedFilteredInternshipList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + InternshipApplication internshipToDeleteContact = lastShownList.get(targetIndex.getZeroBased()); + if (internshipToDeleteContact.getContact() == null) { + throw new CommandException(MESSAGE_CONTACT_NOT_FOUND); + } + InternshipApplication internshipWithContactDeleted = + createInternshipWithContactDeleted(internshipToDeleteContact); + + model.setApplication(internshipToDeleteContact, internshipWithContactDeleted); + return new CommandResult(String.format(MESSAGE_DELETE_CONTACT_SUCCESS, internshipToDeleteContact)); + } + + /** + * Creates and returns a {@code InternshipApplication} with the details of {@code internshipToDeleteDocuments} + * and contact removed. + */ + private static InternshipApplication createInternshipWithContactDeleted( + InternshipApplication internshipToDeleteContact) { + assert internshipToDeleteContact != null; + + CompanyName companyName = internshipToDeleteContact.getCompanyName(); + JobTitle jobTitle = internshipToDeleteContact.getJobTitle(); + Set reviews = internshipToDeleteContact.getReviews(); + Set programmingLanguages = internshipToDeleteContact.getProgrammingLanguages(); + Set qualifications = internshipToDeleteContact.getQualifications(); + Location location = internshipToDeleteContact.getLocation(); + Salary salary = internshipToDeleteContact.getSalary(); + Set notes = internshipToDeleteContact.getNotes(); + Rating rating = internshipToDeleteContact.getRating(); + Set reflections = internshipToDeleteContact.getReflections(); + InternshipStatus status = internshipToDeleteContact.getStatus(); + boolean isArchived = internshipToDeleteContact.isArchived(); + InterviewDate interviewDate = internshipToDeleteContact.getInterviewDate(); + Documents documents = internshipToDeleteContact.getDocuments(); + + return new InternshipApplication(companyName, jobTitle, reviews, programmingLanguages, qualifications, location, + salary, notes, rating, reflections, null, status, isArchived, interviewDate, documents); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteContactCommand // instanceof handles nulls + && targetIndex.equals(((DeleteContactCommand) other).targetIndex)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/contact/EditContactCommand.java b/src/main/java/seedu/address/logic/commands/contact/EditContactCommand.java new file mode 100644 index 00000000000..ac9bc701602 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/contact/EditContactCommand.java @@ -0,0 +1,203 @@ +package seedu.address.logic.commands.contact; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; + +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.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.application.JobTitle; +import seedu.address.model.application.Location; +import seedu.address.model.application.Note; +import seedu.address.model.application.ProgrammingLanguage; +import seedu.address.model.application.Qualification; +import seedu.address.model.application.Rating; +import seedu.address.model.application.Reflection; +import seedu.address.model.application.Review; +import seedu.address.model.application.Salary; +import seedu.address.model.contact.Contact; +import seedu.address.model.contact.Email; +import seedu.address.model.contact.Phone; +import seedu.address.model.documents.Documents; + +/** + * Edits the contact details of an existing application in the list of internship applications. + */ +public class EditContactCommand extends Command { + public static final String COMMAND_WORD = "edit_contact"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the contact of the application identified " + + "by the index number used in the internship application list. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_PHONE + "PHONE] " + + "[" + PREFIX_EMAIL + "EMAIL]\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_PHONE + "98765432 " + + PREFIX_EMAIL + "johndoe@example.com"; + + public static final String MESSAGE_EDIT_CONTACT_SUCCESS = "Edited contact details for application: %1$s"; + public static final String MESSAGE_CONTACT_NOT_FOUND = + "No contact added"; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + + private final Index index; + private final EditContactDescriptor editContactDescriptor; + + /** + * @param index of the application in the filtered internship application list to edit + * @param editContactDescriptor details to edit the contact with + */ + public EditContactCommand(Index index, EditContactDescriptor editContactDescriptor) { + requireNonNull(index); + requireNonNull(editContactDescriptor); + + this.index = index; + this.editContactDescriptor = new EditContactDescriptor(editContactDescriptor); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getSortedFilteredInternshipList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + InternshipApplication internshipToUpdateContact = lastShownList.get(index.getZeroBased()); + if (internshipToUpdateContact.getContact() == null) { + throw new CommandException(MESSAGE_CONTACT_NOT_FOUND); + } + InternshipApplication internshipWithUpdatedContact = + createInternshipWithUpdatedContact(internshipToUpdateContact, editContactDescriptor); + + model.setApplication(internshipToUpdateContact, internshipWithUpdatedContact); + return new CommandResult(String.format(MESSAGE_EDIT_CONTACT_SUCCESS, internshipWithUpdatedContact)); + } + + /** + * Creates and returns a {@code InternshipApplication} with the details of {@code internshipToUpdateContact} + * and contact edited with {@code editContactDescriptor}. + */ + private static InternshipApplication createInternshipWithUpdatedContact( + InternshipApplication internshipToUpdateContact, EditContactDescriptor editContactDescriptor) { + assert internshipToUpdateContact != null; + + CompanyName companyName = internshipToUpdateContact.getCompanyName(); + JobTitle jobTitle = internshipToUpdateContact.getJobTitle(); + Set reviews = internshipToUpdateContact.getReviews(); + Set programmingLanguages = internshipToUpdateContact.getProgrammingLanguages(); + Set qualifications = internshipToUpdateContact.getQualifications(); + Location location = internshipToUpdateContact.getLocation(); + Salary salary = internshipToUpdateContact.getSalary(); + Set notes = internshipToUpdateContact.getNotes(); + Rating rating = internshipToUpdateContact.getRating(); + Set reflections = internshipToUpdateContact.getReflections(); + Phone phone = editContactDescriptor.getPhone() + .orElse(internshipToUpdateContact.getContact().getPhone()); + Email email = editContactDescriptor.getEmail() + .orElse(internshipToUpdateContact.getContact().getEmail()); + Contact newContact = new Contact(phone, email); + InternshipStatus status = internshipToUpdateContact.getStatus(); + boolean isArchived = internshipToUpdateContact.isArchived(); + InterviewDate interviewDate = internshipToUpdateContact.getInterviewDate(); + Documents documents = internshipToUpdateContact.getDocuments(); + + return new InternshipApplication(companyName, jobTitle, reviews, programmingLanguages, qualifications, location, + salary, notes, rating, reflections, newContact, status, isArchived, interviewDate, documents); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditContactCommand)) { + return false; + } + + // state check + EditContactCommand e = (EditContactCommand) other; + return index.equals(e.index) + && editContactDescriptor.equals(e.editContactDescriptor); + } + + /** + * Stores the details to edit the contact with. Each non-empty field value will replace the + * corresponding field value of the contact. + */ + public static class EditContactDescriptor { + private Phone phone; + private Email email; + + public EditContactDescriptor() {} + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public EditContactDescriptor(EditContactDescriptor toCopy) { + setPhone(toCopy.phone); + setEmail(toCopy.email); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(phone, email); + } + + 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); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditContactDescriptor)) { + return false; + } + + // state check + EditContactDescriptor e = (EditContactDescriptor) other; + + return getPhone().equals(e.getPhone()) + && getEmail().equals(e.getEmail()); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/documents/AddDocumentsCommand.java b/src/main/java/seedu/address/logic/commands/documents/AddDocumentsCommand.java new file mode 100644 index 00000000000..7d54548fdd2 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/documents/AddDocumentsCommand.java @@ -0,0 +1,118 @@ +package seedu.address.logic.commands.documents; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COVER_LETTER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RESUME; + +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.application.JobTitle; +import seedu.address.model.application.Location; +import seedu.address.model.application.Note; +import seedu.address.model.application.ProgrammingLanguage; +import seedu.address.model.application.Qualification; +import seedu.address.model.application.Rating; +import seedu.address.model.application.Reflection; +import seedu.address.model.application.Review; +import seedu.address.model.application.Salary; +import seedu.address.model.contact.Contact; +import seedu.address.model.documents.Documents; + +/** + * Adds links to a resume and/or cover letter to an application identified using it's displayed index + * from the list of internship applications. + */ +public class AddDocumentsCommand extends Command { + + public static final String COMMAND_WORD = "add_docs"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Adds link to a resume and/or cover letter to the specified application from the " + + "list of internships applied.\n" + + "Parameters: INDEX (must be a positive integer) " + + PREFIX_RESUME + "RESUME " + + PREFIX_COVER_LETTER + "COVER_LETTER\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_RESUME + "https://docs.google.com/document/d/EXAMPLE_RESUME/edit " + + PREFIX_COVER_LETTER + "https://docs.google.com/document/d/EXAMPLE_COVER_LETTER/edit"; + + public static final String MESSAGE_ADD_DOCUMENTS_SUCCESS = "Resume and/or cover letter added to application: %1$s"; + + private final Index targetIndex; + + + private final Documents toAdd; + + /** + * @param targetIndex of the internship application to add documents + * @param documents documents to be added + */ + public AddDocumentsCommand(Index targetIndex, Documents documents) { + requireNonNull(targetIndex); + + this.targetIndex = targetIndex; + this.toAdd = documents; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getSortedFilteredInternshipList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + InternshipApplication internshipToAddDocuments = lastShownList.get(targetIndex.getZeroBased()); + InternshipApplication internshipWithDocuments = createInternshipWithDocuments(internshipToAddDocuments, toAdd); + + model.setApplication(internshipToAddDocuments, internshipWithDocuments); + return new CommandResult(String.format(MESSAGE_ADD_DOCUMENTS_SUCCESS, internshipToAddDocuments + "\n" + toAdd)); + } + + /** + * Creates and returns a {@code InternshipApplication} with the details of {@code internshipToAddDocuments} + * added with the documents {@code toAdd}. + */ + private static InternshipApplication createInternshipWithDocuments(InternshipApplication internshipToAddDocuments, + Documents documents) { + assert internshipToAddDocuments != null; + + CompanyName companyName = internshipToAddDocuments.getCompanyName(); + JobTitle jobTitle = internshipToAddDocuments.getJobTitle(); + Set reviews = internshipToAddDocuments.getReviews(); + Set programmingLanguages = internshipToAddDocuments.getProgrammingLanguages(); + Set qualifications = internshipToAddDocuments.getQualifications(); + Location location = internshipToAddDocuments.getLocation(); + Salary salary = internshipToAddDocuments.getSalary(); + Set notes = internshipToAddDocuments.getNotes(); + Rating rating = internshipToAddDocuments.getRating(); + Set reflections = internshipToAddDocuments.getReflections(); + Contact contact = internshipToAddDocuments.getContact(); + boolean isArchived = internshipToAddDocuments.isArchived(); + InternshipStatus status = internshipToAddDocuments.getStatus(); + InterviewDate interviewDate = internshipToAddDocuments.getInterviewDate(); + + return new InternshipApplication(companyName, jobTitle, reviews, programmingLanguages, qualifications, location, + salary, notes, rating, reflections, contact, status, isArchived, interviewDate, documents); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddDocumentsCommand // instanceof handles nulls + && targetIndex.equals(((AddDocumentsCommand) other).targetIndex) + && toAdd.equals(((AddDocumentsCommand) other).toAdd)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/documents/DeleteDocumentsCommand.java b/src/main/java/seedu/address/logic/commands/documents/DeleteDocumentsCommand.java new file mode 100644 index 00000000000..a29525daef0 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/documents/DeleteDocumentsCommand.java @@ -0,0 +1,113 @@ +package seedu.address.logic.commands.documents; + +import static java.util.Objects.requireNonNull; + +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.application.JobTitle; +import seedu.address.model.application.Location; +import seedu.address.model.application.Note; +import seedu.address.model.application.ProgrammingLanguage; +import seedu.address.model.application.Qualification; +import seedu.address.model.application.Rating; +import seedu.address.model.application.Reflection; +import seedu.address.model.application.Review; +import seedu.address.model.application.Salary; +import seedu.address.model.contact.Contact; + +/** + * Delete links to the resume and/or cover letter of an application identified using it's displayed index + * from the list of internship applications. + */ +public class DeleteDocumentsCommand extends Command { + + public static final String COMMAND_WORD = "delete_docs"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes link to the resume and/or cover letter of the specified application from the " + + "list of internships applied.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1 "; + + public static final String MESSAGE_DOCUMENTS_NOT_FOUND = + "No resume and/or cover letter added"; + + public static final String MESSAGE_DELETE_DOCUMENTS_SUCCESS = + "Resume and/or cover letter deleted from application: %1$s"; + + private final Index targetIndex; + + /** + * @param targetIndex of the internship application to delete documents + */ + public DeleteDocumentsCommand(Index targetIndex) { + requireNonNull(targetIndex); + + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getSortedFilteredInternshipList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + InternshipApplication internshipToDeleteDocuments = lastShownList.get(targetIndex.getZeroBased()); + if (internshipToDeleteDocuments.getDocuments() == null) { + throw new CommandException(MESSAGE_DOCUMENTS_NOT_FOUND); + } + InternshipApplication internshipWithDocumentsDeleted = + createInternshipWithDocumentsDeleted(internshipToDeleteDocuments); + + model.setApplication(internshipToDeleteDocuments, internshipWithDocumentsDeleted); + return new CommandResult(String.format(MESSAGE_DELETE_DOCUMENTS_SUCCESS, internshipToDeleteDocuments)); + } + + /** + * Creates and returns a {@code InternshipApplication} with the details of {@code internshipToDeleteDocuments} + * and documents removed. + */ + private static InternshipApplication createInternshipWithDocumentsDeleted( + InternshipApplication internshipToDeleteDocuments) { + assert internshipToDeleteDocuments != null; + + CompanyName companyName = internshipToDeleteDocuments.getCompanyName(); + JobTitle jobTitle = internshipToDeleteDocuments.getJobTitle(); + Set reviews = internshipToDeleteDocuments.getReviews(); + Set programmingLanguages = internshipToDeleteDocuments.getProgrammingLanguages(); + Set qualifications = internshipToDeleteDocuments.getQualifications(); + Location location = internshipToDeleteDocuments.getLocation(); + Salary salary = internshipToDeleteDocuments.getSalary(); + Set notes = internshipToDeleteDocuments.getNotes(); + Rating rating = internshipToDeleteDocuments.getRating(); + Set reflections = internshipToDeleteDocuments.getReflections(); + Contact contact = internshipToDeleteDocuments.getContact(); + InternshipStatus status = internshipToDeleteDocuments.getStatus(); + boolean isArchived = internshipToDeleteDocuments.isArchived(); + InterviewDate interviewDate = internshipToDeleteDocuments.getInterviewDate(); + + return new InternshipApplication(companyName, jobTitle, reviews, programmingLanguages, qualifications, location, + salary, notes, rating, reflections, contact, status, isArchived, interviewDate, null); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteDocumentsCommand // instanceof handles nulls + && targetIndex.equals(((DeleteDocumentsCommand) other).targetIndex)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/documents/EditDocumentsCommand.java b/src/main/java/seedu/address/logic/commands/documents/EditDocumentsCommand.java new file mode 100644 index 00000000000..c8a72ca1c89 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/documents/EditDocumentsCommand.java @@ -0,0 +1,201 @@ +package seedu.address.logic.commands.documents; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COVER_LETTER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RESUME; + +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.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.application.JobTitle; +import seedu.address.model.application.Location; +import seedu.address.model.application.Note; +import seedu.address.model.application.ProgrammingLanguage; +import seedu.address.model.application.Qualification; +import seedu.address.model.application.Rating; +import seedu.address.model.application.Reflection; +import seedu.address.model.application.Review; +import seedu.address.model.application.Salary; +import seedu.address.model.contact.Contact; +import seedu.address.model.documents.CoverLetterLink; +import seedu.address.model.documents.Documents; +import seedu.address.model.documents.ResumeLink; + +/** + * Edits links to the resume and/or cover letter of an application identified using it's displayed index + * from the list of internship applications. + */ +public class EditDocumentsCommand extends Command { + + public static final String COMMAND_WORD = "edit_docs"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Edits links to the resume and/or cover letter of the specified application from the " + + "list of internships applied.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_RESUME + "RESUME] " + + "[" + PREFIX_COVER_LETTER + "COVER_LETTER]\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_RESUME + "https://docs.google.com/document/d/EXAMPLE_RESUME/edit " + + PREFIX_COVER_LETTER + "https://docs.google.com/document/d/EXAMPLE_COVER_LETTER/edit"; + + public static final String MESSAGE_DOCUMENTS_NOT_FOUND = + "No resume and/or cover letter added"; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + public static final String MESSAGE_EDIT_DOCUMENTS_SUCCESS = + "Resume and/or cover letter updated for application: %1$s"; + + private final Index targetIndex; + + + private final EditDocumentsDescriptor editDocumentsDescriptor; + + /** + * @param targetIndex of the internship application to edit documents + * @param editDocumentsDescriptor details to edit the documents with + */ + public EditDocumentsCommand(Index targetIndex, EditDocumentsDescriptor editDocumentsDescriptor) { + requireNonNull(targetIndex); + + this.targetIndex = targetIndex; + this.editDocumentsDescriptor = editDocumentsDescriptor; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getSortedFilteredInternshipList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + InternshipApplication internshipToEditDocuments = lastShownList.get(targetIndex.getZeroBased()); + if (internshipToEditDocuments.getDocuments() == null) { + throw new CommandException(MESSAGE_DOCUMENTS_NOT_FOUND); + } + InternshipApplication internshipWithUpdatedDocuments = + createInternshipWithUpdatedDocuments(internshipToEditDocuments, editDocumentsDescriptor); + + model.setApplication(internshipToEditDocuments, internshipWithUpdatedDocuments); + return new CommandResult(String.format(MESSAGE_EDIT_DOCUMENTS_SUCCESS, internshipToEditDocuments)); + } + + /** + * Creates and returns a {@code InternshipApplication} with the details of {@code internshipToEditDocuments} + * edited with {@code editDocumentsDescriptor}. + */ + private static InternshipApplication createInternshipWithUpdatedDocuments( + InternshipApplication internshipToEditDocuments, + EditDocumentsDescriptor editDocumentsDescriptor) { + + assert internshipToEditDocuments != null; + + CompanyName companyName = internshipToEditDocuments.getCompanyName(); + JobTitle jobTitle = internshipToEditDocuments.getJobTitle(); + Set reviews = internshipToEditDocuments.getReviews(); + Set programmingLanguages = internshipToEditDocuments.getProgrammingLanguages(); + Set qualifications = internshipToEditDocuments.getQualifications(); + Location location = internshipToEditDocuments.getLocation(); + Salary salary = internshipToEditDocuments.getSalary(); + Set notes = internshipToEditDocuments.getNotes(); + Rating rating = internshipToEditDocuments.getRating(); + Set reflections = internshipToEditDocuments.getReflections(); + Contact contact = internshipToEditDocuments.getContact(); + InternshipStatus status = internshipToEditDocuments.getStatus(); + InterviewDate interviewDate = internshipToEditDocuments.getInterviewDate(); + boolean isArchived = internshipToEditDocuments.isArchived(); + + ResumeLink resumeLink = editDocumentsDescriptor.getResumeLink() + .orElse(internshipToEditDocuments.getDocuments().getResumeLink()); + CoverLetterLink coverLetterLink = editDocumentsDescriptor.getCoverLetterLink() + .orElse(internshipToEditDocuments.getDocuments().getCoverLetterLink()); + + Documents newDocuments = new Documents(resumeLink, coverLetterLink); + + return new InternshipApplication(companyName, jobTitle, reviews, programmingLanguages, qualifications, location, + salary, notes, rating, reflections, contact, status, isArchived, interviewDate, newDocuments); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EditDocumentsCommand // instanceof handles nulls + && targetIndex.equals(((EditDocumentsCommand) other).targetIndex) + && editDocumentsDescriptor.equals(( + (EditDocumentsCommand) other).editDocumentsDescriptor)); // state check + } + + /** + * Stores the details to edit the documents with. Each non-empty field value will replace the + * corresponding field value of the documents. + */ + public static class EditDocumentsDescriptor { + private ResumeLink resumeLink; + private CoverLetterLink coverLetterLink; + + public EditDocumentsDescriptor() {} + + /** + * Copy constructor. + */ + public EditDocumentsDescriptor(EditDocumentsCommand.EditDocumentsDescriptor toCopy) { + setResumeLink(toCopy.resumeLink); + setCoverLetterLink(toCopy.coverLetterLink); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(resumeLink, coverLetterLink); + } + + public void setResumeLink(ResumeLink resumeLink) { + this.resumeLink = resumeLink; + } + + public Optional getResumeLink() { + return Optional.ofNullable(resumeLink); + } + + public void setCoverLetterLink(CoverLetterLink coverLetterLink) { + this.coverLetterLink = coverLetterLink; + } + + public Optional getCoverLetterLink() { + return Optional.ofNullable(coverLetterLink); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditDocumentsCommand.EditDocumentsDescriptor)) { + return false; + } + + // state check + EditDocumentsCommand.EditDocumentsDescriptor e = (EditDocumentsCommand.EditDocumentsDescriptor) other; + + return getResumeLink().equals(e.getResumeLink()) + && getCoverLetterLink().equals(e.getCoverLetterLink()); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/task/FindTaskCommand.java b/src/main/java/seedu/address/logic/commands/task/FindTaskCommand.java new file mode 100644 index 00000000000..72b7a19801d --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/FindTaskCommand.java @@ -0,0 +1,57 @@ +package seedu.address.logic.commands.task; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.model.Model; +import seedu.address.model.tag.TaskType; +import seedu.address.model.task.ContentContainsKeywordsPredicate; +import seedu.address.model.task.TitleContainsKeywordsPredicate; + +/** + * Finds and lists all todos and notes in record whose name contains any of the argument keywords. + * Keyword matching is case-insensitive. + */ +public class FindTaskCommand extends Command { + + public static final String COMMAND_WORD = "find_task"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all todos and notes whose company names or " + + "contents contain any of the specified keywords (case-insensitive) " + + "and displays them as lists with index numbers.\n" + + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" + + "Example: " + COMMAND_WORD + " google software engineer intern"; + + private static final TaskType type = TaskType.BOTH; + + private final TitleContainsKeywordsPredicate titlePredicate; + private final ContentContainsKeywordsPredicate contentPredicate; + + /** + * Creates a FindTaskCommand instance. + * Finds todo and content that match {@code titlePredicate} or {@code contentPredicate}. + */ + public FindTaskCommand(TitleContainsKeywordsPredicate titlePredicate, + ContentContainsKeywordsPredicate contentPredicate) { + this.titlePredicate = titlePredicate; + this.contentPredicate = contentPredicate; + } + + @Override + public CommandResult execute(Model model) { + assert model != null; + model.updateFilteredTodoList(titlePredicate); + model.updateFilteredNoteList(contentPredicate); + return new CommandResult( + String.format(Messages.MESSAGE_RESULT_LISTED_OVERVIEW, + model.getFilteredNoteList().size() + model.getFilteredTodoList().size()), type); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FindTaskCommand // instanceof handles nulls + && titlePredicate.equals(((FindTaskCommand) other).titlePredicate) + && contentPredicate.equals(((FindTaskCommand) other).contentPredicate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/task/ListTaskCommand.java b/src/main/java/seedu/address/logic/commands/task/ListTaskCommand.java new file mode 100644 index 00000000000..a62f5b66a08 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/ListTaskCommand.java @@ -0,0 +1,45 @@ +package seedu.address.logic.commands.task; + +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_NOTES; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_TODO; + +import java.util.List; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.model.Model; +import seedu.address.model.tag.TaskType; +import seedu.address.model.task.InternshipTodo; +import seedu.address.model.task.Note; + +/** + * Lists all tasks to the user. + * The tasks include todo tasks and notes. + */ +public class ListTaskCommand extends Command { + + public static final String COMMAND_WORD = "list_task"; + + public static final String MESSAGE_SUCCESS = "Listed all todos and notes"; + public static final String MESSAGE_NO_APPLICATIONS = "No task (todo and note) at the moment"; + + private static final TaskType type = TaskType.BOTH; + + + @Override + public CommandResult execute(Model model) { + assert model != null; + + model.updateFilteredTodoList(PREDICATE_SHOW_ALL_TODO); + model.updateFilteredNoteList(PREDICATE_SHOW_ALL_NOTES); + + List lastShownTodoList = model.getFilteredTodoList(); + List lastShownNoteList = model.getFilteredNoteList(); + + if (lastShownTodoList.size() > 0 || lastShownNoteList.size() > 0) { + return new CommandResult(MESSAGE_SUCCESS, type); + } else { + return new CommandResult(MESSAGE_NO_APPLICATIONS, type); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/task/note/AddNoteCommand.java b/src/main/java/seedu/address/logic/commands/task/note/AddNoteCommand.java new file mode 100644 index 00000000000..ebcf7bfc4b9 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/note/AddNoteCommand.java @@ -0,0 +1,59 @@ +package seedu.address.logic.commands.task.note; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE_CONTENT; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.tag.TaskType; +import seedu.address.model.task.Note; + +/** + * Adds a quick note to the note list. + */ +public class AddNoteCommand extends Command { + + public static final String COMMAND_WORD = "add_note"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a note.\n" + + "Parameters: " + + PREFIX_NOTE_CONTENT + "NOTE_CONTENT " + + "Example: " + COMMAND_WORD + " " + + PREFIX_NOTE_CONTENT + "Focus on software engineering jobs!"; + + public static final String MESSAGE_SUCCESS = "New note added: %1$s"; + public static final String MESSAGE_DUPLICATE_NOTE = "This note already exists in the todo list"; + + private static final TaskType type = TaskType.NOTE; + + private final Note note; + + /** + * Creates an AddNoteCommand to add the specified {@code Note}. + */ + public AddNoteCommand(Note noteContent) { + requireNonNull(noteContent); + note = noteContent; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasNote(note)) { + throw new CommandException(MESSAGE_DUPLICATE_NOTE); + } + + model.addNote(note); + return new CommandResult(String.format(MESSAGE_SUCCESS, note), type); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddNoteCommand // instanceof handles nulls + && note.equals(((AddNoteCommand) other).note)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/task/note/ClearNoteCommand.java b/src/main/java/seedu/address/logic/commands/task/note/ClearNoteCommand.java new file mode 100644 index 00000000000..a4944ecfc8b --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/note/ClearNoteCommand.java @@ -0,0 +1,39 @@ +package seedu.address.logic.commands.task.note; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.model.Model; +import seedu.address.model.NoteList; +import seedu.address.model.tag.TaskType; +import seedu.address.model.task.Note; + +/** + * Clears the note list. + */ +public class ClearNoteCommand extends Command { + + public static final String COMMAND_WORD = "clear_note"; + public static final String MESSAGE_SUCCESS = "All notes has been cleared!"; + public static final String MESSAGE_NULL = "There is nothing to clear!"; + + private static final TaskType type = TaskType.NOTE; + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + + List lastShownList = model.getFilteredNoteList(); + + if (lastShownList.size() == 0) { + return new CommandResult(MESSAGE_NULL, type); + } + + model.clearNote(new NoteList()); + + return new CommandResult(MESSAGE_SUCCESS, type); + } +} diff --git a/src/main/java/seedu/address/logic/commands/task/note/DeleteNoteCommand.java b/src/main/java/seedu/address/logic/commands/task/note/DeleteNoteCommand.java new file mode 100644 index 00000000000..3e8cc653597 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/note/DeleteNoteCommand.java @@ -0,0 +1,65 @@ +package seedu.address.logic.commands.task.note; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.tag.TaskType; +import seedu.address.model.task.Note; + +/** + * Deletes a note identified by its displayed index from the note list. + */ +public class DeleteNoteCommand extends Command { + + public static final String COMMAND_WORD = "delete_note"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the specified note from the list of notes.\n" + + "Deletes note at the specified INDEX.\n" + + "The index refers to the index number shown in the displayed note list.\n" + + "Parameters: INDEX (must be a positive integer 1, 2, 3, ...)\n" + + "Example: " + COMMAND_WORD + " 2"; + + public static final String MESSAGE_DELETE_NOTE_SUCCESS = "Deleted note: %1$s"; + + private static final TaskType type = TaskType.NOTE; + + private final Index targetIndex; + + /** + * Creates a DeleteNoteCommand to delete the specified note at {@code targetIndex}. + */ + public DeleteNoteCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredNoteList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_NOTE_DISPLAYED_INDEX); + } + + Note noteToDelete = lastShownList.get(targetIndex.getZeroBased()); + + model.deleteNote(noteToDelete); + + return new CommandResult(String.format(MESSAGE_DELETE_NOTE_SUCCESS, noteToDelete), type); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteNoteCommand // instanceof handles nulls + && targetIndex.equals(((DeleteNoteCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/task/note/ListNoteCommand.java b/src/main/java/seedu/address/logic/commands/task/note/ListNoteCommand.java new file mode 100644 index 00000000000..5de0445df16 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/note/ListNoteCommand.java @@ -0,0 +1,39 @@ +package seedu.address.logic.commands.task.note; + +import static java.util.Objects.requireNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_NOTES; + +import java.util.List; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.model.Model; +import seedu.address.model.tag.TaskType; +import seedu.address.model.task.Note; + +/** + * Lists all notes in the note list to the user. + */ +public class ListNoteCommand extends Command { + + public static final String COMMAND_WORD = "list_note"; + public static final String MESSAGE_SUCCESS = "Listed all notes"; + public static final String MESSAGE_NO_NOTE = "No note at the moment"; + + private static final TaskType type = TaskType.NOTE; + + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + + model.updateFilteredNoteList(PREDICATE_SHOW_ALL_NOTES); + List lastShownList = model.getFilteredNoteList(); + + if (lastShownList.size() > 0) { + return new CommandResult(MESSAGE_SUCCESS, type); + } else { + return new CommandResult(MESSAGE_NO_NOTE, type); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/task/todo/AddTodoCommand.java b/src/main/java/seedu/address/logic/commands/task/todo/AddTodoCommand.java new file mode 100644 index 00000000000..8455a3f611b --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/todo/AddTodoCommand.java @@ -0,0 +1,65 @@ +package seedu.address.logic.commands.task.todo; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_JOB_TITLE; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.tag.TaskType; +import seedu.address.model.task.InternshipTodo; + +/** + * Adds a todo internship application to the todo list. + */ +public class AddTodoCommand extends Command { + + public static final String COMMAND_WORD = "add_todo"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an interested internship todo.\n" + + "Parameters: " + + PREFIX_COMPANY_NAME + "COMPANY_NAME " + + PREFIX_JOB_TITLE + "JOB_TITLE\n" + + PREFIX_DEADLINE + "DEADLINE\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_COMPANY_NAME + "LinkedIn " + + PREFIX_JOB_TITLE + "Software Engineer " + + PREFIX_DEADLINE + "2023-10-01"; + + public static final String MESSAGE_SUCCESS = "New TODO added: %1$s"; + public static final String MESSAGE_DUPLICATE_TODO = "This TODO already exists in the todo list"; + + private static final TaskType type = TaskType.TODO; + + private final InternshipTodo todo; + + /** + * Creates an AddTodoCommand to add the specified {@code InternshipTodo} + */ + public AddTodoCommand(InternshipTodo applicationTodo) { + requireNonNull(applicationTodo); + todo = applicationTodo; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasTodo(todo)) { + throw new CommandException(MESSAGE_DUPLICATE_TODO); + } + + model.addTodo(todo); + return new CommandResult(String.format(MESSAGE_SUCCESS, todo), type); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddTodoCommand // instanceof handles nulls + && todo.equals(((AddTodoCommand) other).todo)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/task/todo/ClearTodoCommand.java b/src/main/java/seedu/address/logic/commands/task/todo/ClearTodoCommand.java new file mode 100644 index 00000000000..28128f6b20a --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/todo/ClearTodoCommand.java @@ -0,0 +1,39 @@ +package seedu.address.logic.commands.task.todo; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.model.Model; +import seedu.address.model.TodoList; +import seedu.address.model.tag.TaskType; +import seedu.address.model.task.InternshipTodo; + +/** + * Clears the todo list. + */ +public class ClearTodoCommand extends Command { + + public static final String COMMAND_WORD = "clear_todo"; + public static final String MESSAGE_SUCCESS = "All todos have been cleared!"; + public static final String MESSAGE_NULL = "There is nothing to clear!"; + + private static final TaskType type = TaskType.TODO; + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + + List lastShownList = model.getFilteredTodoList(); + + if (lastShownList.size() == 0) { + return new CommandResult(MESSAGE_NULL, type); + } + + model.clearTodo(new TodoList()); + + return new CommandResult(MESSAGE_SUCCESS, type); + } +} diff --git a/src/main/java/seedu/address/logic/commands/task/todo/DeleteTodoCommand.java b/src/main/java/seedu/address/logic/commands/task/todo/DeleteTodoCommand.java new file mode 100644 index 00000000000..3e3bd948ae9 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/todo/DeleteTodoCommand.java @@ -0,0 +1,65 @@ +package seedu.address.logic.commands.task.todo; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.tag.TaskType; +import seedu.address.model.task.InternshipTodo; + +/** + * Deletes a todo identified by its displayed index from the todo list. + */ +public class DeleteTodoCommand extends Command { + + public static final String COMMAND_WORD = "delete_todo"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the specified todo from the list of todo.\n" + + "Deletes todo at the specified INDEX.\n" + + "The index refers to the index number shown in the displayed todo list.\n" + + "Parameters: INDEX (must be a positive integer 1, 2, 3, ...)\n" + + "Example: " + COMMAND_WORD + " 2"; + + public static final String MESSAGE_DELETE_TODO_SUCCESS = "Deleted todo: %1$s"; + + private static final TaskType type = TaskType.TODO; + + private final Index targetIndex; + + /** + * Creates a DeleteTodoCommand to delete a todo task at the specified {@code targetIndex}. + */ + public DeleteTodoCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredTodoList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_TODO_DISPLAYED_INDEX); + } + + InternshipTodo todoToDelete = lastShownList.get(targetIndex.getZeroBased()); + + model.deleteTodo(todoToDelete); + + return new CommandResult(String.format(MESSAGE_DELETE_TODO_SUCCESS, todoToDelete), type); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteTodoCommand // instanceof handles nulls + && targetIndex.equals(((DeleteTodoCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/task/todo/EditDeadlineCommand.java b/src/main/java/seedu/address/logic/commands/task/todo/EditDeadlineCommand.java new file mode 100644 index 00000000000..1e3ebd5b641 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/todo/EditDeadlineCommand.java @@ -0,0 +1,100 @@ +package seedu.address.logic.commands.task.todo; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_TODO; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.JobTitle; +import seedu.address.model.tag.TaskType; +import seedu.address.model.task.ApplicationDeadline; +import seedu.address.model.task.InternshipTodo; +import seedu.address.model.task.NoteContent; + +/** + * Edits the deadline of a todo identified by its displayed index from the list of todos. + */ +public class EditDeadlineCommand extends Command { + + public static final String COMMAND_WORD = "edit_deadline"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Edits the deadline of the specified todo from current available todo list.\n" + + "Parameters: INDEX (INDEX must be a positive integer) " + + PREFIX_DEADLINE + "DEADLINE (must be in format yyyy-mm-dd)\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_DEADLINE + "2023-10-01\n"; + + public static final String MESSAGE_UPDATE_STATUS_SUCCESS = "Deadline updated: %1$s"; + + private static final TaskType type = TaskType.TODO; + + private final Index targetIndex; + + private final ApplicationDeadline toUpdate; + + /** + * Creates a EditDeadlineCommand to update the deadline of the todo task specified at index {@code targetIndex} to + * {@code deadline}. + */ + public EditDeadlineCommand(Index targetIndex, ApplicationDeadline deadline) { + requireNonNull(targetIndex); + requireNonNull(deadline); + + this.targetIndex = targetIndex; + toUpdate = deadline; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + List lastShownList = model.getFilteredTodoList(); + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_DISPLAYED_INDEX); + } + + InternshipTodo todoToUpdateDeadline = lastShownList.get( + targetIndex.getZeroBased()); + InternshipTodo updatedTodo = createdUpdatedTodo( + todoToUpdateDeadline, toUpdate); + + model.setTodo(todoToUpdateDeadline, updatedTodo); + model.updateFilteredTodoList(PREDICATE_SHOW_ALL_TODO); + return new CommandResult(String.format(MESSAGE_UPDATE_STATUS_SUCCESS, updatedTodo), type); + } + + /** + * Creates and returns a {@code InternshipTodo} with the deadline of {@code deadline} + */ + private static InternshipTodo createdUpdatedTodo( + InternshipTodo todo, ApplicationDeadline deadline) { + assert todo != null; + + CompanyName companyName = todo.getInternshipTitle(); + JobTitle jobTitle = todo.getJobTitle(); + NoteContent note = todo.getNote(); + + if (note != null) { + return new InternshipTodo(companyName, jobTitle, deadline, note); + } else { + return new InternshipTodo(companyName, jobTitle, deadline); + } + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EditDeadlineCommand // instanceof handles nulls + && targetIndex.equals(((EditDeadlineCommand) other).targetIndex) + && toUpdate.equals(((EditDeadlineCommand) other).toUpdate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/task/todo/EditNoteContentCommand.java b/src/main/java/seedu/address/logic/commands/task/todo/EditNoteContentCommand.java new file mode 100644 index 00000000000..83314d04a05 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/todo/EditNoteContentCommand.java @@ -0,0 +1,95 @@ +package seedu.address.logic.commands.task.todo; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE_CONTENT; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_TODO; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.JobTitle; +import seedu.address.model.tag.TaskType; +import seedu.address.model.task.ApplicationDeadline; +import seedu.address.model.task.InternshipTodo; +import seedu.address.model.task.NoteContent; + +/** + * Edits the note content of a todo identified by its displayed index from the list of todos. + */ +public class EditNoteContentCommand extends Command { + + public static final String COMMAND_WORD = "edit_content"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Edits the note content of the specified todo from current available todo list.\n" + + "Parameters: INDEX (INDEX must be a positive integer) " + + PREFIX_NOTE_CONTENT + "NOTE_CONTENT\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_NOTE_CONTENT + "Change venue\n"; + + public static final String MESSAGE_UPDATE_STATUS_SUCCESS = "NoteList Content updated: %1$s"; + + private static final TaskType type = TaskType.TODO; + + private final Index targetIndex; + + private final NoteContent toUpdate; + + /** + * Creates a EditNoteContentCommand to update the note content of the todo task specified at index + * {@code targetIndex} to {@code content}. + */ + public EditNoteContentCommand(Index targetIndex, NoteContent content) { + requireNonNull(targetIndex); + requireNonNull(content); + + this.targetIndex = targetIndex; + toUpdate = content; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + List lastShownList = model.getFilteredTodoList(); + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_DISPLAYED_INDEX); + } + + InternshipTodo todoToUpdateNote = lastShownList.get( + targetIndex.getZeroBased()); + InternshipTodo updatedTodo = createdUpdatedTodo( + todoToUpdateNote, toUpdate); + + model.setTodo(todoToUpdateNote, updatedTodo); + model.updateFilteredTodoList(PREDICATE_SHOW_ALL_TODO); + return new CommandResult(String.format(MESSAGE_UPDATE_STATUS_SUCCESS, updatedTodo), type); + } + + /** + * Creates and returns a {@code InternshipTodo} with the content of {@code content} + */ + private static InternshipTodo createdUpdatedTodo(InternshipTodo todo, NoteContent content) { + assert todo != null; + + CompanyName companyName = todo.getInternshipTitle(); + JobTitle jobTitle = todo.getJobTitle(); + ApplicationDeadline deadline = todo.getDeadline(); + + return new InternshipTodo(companyName, jobTitle, deadline, content); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EditNoteContentCommand // instanceof handles nulls + && targetIndex.equals(((EditNoteContentCommand) other).targetIndex) + && toUpdate.equals(((EditNoteContentCommand) other).toUpdate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/task/todo/ListTodoCommand.java b/src/main/java/seedu/address/logic/commands/task/todo/ListTodoCommand.java new file mode 100644 index 00000000000..409cf95e670 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/todo/ListTodoCommand.java @@ -0,0 +1,36 @@ +package seedu.address.logic.commands.task.todo; + +import static java.util.Objects.requireNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_TODO; + +import java.util.List; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.model.Model; +import seedu.address.model.tag.TaskType; +import seedu.address.model.task.InternshipTodo; +/** + * Lists all todo applications in the todo list to the user. + */ +public class ListTodoCommand extends Command { + + public static final String COMMAND_WORD = "list_todo"; + public static final String MESSAGE_SUCCESS = "Listed all todos"; + public static final String MESSAGE_NO_APPLICATIONS = "No todo at the moment"; + + private static final TaskType type = TaskType.TODO; + + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredTodoList(PREDICATE_SHOW_ALL_TODO); + List lastShownList = model.getFilteredTodoList(); + if (lastShownList.size() > 0) { + return new CommandResult(MESSAGE_SUCCESS, type); + } else { + return new CommandResult(MESSAGE_NO_APPLICATIONS, type); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java index 3b8bfa035e8..aeed1f8c5f8 100644 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java @@ -1,23 +1,33 @@ package seedu.address.logic.parser; import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_JOB_TITLE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_LOCATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PROGRAMMING_LANGUAGE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_QUALIFICATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RATING; +import static seedu.address.logic.parser.CliSyntax.PREFIX_REFLECTION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_REVIEW; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY; import java.util.Set; import java.util.stream.Stream; import seedu.address.logic.commands.AddCommand; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.JobTitle; +import seedu.address.model.application.Location; +import seedu.address.model.application.Note; +import seedu.address.model.application.ProgrammingLanguage; +import seedu.address.model.application.Qualification; +import seedu.address.model.application.Rating; +import seedu.address.model.application.Reflection; +import seedu.address.model.application.Review; +import seedu.address.model.application.Salary; /** * Parses input arguments and creates a new AddCommand object @@ -31,22 +41,33 @@ public class AddCommandParser implements Parser { */ public AddCommand parse(String args) throws ParseException { ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + ArgumentTokenizer.tokenize(args, PREFIX_COMPANY_NAME, PREFIX_JOB_TITLE, PREFIX_REVIEW, PREFIX_LOCATION, + PREFIX_PROGRAMMING_LANGUAGE, PREFIX_QUALIFICATION, PREFIX_SALARY, PREFIX_RATING, PREFIX_NOTE, + PREFIX_REFLECTION); - if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) + if (!arePrefixesPresent(argMultimap, PREFIX_COMPANY_NAME, PREFIX_JOB_TITLE) || !argMultimap.getPreamble().isEmpty()) { throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); } - Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); - Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()); - Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()); - Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()); - Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); + CompanyName companyName = ParserUtil.parseCompanyName( + argMultimap.getValue(PREFIX_COMPANY_NAME).orElse(null)); + JobTitle jobTitle = ParserUtil.parseJobTitle(argMultimap.getValue(PREFIX_JOB_TITLE).orElse(null)); + Set reviewList = ParserUtil.parseReviews(argMultimap.getAllValues(PREFIX_REVIEW)); + Set programmingLanguages = ParserUtil.parseProgrammingLanguages( + argMultimap.getAllValues(PREFIX_PROGRAMMING_LANGUAGE)); + Set qualifications = ParserUtil.parseQualifications( + argMultimap.getAllValues(PREFIX_QUALIFICATION)); + Location location = ParserUtil.parseLocation(argMultimap.getValue(PREFIX_LOCATION).orElse(null)); + Salary salary = ParserUtil.parseSalary(argMultimap.getValue(PREFIX_SALARY).orElse(null)); + Set notes = ParserUtil.parseNotes(argMultimap.getAllValues(PREFIX_NOTE)); + Rating rating = ParserUtil.parseRating(argMultimap.getValue(PREFIX_RATING).orElse(null)); + Set reflections = ParserUtil.parseReflections(argMultimap.getAllValues(PREFIX_REFLECTION)); - Person person = new Person(name, phone, email, address, tagList); + InternshipApplication application = new InternshipApplication(companyName, jobTitle, reviewList, + programmingLanguages, qualifications, location, salary, notes, rating, reflections); - return new AddCommand(person); + return new AddCommand(application); } /** diff --git a/src/main/java/seedu/address/logic/parser/AddInterviewDateCommandParser.java b/src/main/java/seedu/address/logic/parser/AddInterviewDateCommandParser.java new file mode 100644 index 00000000000..bbde1460efb --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/AddInterviewDateCommandParser.java @@ -0,0 +1,53 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; + +import java.util.stream.Stream; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.AddInterviewDateCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.application.InterviewDate; + +/** + * Parses input arguments and creates a new AddInterviewDateCommand object + */ +public class AddInterviewDateCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the AddInterviewDateCommand + * and returns an AddInterviewDateCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddInterviewDateCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_DATE); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AddInterviewDateCommand.MESSAGE_USAGE)); + } + + if (!arePrefixesPresent(argMultimap, PREFIX_DATE)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AddInterviewDateCommand.MESSAGE_USAGE)); + } + + InterviewDate interviewDate = ParserUtil.parseInterviewDate(argMultimap.getValue(PREFIX_DATE).get()); + + return new AddInterviewDateCommand(index, interviewDate); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java deleted file mode 100644 index 1e466792b46..00000000000 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ /dev/null @@ -1,76 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.ClearCommand; -import seedu.address.logic.commands.Command; -import seedu.address.logic.commands.DeleteCommand; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.ExitCommand; -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.commands.HelpCommand; -import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.parser.exceptions.ParseException; - -/** - * Parses user input. - */ -public class AddressBookParser { - - /** - * Used for initial separation of command word and args. - */ - private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); - - /** - * Parses user input into command for execution. - * - * @param userInput full user input string - * @return the command based on the user input - * @throws ParseException if the user input does not conform the expected format - */ - public Command parseCommand(String userInput) throws ParseException { - final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); - if (!matcher.matches()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); - } - - final String commandWord = matcher.group("commandWord"); - final String arguments = matcher.group("arguments"); - switch (commandWord) { - - case AddCommand.COMMAND_WORD: - return new AddCommandParser().parse(arguments); - - case EditCommand.COMMAND_WORD: - return new EditCommandParser().parse(arguments); - - case DeleteCommand.COMMAND_WORD: - return new DeleteCommandParser().parse(arguments); - - case ClearCommand.COMMAND_WORD: - return new ClearCommand(); - - case FindCommand.COMMAND_WORD: - return new FindCommandParser().parse(arguments); - - case ListCommand.COMMAND_WORD: - return new ListCommand(); - - case ExitCommand.COMMAND_WORD: - return new ExitCommand(); - - case HelpCommand.COMMAND_WORD: - return new HelpCommand(); - - default: - throw new ParseException(MESSAGE_UNKNOWN_COMMAND); - } - } - -} diff --git a/src/main/java/seedu/address/logic/parser/ArchiveCommandParser.java b/src/main/java/seedu/address/logic/parser/ArchiveCommandParser.java new file mode 100644 index 00000000000..a0500cfaf16 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ArchiveCommandParser.java @@ -0,0 +1,28 @@ +package seedu.address.logic.parser; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.ArchiveCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new ArchiveCommand object + */ +public class ArchiveCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ArchiveCommand + * and returns a ArchiveCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ArchiveCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new ArchiveCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ArchiveCommand.MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/seedu/address/logic/parser/ClearByCommandParser.java b/src/main/java/seedu/address/logic/parser/ClearByCommandParser.java new file mode 100644 index 00000000000..253e1dff0e2 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ClearByCommandParser.java @@ -0,0 +1,83 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_JOB_TITLE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_STATUS; + +import java.util.stream.Stream; + +import seedu.address.logic.commands.ClearByCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.JobTitle; + +/** + * Parses input arguments and creates a new ClearByCommand object + */ +public class ClearByCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ClearByCommand + * and returns an ClearByCommand object for execution. + * + * @throws ParseException if the user input does not conform the expected format + */ + public ClearByCommand parse(String args) throws ParseException { + Prefix prefix; + + try { + String trimArgs = args.trim(); + prefix = new Prefix(trimArgs.substring(0, 2)); + } catch (IndexOutOfBoundsException ite) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ClearByCommand.MESSAGE_NO_PARAMETER)); + } + + if (!(prefix.equals(PREFIX_COMPANY_NAME) || prefix.equals(PREFIX_JOB_TITLE) || prefix.equals(PREFIX_STATUS))) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ClearByCommand.MESSAGE_INVALID_PARAMETER)); + } + + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, prefix); + + if (!arePrefixesPresent(argMultimap, prefix) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ClearByCommand.MESSAGE_USAGE)); + } + + return parseParameter(prefix, argMultimap); + } + + private ClearByCommand parseParameter(Prefix prefix, ArgumentMultimap argMultimap) throws ParseException { + if (prefix.equals(PREFIX_COMPANY_NAME)) { + CompanyName companyName = ParserUtil.parseCompanyName( + argMultimap.getValue(PREFIX_COMPANY_NAME).orElse(null)); + + return new ClearByCommand(companyName); + } else if (prefix.equals(PREFIX_JOB_TITLE)) { + JobTitle jobTitle = ParserUtil.parseJobTitle(argMultimap.getValue(PREFIX_JOB_TITLE).orElse(null)); + + return new ClearByCommand(jobTitle); + } else if (prefix.equals(PREFIX_STATUS)) { + InternshipStatus status = ParserUtil.parseInternshipStatus( + argMultimap.getValue(PREFIX_STATUS).orElse(null)); + + return new ClearByCommand(status); + } else { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ClearByCommand.MESSAGE_INVALID_PARAMETER)); + } + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java index 75b1a9bf119..46cbd1bb217 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java @@ -6,10 +6,33 @@ public class CliSyntax { /* Prefix definitions */ - public static final Prefix PREFIX_NAME = new Prefix("n/"); + /* Prefix used in AddCommand */ + public static final Prefix PREFIX_COMPANY_NAME = new Prefix("n/"); + public static final Prefix PREFIX_JOB_TITLE = new Prefix("j/"); + public static final Prefix PREFIX_REVIEW = new Prefix("r/"); + public static final Prefix PREFIX_PROGRAMMING_LANGUAGE = new Prefix("p/"); + public static final Prefix PREFIX_QUALIFICATION = new Prefix("q/"); + public static final Prefix PREFIX_LOCATION = new Prefix("l/"); + public static final Prefix PREFIX_SALARY = new Prefix("s/"); + public static final Prefix PREFIX_NOTE = new Prefix("note/"); + public static final Prefix PREFIX_RATING = new Prefix("rate/"); + public static final Prefix PREFIX_REFLECTION = new Prefix("reflect/"); + + /* Prefix used in AddContactCommand */ public static final Prefix PREFIX_PHONE = new Prefix("p/"); public static final Prefix PREFIX_EMAIL = new Prefix("e/"); - public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); - public static final Prefix PREFIX_TAG = new Prefix("t/"); + public static final Prefix PREFIX_STATUS = new Prefix("s/"); + public static final Prefix PREFIX_DEADLINE = new Prefix("by/"); + public static final Prefix PREFIX_NOTE_CONTENT = new Prefix("c/"); + public static final Prefix PREFIX_DATE = new Prefix("d/"); + public static final Prefix PREFIX_RESUME = new Prefix("rs/"); + public static final Prefix PREFIX_COVER_LETTER = new Prefix("cl/"); + + /* Prefix used in FindDateCommand */ + public static final Prefix PREFIX_DATE_BEFORE = new Prefix("before/"); + public static final Prefix PREFIX_DATE_AFTER = new Prefix("after/"); + public static final Prefix PREFIX_DATE_FROM = new Prefix("from/"); + public static final Prefix PREFIX_DATE_TO = new Prefix("to/"); + } diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java index 522b93081cc..72e965196fa 100644 --- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java @@ -14,6 +14,7 @@ public class DeleteCommandParser implements Parser { /** * Parses the given {@code String} of arguments in the context of the DeleteCommand * and returns a DeleteCommand object for execution. + * * @throws ParseException if the user input does not conform the expected format */ public DeleteCommand parse(String args) throws ParseException { diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java index 845644b7dea..1d185e8ec2e 100644 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/EditCommandParser.java @@ -2,11 +2,16 @@ import static java.util.Objects.requireNonNull; import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_JOB_TITLE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_LOCATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PROGRAMMING_LANGUAGE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_QUALIFICATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RATING; +import static seedu.address.logic.parser.CliSyntax.PREFIX_REFLECTION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_REVIEW; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY; import java.util.Collection; import java.util.Collections; @@ -15,9 +20,13 @@ import seedu.address.commons.core.index.Index; import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; +import seedu.address.logic.commands.EditCommand.EditInternshipDescriptor; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.tag.Tag; +import seedu.address.model.application.Note; +import seedu.address.model.application.ProgrammingLanguage; +import seedu.address.model.application.Qualification; +import seedu.address.model.application.Reflection; +import seedu.address.model.application.Review; /** * Parses input arguments and creates a new EditCommand object @@ -32,7 +41,9 @@ public class EditCommandParser implements Parser { public EditCommand parse(String args) throws ParseException { requireNonNull(args); ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + ArgumentTokenizer.tokenize(args, PREFIX_COMPANY_NAME, PREFIX_JOB_TITLE, PREFIX_LOCATION, PREFIX_SALARY, + PREFIX_RATING, PREFIX_QUALIFICATION, PREFIX_PROGRAMMING_LANGUAGE, PREFIX_REVIEW, PREFIX_NOTE, + PREFIX_REFLECTION); Index index; @@ -42,41 +53,124 @@ public EditCommand parse(String args) throws ParseException { throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe); } - EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); - if (argMultimap.getValue(PREFIX_NAME).isPresent()) { - editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); + EditInternshipDescriptor editInternshipDescriptor = new EditInternshipDescriptor(); + if (argMultimap.getValue(PREFIX_COMPANY_NAME).isPresent()) { + editInternshipDescriptor.setCompanyName(ParserUtil + .parseCompanyName(argMultimap.getValue(PREFIX_COMPANY_NAME).get())); } - if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { - editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); + if (argMultimap.getValue(PREFIX_JOB_TITLE).isPresent()) { + editInternshipDescriptor.setJobTitle(ParserUtil + .parseJobTitle(argMultimap.getValue(PREFIX_JOB_TITLE).get())); } - if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { - editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get())); + if (argMultimap.getValue(PREFIX_LOCATION).isPresent()) { + editInternshipDescriptor.setLocation(ParserUtil + .parseLocation(argMultimap.getValue(PREFIX_LOCATION).get())); } - if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { - editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); + if (argMultimap.getValue(PREFIX_SALARY).isPresent()) { + editInternshipDescriptor.setSalary(ParserUtil.parseSalary(argMultimap.getValue(PREFIX_SALARY).get())); } - parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags); + if (argMultimap.getValue(PREFIX_RATING).isPresent()) { + editInternshipDescriptor.setRating(ParserUtil.parseRating(argMultimap.getValue(PREFIX_RATING).get())); + } + parseQualificationsForEdit(argMultimap.getAllValues(PREFIX_QUALIFICATION)) + .ifPresent(editInternshipDescriptor::setQualifications); + parseProgrammingLanguageForEdit(argMultimap.getAllValues(PREFIX_PROGRAMMING_LANGUAGE)) + .ifPresent(editInternshipDescriptor::setProgrammingLanguages); + parseReviewsForEdit(argMultimap.getAllValues(PREFIX_REVIEW)).ifPresent(editInternshipDescriptor::setReviews); + parseNotesForEdit(argMultimap.getAllValues(PREFIX_NOTE)).ifPresent(editInternshipDescriptor::setNotes); + parseReflectionsForEdit(argMultimap.getAllValues(PREFIX_REFLECTION)) + .ifPresent(editInternshipDescriptor::setReflections); - if (!editPersonDescriptor.isAnyFieldEdited()) { + if (!editInternshipDescriptor.isAnyFieldEdited()) { throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); } - return new EditCommand(index, editPersonDescriptor); + return new EditCommand(index, editInternshipDescriptor); + } + + /** + * Parses {@code Collection qualifications} into a {@code Set} if {@code qualifications} + * is non-empty. If {@code qualifications} contain only one element which is an empty string, it will be parsed into + * a {@code Set} containing zero qualifications. + */ + private Optional> parseQualificationsForEdit(Collection qualifications) + throws ParseException { + assert qualifications != null; + + if (qualifications.isEmpty()) { + return Optional.empty(); + } + Collection qualificationSet = qualifications.size() == 1 && qualifications.contains("") + ? Collections.emptySet() : qualifications; + return Optional.of(ParserUtil.parseQualifications(qualificationSet)); + } + + /** + * Parses {@code Collection programmingLanguages} into a {@code Set} if + * {@code programmingLanguages} is non-empty. If {@code programmingLanguages} contain only one element which is an + * empty string, it will be parsed into a {@code Set} containing zero programmingLanguages. + */ + private Optional> parseProgrammingLanguageForEdit(Collection programmingLanguages) + throws ParseException { + assert programmingLanguages != null; + + if (programmingLanguages.isEmpty()) { + return Optional.empty(); + } + Collection programmingLanguageSet = programmingLanguages.size() == 1 && programmingLanguages + .contains("") ? Collections.emptySet() : programmingLanguages; + return Optional.of(ParserUtil.parseProgrammingLanguages(programmingLanguageSet)); + } + + /** + * Parses {@code Collection reviews} into a {@code Set} if {@code reviews} + * is non-empty. If {@code reviews} contain only one element which is an empty string, it will be parsed into + * a {@code Set} containing zero reviews. + */ + private Optional> parseReviewsForEdit(Collection reviews) + throws ParseException { + assert reviews != null; + + if (reviews.isEmpty()) { + return Optional.empty(); + } + Collection reviewSet = reviews.size() == 1 && reviews.contains("") + ? Collections.emptySet() : reviews; + return Optional.of(ParserUtil.parseReviews(reviewSet)); + } + + /** + * Parses {@code Collection notes} into a {@code Set} if {@code notes} + * is non-empty. If {@code notes} contain only one element which is an empty string, it will be parsed into + * a {@code Set} containing zero notes. + */ + private Optional> parseNotesForEdit(Collection notes) + throws ParseException { + assert notes != null; + + if (notes.isEmpty()) { + return Optional.empty(); + } + Collection noteSet = notes.size() == 1 && notes.contains("") + ? Collections.emptySet() : notes; + return Optional.of(ParserUtil.parseNotes(noteSet)); } /** - * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty. - * If {@code tags} contain only one element which is an empty string, it will be parsed into a - * {@code Set} containing zero tags. + * Parses {@code Collection reflections} into a {@code Set} if {@code reflections} + * is non-empty. If {@code reflections} contain only one element which is an empty string, it will be parsed into + * a {@code Set} containing zero reflections. */ - private Optional> parseTagsForEdit(Collection tags) throws ParseException { - assert tags != null; + private Optional> parseReflectionsForEdit(Collection reflections) + throws ParseException { + assert reflections != null; - if (tags.isEmpty()) { + if (reflections.isEmpty()) { return Optional.empty(); } - Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; - return Optional.of(ParserUtil.parseTags(tagSet)); + Collection reflectionSet = reflections.size() == 1 && reflections.contains("") + ? Collections.emptySet() : reflections; + return Optional.of(ParserUtil.parseReflections(reflectionSet)); } } diff --git a/src/main/java/seedu/address/logic/parser/EditStatusCommandParser.java b/src/main/java/seedu/address/logic/parser/EditStatusCommandParser.java new file mode 100644 index 00000000000..28e5c021a5d --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/EditStatusCommandParser.java @@ -0,0 +1,55 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_STATUS; + +import java.util.stream.Stream; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.EditStatusCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.application.InternshipStatus; + +/** + * Parses input arguments and creates a new EditStatusCommand object + */ +public class EditStatusCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditStatusCommand + * and returns an EditStatusCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EditStatusCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_STATUS); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditStatusCommand.MESSAGE_USAGE), + pe); + } + + if (!arePrefixesPresent(argMultimap, PREFIX_STATUS)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditStatusCommand.MESSAGE_USAGE)); + } + + InternshipStatus status = ParserUtil.parseInternshipStatus(argMultimap.getValue(PREFIX_STATUS).get()); + + return new EditStatusCommand(index, status); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java index 4fb71f23103..a7bd23a89c0 100644 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/FindCommandParser.java @@ -1,18 +1,142 @@ package seedu.address.logic.parser; +import static java.util.Objects.requireNonNull; import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE_AFTER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE_BEFORE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE_FROM; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE_TO; +import static seedu.address.logic.parser.CliSyntax.PREFIX_STATUS; +import static seedu.address.logic.parser.ParserUtil.MESSAGE_INVALID_INTERVAL; import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; +import seedu.address.commons.core.LogsCenter; import seedu.address.logic.commands.FindCommand; +import seedu.address.logic.commands.FindDateCommand; +import seedu.address.logic.commands.FindStatusCommand; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.application.AfterDatePredicate; +import seedu.address.model.application.BeforeDatePredicate; +import seedu.address.model.application.BetweenDatePredicate; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.application.NameContainsKeywordsPredicate; +import seedu.address.model.application.StatusPredicate; /** * Parses input arguments and creates a new FindCommand object */ public class FindCommandParser implements Parser { + /** + * Checks if argMultimap contains valid FindCommand options. + * + * @param argMultimap Maps containing mapping of prefixes to their respective arguments + * @return true if and only if the map does not contain any other prefix + */ + private boolean isFindKeywordsCommand(ArgumentMultimap argMultimap) { + return !argMultimap.getValue(PREFIX_STATUS).isPresent() + && !argMultimap.getValue(PREFIX_DATE_BEFORE).isPresent() + && !argMultimap.getValue(PREFIX_DATE_AFTER).isPresent() + && !argMultimap.getValue(PREFIX_DATE_FROM).isPresent() + && !argMultimap.getValue(PREFIX_DATE_TO).isPresent(); + } + + /** + * Checks if argMultimap contains valid FindCommand options + * + * @param argMultimap Maps containing mapping of prefixes to their respective arguments + * @return true if and only if the map contains only prefix {@code PREFIX_STATUS} to its argument + */ + private boolean isFindStatusCommand(ArgumentMultimap argMultimap) { + return argMultimap.getValue(PREFIX_STATUS).isPresent() + && !argMultimap.getValue(PREFIX_DATE_BEFORE).isPresent() + && !argMultimap.getValue(PREFIX_DATE_AFTER).isPresent() + && !argMultimap.getValue(PREFIX_DATE_FROM).isPresent() + && !argMultimap.getValue(PREFIX_DATE_TO).isPresent(); + } + + /** + * Checks if argMultimap contains valid FindCommand options + * + * @param argMultimap Maps containing mapping of prefixes to their respective arguments + * @return true if and only if the map contains only prefix {@code PREFIX_DATE_BEFORE} to its argument + */ + private boolean isFindBeforeDateCommand(ArgumentMultimap argMultimap) { + return !argMultimap.getValue(PREFIX_STATUS).isPresent() + && argMultimap.getValue(PREFIX_DATE_BEFORE).isPresent() + && !argMultimap.getValue(PREFIX_DATE_AFTER).isPresent() + && !argMultimap.getValue(PREFIX_DATE_FROM).isPresent() + && !argMultimap.getValue(PREFIX_DATE_TO).isPresent(); + } + + /** + * Checks if argMultimap contains valid FindCommand options + * + * @param argMultimap Maps containing mapping of prefixes to their respective arguments + * @return true if and only if the map contains only prefix {@code PREFIX_DATE_AFTER} to its argument + */ + private boolean isFindAfterDateCommand(ArgumentMultimap argMultimap) { + return !argMultimap.getValue(PREFIX_STATUS).isPresent() + && !argMultimap.getValue(PREFIX_DATE_BEFORE).isPresent() + && argMultimap.getValue(PREFIX_DATE_AFTER).isPresent() + && !argMultimap.getValue(PREFIX_DATE_FROM).isPresent() + && !argMultimap.getValue(PREFIX_DATE_TO).isPresent(); + } + + /** + * Checks if argMultimap contains valid FindCommand options + * + * @param argMultimap Maps containing mapping of prefixes to their respective arguments + * @return true if and only if the map contains only prefixes {@code PREFIX_DATE_FROM} + * and {@code PREFIX_DATE_TO} to its argument + */ + private boolean isFindBetweenDateCommand(ArgumentMultimap argMultimap) { + return !argMultimap.getValue(PREFIX_STATUS).isPresent() + && !argMultimap.getValue(PREFIX_DATE_BEFORE).isPresent() + && !argMultimap.getValue(PREFIX_DATE_AFTER).isPresent() + && argMultimap.getValue(PREFIX_DATE_FROM).isPresent() + && argMultimap.getValue(PREFIX_DATE_TO).isPresent(); + } + + /** + * Checks if argMultimap contains valid FindCommand options. + * + * @param argMultimap Maps containing mapping of prefixes to their respective arguments + * @return true if and only if the map contains valid combination of options + * @throws ParseException if the user input does not conform the expected format + */ + private boolean verifyFindCommandOptions(ArgumentMultimap argMultimap) throws ParseException { + if (!(isFindKeywordsCommand(argMultimap) + || isFindStatusCommand(argMultimap) + || isFindBeforeDateCommand(argMultimap) + || isFindAfterDateCommand(argMultimap) + || isFindBetweenDateCommand(argMultimap))) { + Logger logger = LogsCenter.getLogger(FindCommandParser.class); + logger.log(Level.INFO, "User used an invalid combination of prefixes"); + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + } + return true; + } + + /** + * Checks if the given two dates forms valid interval. + * + * @param startDate The start date of the interval + * @param endDate The end date of the interval + * @return true if the two dates form a valid interval + * @throws ParseException if two dates doesn't form a valid interval + */ + private boolean checkValidInterval(InterviewDate startDate, InterviewDate endDate) throws ParseException { + if (!(startDate.isBeforeInclusive(endDate))) { + throw new ParseException(MESSAGE_INVALID_INTERVAL); + } + return true; + } + /** * Parses the given {@code String} of arguments in the context of the FindCommand * and returns a FindCommand object for execution. @@ -24,9 +148,30 @@ public FindCommand parse(String args) throws ParseException { throw new ParseException( String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); } + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, + PREFIX_STATUS, PREFIX_DATE_BEFORE, PREFIX_DATE_AFTER, PREFIX_DATE_FROM, PREFIX_DATE_TO); + verifyFindCommandOptions(argMultimap); + if (isFindStatusCommand(argMultimap)) { + InternshipStatus status = ParserUtil.parseInternshipStatus(argMultimap.getValue(PREFIX_STATUS).get()); + return new FindStatusCommand(new StatusPredicate(status)); + } + if (isFindBeforeDateCommand(argMultimap)) { + InterviewDate date = ParserUtil.parseInterviewDate(argMultimap.getValue(PREFIX_DATE_BEFORE).get()); + return new FindDateCommand(new BeforeDatePredicate(date)); + } + if (isFindAfterDateCommand(argMultimap)) { + InterviewDate date = ParserUtil.parseInterviewDate(argMultimap.getValue(PREFIX_DATE_AFTER).get()); + return new FindDateCommand(new AfterDatePredicate(date)); + } + if (isFindBetweenDateCommand(argMultimap)) { + InterviewDate startDate = ParserUtil.parseInterviewDate(argMultimap.getValue(PREFIX_DATE_FROM).get()); + InterviewDate endDate = ParserUtil.parseInterviewDate(argMultimap.getValue(PREFIX_DATE_TO).get()); + checkValidInterval(startDate, endDate); + return new FindDateCommand(new BetweenDatePredicate(startDate, endDate)); + } String[] nameKeywords = trimmedArgs.split("\\s+"); - return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); } diff --git a/src/main/java/seedu/address/logic/parser/InternEaseParser.java b/src/main/java/seedu/address/logic/parser/InternEaseParser.java new file mode 100644 index 00000000000..6cccf19c260 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/InternEaseParser.java @@ -0,0 +1,172 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import seedu.address.logic.commands.AddCommand; +import seedu.address.logic.commands.AddInterviewDateCommand; +import seedu.address.logic.commands.ArchiveCommand; +import seedu.address.logic.commands.ClearByCommand; +import seedu.address.logic.commands.ClearCommand; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.DeleteCommand; +import seedu.address.logic.commands.EditCommand; +import seedu.address.logic.commands.EditStatusCommand; +import seedu.address.logic.commands.ExitCommand; +import seedu.address.logic.commands.FindCommand; +import seedu.address.logic.commands.HelpCommand; +import seedu.address.logic.commands.ListArchivedCommand; +import seedu.address.logic.commands.ListCommand; +import seedu.address.logic.commands.RemindCommand; +import seedu.address.logic.commands.RevertAllCommand; +import seedu.address.logic.commands.RevertCommand; +import seedu.address.logic.commands.SortCommand; +import seedu.address.logic.commands.UnarchiveCommand; +import seedu.address.logic.commands.contact.AddContactCommand; +import seedu.address.logic.commands.contact.DeleteContactCommand; +import seedu.address.logic.commands.contact.EditContactCommand; +import seedu.address.logic.commands.documents.AddDocumentsCommand; +import seedu.address.logic.commands.documents.DeleteDocumentsCommand; +import seedu.address.logic.commands.documents.EditDocumentsCommand; +import seedu.address.logic.commands.task.FindTaskCommand; +import seedu.address.logic.commands.task.ListTaskCommand; +import seedu.address.logic.commands.task.note.AddNoteCommand; +import seedu.address.logic.commands.task.note.ClearNoteCommand; +import seedu.address.logic.commands.task.note.DeleteNoteCommand; +import seedu.address.logic.commands.task.note.ListNoteCommand; +import seedu.address.logic.commands.task.todo.AddTodoCommand; +import seedu.address.logic.commands.task.todo.ClearTodoCommand; +import seedu.address.logic.commands.task.todo.DeleteTodoCommand; +import seedu.address.logic.commands.task.todo.EditDeadlineCommand; +import seedu.address.logic.commands.task.todo.EditNoteContentCommand; +import seedu.address.logic.commands.task.todo.ListTodoCommand; +import seedu.address.logic.parser.contact.ContactParser; +import seedu.address.logic.parser.documents.DocumentsParser; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.logic.parser.task.TaskParser; + +/** + * Parses user input. + */ +public class InternEaseParser { + + /** + * Used for initial separation of command word and args. + */ + private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); + private final TaskParser taskParser; + private final DocumentsParser documentsParser; + private final ContactParser contactParser; + + /** + * Creates a TaskParser instance for every InternEase parser object. + */ + public InternEaseParser() { + taskParser = new TaskParser(); + documentsParser = new DocumentsParser(); + contactParser = new ContactParser(); + } + + /** + * Parses user input into command for execution. + * + * @param userInput full user input string + * @return the command based on the user input + * @throws ParseException if the user input does not conform the expected format + */ + public Command parseCommand(String userInput) throws ParseException { + final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); + if (!matcher.matches()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + } + + final String commandWord = matcher.group("commandWord"); + final String arguments = matcher.group("arguments"); + switch (commandWord) { + + case AddCommand.COMMAND_WORD: + return new AddCommandParser().parse(arguments); + + case EditCommand.COMMAND_WORD: + return new EditCommandParser().parse(arguments); + + case DeleteCommand.COMMAND_WORD: + return new DeleteCommandParser().parse(arguments); + + case ClearCommand.COMMAND_WORD: + return new ClearCommand(); + + case FindCommand.COMMAND_WORD: + return new FindCommandParser().parse(arguments); + + case SortCommand.COMMAND_WORD: + return new SortCommandParser().parse(arguments); + + case ListCommand.COMMAND_WORD: + return new ListCommand(); + + case ExitCommand.COMMAND_WORD: + return new ExitCommand(); + + case HelpCommand.COMMAND_WORD: + return new HelpCommand(); + + case RemindCommand.COMMAND_WORD: + return new RemindCommand(); + + case EditStatusCommand.COMMAND_WORD: + return new EditStatusCommandParser().parse(arguments); + + case AddContactCommand.COMMAND_WORD: + case EditContactCommand.COMMAND_WORD: + case DeleteContactCommand.COMMAND_WORD: + return contactParser.parseContactCommand(commandWord, arguments); + + case ClearByCommand.COMMAND_WORD: + return new ClearByCommandParser().parse(arguments); + + case ArchiveCommand.COMMAND_WORD: + return new ArchiveCommandParser().parse(arguments); + + case UnarchiveCommand.COMMAND_WORD: + return new UnarchiveCommandParser().parse(arguments); + + case ListArchivedCommand.COMMAND_WORD: + return new ListArchivedCommand(); + + case AddInterviewDateCommand.COMMAND_WORD: + return new AddInterviewDateCommandParser().parse(arguments); + + case RevertCommand.COMMAND_WORD: + return new RevertCommand(); + + case RevertAllCommand.COMMAND_WORD: + return new RevertAllCommand(); + + case ListTaskCommand.COMMAND_WORD: + case FindTaskCommand.COMMAND_WORD: + case AddTodoCommand.COMMAND_WORD: + case ListTodoCommand.COMMAND_WORD: + case EditDeadlineCommand.COMMAND_WORD: + case EditNoteContentCommand.COMMAND_WORD: + case DeleteTodoCommand.COMMAND_WORD: + case ClearTodoCommand.COMMAND_WORD: + case AddNoteCommand.COMMAND_WORD: + case ListNoteCommand.COMMAND_WORD: + case DeleteNoteCommand.COMMAND_WORD: + case ClearNoteCommand.COMMAND_WORD: + return taskParser.parseTaskCommand(commandWord, arguments); + + case AddDocumentsCommand.COMMAND_WORD: + case EditDocumentsCommand.COMMAND_WORD: + case DeleteDocumentsCommand.COMMAND_WORD: + return documentsParser.parseDocumentsCommand(commandWord, arguments); + + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java index b117acb9c55..e23f3497327 100644 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java @@ -2,6 +2,7 @@ import static java.util.Objects.requireNonNull; +import java.time.LocalDate; import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -9,11 +10,24 @@ import seedu.address.commons.core.index.Index; import seedu.address.commons.util.StringUtil; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.application.JobTitle; +import seedu.address.model.application.Location; +import seedu.address.model.application.Note; +import seedu.address.model.application.ProgrammingLanguage; +import seedu.address.model.application.Qualification; +import seedu.address.model.application.Rating; +import seedu.address.model.application.Reflection; +import seedu.address.model.application.Review; +import seedu.address.model.application.Salary; +import seedu.address.model.contact.Email; +import seedu.address.model.contact.Phone; +import seedu.address.model.documents.CoverLetterLink; +import seedu.address.model.documents.ResumeLink; +import seedu.address.model.task.ApplicationDeadline; +import seedu.address.model.task.NoteContent; /** * Contains utility methods used for parsing strings in the various *Parser classes. @@ -21,6 +35,7 @@ public class ParserUtil { public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer."; + public static final String MESSAGE_INVALID_INTERVAL = "The start date must be before or equal to the end date."; /** * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be @@ -36,18 +51,220 @@ public static Index parseIndex(String oneBasedIndex) throws ParseException { } /** - * Parses a {@code String name} into a {@code Name}. + * Parses a {@code String companyName} into a {@code CompanyName}. * Leading and trailing whitespaces will be trimmed. * - * @throws ParseException if the given {@code name} is invalid. + * @throws ParseException if the given {@code companyName} is invalid. */ - public static Name parseName(String name) throws ParseException { - requireNonNull(name); - String trimmedName = name.trim(); - if (!Name.isValidName(trimmedName)) { - throw new ParseException(Name.MESSAGE_CONSTRAINTS); + public static CompanyName parseCompanyName(String companyName) throws ParseException { + requireNonNull(companyName); + String trimmedName = companyName.trim(); + if (!CompanyName.isValidCompanyName(trimmedName)) { + throw new ParseException(CompanyName.MESSAGE_CONSTRAINTS); } - return new Name(trimmedName); + return new CompanyName(trimmedName); + } + + /** + * Parses a {@code String jobTitle} into a {@code JobTitle}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code jobTitle} is invalid. + */ + public static JobTitle parseJobTitle(String jobTitle) throws ParseException { + requireNonNull(jobTitle); + String trimmedJobTitle = jobTitle.trim(); + if (!JobTitle.isValidJobTitle(trimmedJobTitle)) { + throw new ParseException(JobTitle.MESSAGE_CONSTRAINTS); + } + return new JobTitle(trimmedJobTitle); + } + + /** + * Parses a {@code String Review} into a {@code Review}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code review} is invalid. + */ + public static Review parseReview(String review) throws ParseException { + requireNonNull(review); + String trimmedReview = review.trim(); + if (!Review.isValidReview(trimmedReview)) { + throw new ParseException(Review.MESSAGE_CONSTRAINTS); + } + return new Review(trimmedReview); + } + + /** + * Parses {@code Collection reviews} into a {@code Set}. + */ + public static Set parseReviews(Collection reviews) throws ParseException { + requireNonNull(reviews); + final Set reviewList = new HashSet<>(); + for (String review : reviews) { + reviewList.add(parseReview(review)); + } + return reviewList; + } + + /** + * Parses a {@code String Programming Language} into a {@code Programming Language}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code programmingLanguage} is invalid. + */ + public static ProgrammingLanguage parseProgrammingLanguage(String programmingLanguage) throws ParseException { + requireNonNull(programmingLanguage); + String trimmedProgrammingLanguage = programmingLanguage.trim(); + if (!ProgrammingLanguage.isValidProgrammingLanguage(trimmedProgrammingLanguage)) { + throw new ParseException(ProgrammingLanguage.MESSAGE_CONSTRAINTS); + } + return new ProgrammingLanguage(trimmedProgrammingLanguage); + } + + /** + * Parses {@code Collection programming languages} into a {@code Set}. + */ + public static Set parseProgrammingLanguages( + Collection programmingLanguages) throws ParseException { + requireNonNull(programmingLanguages); + final Set programmingLanguageList = new HashSet<>(); + for (String programmingLanguage : programmingLanguages) { + programmingLanguageList.add(parseProgrammingLanguage(programmingLanguage)); + } + return programmingLanguageList; + } + + /** + * Parses a {@code String Qualification} into a {@code Qualification}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code qualification} is invalid. + */ + public static Qualification parseQualification(String qualification) throws ParseException { + requireNonNull(qualification); + String trimmedQualification = qualification.trim(); + if (!Qualification.isValidQualification(trimmedQualification)) { + throw new ParseException(Qualification.MESSAGE_CONSTRAINTS); + } + return new Qualification(trimmedQualification); + } + + /** + * Parses {@code Collection qualifications} into a {@code Set}. + */ + public static Set parseQualifications(Collection qualifications) throws ParseException { + requireNonNull(qualifications); + final Set qualificationList = new HashSet<>(); + for (String qualification : qualifications) { + qualificationList.add(parseQualification(qualification)); + } + return qualificationList; + } + + /** + * Parses a {@code String location} into a {@code Location}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code location} is invalid. + */ + public static Location parseLocation(String location) throws ParseException { + if (location == null) { + return new Location(null); + } + String trimmedLocation = location.trim(); + if (!Location.isValidLocation(trimmedLocation)) { + throw new ParseException(Location.MESSAGE_CONSTRAINTS); + } + return new Location(trimmedLocation); + } + + /** + * Parses a {@code String salary} into a {@code Salary}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code salary} is invalid. + */ + public static Salary parseSalary(String salary) throws ParseException { + if (salary == null) { + return new Salary(null); + } + String trimmedSalary = salary.trim(); + if (!Salary.isValidSalary(trimmedSalary)) { + throw new ParseException(Salary.MESSAGE_CONSTRAINTS); + } + return new Salary(trimmedSalary); + } + + /** + * Parses a {@code String Note} into a {@code Note}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code note} is invalid. + */ + public static Note parseNote(String note) throws ParseException { + requireNonNull(note); + String trimmedNote = note.trim(); + if (!Note.isValidNote(trimmedNote)) { + throw new ParseException(Note.MESSAGE_CONSTRAINTS); + } + return new Note(trimmedNote); + } + + /** + * Parses {@code Collection notes} into a {@code Set}. + */ + public static Set parseNotes(Collection notes) throws ParseException { + requireNonNull(notes); + final Set noteList = new HashSet<>(); + for (String note : notes) { + noteList.add(parseNote(note)); + } + return noteList; + } + + /** + * Parses a {@code String rating} into a {@code Rating}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code rating} is invalid. + */ + public static Rating parseRating(String rating) throws ParseException { + if (rating == null) { + return new Rating(null); + } + String trimmedRating = rating.trim(); + if (!Rating.isValidRating(trimmedRating)) { + throw new ParseException(Rating.MESSAGE_CONSTRAINTS); + } + return new Rating(trimmedRating); + } + + /** + * Parses a {@code String Reflection} into a {@code Reflection}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code reflection} is invalid. + */ + public static Reflection parseReflection(String reflection) throws ParseException { + requireNonNull(reflection); + String trimmedReflection = reflection.trim(); + if (!Reflection.isValidReflection(trimmedReflection)) { + throw new ParseException(Reflection.MESSAGE_CONSTRAINTS); + } + return new Reflection(trimmedReflection); + } + + /** + * Parses {@code Collection reflections} into a {@code Set}. + */ + public static Set parseReflections(Collection reflections) throws ParseException { + requireNonNull(reflections); + final Set reflectionList = new HashSet<>(); + for (String reflection : reflections) { + reflectionList.add(parseReflection(reflection)); + } + return reflectionList; } /** @@ -65,21 +282,6 @@ public static Phone parsePhone(String phone) throws ParseException { return new Phone(trimmedPhone); } - /** - * Parses a {@code String address} into an {@code Address}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code address} is invalid. - */ - public static Address parseAddress(String address) throws ParseException { - requireNonNull(address); - String trimmedAddress = address.trim(); - if (!Address.isValidAddress(trimmedAddress)) { - throw new ParseException(Address.MESSAGE_CONSTRAINTS); - } - return new Address(trimmedAddress); - } - /** * Parses a {@code String email} into an {@code Email}. * Leading and trailing whitespaces will be trimmed. @@ -96,29 +298,92 @@ public static Email parseEmail(String email) throws ParseException { } /** - * Parses a {@code String tag} into a {@code Tag}. + * Parses a {@code String interviewDate} into an {@code InterviewDate}. + * Leading and trailing whitespaces will be trimmed. + */ + public static InterviewDate parseInterviewDate(String interviewDate) { + requireNonNull(interviewDate); + String trimmedInterviewDate = interviewDate.trim(); + return new InterviewDate(interviewDate); + } + + /** + * Parses a {@code String status} into a {@code InternshipStatus}. + * Leading and trailing whitespaces will be trimmed. It's case-insensitive + * + * @throws ParseException if the given {@code status} is invalid. + */ + public static InternshipStatus parseInternshipStatus(String status) throws ParseException { + requireNonNull(status); + String trimmedStatus = status.trim().toUpperCase(); + if (!InternshipStatus.isValidStatus(trimmedStatus)) { + throw new ParseException(InternshipStatus.MESSAGE_CONSTRAINTS); + } + return InternshipStatus.valueOf(trimmedStatus); + } + + /** + * Parses a {@code String deadline} into a {@code ApplicationDeadline}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code deadline} is invalid. + */ + public static ApplicationDeadline parseDeadline(String deadline) throws ParseException { + requireNonNull(deadline); + + String trimmedDeadline = deadline.trim(); + LocalDate formattedDate = LocalDate.parse(trimmedDeadline); + + if (!ApplicationDeadline.isValidDate(formattedDate)) { + throw new ParseException(ApplicationDeadline.MESSAGE_CONSTRAINTS); + } + return new ApplicationDeadline(formattedDate); + } + + /** + * Parses a {@code String content} into a {@code NoteContent}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code content} is invalid. + */ + public static NoteContent parseContent(String content) throws ParseException { + requireNonNull(content); + + String trimmedContent = content.trim(); + + if (!NoteContent.isValidContent(content)) { + throw new ParseException(NoteContent.MESSAGE_CONSTRAINTS); + } + return new NoteContent(trimmedContent); + } + + /** + * Parses a {@code String status} into a {@code ResumeLink}. * Leading and trailing whitespaces will be trimmed. * - * @throws ParseException if the given {@code tag} is invalid. + * @throws ParseException if the given {@code status} is invalid. */ - public static Tag parseTag(String tag) throws ParseException { - requireNonNull(tag); - String trimmedTag = tag.trim(); - if (!Tag.isValidTagName(trimmedTag)) { - throw new ParseException(Tag.MESSAGE_CONSTRAINTS); + public static ResumeLink parseResumeLink(String status) throws ParseException { + requireNonNull(status); + String trimmedStatus = status.trim(); + if (!ResumeLink.isValidResumeLink(trimmedStatus)) { + throw new ParseException(ResumeLink.MESSAGE_CONSTRAINTS); } - return new Tag(trimmedTag); + return new ResumeLink(trimmedStatus); } /** - * Parses {@code Collection tags} into a {@code Set}. + * Parses a {@code String status} into a {@code CoverLetterLink}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code status} is invalid. */ - public static Set parseTags(Collection tags) throws ParseException { - requireNonNull(tags); - final Set tagSet = new HashSet<>(); - for (String tagName : tags) { - tagSet.add(parseTag(tagName)); + public static CoverLetterLink parseCoverLetterLink(String status) throws ParseException { + requireNonNull(status); + String trimmedStatus = status.trim(); + if (!CoverLetterLink.isValidCoverLetterLink(trimmedStatus)) { + throw new ParseException(CoverLetterLink.MESSAGE_CONSTRAINTS); } - return tagSet; + return new CoverLetterLink(trimmedStatus); } } diff --git a/src/main/java/seedu/address/logic/parser/SortCommandParser.java b/src/main/java/seedu/address/logic/parser/SortCommandParser.java new file mode 100644 index 00000000000..88d7bcea7bb --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/SortCommandParser.java @@ -0,0 +1,67 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_JOB_TITLE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_STATUS; + +import seedu.address.logic.commands.FindCommand; +import seedu.address.logic.commands.SortCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.application.comparator.CompanyNameComparator; +import seedu.address.model.application.comparator.InterviewDateComparator; +import seedu.address.model.application.comparator.JobTitleComparator; +import seedu.address.model.application.comparator.StatusComparator; + +/** + * Parses input arguments and creates a new FindCommand object + */ +public class SortCommandParser implements Parser { + + /** + * Checks if the prefix p specified is valid. + * + * @param p The prefix to be verified + * @throws ParseException if none of the prefixes is matched + */ + private void verifySortCommandOptions(Prefix p) throws ParseException { + for (Prefix i : SortCommand.PREFIXES_SUPPORTED) { + if (p.equals(i)) { + return; + } + } + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + } + + /** + * Parses the given {@code String} of arguments in the context of the SortCommand + * and returns a SortCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public SortCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE)); + } + String[] prefixes = trimmedArgs.split("\\s+"); + if (prefixes.length != 1) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + } + Prefix prefix = new Prefix(prefixes[0]); + verifySortCommandOptions(prefix); + if (PREFIX_COMPANY_NAME.equals(prefix)) { + return new SortCommand(new CompanyNameComparator()); + } else if (PREFIX_JOB_TITLE.equals(prefix)) { + return new SortCommand(new JobTitleComparator()); + } else if (PREFIX_STATUS.equals(prefix)) { + return new SortCommand(new StatusComparator()); + } else if (PREFIX_DATE.equals(prefix)) { + return new SortCommand(new InterviewDateComparator()); + } + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + } + +} + diff --git a/src/main/java/seedu/address/logic/parser/UnarchiveCommandParser.java b/src/main/java/seedu/address/logic/parser/UnarchiveCommandParser.java new file mode 100644 index 00000000000..8e0f0b2259b --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/UnarchiveCommandParser.java @@ -0,0 +1,29 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.UnarchiveCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new UnarchiveCommand object + */ +public class UnarchiveCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the UnarchiveCommand + * and returns a UnarchiveCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public UnarchiveCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new UnarchiveCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, UnarchiveCommand.MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/seedu/address/logic/parser/contact/AddContactCommandParser.java b/src/main/java/seedu/address/logic/parser/contact/AddContactCommandParser.java new file mode 100644 index 00000000000..32579b15e17 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/contact/AddContactCommandParser.java @@ -0,0 +1,65 @@ +package seedu.address.logic.parser.contact; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; + +import java.util.stream.Stream; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.contact.AddContactCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.Prefix; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.contact.Contact; +import seedu.address.model.contact.Email; +import seedu.address.model.contact.Phone; + +/** +* Parses input arguments and creates a new AddContactCommand object +*/ +public class AddContactCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the AddContactCommand + * and returns an AddContactCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddContactCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_PHONE, PREFIX_EMAIL); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddContactCommand.MESSAGE_USAGE), + pe); + } + + if (!arePrefixesPresent(argMultimap, PREFIX_PHONE, PREFIX_EMAIL)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddContactCommand.MESSAGE_USAGE)); + } + + Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()); + Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()); + + Contact contact = new Contact(phone, email); + + return new AddContactCommand(index, contact); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/contact/ContactParser.java b/src/main/java/seedu/address/logic/parser/contact/ContactParser.java new file mode 100644 index 00000000000..ae32de53582 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/contact/ContactParser.java @@ -0,0 +1,39 @@ +package seedu.address.logic.parser.contact; + +import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.contact.AddContactCommand; +import seedu.address.logic.commands.contact.DeleteContactCommand; +import seedu.address.logic.commands.contact.EditContactCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses user input related to task package. + */ +public class ContactParser { + + /** + * Parses user input into task related command for execution. + * + * @param commandWord command word in user input + * @param arguments command argument related to specified parameter types + * @return the command based on the user input + * @throws ParseException if the user input does not conform the expected format + */ + public Command parseContactCommand(String commandWord, String arguments) throws ParseException { + switch (commandWord) { + case AddContactCommand.COMMAND_WORD: + return new AddContactCommandParser().parse(arguments); + + case EditContactCommand.COMMAND_WORD: + return new EditContactCommandParser().parse(arguments); + + case DeleteContactCommand.COMMAND_WORD: + return new DeleteContactCommandParser().parse(arguments); + + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/contact/DeleteContactCommandParser.java b/src/main/java/seedu/address/logic/parser/contact/DeleteContactCommandParser.java new file mode 100644 index 00000000000..07ef5fe9e7e --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/contact/DeleteContactCommandParser.java @@ -0,0 +1,29 @@ +package seedu.address.logic.parser.contact; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.contact.DeleteContactCommand; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteContactCommand object + */ +public class DeleteContactCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the DeleteContactCommand + * and returns a DeleteContactCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteContactCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteContactCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteContactCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/contact/EditContactCommandParser.java b/src/main/java/seedu/address/logic/parser/contact/EditContactCommandParser.java new file mode 100644 index 00000000000..510934c753d --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/contact/EditContactCommandParser.java @@ -0,0 +1,68 @@ +package seedu.address.logic.parser.contact; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; + +import java.util.stream.Stream; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.contact.EditContactCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.Prefix; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new EditContactCommand object + */ +public class EditContactCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the EditContactCommand + * and returns an EditContactCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EditContactCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_PHONE, PREFIX_EMAIL); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditContactCommand.MESSAGE_USAGE), + pe); + } + + EditContactCommand.EditContactDescriptor editDocumentsDescriptor = + new EditContactCommand.EditContactDescriptor(); + if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { + editDocumentsDescriptor.setPhone( + ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); + } + if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { + editDocumentsDescriptor.setEmail( + ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get())); + } + + if (!editDocumentsDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditContactCommand.MESSAGE_NOT_EDITED); + } + + return new EditContactCommand(index, editDocumentsDescriptor); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/documents/AddDocumentsCommandParser.java b/src/main/java/seedu/address/logic/parser/documents/AddDocumentsCommandParser.java new file mode 100644 index 00000000000..3a6264258f4 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/documents/AddDocumentsCommandParser.java @@ -0,0 +1,66 @@ +package seedu.address.logic.parser.documents; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COVER_LETTER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RESUME; + +import java.util.stream.Stream; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.documents.AddDocumentsCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.Prefix; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.documents.CoverLetterLink; +import seedu.address.model.documents.Documents; +import seedu.address.model.documents.ResumeLink; + +/** + * Parses input arguments and creates a new AddContactCommand object + */ +public class AddDocumentsCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the AddContactCommand + * and returns an AddContactCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddDocumentsCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_RESUME, PREFIX_COVER_LETTER); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddDocumentsCommand.MESSAGE_USAGE), + pe); + } + + if (!arePrefixesPresent(argMultimap, PREFIX_RESUME, PREFIX_COVER_LETTER)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddDocumentsCommand.MESSAGE_USAGE)); + } + + ResumeLink resumeLink = ParserUtil.parseResumeLink(argMultimap.getValue(PREFIX_RESUME).get()); + CoverLetterLink coverLetterLink = ParserUtil.parseCoverLetterLink(argMultimap.getValue(PREFIX_COVER_LETTER) + .get()); + + Documents documents = new Documents(resumeLink, coverLetterLink); + + return new AddDocumentsCommand(index, documents); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/documents/DeleteDocumentsCommandParser.java b/src/main/java/seedu/address/logic/parser/documents/DeleteDocumentsCommandParser.java new file mode 100644 index 00000000000..7da4397b06f --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/documents/DeleteDocumentsCommandParser.java @@ -0,0 +1,29 @@ +package seedu.address.logic.parser.documents; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.documents.DeleteDocumentsCommand; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteDocumentsCommand object + */ +public class DeleteDocumentsCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the DeleteDocumentsCommand + * and returns a DeleteDocumentsCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteDocumentsCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteDocumentsCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteDocumentsCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/documents/DocumentsParser.java b/src/main/java/seedu/address/logic/parser/documents/DocumentsParser.java new file mode 100644 index 00000000000..4696db3ba9e --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/documents/DocumentsParser.java @@ -0,0 +1,43 @@ +package seedu.address.logic.parser.documents; + +import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; + +import java.util.regex.Pattern; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.documents.AddDocumentsCommand; +import seedu.address.logic.commands.documents.DeleteDocumentsCommand; +import seedu.address.logic.commands.documents.EditDocumentsCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses user input for commands related to documents. + */ +public class DocumentsParser { + /** + * Used for initial separation of command word and args. + */ + private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); + + /** + * Parses user input into command for execution. + * + * @return the command based on the user input + * @throws ParseException if the user input does not conform the expected format + */ + public Command parseDocumentsCommand(String commandWord, String arguments) throws ParseException { + switch (commandWord) { + case AddDocumentsCommand.COMMAND_WORD: + return new AddDocumentsCommandParser().parse(arguments); + + case EditDocumentsCommand.COMMAND_WORD: + return new EditDocumentsCommandParser().parse(arguments); + + case DeleteDocumentsCommand.COMMAND_WORD: + return new DeleteDocumentsCommandParser().parse(arguments); + + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/documents/EditDocumentsCommandParser.java b/src/main/java/seedu/address/logic/parser/documents/EditDocumentsCommandParser.java new file mode 100644 index 00000000000..33683e61ad6 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/documents/EditDocumentsCommandParser.java @@ -0,0 +1,68 @@ +package seedu.address.logic.parser.documents; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COVER_LETTER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RESUME; + +import java.util.stream.Stream; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.documents.EditDocumentsCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.Prefix; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new EditDocumentsCommand object + */ +public class EditDocumentsCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the EditDocumentsCommand + * and returns an EditDocumentsCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EditDocumentsCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_RESUME, PREFIX_COVER_LETTER); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditDocumentsCommand.MESSAGE_USAGE), + pe); + } + + EditDocumentsCommand.EditDocumentsDescriptor editDocumentsDescriptor = + new EditDocumentsCommand.EditDocumentsDescriptor(); + if (argMultimap.getValue(PREFIX_RESUME).isPresent()) { + editDocumentsDescriptor.setResumeLink( + ParserUtil.parseResumeLink(argMultimap.getValue(PREFIX_RESUME).get())); + } + if (argMultimap.getValue(PREFIX_COVER_LETTER).isPresent()) { + editDocumentsDescriptor.setCoverLetterLink( + ParserUtil.parseCoverLetterLink(argMultimap.getValue(PREFIX_COVER_LETTER).get())); + } + + if (!editDocumentsDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditDocumentsCommand.MESSAGE_NOT_EDITED); + } + + return new EditDocumentsCommand(index, editDocumentsDescriptor); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/task/FindTaskCommandParser.java b/src/main/java/seedu/address/logic/parser/task/FindTaskCommandParser.java new file mode 100644 index 00000000000..d00aa489472 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/task/FindTaskCommandParser.java @@ -0,0 +1,42 @@ +package seedu.address.logic.parser.task; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.Arrays; + +import seedu.address.logic.commands.task.FindTaskCommand; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.task.ContentContainsKeywordsPredicate; +import seedu.address.model.task.TitleContainsKeywordsPredicate; + + +/** + * Parses input arguments and creates a new FindTaskCommand object + */ +public class FindTaskCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FindTaskCommand + * and returns a FindTaskCommand object for execution. + * + * @throws ParseException if the user input does not conform the expected format + */ + public FindTaskCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindTaskCommand.MESSAGE_USAGE)); + } + + String[] nameKeywords = trimmedArgs.split("\\s+"); + + TitleContainsKeywordsPredicate titlePredicate = new TitleContainsKeywordsPredicate( + Arrays.asList(nameKeywords)); + ContentContainsKeywordsPredicate contentPredicate = new ContentContainsKeywordsPredicate( + Arrays.asList(nameKeywords)); + + return new FindTaskCommand(titlePredicate, contentPredicate); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/task/TaskParser.java b/src/main/java/seedu/address/logic/parser/task/TaskParser.java new file mode 100644 index 00000000000..334d23794f3 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/task/TaskParser.java @@ -0,0 +1,107 @@ +package seedu.address.logic.parser.task; + +import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.task.FindTaskCommand; +import seedu.address.logic.commands.task.ListTaskCommand; +import seedu.address.logic.commands.task.note.AddNoteCommand; +import seedu.address.logic.commands.task.note.ClearNoteCommand; +import seedu.address.logic.commands.task.note.DeleteNoteCommand; +import seedu.address.logic.commands.task.note.ListNoteCommand; +import seedu.address.logic.commands.task.todo.AddTodoCommand; +import seedu.address.logic.commands.task.todo.ClearTodoCommand; +import seedu.address.logic.commands.task.todo.DeleteTodoCommand; +import seedu.address.logic.commands.task.todo.EditDeadlineCommand; +import seedu.address.logic.commands.task.todo.EditNoteContentCommand; +import seedu.address.logic.commands.task.todo.ListTodoCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.logic.parser.task.note.AddNoteCommandParser; +import seedu.address.logic.parser.task.note.DeleteNoteCommandParser; +import seedu.address.logic.parser.task.todo.AddTodoCommandParser; +import seedu.address.logic.parser.task.todo.DeleteTodoCommandParser; +import seedu.address.logic.parser.task.todo.EditContentCommandParser; +import seedu.address.logic.parser.task.todo.EditDeadlineCommandParser; + +/** + * Parses user inputs related to the task package. + */ +public class TaskParser { + + /** + * Parses user input into task related command according to the {@code commandWord} with the {@code arguments} for + * execution. + * + * @throws ParseException if the user input does not conform the expected format + */ + public Command parseTaskCommand(String commandWord, String arguments) throws ParseException { + switch (commandWord) { + case ListTaskCommand.COMMAND_WORD: + return new ListTaskCommand(); + + case FindTaskCommand.COMMAND_WORD: + return new FindTaskCommandParser().parse(arguments); + + case AddTodoCommand.COMMAND_WORD: + case ListTodoCommand.COMMAND_WORD: + case EditDeadlineCommand.COMMAND_WORD: + case EditNoteContentCommand.COMMAND_WORD: + case DeleteTodoCommand.COMMAND_WORD: + case ClearTodoCommand.COMMAND_WORD: + return parseTodoCommand(commandWord, arguments); + + case AddNoteCommand.COMMAND_WORD: + case ListNoteCommand.COMMAND_WORD: + case DeleteNoteCommand.COMMAND_WORD: + case ClearNoteCommand.COMMAND_WORD: + return parseNoteCommand(commandWord, arguments); + + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } + + private Command parseTodoCommand(String commandWord, String arguments) throws ParseException { + switch (commandWord) { + case AddTodoCommand.COMMAND_WORD: + return new AddTodoCommandParser().parse(arguments); + + case ListTodoCommand.COMMAND_WORD: + return new ListTodoCommand(); + + case EditDeadlineCommand.COMMAND_WORD: + return new EditDeadlineCommandParser().parse(arguments); + + case EditNoteContentCommand.COMMAND_WORD: + return new EditContentCommandParser().parse(arguments); + + case DeleteTodoCommand.COMMAND_WORD: + return new DeleteTodoCommandParser().parse(arguments); + + case ClearTodoCommand.COMMAND_WORD: + return new ClearTodoCommand(); + + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } + + private Command parseNoteCommand(String commandWord, String arguments) throws ParseException { + switch (commandWord) { + case AddNoteCommand.COMMAND_WORD: + return new AddNoteCommandParser().parse(arguments); + + case ListNoteCommand.COMMAND_WORD: + return new ListNoteCommand(); + + case DeleteNoteCommand.COMMAND_WORD: + return new DeleteNoteCommandParser().parse(arguments); + + case ClearNoteCommand.COMMAND_WORD: + return new ClearNoteCommand(); + + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/task/note/AddNoteCommandParser.java b/src/main/java/seedu/address/logic/parser/task/note/AddNoteCommandParser.java new file mode 100644 index 00000000000..d53b986231f --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/task/note/AddNoteCommandParser.java @@ -0,0 +1,53 @@ +package seedu.address.logic.parser.task.note; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE_CONTENT; + +import java.util.stream.Stream; + +import seedu.address.logic.commands.task.note.AddNoteCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.Prefix; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.task.Note; +import seedu.address.model.task.NoteContent; + +/** + * Parses input arguments and creates a new AddNoteCommand object + */ +public class AddNoteCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddNoteCommand + * and returns an AddNoteCommand object for execution. + * + * @throws ParseException if the user input does not conform the expected format + */ + public AddNoteCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NOTE_CONTENT); + + if (!arePrefixesPresent(argMultimap, PREFIX_NOTE_CONTENT) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddNoteCommand.MESSAGE_USAGE)); + } + + NoteContent content = ParserUtil.parseContent(argMultimap.getValue(PREFIX_NOTE_CONTENT).orElse(null)); + + + Note note = new Note(content); + + return new AddNoteCommand(note); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/task/note/DeleteNoteCommandParser.java b/src/main/java/seedu/address/logic/parser/task/note/DeleteNoteCommandParser.java new file mode 100644 index 00000000000..69da31dfe42 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/task/note/DeleteNoteCommandParser.java @@ -0,0 +1,32 @@ +package seedu.address.logic.parser.task.note; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.task.note.DeleteNoteCommand; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteNoteCommand object + */ +public class DeleteNoteCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteNoteCommand + * and returns a DeleteNoteCommand object for execution. + * + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteNoteCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteNoteCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteNoteCommand.MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/seedu/address/logic/parser/task/todo/AddTodoCommandParser.java b/src/main/java/seedu/address/logic/parser/task/todo/AddTodoCommandParser.java new file mode 100644 index 00000000000..0bcb0dbd360 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/task/todo/AddTodoCommandParser.java @@ -0,0 +1,63 @@ +package seedu.address.logic.parser.task.todo; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_JOB_TITLE; + +import java.util.stream.Stream; + +import seedu.address.logic.commands.task.todo.AddTodoCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.Prefix; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.JobTitle; +import seedu.address.model.task.ApplicationDeadline; +import seedu.address.model.task.InternshipTodo; + +/** + * Parses input arguments and creates a new AddTodoCommand object + */ +public class AddTodoCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddTodoCommand + * and returns an AddTodoCommand object for execution. + * + * @throws ParseException if the user input does not conform the expected format + */ + public AddTodoCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_COMPANY_NAME, PREFIX_JOB_TITLE, PREFIX_DEADLINE); + + if (!arePrefixesPresent(argMultimap, PREFIX_COMPANY_NAME, PREFIX_JOB_TITLE, PREFIX_DEADLINE) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTodoCommand.MESSAGE_USAGE)); + } + + CompanyName companyName = ParserUtil.parseCompanyName( + argMultimap.getValue(PREFIX_COMPANY_NAME).orElse(null)); + JobTitle jobTitle = ParserUtil.parseJobTitle( + argMultimap.getValue(PREFIX_JOB_TITLE).orElse(null)); + ApplicationDeadline deadline = ParserUtil.parseDeadline( + argMultimap.getValue(PREFIX_DEADLINE).orElse(null)); + + InternshipTodo todo = new InternshipTodo( + companyName, jobTitle, deadline); + + return new AddTodoCommand(todo); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/task/todo/DeleteTodoCommandParser.java b/src/main/java/seedu/address/logic/parser/task/todo/DeleteTodoCommandParser.java new file mode 100644 index 00000000000..d9fd574fcd5 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/task/todo/DeleteTodoCommandParser.java @@ -0,0 +1,32 @@ +package seedu.address.logic.parser.task.todo; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.task.todo.DeleteTodoCommand; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteTodoCommand object + */ +public class DeleteTodoCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteTodoCommand + * and returns a DeleteTodoCommand object for execution. + * + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteTodoCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteTodoCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTodoCommand.MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/seedu/address/logic/parser/task/todo/EditContentCommandParser.java b/src/main/java/seedu/address/logic/parser/task/todo/EditContentCommandParser.java new file mode 100644 index 00000000000..d4e53391802 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/task/todo/EditContentCommandParser.java @@ -0,0 +1,63 @@ +package seedu.address.logic.parser.task.todo; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE_CONTENT; + +import java.util.stream.Stream; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.task.todo.EditNoteContentCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.Prefix; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.task.NoteContent; + +/** + * Parses input arguments and creates a new EditNoteContentCommand object + */ +public class EditContentCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditNoteContentCommand + * and returns an EditNoteContentCommand object for execution. + * + * @throws ParseException if the user input does not conform the expected format + */ + public EditNoteContentCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NOTE_CONTENT); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditNoteContentCommand.MESSAGE_USAGE), pe); + } + + if (!arePrefixesPresent(argMultimap, PREFIX_NOTE_CONTENT)) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditNoteContentCommand.MESSAGE_USAGE)); + } + + NoteContent content = ParserUtil.parseContent( + argMultimap.getValue(PREFIX_NOTE_CONTENT).orElse(null)); + + return new EditNoteContentCommand(index, content); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/task/todo/EditDeadlineCommandParser.java b/src/main/java/seedu/address/logic/parser/task/todo/EditDeadlineCommandParser.java new file mode 100644 index 00000000000..1eed753020b --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/task/todo/EditDeadlineCommandParser.java @@ -0,0 +1,62 @@ +package seedu.address.logic.parser.task.todo; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE; + +import java.util.stream.Stream; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.task.todo.EditDeadlineCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.Prefix; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.task.ApplicationDeadline; + +/** + * Parses input arguments and creates a new EditDeadlineCommand object + */ +public class EditDeadlineCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditDeadlineCommand + * and returns an EditDeadlineCommand object for execution. + * + * @throws ParseException if the user input does not conform the expected format + */ + public EditDeadlineCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_DEADLINE); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditDeadlineCommand.MESSAGE_USAGE), + pe); + } + + if (!arePrefixesPresent(argMultimap, PREFIX_DEADLINE)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditDeadlineCommand.MESSAGE_USAGE)); + } + + ApplicationDeadline deadline = ParserUtil.parseDeadline( + argMultimap.getValue(PREFIX_DEADLINE).orElse(null)); + + return new EditDeadlineCommand(index, deadline); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java index 1a943a0781a..6a335c34d7e 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/address/model/AddressBook.java @@ -5,8 +5,8 @@ import java.util.List; import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.UniquePersonList; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.UniqueApplicationList; /** * Wraps all data at the address-book level @@ -14,17 +14,16 @@ */ public class AddressBook implements ReadOnlyAddressBook { - private final UniquePersonList persons; - + private final UniqueApplicationList applications; /* * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html * - * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication + * NoteList that non-static init blocks are not recommended to use. There are other ways to avoid duplication * among constructors. */ { - persons = new UniquePersonList(); + applications = new UniqueApplicationList(); } public AddressBook() {} @@ -40,11 +39,11 @@ public AddressBook(ReadOnlyAddressBook toBeCopied) { //// list overwrite operations /** - * Replaces the contents of the person list with {@code persons}. - * {@code persons} must not contain duplicate persons. + * Replaces the contents of the person list with {@code applications}. + * {@code applications} must not contain duplicate applications. */ - public void setPersons(List persons) { - this.persons.setPersons(persons); + public void setApplications(List applications) { + this.applications.setApplications(applications); } /** @@ -53,68 +52,86 @@ public void setPersons(List persons) { public void resetData(ReadOnlyAddressBook newData) { requireNonNull(newData); - setPersons(newData.getPersonList()); + setApplications(newData.getInternshipList()); } + /// application-level operations + /** + * Returns true if an internship application with the same identity + * as {@code internshipApplication} exists in the address book. + */ + public boolean hasApplication(InternshipApplication application) { + requireNonNull(application); + return applications.contains(application); + } //// person-level operations /** - * Returns true if a person with the same identity as {@code person} exists in the address book. + * Adds an internship application to the tracker. + * The application must not already exist in the tracker. */ - public boolean hasPerson(Person person) { - requireNonNull(person); - return persons.contains(person); + public void addApplication(InternshipApplication application) { + applications.add(application); } /** - * Adds a person to the address book. - * The person must not already exist in the address book. + * Adds internship applications to the tracker. + * The application must not already exist in the tracker. */ - public void addPerson(Person p) { - persons.add(p); + public void addApplications(List application) { + applications.add(application); } + /** - * Replaces the given person {@code target} in the list with {@code editedPerson}. + * Adds an application to the address book. + * The application must not already exist in the address book. + */ + public void addInternshipApplication(InternshipApplication internshipApplication) { + applications.add(internshipApplication); + } + + /** + * Replaces the given person {@code target} in the list with {@code editedApplication}. * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. + * The application identity of {@code editedApplication} + * must not be the same as another existing application in the address book. */ - public void setPerson(Person target, Person editedPerson) { - requireNonNull(editedPerson); + public void setApplication(InternshipApplication target, InternshipApplication editedApplication) { + requireNonNull(editedApplication); - persons.setPerson(target, editedPerson); + applications.setApplication(target, editedApplication); } /** * Removes {@code key} from this {@code AddressBook}. * {@code key} must exist in the address book. */ - public void removePerson(Person key) { - persons.remove(key); + public void removeApplication(InternshipApplication key) { + applications.remove(key); } - //// util methods @Override public String toString() { - return persons.asUnmodifiableObservableList().size() + " persons"; - // TODO: refine later + return applications.asUnmodifiableObservableList().size() + " applications"; } @Override - public ObservableList getPersonList() { - return persons.asUnmodifiableObservableList(); + public ObservableList getInternshipList() { + return applications.asUnmodifiableObservableList(); } + @Override public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof AddressBook // instanceof handles nulls - && persons.equals(((AddressBook) other).persons)); + && applications.equals(((AddressBook) other).applications)); } @Override public int hashCode() { - return persons.hashCode(); + return applications.hashCode(); } } diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index d54df471c1f..0fc8df80925 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -1,19 +1,38 @@ package seedu.address.model; import java.nio.file.Path; +import java.util.Comparator; +import java.util.List; import java.util.function.Predicate; import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.Person; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.task.InternshipTodo; +import seedu.address.model.task.Note; /** * The API of the Model component. */ public interface Model { + + /** + * {@code Predicate} that always evaluate to true + */ + Predicate PREDICATE_SHOW_ALL_APPLICATIONS = unused -> true; + + /** {@code Predicate} that evaluate to true for all unarchived applications */ + Predicate PREDICATE_SHOW_ONGOING_APPLICATIONS = internshipApplication -> + !internshipApplication.isArchived(); + + /** {@code Predicate} that evaluate to true for all archived internship applications */ + Predicate PREDICATE_SHOW_ARCHIVED_APPLICATIONS = InternshipApplication::isArchived; + /** {@code Predicate} that always evaluate to true */ - Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; + Predicate PREDICATE_SHOW_ALL_TODO = unused -> true; + /** {@code Predicate} that always evaluate to true */ + Predicate PREDICATE_SHOW_ALL_NOTES = unused -> true; /** * Replaces user prefs data with the data in {@code userPrefs}. */ @@ -39,49 +58,196 @@ public interface Model { */ Path getAddressBookFilePath(); + /** + * Returns the user prefs' todo list file path. + */ + Path getTodoListFilePath(); + + /** + * Returns the user prefs' note list file path. + */ + Path getNoteListFilePath(); + /** * Sets the user prefs' address book file path. */ void setAddressBookFilePath(Path addressBookFilePath); /** - * Replaces address book data with the data in {@code addressBook}. + * Replaces address book data with the data in {@code internEase}. */ - void setAddressBook(ReadOnlyAddressBook addressBook); + void setInternEase(ReadOnlyAddressBook internEase); - /** Returns the AddressBook */ + /** + * Returns the AddressBook + */ ReadOnlyAddressBook getAddressBook(); + /** Returns the TodoList */ + ReadOnlyTodoList getTodoList(); + + /** Returns the NoteList */ + ReadOnlyNote getNoteList(); + + /** + * Returns true if an internship application with the same identity as + * {@code internshipApplication} exists in the address book. + */ + boolean hasApplication(InternshipApplication person); + + /** + * Returns true if a todo with the same identity as + * {@code todo} exists in the tracker. + */ + boolean hasTodo(InternshipTodo todo); + + /** + * Returns true if a note with the same identity as + * {@code note} exists in the tracker. + */ + boolean hasNote(Note note); + + /** + * Deletes the given internship application. + * The application must exist in the address book. + */ + void deleteInternship(InternshipApplication application); + + /** + * Deletes the given todo. + * The todo must exist in the todo list. + */ + void deleteTodo(InternshipTodo target); + /** - * Returns true if a person with the same identity as {@code person} exists in the address book. + * Deletes the given note. + * The note must exist in the note list. */ - boolean hasPerson(Person person); + void deleteNote(Note target); /** - * Deletes the given person. - * The person must exist in the address book. + * Clears todo list. */ - void deletePerson(Person target); + void clearTodo(ReadOnlyTodoList internEase); /** - * Adds the given person. - * {@code person} must not already exist in the address book. + * Clears note list. */ - void addPerson(Person person); + void clearNote(ReadOnlyNote internEase); /** - * Replaces the given person {@code target} with {@code editedPerson}. + * Adds the given application. + * {@code InternshipApplication} must not already exist in the tracker. + */ + void addApplication(InternshipApplication application); + + /** + * Adds the given todo. + * {@code todo} must not already exist in the tracker. + */ + void addTodo(InternshipTodo todo); + + /** + * Adds the given note. + * {@code note} must not already exist in the tracker. + */ + void addNote(Note note); + + /** + * Adds the given applications. + * {@code InternshipApplications} must not already exist in the tracker. + */ + void addApplications(List applications); + + /** + * Replaces the given internshipApplication {@code target} with {@code editedApplication}. * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. + * The application identity of {@code editedApplication} must not be the + * same as another existing application in the address book. + */ + void setApplication(InternshipApplication target, InternshipApplication editedApplication); + + /** + * Replaces the given todo {@code target} with {@code editedTodo}. + * {@code target} must exist in the tracker. + * The identity of {@code editedTodo} must not be the same as another existing todo in the tracker. */ - void setPerson(Person target, Person editedPerson); + void setTodo(InternshipTodo target, + InternshipTodo editedTodo); + + /** Returns the InternshipApplication with the most imminent interview */ + InternshipApplication getReminder(); - /** Returns an unmodifiable view of the filtered person list */ - ObservableList getFilteredPersonList(); + /** Replaces current reminder with another InternshipApplication with an interview date that is earlier */ + void updateReminder(); + + /** + * Replaces the given note {@code target} with {@code editedNote}. + * {@code target} must exist in the tracker. + * The identity of {@code editedNote} must not be the same as another existing note in the tracker. + */ + void setNote(Note target, Note editedNote); + + /** + * Returns an unmodifiable view of the sorted filtered internship list + */ + ObservableList getSortedFilteredInternshipList(); + + /** Returns an unmodifiable view of the filtered todo list */ + ObservableList getFilteredTodoList(); + + /** Returns an unmodifiable view of the filtered note list */ + ObservableList getFilteredNoteList(); + + /** + * Updates the comparator of the sorted filtered internship list to sort by the given {@code comparator}. + * + * @throws NullPointerException if {@code comparator} is null. + */ + void updateSortedFilteredInternshipList(Comparator comparator); + + /** + * Updates the filter of the filtered internship list to filter by the given {@code predicate}. + * + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredInternshipList(Predicate predicate); /** - * Updates the filter of the filtered person list to filter by the given {@code predicate}. + * Updates the filter of the filtered todo list to filter by the given {@code predicate}. * @throws NullPointerException if {@code predicate} is null. */ - void updateFilteredPersonList(Predicate predicate); + void updateFilteredTodoList(Predicate predicate); + + /** + * Updates the filter of the filtered note list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredNoteList(Predicate predicate); + + /** + * Returns an unmodifiable view of the cached internship list. + */ + List getCachedInternshipList(); + + + /** + * Gets and remove item from the cached internship list. + */ + InternshipApplication getAndRemoveCachedApplication(); + + /** + * Add current deleted internship application to the end of the cached internship list. + */ + void addInternshipToCache(InternshipApplication application); + + /** + * Add all cleared internship applications to the end of the cached internship list. + */ + void addAllInternshipToCache(List application); + + /** + * Empties the internship cache list. + */ + void setEmptyInternshipCacheList(); } diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 86c1df298d7..0e916189aa8 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -4,14 +4,20 @@ import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; import java.util.function.Predicate; import java.util.logging.Logger; import javafx.collections.ObservableList; import javafx.collections.transformation.FilteredList; +import javafx.collections.transformation.SortedList; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.task.InternshipTodo; +import seedu.address.model.task.Note; /** * Represents the in-memory model of the address book data. @@ -21,27 +27,42 @@ public class ModelManager implements Model { private final AddressBook addressBook; private final UserPrefs userPrefs; - private final FilteredList filteredPersons; + private final TodoList todoList; + private final NoteList noteList; + private final FilteredList filteredInternships; + private final SortedList sortedFilteredInternships; + private final FilteredList filteredTodo; + private final FilteredList filteredNote; + private final Reminder reminder; + private List cachedInternshipList; /** * Initializes a ModelManager with the given addressBook and userPrefs. */ - public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) { + public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs, + ReadOnlyTodoList todoList, ReadOnlyNote noteList) { requireAllNonNull(addressBook, userPrefs); logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs); this.addressBook = new AddressBook(addressBook); this.userPrefs = new UserPrefs(userPrefs); - filteredPersons = new FilteredList<>(this.addressBook.getPersonList()); + this.noteList = new NoteList(noteList); + this.todoList = new TodoList(todoList); + filteredInternships = new FilteredList<>(this.addressBook.getInternshipList()); + sortedFilteredInternships = new SortedList<>(filteredInternships); + filteredTodo = new FilteredList<>(this.todoList.getTodoList()); + filteredNote = new FilteredList<>(this.noteList.getNoteList()); + this.reminder = new Reminder(this.addressBook.getInternshipList()); + cachedInternshipList = new ArrayList<>(); + updateFilteredInternshipList(PREDICATE_SHOW_ONGOING_APPLICATIONS); } public ModelManager() { - this(new AddressBook(), new UserPrefs()); + this(new AddressBook(), new UserPrefs(), new TodoList(), new NoteList()); } //=========== UserPrefs ================================================================================== - @Override public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { requireNonNull(userPrefs); @@ -69,6 +90,16 @@ public Path getAddressBookFilePath() { return userPrefs.getAddressBookFilePath(); } + @Override + public Path getTodoListFilePath() { + return userPrefs.getTodoListFilePath(); + } + + @Override + public Path getNoteListFilePath() { + return userPrefs.getNoteListFilePath(); + } + @Override public void setAddressBookFilePath(Path addressBookFilePath) { requireNonNull(addressBookFilePath); @@ -78,8 +109,8 @@ public void setAddressBookFilePath(Path addressBookFilePath) { //=========== AddressBook ================================================================================ @Override - public void setAddressBook(ReadOnlyAddressBook addressBook) { - this.addressBook.resetData(addressBook); + public void setInternEase(ReadOnlyAddressBook internEase) { + this.addressBook.resetData(internEase); } @Override @@ -88,44 +119,186 @@ public ReadOnlyAddressBook getAddressBook() { } @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return addressBook.hasPerson(person); + public ReadOnlyTodoList getTodoList() { + return todoList; + } + + @Override + public ReadOnlyNote getNoteList() { + return noteList; + } + + @Override + public void updateReminder() { + reminder.setClosestUpcomingInterview(); + } + + @Override + public InternshipApplication getReminder() { + return reminder.getClosestUpcomingInterview(); + } + + @Override + public boolean hasApplication(InternshipApplication application) { + requireNonNull(application); + return addressBook.hasApplication(application); + } + + @Override + public boolean hasTodo(InternshipTodo todo) { + requireNonNull(todo); + return todoList.hasTodo(todo); + } + + @Override + public boolean hasNote(Note note) { + requireNonNull(note); + return noteList.hasNote(note); + } + + @Override + public void deleteInternship(InternshipApplication application) { + addressBook.removeApplication(application); + } + + @Override + public void deleteTodo(InternshipTodo target) { + todoList.removeTodo(target); + } + + @Override + public void deleteNote(Note target) { + noteList.removeNote(target); + } + + @Override + public void clearTodo(ReadOnlyTodoList internEase) { + todoList.clearTodo(internEase); + } + + @Override + public void clearNote(ReadOnlyNote internEase) { + noteList.clearNote(internEase); + } + + @Override + public void addApplication(InternshipApplication application) { + addressBook.addApplication(application); + } + + @Override + public void addTodo(InternshipTodo todo) { + todoList.addTodo(todo); + updateFilteredTodoList(PREDICATE_SHOW_ALL_TODO); } @Override - public void deletePerson(Person target) { - addressBook.removePerson(target); + public void addNote(Note note) { + noteList.addNote(note); + updateFilteredNoteList(PREDICATE_SHOW_ALL_NOTES); + } + + /** + * Add applications into the tracker. + */ + public void addApplications(List applications) { + addressBook.addApplications(applications); } @Override - public void addPerson(Person person) { - addressBook.addPerson(person); - updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + public void setApplication(InternshipApplication target, InternshipApplication editedApplication) { + requireAllNonNull(target, editedApplication); + + addressBook.setApplication(target, editedApplication); + } + + @Override + public void setTodo(InternshipTodo target, + InternshipTodo editedTodo) { + requireAllNonNull(target, editedTodo); + + todoList.setTodo(target, editedTodo); } @Override - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); + public void setNote(Note target, Note editedNote) { + requireAllNonNull(target, editedNote); - addressBook.setPerson(target, editedPerson); + noteList.setNote(target, editedNote); } - //=========== Filtered Person List Accessors ============================================================= + //=========== Filtered Internship List Accessors ============================================================= /** - * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of + * Returns an unmodifiable view of the list of {@code InternshipApplication} backed by the internal list of * {@code versionedAddressBook} */ @Override - public ObservableList getFilteredPersonList() { - return filteredPersons; + public ObservableList getSortedFilteredInternshipList() { + return sortedFilteredInternships; + } + + @Override + public void updateFilteredInternshipList(Predicate predicate) { + requireNonNull(predicate); + filteredInternships.setPredicate(predicate); + } + + @Override + public void updateSortedFilteredInternshipList(Comparator comparator) { + requireNonNull(comparator); + sortedFilteredInternships.setComparator(comparator); + } + + @Override + public ObservableList getFilteredTodoList() { + return filteredTodo; + } + + @Override + public void updateFilteredTodoList(Predicate predicate) { + requireNonNull(predicate); + filteredTodo.setPredicate(predicate); + } + + @Override + public ObservableList getFilteredNoteList() { + return filteredNote; } @Override - public void updateFilteredPersonList(Predicate predicate) { + public void updateFilteredNoteList(Predicate predicate) { requireNonNull(predicate); - filteredPersons.setPredicate(predicate); + filteredNote.setPredicate(predicate); + } + + @Override + public List getCachedInternshipList() { + return cachedInternshipList; + } + + @Override + public InternshipApplication getAndRemoveCachedApplication() { + InternshipApplication application = cachedInternshipList.get(cachedInternshipList.size() - 1); + cachedInternshipList.remove(application); + return application; + } + + @Override + public void addInternshipToCache(InternshipApplication application) { + cachedInternshipList.remove(application); + cachedInternshipList.add(application); + } + + @Override + public void addAllInternshipToCache(List application) { + cachedInternshipList.removeAll(application); + cachedInternshipList.addAll(application); + } + + @Override + public void setEmptyInternshipCacheList() { + cachedInternshipList = new ArrayList<>(); } @Override @@ -142,9 +315,11 @@ public boolean equals(Object obj) { // state check ModelManager other = (ModelManager) obj; + return addressBook.equals(other.addressBook) && userPrefs.equals(other.userPrefs) - && filteredPersons.equals(other.filteredPersons); + && filteredTodo.equals(other.filteredTodo) + && filteredNote.equals(other.filteredNote); } } diff --git a/src/main/java/seedu/address/model/NoteList.java b/src/main/java/seedu/address/model/NoteList.java new file mode 100644 index 00000000000..d4023e0b366 --- /dev/null +++ b/src/main/java/seedu/address/model/NoteList.java @@ -0,0 +1,122 @@ +package seedu.address.model; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import javafx.collections.ObservableList; +import seedu.address.model.task.Note; +import seedu.address.model.task.UniqueNoteList; + +/** + * Wraps all data at the note-list level + * Duplicates are not allowed (by .isSameNote comparison) + */ +public class NoteList implements ReadOnlyNote { + + private final UniqueNoteList notes; + + /* + * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication + * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html + * + * NoteList that non-static init blocks are not recommended to use. There are other ways to avoid duplication + * among constructors. + */ + { + notes = new UniqueNoteList(); + } + + public NoteList() {} + + /** + * Creates a NoteList using the Notes in the {@code toBeCopied} + */ + public NoteList(ReadOnlyNote toBeCopied) { + this(); + resetData(toBeCopied); + } + + /** + * Replaces the contents of the note list with {@code notes}. + * {@code notes} must not contain duplicate notes. + */ + public void setNote(List notes) { + this.notes.setNotes(notes); + } + + /** + * Replaces the given note {@code target} with {@code editedNote}. + * {@code target} must exist in the tracker. + * The identity of {@code editedNote} must not be the same as another existing Note in the tracker. + */ + public void setNote(Note target, Note editedNote) { + requireNonNull(editedNote); + + notes.setNotes(target, editedNote); + } + + /** + * Resets the existing data of this {@code NoteList} with {@code newData}. + */ + public void resetData(ReadOnlyNote newData) { + requireNonNull(newData); + + setNote(newData.getNoteList()); + } + + /** + * Returns true if a note with the same identity + * as {@code note} exists in the tracker. + */ + public boolean hasNote(Note note) { + requireNonNull(note); + return notes.containsNote(note); + } + + /** + * Adds a note to the tracker. + * The note must not already exist in the tracker. + */ + public void addNote(Note note) { + notes.addNote(note); + } + + /** + * Removes {@code key} from this {@code NoteList}. + * {@code key} must exist in the note list. + */ + public void removeNote(Note key) { + notes.remove(key); + } + + /** + * Clear note list. + */ + public void clearNote(ReadOnlyNote newData) { + setNote(newData.getNoteList()); + } + + //// util methods + @Override + public String toString() { + return notes.asUnmodifiableObservableList().size() + " notes"; + } + + @Override + public ObservableList getNoteList() { + return notes.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof NoteList // instanceof handles nulls + && notes.equals(((NoteList) other).notes)); + } + + @Override + public int hashCode() { + return notes.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java index 6ddc2cd9a29..ded4b531651 100644 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java @@ -1,7 +1,8 @@ package seedu.address.model; import javafx.collections.ObservableList; -import seedu.address.model.person.Person; +import seedu.address.model.application.InternshipApplication; + /** * Unmodifiable view of an address book @@ -9,9 +10,9 @@ public interface ReadOnlyAddressBook { /** - * Returns an unmodifiable view of the persons list. - * This list will not contain any duplicate persons. + * Returns an unmodifiable view of the applications list. + * This list will not contain any duplicate applications. */ - ObservableList getPersonList(); + ObservableList getInternshipList(); } diff --git a/src/main/java/seedu/address/model/ReadOnlyNote.java b/src/main/java/seedu/address/model/ReadOnlyNote.java new file mode 100644 index 00000000000..68b47528b8a --- /dev/null +++ b/src/main/java/seedu/address/model/ReadOnlyNote.java @@ -0,0 +1,17 @@ +package seedu.address.model; + +import javafx.collections.ObservableList; +import seedu.address.model.task.Note; + +/** + * Unmodifiable view of a note list + */ +public interface ReadOnlyNote { + + /** + * Returns an unmodifiable view of the notes list. + * This list will not contain any duplicate notes. + */ + ObservableList getNoteList(); + +} diff --git a/src/main/java/seedu/address/model/ReadOnlyTodoList.java b/src/main/java/seedu/address/model/ReadOnlyTodoList.java new file mode 100644 index 00000000000..839230b6818 --- /dev/null +++ b/src/main/java/seedu/address/model/ReadOnlyTodoList.java @@ -0,0 +1,17 @@ +package seedu.address.model; + +import javafx.collections.ObservableList; +import seedu.address.model.task.InternshipTodo; + +/** + * Unmodifiable view of a todo list + */ +public interface ReadOnlyTodoList { + + /** + * Returns an unmodifiable view of the todos list. + * This list will not contain any duplicate todo tasks. + */ + ObservableList getTodoList(); + +} diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java index befd58a4c73..b52ad11d7d8 100644 --- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java +++ b/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java @@ -13,4 +13,7 @@ public interface ReadOnlyUserPrefs { Path getAddressBookFilePath(); + Path getTodoListFilePath(); + + Path getNoteListFilePath(); } diff --git a/src/main/java/seedu/address/model/Reminder.java b/src/main/java/seedu/address/model/Reminder.java new file mode 100644 index 00000000000..6883b155da0 --- /dev/null +++ b/src/main/java/seedu/address/model/Reminder.java @@ -0,0 +1,65 @@ +package seedu.address.model; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.time.LocalDateTime; +import java.util.List; + +import seedu.address.model.application.InternshipApplication; + +/** + * Represents an internship application with the most imminent interview + */ +public class Reminder { + private InternshipApplication upcomingApplication; + private final List applications; + + /** + * Every field must be present and not null. + */ + public Reminder(List applications) { + requireAllNonNull(applications); + this.applications = applications; + this.upcomingApplication = null; + } + + public InternshipApplication getClosestUpcomingInterview() { + return upcomingApplication; + } + + public void setClosestUpcomingInterview() { + LocalDateTime now = LocalDateTime.now(); + InternshipApplication closestApplication = null; + if (applications == null || applications.isEmpty()) { + this.upcomingApplication = null; + } else { + for (int i = 0; i < applications.size(); i++) { + InternshipApplication application = applications.get(i); + if (application.getInterviewDate() != null) { + if (application.getInterviewDate().getDateTime().isAfter(now)) { + closestApplication = application; + break; + } + } + } + for (int i = 0; i < applications.size(); i++) { + InternshipApplication application = applications.get(i); + if (application.getInterviewDate() != null) { + if (application.getInterviewDate().getDateTime().isAfter(now)) { + if (closestApplication.getInterviewDate() == null + || application.getInterviewDate() + .isBeforeInclusive(closestApplication.getInterviewDate())) { + closestApplication = application; + } + } + } + } + if (closestApplication == null) { + this.upcomingApplication = null; + } else { + this.upcomingApplication = closestApplication; + } + } + } +} + diff --git a/src/main/java/seedu/address/model/TodoList.java b/src/main/java/seedu/address/model/TodoList.java new file mode 100644 index 00000000000..42d3f953222 --- /dev/null +++ b/src/main/java/seedu/address/model/TodoList.java @@ -0,0 +1,124 @@ +package seedu.address.model; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import javafx.collections.ObservableList; +import seedu.address.model.task.InternshipTodo; +import seedu.address.model.task.UniqueTodoList; + +/** + * Wraps all data at the todo-list level + * Duplicates are not allowed (by .isSameTodo comparison) + */ +public class TodoList implements ReadOnlyTodoList { + + private final UniqueTodoList todos; + + /* + * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication + * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html + * + * TodoList that non-static init blocks are not recommended to use. There are other ways to avoid duplication + * among constructors. + */ + { + todos = new UniqueTodoList(); + } + + public TodoList() {} + + /** + * Creates an TodoList using the InternshipTodos in the {@code toBeCopied} + */ + public TodoList(ReadOnlyTodoList toBeCopied) { + this(); + resetData(toBeCopied); + } + + /** + * Replaces the contents of the todo list with {@code todos}. + * {@code todos} must not contain duplicate todos. + */ + public void setTodo(List todos) { + this.todos.setTodo(todos); + } + + /** + * Replaces the given todo {@code target} with {@code editedTodo}. + * {@code target} must exist in the tracker. + * The identity of {@code editedTodo} must not be the same as another existing todo in the tracker. + */ + public void setTodo(InternshipTodo target, + InternshipTodo editedTodo) { + requireNonNull(editedTodo); + + todos.setTodo(target, editedTodo); + } + + /** + * Resets the existing data of this {@code TodoList} with {@code newData}. + */ + public void resetData(ReadOnlyTodoList newData) { + requireNonNull(newData); + + setTodo(newData.getTodoList()); + } + + /** + * Returns true if a todo with the same identity + * as {@code todo} exists in the tracker. + */ + public boolean hasTodo(InternshipTodo todo) { + requireNonNull(todo); + return todos.containsTodo(todo); + } + + /** + * Adds a todo to the tracker. + * The todo must not already exist in the tracker. + */ + public void addTodo(InternshipTodo todo) { + todos.addTodo(todo); + } + + /** + * Removes {@code key} from this {@code TodoList}. + * {@code key} must exist in the todo list. + */ + public void removeTodo(InternshipTodo key) { + todos.remove(key); + } + + /** + * Clear todo list. + */ + public void clearTodo(ReadOnlyTodoList newData) { + setTodo(newData.getTodoList()); + } + + //// util methods + + @Override + public String toString() { + return todos.asUnmodifiableObservableList().size() + " todos"; + } + + @Override + public ObservableList getTodoList() { + return todos.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TodoList // instanceof handles nulls + && todos.equals(((TodoList) other).todos)); + } + + @Override + public int hashCode() { + return todos.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/address/model/UserPrefs.java index 25a5fd6eab9..584360f3f49 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/address/model/UserPrefs.java @@ -15,6 +15,8 @@ public class UserPrefs implements ReadOnlyUserPrefs { private GuiSettings guiSettings = new GuiSettings(); private Path addressBookFilePath = Paths.get("data" , "addressbook.json"); + private Path todoListFilePath = Paths.get("data" , "todolist.json"); + private Path noteListFilePath = Paths.get("data" , "note.json"); /** * Creates a {@code UserPrefs} with default values. @@ -36,6 +38,8 @@ public void resetData(ReadOnlyUserPrefs newUserPrefs) { requireNonNull(newUserPrefs); setGuiSettings(newUserPrefs.getGuiSettings()); setAddressBookFilePath(newUserPrefs.getAddressBookFilePath()); + setTodoListFilePath(newUserPrefs.getTodoListFilePath()); + setNoteListFilePath(newUserPrefs.getNoteListFilePath()); } public GuiSettings getGuiSettings() { @@ -56,6 +60,24 @@ public void setAddressBookFilePath(Path addressBookFilePath) { this.addressBookFilePath = addressBookFilePath; } + public Path getTodoListFilePath() { + return todoListFilePath; + } + + public void setTodoListFilePath(Path todoListFilePath) { + requireNonNull(todoListFilePath); + this.todoListFilePath = todoListFilePath; + } + + public Path getNoteListFilePath() { + return noteListFilePath; + } + + public void setNoteListFilePath(Path noteListFilePath) { + requireNonNull(noteListFilePath); + this.noteListFilePath = noteListFilePath; + } + @Override public boolean equals(Object other) { if (other == this) { @@ -68,12 +90,14 @@ public boolean equals(Object other) { UserPrefs o = (UserPrefs) other; return guiSettings.equals(o.guiSettings) - && addressBookFilePath.equals(o.addressBookFilePath); + && addressBookFilePath.equals(o.addressBookFilePath) + && todoListFilePath.equals(o.todoListFilePath) + && noteListFilePath.equals(o.noteListFilePath); } @Override public int hashCode() { - return Objects.hash(guiSettings, addressBookFilePath); + return Objects.hash(guiSettings, addressBookFilePath, todoListFilePath, noteListFilePath); } @Override @@ -81,6 +105,8 @@ public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Gui Settings : " + guiSettings); sb.append("\nLocal data file location : " + addressBookFilePath); + sb.append("\nTodolist data file location : " + todoListFilePath); + sb.append("User note file location : " + noteListFilePath); return sb.toString(); } diff --git a/src/main/java/seedu/address/model/application/AfterDatePredicate.java b/src/main/java/seedu/address/model/application/AfterDatePredicate.java new file mode 100644 index 00000000000..b019a327f60 --- /dev/null +++ b/src/main/java/seedu/address/model/application/AfterDatePredicate.java @@ -0,0 +1,32 @@ +package seedu.address.model.application; + +/** + * Tests that an {@code InternshipApplication}'s {@code InternshipDate} + * is after the specified date given. + */ +public class AfterDatePredicate extends DatePredicate { + private final InterviewDate afterDate; + + /** + * Creates a AfterDatePredicate with boundary date specified (inclusive). + * @param afterDate The date to compare to + */ + public AfterDatePredicate(InterviewDate afterDate) { + assert afterDate != null : "date should not be null"; + this.afterDate = afterDate; + } + + @Override + public boolean test(InternshipApplication internshipApplication) { + assert internshipApplication != null : "internshipApplication should not be null"; + return internshipApplication.getInterviewDate() != null + && internshipApplication.getInterviewDate().isAfterInclusive(afterDate); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AfterDatePredicate // instanceof handles nulls + && afterDate.equals(((AfterDatePredicate) other).afterDate)); // state check + } +} diff --git a/src/main/java/seedu/address/model/application/BeforeDatePredicate.java b/src/main/java/seedu/address/model/application/BeforeDatePredicate.java new file mode 100644 index 00000000000..08b33f3be2f --- /dev/null +++ b/src/main/java/seedu/address/model/application/BeforeDatePredicate.java @@ -0,0 +1,33 @@ +package seedu.address.model.application; + +/** + * Tests that an {@code InternshipApplication}'s {@code InternshipDate} + * is before the specified date given. + */ +public class BeforeDatePredicate extends DatePredicate { + private final InterviewDate beforeDate; + + /** + * Creates a BeforeDatePredicate with boundary date specified (inclusive). + * + * @param beforeDate The date to compare to + */ + public BeforeDatePredicate(InterviewDate beforeDate) { + assert beforeDate != null : "date should not be null"; + this.beforeDate = beforeDate; + } + + @Override + public boolean test(InternshipApplication internshipApplication) { + assert internshipApplication != null : "internshipApplication should not be null"; + return internshipApplication.getInterviewDate() != null + && internshipApplication.getInterviewDate().isBeforeInclusive(beforeDate); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof BeforeDatePredicate // instanceof handles nulls + && beforeDate.equals(((BeforeDatePredicate) other).beforeDate)); // state check + } +} diff --git a/src/main/java/seedu/address/model/application/BetweenDatePredicate.java b/src/main/java/seedu/address/model/application/BetweenDatePredicate.java new file mode 100644 index 00000000000..57eeeb40cf8 --- /dev/null +++ b/src/main/java/seedu/address/model/application/BetweenDatePredicate.java @@ -0,0 +1,38 @@ +package seedu.address.model.application; + +/** + * Tests that an {@code InternshipApplication}'s {@code InternshipDate} + * is between the specified dates given. + */ +public class BetweenDatePredicate extends DatePredicate { + private final InterviewDate startDate; + private final InterviewDate endDate; + + /** + * Creates a BetweenDatePredicate with boundary dates specified (both inclusive). + * + * @param startDate The start date of the interval + * @param endDate The end date of the interval + */ + public BetweenDatePredicate(InterviewDate startDate, InterviewDate endDate) { + assert startDate != null : "date should not be null"; + assert endDate != null : "date should not be null"; + this.startDate = startDate; + this.endDate = endDate; + } + + @Override + public boolean test(InternshipApplication internshipApplication) { + assert internshipApplication != null : "internshipApplication should not be null"; + return internshipApplication.getInterviewDate() != null + && internshipApplication.getInterviewDate().isBetweenInclusive(startDate, endDate); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof BetweenDatePredicate // instanceof handles nulls + && startDate.equals(((BetweenDatePredicate) other).startDate) + && endDate.equals(((BetweenDatePredicate) other).endDate)); // state check + } +} diff --git a/src/main/java/seedu/address/model/application/CompanyName.java b/src/main/java/seedu/address/model/application/CompanyName.java new file mode 100644 index 00000000000..f301586c00a --- /dev/null +++ b/src/main/java/seedu/address/model/application/CompanyName.java @@ -0,0 +1,64 @@ +package seedu.address.model.application; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents an InternshipApplication's companyName in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidCompanyName(String)} + */ +public class CompanyName extends InternshipApplicationAttribute implements Comparable { + + public static final String MESSAGE_CONSTRAINTS = + "Company Names should only contain alphanumeric characters and spaces, and it should not be blank"; + + /* + * The first character of the address must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; + + public final String fullName; + + /** + * Constructs a {@code CompanyName}. + * + * @param companyName A valid companyName. + */ + public CompanyName(String companyName) { + requireNonNull(companyName); + checkArgument(isValidCompanyName(companyName), MESSAGE_CONSTRAINTS); + fullName = companyName; + } + + /** + * Returns true if a given string is a valid companyName. + */ + public static boolean isValidCompanyName(String test) { + return test.matches(VALIDATION_REGEX); + } + + + @Override + public String toString() { + return fullName; + } + + @Override + public int compareTo(CompanyName companyName) { + return fullName.toUpperCase().compareTo(companyName.fullName.toUpperCase()); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof CompanyName // instanceof handles nulls + && fullName.equals(((CompanyName) other).fullName)); // state check + } + + @Override + public int hashCode() { + return fullName.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/application/DatePredicate.java b/src/main/java/seedu/address/model/application/DatePredicate.java new file mode 100644 index 00000000000..bdb03ffcf38 --- /dev/null +++ b/src/main/java/seedu/address/model/application/DatePredicate.java @@ -0,0 +1,10 @@ +package seedu.address.model.application; + +import java.util.function.Predicate; + +/** + * Tests that an {@code InternshipApplication}'s {@code InternshipDate} + * against the specified date given. + */ +public abstract class DatePredicate implements Predicate { +} diff --git a/src/main/java/seedu/address/model/application/InternshipApplication.java b/src/main/java/seedu/address/model/application/InternshipApplication.java new file mode 100644 index 00000000000..95f5644c8f9 --- /dev/null +++ b/src/main/java/seedu/address/model/application/InternshipApplication.java @@ -0,0 +1,311 @@ +package seedu.address.model.application; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import seedu.address.model.contact.Contact; +import seedu.address.model.documents.Documents; + +/** + * Represents an InternshipApplication in the tracker. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class InternshipApplication implements Comparable { + // Identity fields + private final CompanyName companyName; + private final JobTitle jobTitle; + private final Set reviews = new HashSet<>(); + private final Set programmingLanguages = new HashSet<>(); + private final Set qualifications = new HashSet<>(); + private final Location location; + private final Salary salary; + private final Set notes = new HashSet<>(); + private final Rating rating; + private final Set reflections = new HashSet<>(); + + // Interview fields + private final InterviewDate interviewDate; + private final InternshipStatus status; + private final boolean isArchived; + + // Data fields + private final Contact contact; + private final Documents documents; + + /** + * Every field must be present and not null. + */ + public InternshipApplication(CompanyName name, JobTitle job) { + + requireAllNonNull(name, job); + //Identity field + this.companyName = name; + this.jobTitle = job; + this.location = null; + this.salary = null; + this.rating = null; + + // Data field + this.contact = null; + this.status = InternshipStatus.PENDING; + this.isArchived = false; + this.documents = null; + + //Interview field + this.interviewDate = null; + } + + /** + * Company name and job title field must be present and not null. + */ + public InternshipApplication(CompanyName name, JobTitle job, Set reviews, + Set programmingLanguages, Set qualifications, Location location, + Salary salary, Set notes, Rating rating, Set reflections) { + + requireAllNonNull(name, job); + //Identity field + this.companyName = name; + this.jobTitle = job; + this.reviews.addAll(reviews); + this.programmingLanguages.addAll(programmingLanguages); + this.qualifications.addAll(qualifications); + this.location = location; + this.salary = salary; + this.notes.addAll(notes); + this.rating = rating; + this.reflections.addAll(reflections); + + // Data field + this.contact = null; + this.status = InternshipStatus.PENDING; + this.isArchived = false; + this.documents = null; + + //Interview field + this.interviewDate = null; + } + + /** + * Every field must be present and not null. + */ + public InternshipApplication(CompanyName name, JobTitle job, Set reviews, Contact contact, + InternshipStatus status, boolean isArchived, Documents documents) { + requireAllNonNull(name, job); + this.companyName = name; + this.jobTitle = job; + this.reviews.addAll(reviews); + this.contact = contact; + this.status = status; + this.interviewDate = null; + this.location = null; + this.salary = null; + this.rating = null; + this.documents = documents; + this.isArchived = isArchived; + } + + /** + * The company name and job title field must be present and not null. + */ + public InternshipApplication(CompanyName companyName, JobTitle job, Set reviews, + Set programmingLanguages, Set qualifications, Location location, + Salary salary, Set notes, Rating rating, Set reflections, Contact contact, + InternshipStatus status, boolean isArchived, InterviewDate interviewDate, Documents documents) { + requireAllNonNull(companyName, job); + //Identity field + this.companyName = companyName; + this.jobTitle = job; + this.reviews.addAll(reviews); + this.programmingLanguages.addAll(programmingLanguages); + this.qualifications.addAll(qualifications); + this.location = location; + this.salary = salary; + this.notes.addAll(notes); + this.rating = rating; + this.reflections.addAll(reflections); + + // Data field + this.contact = contact; + this.status = status; + this.isArchived = isArchived; + this.documents = documents; + + //Interview field + this.interviewDate = interviewDate; + } + + public CompanyName getCompanyName() { + return companyName; + } + + public JobTitle getJobTitle() { + return jobTitle; + } + + public Set getReviews() { + return Collections.unmodifiableSet(reviews); + } + + public Set getProgrammingLanguages() { + return Collections.unmodifiableSet(programmingLanguages); + } + + public Set getQualifications() { + return Collections.unmodifiableSet(qualifications); + } + + public Location getLocation() { + return location; + } + + public Salary getSalary() { + return salary; + } + + public Set getNotes() { + return Collections.unmodifiableSet(notes); + } + + public Rating getRating() { + return rating; + } + + public Set getReflections() { + return Collections.unmodifiableSet(reflections); + } + + public InternshipStatus getStatus() { + return status; + } + + public Contact getContact() { + return contact; + } + + public InterviewDate getInterviewDate() { + return interviewDate; + } + + public boolean isArchived() { + return isArchived; + } + + public Documents getDocuments() { + return documents; + } + + /** + * Returns true if both internship applications have the same company name and job title. + * This defines a weaker notion of equality between two internship applications. + */ + public boolean isSameApplication(InternshipApplication otherApplication) { + if (otherApplication == this) { + return true; + } + + return otherApplication != null + && otherApplication.getCompanyName().equals(getCompanyName()) + && otherApplication.getJobTitle().equals(getJobTitle()); + } + + /** + * Returns true if both internship applications have the same identity and data fields. + * This defines a stronger notion of equality between two internship applications. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof InternshipApplication)) { + return false; + } + + InternshipApplication otherApplication = (InternshipApplication) other; + return otherApplication.getCompanyName().equals(getCompanyName()) + && otherApplication.getJobTitle().equals(getJobTitle()) + && otherApplication.getStatus().equals(getStatus()); + } + + @Override + public int compareTo(InternshipApplication internshipApplication) { + return this.getCompanyName().compareTo(internshipApplication.getCompanyName()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(companyName, jobTitle); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getCompanyName()) + .append("; Job Title: ") + .append(getJobTitle()) + .append("; Status: ") + .append(getStatus()) + .append("; Archived: ") + .append(isArchived()); + + if (!reviews.isEmpty()) { + builder.append("; Review: "); + reviews.forEach(builder::append); + } + + if (!programmingLanguages.isEmpty()) { + builder.append("; Programming Language: "); + programmingLanguages.forEach(builder::append); + } + + if (!qualifications.isEmpty()) { + builder.append("; Qualification: "); + qualifications.forEach(builder::append); + } + + if (location != null && location.value != null && !location.value.isBlank()) { + builder.append("; Location: ") + .append(getLocation()); + } + + if (salary != null && salary.value != null && !salary.value.isBlank()) { + builder.append("; Salary: ") + .append(getSalary()); + } + + if (!notes.isEmpty()) { + builder.append("; Note: "); + notes.forEach(builder::append); + } + + if (rating != null && rating.value != null && !rating.value.isBlank()) { + builder.append("; Rating: ") + .append(getRating()); + } + + if (!reflections.isEmpty()) { + builder.append("; Reflection: "); + reflections.forEach(builder::append); + } + + if (contact != null) { + builder.append("; Company Phone: ") + .append(contact.getPhone()) + .append("; Company Email: ") + .append(contact.getEmail()); + } + + if (interviewDate != null) { + builder.append("; Interview Date: ") + .append(getInterviewDate()); + } + return builder.toString(); + } + +} diff --git a/src/main/java/seedu/address/model/application/InternshipApplicationAttribute.java b/src/main/java/seedu/address/model/application/InternshipApplicationAttribute.java new file mode 100644 index 00000000000..855a27fc0ed --- /dev/null +++ b/src/main/java/seedu/address/model/application/InternshipApplicationAttribute.java @@ -0,0 +1,7 @@ +package seedu.address.model.application; + +/** + * Represents an attribute for a {@code InternshipApplication} in the tracker. + */ +public abstract class InternshipApplicationAttribute { +} diff --git a/src/main/java/seedu/address/model/application/InternshipStatus.java b/src/main/java/seedu/address/model/application/InternshipStatus.java new file mode 100644 index 00000000000..d59402465c5 --- /dev/null +++ b/src/main/java/seedu/address/model/application/InternshipStatus.java @@ -0,0 +1,38 @@ +package seedu.address.model.application; + +/** + * Represents the possible statuses for an internship application. + */ +public enum InternshipStatus { + PENDING("Pending reply", "#FDA50F"), + RECEIVED("Offer received", "#89CFF0"), + ACCEPTED("Offer accepted", "#32CD32"), + DECLINED("Offer rejected", "#808080"), + REJECTED("Application rejected", "#D40E2F"); + + public static final String MESSAGE_CONSTRAINTS = + "Status can only be of one of the following five types: PENDING, RECEIVED, ACCEPTED, DECLINED, REJECTED"; + + public final String label; + public final String labelColour; + + private InternshipStatus(String label, String labelColour) { + this.label = label; + this.labelColour = labelColour; + } + + /** + * Checks whether the String passed is a valid application status + * @param test String to check + * @return Boolean indicating whether the String is a valid status + */ + public static boolean isValidStatus(String test) { + InternshipStatus[] internshipStatuses = InternshipStatus.values(); + for (InternshipStatus internshipStatus : internshipStatuses) { + if (internshipStatus.name().equals(test.toUpperCase())) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/seedu/address/model/application/InterviewDate.java b/src/main/java/seedu/address/model/application/InterviewDate.java new file mode 100644 index 00000000000..8fe0f8fc853 --- /dev/null +++ b/src/main/java/seedu/address/model/application/InterviewDate.java @@ -0,0 +1,99 @@ +package seedu.address.model.application; + +import static java.util.Objects.requireNonNull; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +/** + * Represents an InternshipApplication's interviewDate in the tracker. + */ +public class InterviewDate extends InternshipApplicationAttribute { + + public static final String MESSAGE_CONSTRAINTS = "Interview dates should be of the format 'yyyy-MM-dd hh:mm a'," + + " where 'a' can be AM or PM," + + " and the date must be after the current date and time."; + public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm a"); + public final LocalDateTime value; + + /** + * Constructs an {@code InterviewDate}. + * + * @param interviewDate A valid date-string. + */ + public InterviewDate(String interviewDate) { + requireNonNull(interviewDate); + value = LocalDateTime.parse(interviewDate, FORMATTER); + } + + /** + * Returns if a given LocalDateTime is valid or null (ie interviewDate has not been added). + */ + public static boolean isValidInterviewDate(String test) { + if (test == null) { + return true; + } + try { + LocalDateTime dateTime = LocalDateTime.parse(test, FORMATTER); + if (dateTime.isBefore(LocalDateTime.now())) { + return false; + } + } catch (DateTimeParseException e) { + return false; + } + return true; + } + + /** + * Checks if this {@code InterviewDate} is before the specified {@code InterviewDate} (inclusive). + * + * @param interviewDate The other date to compare to + * @return true if this date is before the {@code interviewDate} (inclusive) + */ + public boolean isBeforeInclusive(InterviewDate interviewDate) { + return value.isBefore(interviewDate.value) || value.isEqual(interviewDate.value); + } + + /** + * Checks if this {@code InterviewDate} is after the specified {@code InterviewDate} (inclusive). + * + * @param interviewDate The other date to compare to + * @return true if this date is after the {@code interviewDate} (inclusive) + */ + public boolean isAfterInclusive(InterviewDate interviewDate) { + return value.isAfter(interviewDate.value) || value.isEqual(interviewDate.value); + } + + /** + * Checks if this {@code InterviewDate} is between the specified {@code InterviewDate}'s (both inclusive). + * + * @param startDate The start date of the period + * @param endDate The end date of the period + * @return true if this date lies between both dates (both inclusive) + */ + public boolean isBetweenInclusive(InterviewDate startDate, InterviewDate endDate) { + return isAfterInclusive(startDate) && isBeforeInclusive(endDate); + } + + @Override + public String toString() { + return value.format(FORMATTER); + } + + public LocalDateTime getDateTime() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof InterviewDate // instanceof handles nulls + && value.equals(((InterviewDate) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/application/JobTitle.java b/src/main/java/seedu/address/model/application/JobTitle.java new file mode 100644 index 00000000000..be614b96be4 --- /dev/null +++ b/src/main/java/seedu/address/model/application/JobTitle.java @@ -0,0 +1,63 @@ +package seedu.address.model.application; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents an InternshipApplication's jobTitle in the tracker. + * Guarantees: immutable; is valid as declared in {@link #isValidJobTitle(String)} + */ +public class JobTitle extends InternshipApplicationAttribute implements Comparable { + + public static final String MESSAGE_CONSTRAINTS = + "Job titles should only contain alphanumeric characters and spaces, and it should not be blank"; + + /* + * The first character of the address must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; + + public final String fullName; + + /** + * Constructs a {@code JobTitle}. + * + * @param jobTitle A valid jobTitle. + */ + public JobTitle(String jobTitle) { + requireNonNull(jobTitle); + checkArgument(isValidJobTitle(jobTitle), MESSAGE_CONSTRAINTS); + fullName = jobTitle; + } + + /** + * Returns true if a given string is a valid jobTitle. + */ + public static boolean isValidJobTitle(String test) { + return test.matches(VALIDATION_REGEX); + } + + + @Override + public String toString() { + return fullName; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof JobTitle // instanceof handles nulls + && fullName.equals(((JobTitle) other).fullName)); // state check + } + + @Override + public int compareTo(JobTitle jobTitle) { + return this.fullName.toUpperCase().compareTo(jobTitle.fullName.toUpperCase()); + } + + @Override + public int hashCode() { + return fullName.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/application/Location.java b/src/main/java/seedu/address/model/application/Location.java new file mode 100644 index 00000000000..bee29c62ba1 --- /dev/null +++ b/src/main/java/seedu/address/model/application/Location.java @@ -0,0 +1,55 @@ +package seedu.address.model.application; + +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents an internship's location in the tracker. + * Guarantees: immutable; is valid as declared in {@link #isValidLocation(String)} + */ +public class Location extends InternshipApplicationAttribute { + + public static final String MESSAGE_CONSTRAINTS = "Locations can take any values"; + + public static final String VALIDATION_REGEX = ".*"; + + public final String value; + + /** + * Constructs an {@code Location}. + * + * @param x A valid Location. + */ + public Location(String x) { + checkArgument(isValidLocation(x), MESSAGE_CONSTRAINTS); + value = x; + } + + /** + * Returns true if a given string is a valid location. + */ + public static boolean isValidLocation(String test) { + if (test == null) { + return true; + } + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Location// instanceof handles nulls + && value.equals(((Location) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} + diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/application/NameContainsKeywordsPredicate.java similarity index 62% rename from src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java rename to src/main/java/seedu/address/model/application/NameContainsKeywordsPredicate.java index c9b5868427c..e39189fbc40 100644 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ b/src/main/java/seedu/address/model/application/NameContainsKeywordsPredicate.java @@ -1,4 +1,4 @@ -package seedu.address.model.person; +package seedu.address.model.application; import java.util.List; import java.util.function.Predicate; @@ -6,9 +6,10 @@ import seedu.address.commons.util.StringUtil; /** - * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. + * Tests that an {@code InternshipApplication}'s {@code CompanyName} + * and {@code JobTitle} matches any of the keywords given. */ -public class NameContainsKeywordsPredicate implements Predicate { +public class NameContainsKeywordsPredicate implements Predicate { private final List keywords; public NameContainsKeywordsPredicate(List keywords) { @@ -16,9 +17,12 @@ public NameContainsKeywordsPredicate(List keywords) { } @Override - public boolean test(Person person) { + public boolean test(InternshipApplication internshipApplication) { return keywords.stream() - .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword)); + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase( + internshipApplication.getCompanyName().toString() + + " " + internshipApplication.getJobTitle().toString(), + keyword)); } @Override diff --git a/src/main/java/seedu/address/model/application/Note.java b/src/main/java/seedu/address/model/application/Note.java new file mode 100644 index 00000000000..e6371b86b61 --- /dev/null +++ b/src/main/java/seedu/address/model/application/Note.java @@ -0,0 +1,58 @@ +package seedu.address.model.application; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents an internship's Note in the tracker. + * Guarantees: immutable; is valid as declared in {@link #isValidNote(String)} + */ +public class Note extends InternshipApplicationAttribute { + + public static final String MESSAGE_CONSTRAINTS = "Notes can take any values, and it should not be blank"; + + /* + * The first character of the Note must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "[^\\s].*"; + + public final String value; + + /** + * Constructs an {@code Note}. + * + * @param x A valid Note. + */ + public Note(String x) { + requireNonNull(x); + checkArgument(isValidNote(x), MESSAGE_CONSTRAINTS); + value = x; + } + + /** + * Returns true if a given string is a valid Note. + */ + public static boolean isValidNote(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Note// instanceof handles nulls + && value.equals(((Note) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} + diff --git a/src/main/java/seedu/address/model/application/ProgrammingLanguage.java b/src/main/java/seedu/address/model/application/ProgrammingLanguage.java new file mode 100644 index 00000000000..a9ee1d53ecd --- /dev/null +++ b/src/main/java/seedu/address/model/application/ProgrammingLanguage.java @@ -0,0 +1,59 @@ +package seedu.address.model.application; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents an internship's ProgrammingLanguage in the tracker. + * Guarantees: immutable; is valid as declared in {@link #isValidProgrammingLanguage(String)} + */ +public class ProgrammingLanguage extends InternshipApplicationAttribute { + + public static final String MESSAGE_CONSTRAINTS = "Programming Languages can take any values, " + + "and it should not be blank"; + + /* + * The first character of the ProgrammingLanguage must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "[^\\s].*"; + + public final String value; + + /** + * Constructs an {@code ProgrammingLanguage}. + * + * @param x A valid ProgrammingLanguage. + */ + public ProgrammingLanguage(String x) { + requireNonNull(x); + checkArgument(isValidProgrammingLanguage(x), MESSAGE_CONSTRAINTS); + value = x; + } + + /** + * Returns true if a given string is a valid ProgrammingLanguage. + */ + public static boolean isValidProgrammingLanguage(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ProgrammingLanguage// instanceof handles nulls + && value.equals(((ProgrammingLanguage) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} + diff --git a/src/main/java/seedu/address/model/application/Qualification.java b/src/main/java/seedu/address/model/application/Qualification.java new file mode 100644 index 00000000000..9ba4d46a7b9 --- /dev/null +++ b/src/main/java/seedu/address/model/application/Qualification.java @@ -0,0 +1,58 @@ +package seedu.address.model.application; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents an internship's qualification in the tracker. + * Guarantees: immutable; is valid as declared in {@link #isValidQualification(String)} + */ +public class Qualification extends InternshipApplicationAttribute { + + public static final String MESSAGE_CONSTRAINTS = "Qualifications can take any values, and it should not be blank"; + + /* + * The first character of the qualification must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "[^\\s].*"; + + public final String value; + + /** + * Constructs an {@code Qualification}. + * + * @param x A valid qualification. + */ + public Qualification(String x) { + requireNonNull(x); + checkArgument(isValidQualification(x), MESSAGE_CONSTRAINTS); + value = x; + } + + /** + * Returns true if a given string is a valid qualification. + */ + public static boolean isValidQualification(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Qualification// instanceof handles nulls + && value.equals(((Qualification) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} + diff --git a/src/main/java/seedu/address/model/application/Rating.java b/src/main/java/seedu/address/model/application/Rating.java new file mode 100644 index 00000000000..32fc1223e1f --- /dev/null +++ b/src/main/java/seedu/address/model/application/Rating.java @@ -0,0 +1,54 @@ +package seedu.address.model.application; + +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents an internship's Rating in the tracker. + * Guarantees: immutable; is valid as declared in {@link #isValidRating(String)} + */ +public class Rating extends InternshipApplicationAttribute { + + public static final String MESSAGE_CONSTRAINTS = "Ratings can take any values"; + public static final String VALIDATION_REGEX = ".*"; + + public final String value; + + /** + * Constructs an {@code Rating}. + * + * @param x A valid Rating. + */ + public Rating(String x) { + checkArgument(isValidRating(x), MESSAGE_CONSTRAINTS); + value = x; + } + + /** + * Returns true if a given string is a valid Rating. + */ + public static boolean isValidRating(String test) { + if (test == null) { + return true; + } + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Rating// instanceof handles nulls + && value.equals(((Rating) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} + diff --git a/src/main/java/seedu/address/model/application/Reflection.java b/src/main/java/seedu/address/model/application/Reflection.java new file mode 100644 index 00000000000..4c74607c3ae --- /dev/null +++ b/src/main/java/seedu/address/model/application/Reflection.java @@ -0,0 +1,58 @@ +package seedu.address.model.application; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents an internship's reflection in the tracker. + * Guarantees: immutable; is valid as declared in {@link #isValidReflection(String)} + */ +public class Reflection extends InternshipApplicationAttribute { + + public static final String MESSAGE_CONSTRAINTS = "Reflections can take any values, and it should not be blank"; + + /* + * The first character of the reflection must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "[^\\s].*"; + + public final String value; + + /** + * Constructs an {@code Reflection}. + * + * @param x A valid reflection. + */ + public Reflection(String x) { + requireNonNull(x); + checkArgument(isValidReflection(x), MESSAGE_CONSTRAINTS); + value = x; + } + + /** + * Returns true if a given string is a valid reflection. + */ + public static boolean isValidReflection(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Reflection// instanceof handles nulls + && value.equals(((Reflection) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} + diff --git a/src/main/java/seedu/address/model/application/Review.java b/src/main/java/seedu/address/model/application/Review.java new file mode 100644 index 00000000000..196c71b75a4 --- /dev/null +++ b/src/main/java/seedu/address/model/application/Review.java @@ -0,0 +1,58 @@ +package seedu.address.model.application; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents an internship's review in the tracker. + * Guarantees: immutable; is valid as declared in {@link #isValidReview(String)} + */ +public class Review extends InternshipApplicationAttribute { + + public static final String MESSAGE_CONSTRAINTS = "Reviews can take any values, and it should not be blank"; + + /* + * The first character of the review must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "[^\\s].*"; + + public final String value; + + /** + * Constructs a {@code Review}. + * + * @param review A valid review. + */ + public Review(String review) { + requireNonNull(review); + checkArgument(isValidReview(review), MESSAGE_CONSTRAINTS); + value = review; + } + + /** + * Returns true if a given string is a valid review. + */ + public static boolean isValidReview(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Review // instanceof handles nulls + && value.equals(((Review) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} + diff --git a/src/main/java/seedu/address/model/application/Salary.java b/src/main/java/seedu/address/model/application/Salary.java new file mode 100644 index 00000000000..8b0066d83e3 --- /dev/null +++ b/src/main/java/seedu/address/model/application/Salary.java @@ -0,0 +1,55 @@ +package seedu.address.model.application; + +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents an internship's salary in the tracker. + * Guarantees: immutable; is valid as declared in {@link #isValidSalary(String)} + */ +public class Salary extends InternshipApplicationAttribute { + + public static final String MESSAGE_CONSTRAINTS = "Salaries must be in the form [number] [CURRENCY] or blank"; + + public static final String VALIDATION_REGEX = "([0-9]+ [A-Z]+|)"; + + public final String value; + + /** + * Constructs an {@code Salary}. + * + * @param x A valid Salary. + */ + public Salary(String x) { + checkArgument(isValidSalary(x), MESSAGE_CONSTRAINTS); + value = x; + } + + /** + * Returns true if a given string is a valid salary. + */ + public static boolean isValidSalary(String test) { + if (test == null) { + return true; + } + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Salary// instanceof handles nulls + && value.equals(((Salary) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} + diff --git a/src/main/java/seedu/address/model/application/StatusPredicate.java b/src/main/java/seedu/address/model/application/StatusPredicate.java new file mode 100644 index 00000000000..ca33b8490c3 --- /dev/null +++ b/src/main/java/seedu/address/model/application/StatusPredicate.java @@ -0,0 +1,27 @@ +package seedu.address.model.application; + +import java.util.function.Predicate; + +/** + * Tests that an {@code InternshipApplication}'s {@code InternshipStatus} + * matches the specified status given. + */ +public class StatusPredicate implements Predicate { + private final InternshipStatus status; + + public StatusPredicate(InternshipStatus status) { + this.status = status; + } + + @Override + public boolean test(InternshipApplication internshipApplication) { + return internshipApplication.getStatus() == status; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof StatusPredicate // instanceof handles nulls + && status.equals(((StatusPredicate) other).status)); // state check + } +} diff --git a/src/main/java/seedu/address/model/application/UniqueApplicationList.java b/src/main/java/seedu/address/model/application/UniqueApplicationList.java new file mode 100644 index 00000000000..98ee62fd88b --- /dev/null +++ b/src/main/java/seedu/address/model/application/UniqueApplicationList.java @@ -0,0 +1,153 @@ +package seedu.address.model.application; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.address.model.application.exceptions.ApplicationNotFoundException; +import seedu.address.model.application.exceptions.DuplicateApplicationException; + +/** + * A list of InternshipApplications that enforces uniqueness between its elements and does not allow nulls. + * An InternshipApplication is considered unique by comparing using + * {@code InternshipApplication#isSameApplication(InternshipApplication)}. As such, adding and updating of + * persons uses InternshipApplication#isSameApplication(InternshipApplication) for equality + * so as to ensure that the person being added or updated is unique in terms of identity in the UniquePersonList. + * However, the removal of a person uses InternshipApplication#equals(Object) so + * as to ensure that the InternshipApplication with exactly the same fields will be removed. + * + * Supports a minimal set of list operations. + * + * @see Person#isSamePerson(Person) + */ +public class UniqueApplicationList implements Iterable { + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent application as the given argument. + */ + public boolean contains(InternshipApplication toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameApplication); + } + + /** + * Adds an application to the list. + * The application must not already exist in the list. + */ + public void add(InternshipApplication toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateApplicationException(); + } + internalList.add(toAdd); + } + + /** + * Adds applications to the list. + * The application must not already exist in the list. + */ + public void add(List toAdds) { + requireNonNull(toAdds); + + for (InternshipApplication toAdd : toAdds) { + if (!contains(toAdd)) { + internalList.add(toAdd); + } + } + } + + /** + * Replaces the application {@code target} in the list with {@code editedApplication}. + * {@code target} must exist in the list. + * The person identity of {@code editedApplication} must not be the same + * as another existing application in the list. + */ + public void setApplication(InternshipApplication target, InternshipApplication editedPerson) { + requireAllNonNull(target, editedPerson); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new ApplicationNotFoundException(); + } + + if (!target.isSameApplication(editedPerson) && contains(editedPerson)) { + throw new DuplicateApplicationException(); + } + + internalList.set(index, editedPerson); + } + + /** + * Removes the equivalent person from the list. + * The person must exist in the list. + */ + public void remove(InternshipApplication toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new ApplicationNotFoundException(); + } + } + + public void setApplications(UniqueApplicationList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code persons}. + * {@code persons} must not contain duplicate persons. + */ + public void setApplications(List applications) { + requireAllNonNull(applications); + if (!applicationsAreUnique(applications)) { + throw new DuplicateApplicationException(); + } + + internalList.setAll(applications); + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList asUnmodifiableObservableList() { + return internalUnmodifiableList; + } + + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueApplicationList // instanceof handles nulls + && internalList.equals(((UniqueApplicationList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code internships} contains only unique internships. + */ + private boolean applicationsAreUnique(List persons) { + for (int i = 0; i < persons.size() - 1; i++) { + for (int j = i + 1; j < persons.size(); j++) { + if (persons.get(i).isSameApplication(persons.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/seedu/address/model/application/comparator/CompanyNameComparator.java b/src/main/java/seedu/address/model/application/comparator/CompanyNameComparator.java new file mode 100644 index 00000000000..27580520562 --- /dev/null +++ b/src/main/java/seedu/address/model/application/comparator/CompanyNameComparator.java @@ -0,0 +1,26 @@ +package seedu.address.model.application.comparator; + +import java.util.Comparator; + +import seedu.address.model.application.InternshipApplication; + +/** + * Encapsulates the comparator that compares CompanyName. + */ +public class CompanyNameComparator implements Comparator { + @Override + public int compare(InternshipApplication i, InternshipApplication j) { + if (i.getCompanyName() == null) { + return 1; + } else if (j.getCompanyName() == null) { + return -1; + } + return i.getCompanyName().compareTo(j.getCompanyName()); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof CompanyNameComparator); // instanceof handles nulls + } +} diff --git a/src/main/java/seedu/address/model/application/comparator/InterviewDateComparator.java b/src/main/java/seedu/address/model/application/comparator/InterviewDateComparator.java new file mode 100644 index 00000000000..d424763d431 --- /dev/null +++ b/src/main/java/seedu/address/model/application/comparator/InterviewDateComparator.java @@ -0,0 +1,31 @@ +package seedu.address.model.application.comparator; + +import java.util.Comparator; + +import seedu.address.model.application.InternshipApplication; + +/** + * Encapsulates the comparator that compares InterviewDate. + */ +public class InterviewDateComparator implements Comparator { + @Override + public int compare(InternshipApplication i, InternshipApplication j) { + if (i.getInterviewDate() == null) { + return 1; + } else if (j.getInterviewDate() == null) { + return -1; + } + + if (i.getInterviewDate().isBeforeInclusive(j.getInterviewDate())) { + return i.getInterviewDate().equals(j.getInterviewDate()) ? 0 : -1; + } else { + return 1; + } + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof InterviewDateComparator); // instanceof handles nulls + } +} diff --git a/src/main/java/seedu/address/model/application/comparator/JobTitleComparator.java b/src/main/java/seedu/address/model/application/comparator/JobTitleComparator.java new file mode 100644 index 00000000000..0488a49cd04 --- /dev/null +++ b/src/main/java/seedu/address/model/application/comparator/JobTitleComparator.java @@ -0,0 +1,26 @@ +package seedu.address.model.application.comparator; + +import java.util.Comparator; + +import seedu.address.model.application.InternshipApplication; + +/** + * Encapsulates the comparator that compares JobTitle. + */ +public class JobTitleComparator implements Comparator { + @Override + public int compare(InternshipApplication i, InternshipApplication j) { + if (i.getJobTitle() == null) { + return 1; + } else if (j.getJobTitle() == null) { + return -1; + } + return i.getJobTitle().compareTo(j.getJobTitle()); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof JobTitleComparator); // instanceof handles nulls + } +} diff --git a/src/main/java/seedu/address/model/application/comparator/StatusComparator.java b/src/main/java/seedu/address/model/application/comparator/StatusComparator.java new file mode 100644 index 00000000000..c4ad3f95e7f --- /dev/null +++ b/src/main/java/seedu/address/model/application/comparator/StatusComparator.java @@ -0,0 +1,21 @@ +package seedu.address.model.application.comparator; + +import java.util.Comparator; + +import seedu.address.model.application.InternshipApplication; + +/** + * Encapsulates the comparator that compares InternshipStatus. + */ +public class StatusComparator implements Comparator { + @Override + public int compare(InternshipApplication i, InternshipApplication j) { + return i.getStatus().compareTo(j.getStatus()); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof StatusComparator); // instanceof handles nulls + } +} diff --git a/src/main/java/seedu/address/model/application/exceptions/ApplicationNotFoundException.java b/src/main/java/seedu/address/model/application/exceptions/ApplicationNotFoundException.java new file mode 100644 index 00000000000..258de15d0e3 --- /dev/null +++ b/src/main/java/seedu/address/model/application/exceptions/ApplicationNotFoundException.java @@ -0,0 +1,6 @@ +package seedu.address.model.application.exceptions; + +/** + * Signals that the operation is unable to find the specified person. + */ +public class ApplicationNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/seedu/address/model/application/exceptions/DuplicateApplicationException.java similarity index 56% rename from src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java rename to src/main/java/seedu/address/model/application/exceptions/DuplicateApplicationException.java index d7290f59442..7d44918055c 100644 --- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java +++ b/src/main/java/seedu/address/model/application/exceptions/DuplicateApplicationException.java @@ -1,11 +1,11 @@ -package seedu.address.model.person.exceptions; +package seedu.address.model.application.exceptions; /** * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same * identity). */ -public class DuplicatePersonException extends RuntimeException { - public DuplicatePersonException() { +public class DuplicateApplicationException extends RuntimeException { + public DuplicateApplicationException() { super("Operation would result in duplicate persons"); } } diff --git a/src/main/java/seedu/address/model/contact/Contact.java b/src/main/java/seedu/address/model/contact/Contact.java new file mode 100644 index 00000000000..815eb2797e2 --- /dev/null +++ b/src/main/java/seedu/address/model/contact/Contact.java @@ -0,0 +1,70 @@ +package seedu.address.model.contact; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Objects; + +/** + * Represents a contact in the internship application. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Contact { + + public static final String MESSAGE_CONSTRAINTS = "Contact should contain both a phone and an email."; + + // Identity fields + private final Phone phone; + private final Email email; + + /** + * Every field must be present and not null. + */ + public Contact(Phone phone, Email email) { + requireAllNonNull(phone, email); + this.phone = phone; + this.email = email; + } + + public Phone getPhone() { + return phone; + } + + public Email getEmail() { + return email; + } + + /** + * Returns true if both contacts have the same identity. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Contact)) { + return false; + } + + Contact otherContact = (Contact) other; + return otherContact.getPhone().equals(getPhone()) + && otherContact.getEmail().equals(getEmail()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(phone, email); + } + + @Override + public String toString() { + String builder = "Phone: " + + getPhone() + + "; Email: " + + getEmail(); + + return builder; + } + +} diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/contact/Email.java similarity index 74% rename from src/main/java/seedu/address/model/person/Email.java rename to src/main/java/seedu/address/model/contact/Email.java index f866e7133de..fc2b2b7b2db 100644 --- a/src/main/java/seedu/address/model/person/Email.java +++ b/src/main/java/seedu/address/model/contact/Email.java @@ -1,35 +1,40 @@ -package seedu.address.model.person; +package seedu.address.model.contact; import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; +import seedu.address.model.application.InternshipApplicationAttribute; + +// @@author SE-EDU +// with minor modifications /** - * Represents a Person's email in the address book. + * Represents a company's email in the internship application. * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)} */ -public class Email { +public class Email extends InternshipApplicationAttribute { - private static final String SPECIAL_CHARACTERS = "+_.-"; + private static final String SPECIAL_CHARACTERS = "_+&*-."; public static final String MESSAGE_CONSTRAINTS = "Emails should be of the format local-part@domain " + "and adhere to the following constraints:\n" + "1. The local-part should only contain alphanumeric characters and these special characters, excluding " - + "the parentheses, (" + SPECIAL_CHARACTERS + "). The local-part may not start or end with any special " + + "the parentheses, (" + SPECIAL_CHARACTERS + ").\n. The local-part may not start or end with any special" + "characters.\n" + "2. This is followed by a '@' and then a domain name. The domain name is made up of domain labels " + "separated by periods.\n" + "The domain name must:\n" - + " - end with a domain label at least 2 characters long\n" + + " - end with a domain label of 2 to 7 characters long\n" + " - have each domain label start and end with alphanumeric characters\n" + " - have each domain label consist of alphanumeric characters, separated only by hyphens, if any."; - // alphanumeric and special characters + private static final String ALPHANUMERIC_NO_UNDERSCORE = "[^\\W_]+"; // alphanumeric characters except underscore private static final String LOCAL_PART_REGEX = "^" + ALPHANUMERIC_NO_UNDERSCORE + "([" + SPECIAL_CHARACTERS + "]" + ALPHANUMERIC_NO_UNDERSCORE + ")*"; private static final String DOMAIN_PART_REGEX = ALPHANUMERIC_NO_UNDERSCORE + "(-" + ALPHANUMERIC_NO_UNDERSCORE + ")*"; - private static final String DOMAIN_LAST_PART_REGEX = "(" + DOMAIN_PART_REGEX + "){2,}$"; // At least two chars - private static final String DOMAIN_REGEX = "(" + DOMAIN_PART_REGEX + "\\.)*" + DOMAIN_LAST_PART_REGEX; - public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@" + DOMAIN_REGEX; + + //Adapted from https://owasp.org/www-community/OWASP_Validation_Regex_Repository + private static final String VALIDATION_REGEX = + LOCAL_PART_REGEX + "@" + "(" + DOMAIN_PART_REGEX + "\\.)+[a-zA-Z]{2,7}$"; public final String value; diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/contact/Phone.java similarity index 82% rename from src/main/java/seedu/address/model/person/Phone.java rename to src/main/java/seedu/address/model/contact/Phone.java index 872c76b382f..b1d29233879 100644 --- a/src/main/java/seedu/address/model/person/Phone.java +++ b/src/main/java/seedu/address/model/contact/Phone.java @@ -1,13 +1,17 @@ -package seedu.address.model.person; +package seedu.address.model.contact; import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; +import seedu.address.model.application.InternshipApplicationAttribute; + +// @@author SE-EDU +// with minor modifications /** - * Represents a Person's phone number in the address book. + * Represents a company's phone number in the address book. * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)} */ -public class Phone { +public class Phone extends InternshipApplicationAttribute { public static final String MESSAGE_CONSTRAINTS = diff --git a/src/main/java/seedu/address/model/documents/CoverLetterLink.java b/src/main/java/seedu/address/model/documents/CoverLetterLink.java new file mode 100644 index 00000000000..2fe042e5e1c --- /dev/null +++ b/src/main/java/seedu/address/model/documents/CoverLetterLink.java @@ -0,0 +1,83 @@ +package seedu.address.model.documents; + +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; + +import seedu.address.model.application.InternshipApplicationAttribute; + +/** + * Represents a link to a cover letter in the internship application. + * Guarantees: immutable; is valid as declared in {@link #isValidCoverLetterLink(String)} + */ +public class CoverLetterLink extends InternshipApplicationAttribute { + + public static final String MESSAGE_CONSTRAINTS = "Link to cover letter should be in the form http://domain/path " + + "or https://domain/path\n" + + "The domain part can be an IP address, otherwise it is a domain name that must:\n" + + " - cannot be localhost\n" + + " - end with a domain label of 2 to 7 characters long\n" + + " - have each domain label start and end with alphanumeric characters\n" + + " - have each domain label consist of alphanumeric characters, separated only by hyphens, if any."; + + //Adapted from https://owasp.org/www-community/OWASP_Validation_Regex_Repository + private static final String DOMAIN_REGEX = "(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$"; + //Adapted from https://owasp.org/www-community/OWASP_Validation_Regex_Repository + private static final String IP_ADDRESS_REGEX = + "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"; + + public final String value; + + /** + * Constructs an {@code CoverLetter}. + * + * @param coverLetterLink A valid link to a cover letter. + */ + public CoverLetterLink(String coverLetterLink) { + requireNonNull(coverLetterLink); + checkArgument(isValidCoverLetterLink(coverLetterLink), MESSAGE_CONSTRAINTS); + value = coverLetterLink; + } + + /** + * Returns if a given string is a valid link to a cover letter. + */ + public static boolean isValidCoverLetterLink(String test) { + requireNonNull(test); + try { + URI uri = new URL(test).toURI(); + String domain = uri.getHost(); + if (isNull(domain)) { + return false; + } + return test.matches("^(https?)://.*$") && (domain.matches(DOMAIN_REGEX) + || domain.matches(IP_ADDRESS_REGEX)); + } catch (URISyntaxException | MalformedURLException exception) { + return false; + } + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof CoverLetterLink // instanceof handles nulls + && value.equals(((CoverLetterLink) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/documents/Documents.java b/src/main/java/seedu/address/model/documents/Documents.java new file mode 100644 index 00000000000..b7e8b33e2a0 --- /dev/null +++ b/src/main/java/seedu/address/model/documents/Documents.java @@ -0,0 +1,70 @@ +package seedu.address.model.documents; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Objects; + +/** + * Represents a contact in the internship application. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Documents { + + public static final String MESSAGE_CONSTRAINTS = "Document should contain both a resume and a cover letter."; + + // Identity fields + private final ResumeLink resumeLink; + private final CoverLetterLink coverLetterLink; + + /** + * Every field must be present and not null. + */ + public Documents(ResumeLink resumeLink, CoverLetterLink coverLetterLink) { + requireAllNonNull(resumeLink, coverLetterLink); + this.resumeLink = resumeLink; + this.coverLetterLink = coverLetterLink; + } + + public ResumeLink getResumeLink() { + return resumeLink; + } + + public CoverLetterLink getCoverLetterLink() { + return coverLetterLink; + } + + /** + * Returns true if both contacts have the same identity. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Documents)) { + return false; + } + + Documents otherDocuments = (Documents) other; + return otherDocuments.getResumeLink().equals(getResumeLink()) + && otherDocuments.getCoverLetterLink().equals(getCoverLetterLink()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(resumeLink, coverLetterLink); + } + + @Override + public String toString() { + String builder = "Resume: " + + getResumeLink() + + "; Cover letter: " + + getCoverLetterLink(); + + return builder; + } + +} diff --git a/src/main/java/seedu/address/model/documents/ResumeLink.java b/src/main/java/seedu/address/model/documents/ResumeLink.java new file mode 100644 index 00000000000..16bb7b727df --- /dev/null +++ b/src/main/java/seedu/address/model/documents/ResumeLink.java @@ -0,0 +1,83 @@ +package seedu.address.model.documents; + +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; + +import seedu.address.model.application.InternshipApplicationAttribute; + +/** + * Represents a link to a resume in the internship application. + * Guarantees: immutable; is valid as declared in {@link #isValidResumeLink(String)} + */ +public class ResumeLink extends InternshipApplicationAttribute { + + public static final String MESSAGE_CONSTRAINTS = "Link to resume should be in the form http://domain/path " + + "or https://domain/path\n" + + "The domain part can be an IP address, otherwise it is a domain name that must:\n" + + " - cannot be localhost\n" + + " - end with a domain label of 2 to 7 characters long\n" + + " - have each domain label start and end with alphanumeric characters\n" + + " - have each domain label consist of alphanumeric characters, separated only by hyphens, if any."; + + //Adapted from https://owasp.org/www-community/OWASP_Validation_Regex_Repository + private static final String DOMAIN_REGEX = "(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$"; + //Adapted from https://owasp.org/www-community/OWASP_Validation_Regex_Repository + private static final String IP_ADDRESS_REGEX = + "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"; + + public final String value; + + /** + * Constructs an {@code Resume}. + * + * @param resumeLink A valid link to a resume. + */ + public ResumeLink(String resumeLink) { + requireNonNull(resumeLink); + checkArgument(isValidResumeLink(resumeLink), MESSAGE_CONSTRAINTS); + value = resumeLink; + } + + /** + * Returns if a given string is a valid link to a resume. + */ + public static boolean isValidResumeLink(String test) { + requireNonNull(test); + try { + URI uri = new URL(test).toURI(); + String domain = uri.getHost(); + if (isNull(domain)) { + return false; + } + return test.matches("^(https?)://.*$") && (domain.matches(DOMAIN_REGEX) + || domain.matches(IP_ADDRESS_REGEX)); + } catch (URISyntaxException | MalformedURLException exception) { + return false; + } + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ResumeLink // instanceof handles nulls + && value.equals(((ResumeLink) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/person/Address.java deleted file mode 100644 index 60472ca22a0..00000000000 --- a/src/main/java/seedu/address/model/person/Address.java +++ /dev/null @@ -1,57 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's address in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)} - */ -public class Address { - - public static final String MESSAGE_CONSTRAINTS = "Addresses can take any values, and it should not be blank"; - - /* - * The first character of the address must not be a whitespace, - * otherwise " " (a blank string) becomes a valid input. - */ - public static final String VALIDATION_REGEX = "[^\\s].*"; - - public final String value; - - /** - * Constructs an {@code Address}. - * - * @param address A valid address. - */ - public Address(String address) { - requireNonNull(address); - checkArgument(isValidAddress(address), MESSAGE_CONSTRAINTS); - value = address; - } - - /** - * Returns true if a given string is a valid email. - */ - public static boolean isValidAddress(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Address // instanceof handles nulls - && value.equals(((Address) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/person/Name.java deleted file mode 100644 index 79244d71cf7..00000000000 --- a/src/main/java/seedu/address/model/person/Name.java +++ /dev/null @@ -1,59 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's name in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} - */ -public class Name { - - public static final String MESSAGE_CONSTRAINTS = - "Names should only contain alphanumeric characters and spaces, and it should not be blank"; - - /* - * The first character of the address must not be a whitespace, - * otherwise " " (a blank string) becomes a valid input. - */ - public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; - - public final String fullName; - - /** - * Constructs a {@code Name}. - * - * @param name A valid name. - */ - public Name(String name) { - requireNonNull(name); - checkArgument(isValidName(name), MESSAGE_CONSTRAINTS); - fullName = name; - } - - /** - * Returns true if a given string is a valid name. - */ - public static boolean isValidName(String test) { - return test.matches(VALIDATION_REGEX); - } - - - @Override - public String toString() { - return fullName; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Name // instanceof handles nulls - && fullName.equals(((Name) other).fullName)); // state check - } - - @Override - public int hashCode() { - return fullName.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java deleted file mode 100644 index 8ff1d83fe89..00000000000 --- a/src/main/java/seedu/address/model/person/Person.java +++ /dev/null @@ -1,123 +0,0 @@ -package seedu.address.model.person; - -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import seedu.address.model.tag.Tag; - -/** - * Represents a Person in the address book. - * Guarantees: details are present and not null, field values are validated, immutable. - */ -public class Person { - - // Identity fields - private final Name name; - private final Phone phone; - private final Email email; - - // Data fields - private final Address address; - private final Set tags = new HashSet<>(); - - /** - * Every field must be present and not null. - */ - public Person(Name name, Phone phone, Email email, Address address, Set tags) { - requireAllNonNull(name, phone, email, address, tags); - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - this.tags.addAll(tags); - } - - public Name getName() { - return name; - } - - public Phone getPhone() { - return phone; - } - - public Email getEmail() { - return email; - } - - public Address getAddress() { - return address; - } - - /** - * Returns an immutable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - */ - public Set getTags() { - return Collections.unmodifiableSet(tags); - } - - /** - * Returns true if both persons have the same name. - * This defines a weaker notion of equality between two persons. - */ - public boolean isSamePerson(Person otherPerson) { - if (otherPerson == this) { - return true; - } - - return otherPerson != null - && otherPerson.getName().equals(getName()); - } - - /** - * Returns true if both persons have the same identity and data fields. - * This defines a stronger notion of equality between two persons. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof Person)) { - return false; - } - - Person otherPerson = (Person) other; - return otherPerson.getName().equals(getName()) - && otherPerson.getPhone().equals(getPhone()) - && otherPerson.getEmail().equals(getEmail()) - && otherPerson.getAddress().equals(getAddress()) - && otherPerson.getTags().equals(getTags()); - } - - @Override - public int hashCode() { - // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append(getName()) - .append("; Phone: ") - .append(getPhone()) - .append("; Email: ") - .append(getEmail()) - .append("; Address: ") - .append(getAddress()); - - Set tags = getTags(); - if (!tags.isEmpty()) { - builder.append("; Tags: "); - tags.forEach(builder::append); - } - return builder.toString(); - } - -} diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java deleted file mode 100644 index 0fee4fe57e6..00000000000 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ /dev/null @@ -1,137 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Iterator; -import java.util.List; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; - -/** - * A list of persons that enforces uniqueness between its elements and does not allow nulls. - * A person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, adding and updating of - * persons uses Person#isSamePerson(Person) for equality so as to ensure that the person being added or updated is - * unique in terms of identity in the UniquePersonList. However, the removal of a person uses Person#equals(Object) so - * as to ensure that the person with exactly the same fields will be removed. - * - * Supports a minimal set of list operations. - * - * @see Person#isSamePerson(Person) - */ -public class UniquePersonList implements Iterable { - - private final ObservableList internalList = FXCollections.observableArrayList(); - private final ObservableList internalUnmodifiableList = - FXCollections.unmodifiableObservableList(internalList); - - /** - * Returns true if the list contains an equivalent person as the given argument. - */ - public boolean contains(Person toCheck) { - requireNonNull(toCheck); - return internalList.stream().anyMatch(toCheck::isSamePerson); - } - - /** - * Adds a person to the list. - * The person must not already exist in the list. - */ - public void add(Person toAdd) { - requireNonNull(toAdd); - if (contains(toAdd)) { - throw new DuplicatePersonException(); - } - internalList.add(toAdd); - } - - /** - * Replaces the person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the list. - * The person identity of {@code editedPerson} must not be the same as another existing person in the list. - */ - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); - - int index = internalList.indexOf(target); - if (index == -1) { - throw new PersonNotFoundException(); - } - - if (!target.isSamePerson(editedPerson) && contains(editedPerson)) { - throw new DuplicatePersonException(); - } - - internalList.set(index, editedPerson); - } - - /** - * Removes the equivalent person from the list. - * The person must exist in the list. - */ - public void remove(Person toRemove) { - requireNonNull(toRemove); - if (!internalList.remove(toRemove)) { - throw new PersonNotFoundException(); - } - } - - public void setPersons(UniquePersonList replacement) { - requireNonNull(replacement); - internalList.setAll(replacement.internalList); - } - - /** - * Replaces the contents of this list with {@code persons}. - * {@code persons} must not contain duplicate persons. - */ - public void setPersons(List persons) { - requireAllNonNull(persons); - if (!personsAreUnique(persons)) { - throw new DuplicatePersonException(); - } - - internalList.setAll(persons); - } - - /** - * Returns the backing list as an unmodifiable {@code ObservableList}. - */ - public ObservableList asUnmodifiableObservableList() { - return internalUnmodifiableList; - } - - @Override - public Iterator iterator() { - return internalList.iterator(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof UniquePersonList // instanceof handles nulls - && internalList.equals(((UniquePersonList) other).internalList)); - } - - @Override - public int hashCode() { - return internalList.hashCode(); - } - - /** - * Returns true if {@code persons} contains only unique persons. - */ - private boolean personsAreUnique(List persons) { - for (int i = 0; i < persons.size() - 1; i++) { - for (int j = i + 1; j < persons.size(); j++) { - if (persons.get(i).isSamePerson(persons.get(j))) { - return false; - } - } - } - return true; - } -} diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java deleted file mode 100644 index fa764426ca7..00000000000 --- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java +++ /dev/null @@ -1,6 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation is unable to find the specified person. - */ -public class PersonNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/address/model/statstics/StatsInformation.java b/src/main/java/seedu/address/model/statstics/StatsInformation.java new file mode 100644 index 00000000000..03c5c104b51 --- /dev/null +++ b/src/main/java/seedu/address/model/statstics/StatsInformation.java @@ -0,0 +1,12 @@ +package seedu.address.model.statstics; + +/** + * Encapsulates statistics information of InternEase. + */ +public abstract class StatsInformation { + public abstract void updateStatsInformation(); + + public abstract Number getStatsInformation(); + + public abstract String getDescription(); +} diff --git a/src/main/java/seedu/address/model/statstics/StatsManager.java b/src/main/java/seedu/address/model/statstics/StatsManager.java new file mode 100644 index 00000000000..f9dbef9000a --- /dev/null +++ b/src/main/java/seedu/address/model/statstics/StatsManager.java @@ -0,0 +1,80 @@ +package seedu.address.model.statstics; + +import static java.util.Objects.requireNonNull; + +import java.util.Arrays; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import seedu.address.model.Model; +import seedu.address.model.application.InternshipStatus; + +/** + * The main manager of statistics of the internship application status. + */ +public class StatsManager { + private final Model model; + private final ObservableList statsInformations; + private final FilteredList filteredStatsInformations; + + /** + * Creates a new StatsManager. + * + * @param model The model from which we get the statistics information. + */ + public StatsManager(Model model) { + requireNonNull(model); + this.model = model; + this.statsInformations = FXCollections.observableArrayList(); + this.filteredStatsInformations = new FilteredList<>(statsInformations); + this.initStatsInformation(); + } + + /** + * Initializes the StatsManager. + */ + private void initStatsInformation() { + List l = Arrays.asList(InternshipStatus.values()); + StatsInformation total = new TotalStatsInformation(model); + statsInformations.add(total); + for (InternshipStatus s: l) { + statsInformations.add(new StatusStatsInformation(model, s)); + } + } + + /** + * Update current statistics information. + */ + public void updateAllStatsInformation() { + for (StatsInformation s: statsInformations) { + s.updateStatsInformation(); + } + } + + /** + * Update underlying list of statistics information. + */ + public void updateFilteredStatsInformationList() { + filteredStatsInformations.setPredicate(p -> true); + } + + /** + * Returns the list of statistics information. + * + * @return The list of statistics information. + */ + public ObservableList getStatsInformations() { + return statsInformations; + } + + /** + * Returns the filtered list of statistics information. + * + * @return The filtered list of statistics information. + */ + public FilteredList getFilteredStatsInformations() { + return filteredStatsInformations; + } +} diff --git a/src/main/java/seedu/address/model/statstics/StatusStatsInformation.java b/src/main/java/seedu/address/model/statstics/StatusStatsInformation.java new file mode 100644 index 00000000000..1c59dce6387 --- /dev/null +++ b/src/main/java/seedu/address/model/statstics/StatusStatsInformation.java @@ -0,0 +1,77 @@ +package seedu.address.model.statstics; + +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import seedu.address.model.Model; +import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.StatusPredicate; + +/** + * Encapsulates the statistics information for a given status. + */ +public class StatusStatsInformation extends StatsInformation { + private final Model model; + private final InternshipStatus status; + private final String description; + private int numberOfInternshipApplication; + + /** + * Creates a new StatsInformation instance with given status. + * + * @param model The model where we get our statistics information from. + * @param status The internship status of the application. + */ + public StatusStatsInformation(Model model, InternshipStatus status) { + this.model = model; + this.status = status; + this.description = status.toString(); + this.numberOfInternshipApplication = getNumberOfApplicationWithStatus(InternshipStatus.valueOf(description)); + } + + @Override + public String getDescription() { + return description; + } + + /** + * Returns the number of application in this status. + * + * @param status The status specified. + * @return + */ + public int getNumberOfApplicationWithStatus(InternshipStatus status) { + ReadOnlyAddressBook addressBook = model.getAddressBook(); + ObservableList internshipApplications = addressBook.getInternshipList(); + FilteredList filteredList = + internshipApplications.filtered(new StatusPredicate(status)); + return filteredList.size(); + } + + @Override + public Integer getStatsInformation() { + return numberOfInternshipApplication; + } + + @Override + public void updateStatsInformation() { + this.numberOfInternshipApplication = getNumberOfApplicationWithStatus(status); + } + + @Override + public String toString() { + return "[StatsInformation] " + this.description + ": " + numberOfInternshipApplication; + } + + /** + * Returns true if both internship applications have the same description and the number of internship application. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof StatusStatsInformation // instanceof handles nulls + && description.equals(((StatusStatsInformation) other).description) // state check + && numberOfInternshipApplication == ((StatusStatsInformation) other).numberOfInternshipApplication); + } +} diff --git a/src/main/java/seedu/address/model/statstics/TotalStatsInformation.java b/src/main/java/seedu/address/model/statstics/TotalStatsInformation.java new file mode 100644 index 00000000000..36ffa678041 --- /dev/null +++ b/src/main/java/seedu/address/model/statstics/TotalStatsInformation.java @@ -0,0 +1,67 @@ +package seedu.address.model.statstics; + +import javafx.collections.ObservableList; +import seedu.address.model.Model; +import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.application.InternshipApplication; + +/** + * Encapsulates the statistics information for total number of application added. + */ +public class TotalStatsInformation extends StatsInformation { + private final Model model; + private final String description = "Total"; + private int numberOfInternshipApplication; + + /** + * Creates a new TotalStatsInformation instance. + * @param model + */ + public TotalStatsInformation(Model model) { + this.model = model; + this.numberOfInternshipApplication = getTotalNumberOfApplication(); + } + + @Override + public String getDescription() { + return description; + } + + @Override + public Integer getStatsInformation() { + return numberOfInternshipApplication; + } + + /** + * Returns the total number of application. + * + * @return The total number of application. + */ + public int getTotalNumberOfApplication() { + ReadOnlyAddressBook addressBook = model.getAddressBook(); + ObservableList internshipApplications = addressBook.getInternshipList(); + return internshipApplications.size(); + } + + @Override + public void updateStatsInformation() { + this.numberOfInternshipApplication = getTotalNumberOfApplication(); + } + + @Override + public String toString() { + return "[StatsInformation] " + this.description + ": " + numberOfInternshipApplication; + } + + /** + * Returns true if both internship applications have the same description. The number of internship application + * is not compared for equality check. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TotalStatsInformation // instanceof handles nulls + && description.equals(((TotalStatsInformation) other).description) // state check + && numberOfInternshipApplication == ((TotalStatsInformation) other).numberOfInternshipApplication); + } +} diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java deleted file mode 100644 index b0ea7e7dad7..00000000000 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ /dev/null @@ -1,54 +0,0 @@ -package seedu.address.model.tag; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Tag in the address book. - * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)} - */ -public class Tag { - - public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric"; - public static final String VALIDATION_REGEX = "\\p{Alnum}+"; - - public final String tagName; - - /** - * Constructs a {@code Tag}. - * - * @param tagName A valid tag name. - */ - public Tag(String tagName) { - requireNonNull(tagName); - checkArgument(isValidTagName(tagName), MESSAGE_CONSTRAINTS); - this.tagName = tagName; - } - - /** - * Returns true if a given string is a valid tag name. - */ - public static boolean isValidTagName(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Tag // instanceof handles nulls - && tagName.equals(((Tag) other).tagName)); // state check - } - - @Override - public int hashCode() { - return tagName.hashCode(); - } - - /** - * Format state as text for viewing. - */ - public String toString() { - return '[' + tagName + ']'; - } - -} diff --git a/src/main/java/seedu/address/model/tag/TaskType.java b/src/main/java/seedu/address/model/tag/TaskType.java new file mode 100644 index 00000000000..44e7ebe9e55 --- /dev/null +++ b/src/main/java/seedu/address/model/tag/TaskType.java @@ -0,0 +1,29 @@ +package seedu.address.model.tag; + +/** + * Represents the possible types of the auto assigned type to relevant objects in the respective command. + * TODO for {@code InternshipTodo}, NOTE for {@code Note}, BOTH for {@code InternshipTodo} and {@code Note} in the + * task-related commands and NONE for the {@code InternshipApplication}. + */ +public enum TaskType { + TODO, + NOTE, + BOTH, + NONE; + + public static final String MESSAGE_CONSTRAINTS = "%s type is invalid!"; + + /** + * Checks if the type in string is TODO. + */ + public static boolean isValidTodo(String type) { + return type.equals((TaskType.TODO).toString()); + } + + /** + * Checks if the type in string is NOTE. + */ + public static boolean isValidNote(String type) { + return type.equals((TaskType.NOTE).toString()); + } +} diff --git a/src/main/java/seedu/address/model/task/ApplicationDeadline.java b/src/main/java/seedu/address/model/task/ApplicationDeadline.java new file mode 100644 index 00000000000..dc1518a1a6f --- /dev/null +++ b/src/main/java/seedu/address/model/task/ApplicationDeadline.java @@ -0,0 +1,62 @@ +package seedu.address.model.task; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +/** + * Represents an ApplicationDeadline in {@code InternshipTodo}. + * Guarantees: immutable. + */ +public class ApplicationDeadline { + + public static final String MESSAGE_CONSTRAINTS = "Input date is invalid, it should be from today onwards!"; + + public final LocalDate applicationDeadline; + public final String fullName; + + /** + * Constructs a {@code ApplicationDeadline} with {@code date}. + */ + public ApplicationDeadline(LocalDate date) { + requireNonNull(date); + checkArgument(isValidDate(date), MESSAGE_CONSTRAINTS); + applicationDeadline = date; + fullName = DateTimeFormatter.ofPattern("dd MMM yyyy, EEEE").format(date); + } + + /** + * Returns true if a given string is a valid date. + */ + public static boolean isValidDate(LocalDate test) { + return !test.isBefore(LocalDate.now()); + } + + /** + * Getter for the respective application deadline. + */ + public LocalDate getDeadline() { + return applicationDeadline; + } + + @Override + public String toString() { + return fullName; + } + + @Override + public boolean equals(Object other) { + return other == this + || (other instanceof seedu.address.model.task.ApplicationDeadline + && applicationDeadline.equals((( + seedu.address.model.task.ApplicationDeadline) other).applicationDeadline)); + } + + @Override + public int hashCode() { + return applicationDeadline.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/task/ContentContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/task/ContentContainsKeywordsPredicate.java new file mode 100644 index 00000000000..2a43e67f8dd --- /dev/null +++ b/src/main/java/seedu/address/model/task/ContentContainsKeywordsPredicate.java @@ -0,0 +1,33 @@ +package seedu.address.model.task; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.util.StringUtil; + +/** + * Tests that a {@code Note}'s {@code NoteContent} matches any of the keywords given. + */ +public class ContentContainsKeywordsPredicate implements Predicate { + private final List keywords; + + /** + * Creates a {@code ContentContainsKeywordsPredicate} instance using the provided {@code keyword}. + */ + public ContentContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Note note) { + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(note.getNote().toString(), keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ContentContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((ContentContainsKeywordsPredicate) other).keywords)); // state check + } +} diff --git a/src/main/java/seedu/address/model/task/InternshipTodo.java b/src/main/java/seedu/address/model/task/InternshipTodo.java new file mode 100644 index 00000000000..822f4dbabd7 --- /dev/null +++ b/src/main/java/seedu/address/model/task/InternshipTodo.java @@ -0,0 +1,247 @@ +package seedu.address.model.task; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Objects; + +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.JobTitle; +import seedu.address.model.tag.TaskType; + +/** + * Represents an interested Internship that has not applied in the {@code TodoList}. + * Guarantees: details are present, field values are validated, immutable. + */ +public class InternshipTodo { + // Identity fields + private final CompanyName title; + private final JobTitle jobTitle; + private final LocalDate date; + private final TaskType type; + private ApplicationDeadline deadline; + + //Optional field + private NoteContent note; + + /** + * An InternshipTodo constructor to create an instance of todo internship application with company name + * {@code title}, {@code jobTitle} and {@code deadline}. + * Every field must be present and not null. + */ + public InternshipTodo(CompanyName title, JobTitle jobTitle, ApplicationDeadline deadline) { + requireAllNonNull(title, jobTitle, deadline); + this.title = title; + this.jobTitle = jobTitle; + this.deadline = deadline; + this.note = null; + this.date = LocalDate.now(); + this.type = TaskType.TODO; + } + + /** + * An InternshipTodo constructor to create an instance of todo internship application with company name + * {@code title}, {@code jobTitle}, {@code deadline}, date created {@code date} and {@code type}. + * Every field must be present and not null. + * This constructor is used to create {@code InternshipTodo} from the Json data file when {@code note} is null. + */ + public InternshipTodo(CompanyName title, JobTitle jobTitle, ApplicationDeadline deadline, LocalDate date, + TaskType type) { + requireAllNonNull(title, jobTitle, deadline, date, type); + this.title = title; + this.jobTitle = jobTitle; + this.deadline = deadline; + this.note = null; + this.date = date; + this.type = type; + } + + /** + * An InternshipTodo constructor to create an instance of todo internship application with company name + * {@code title}, {@code jobTitle}, {@code deadline} and {@code note}. + * Every field must be present and not null. + */ + public InternshipTodo(CompanyName title, JobTitle jobTitle, ApplicationDeadline deadline, NoteContent note) { + requireAllNonNull(title, jobTitle, deadline); + this.title = title; + this.jobTitle = jobTitle; + this.deadline = deadline; + this.note = note; + this.date = LocalDate.now(); + this.type = TaskType.TODO; + } + + /** + * An InternshipTodo constructor to create an instance of todo internship application with company name + * {@code title}, {@code jobTitle}, {@code deadline}, {@code note}, date created {@code date} and {@code type}. + * Every field must be present and not null. + * This constructor is used to create {@code InternshipTodo} from the Json data file, when the {@code note} is + * not null. + */ + public InternshipTodo(CompanyName title, JobTitle jobTitle, ApplicationDeadline deadline, NoteContent note, + LocalDate date, TaskType type) { + requireAllNonNull(title, jobTitle, deadline, date, type); + this.title = title; + this.jobTitle = jobTitle; + this.deadline = deadline; + this.note = note; + this.date = date; + this.type = type; + } + + /** + * Getter for a todo application's title. + */ + public CompanyName getInternshipTitle() { + return title; + } + + /** + * Getter for a todo application's job title. + */ + public JobTitle getJobTitle() { + return jobTitle; + } + + /** + * Getter for a todo application's deadline. + */ + public ApplicationDeadline getDeadline() { + return deadline; + } + + /** + * Getter for a todo application's deadline, it produces string of date in an acceptable format for the date parser + * before being stored in the Json file. + */ + public String getJsonDeadline() { + return DateTimeFormatter.ofPattern("yyyy-MM-dd").format(deadline.getDeadline()); + } + + /** + * Getter for a todo application's note content. + */ + public NoteContent getNote() { + return note; + } + + /** + * Getter for a todo application's created date. + */ + public LocalDate getDate() { + return date; + } + + /** + * Getter for a todo application's created date, it produces string of date in an acceptable format for the date + * parser before being stored in the Json file. + */ + public String getJsonDate() { + return DateTimeFormatter.ofPattern("yyyy-MM-dd").format(date); + } + + /** + * Getter for a todo application's created date string, it produces string of date in format for the displayed date. + */ + public String getDateString() { + return DateTimeFormatter.ofPattern("dd MMM yyyy, EEEE").format(date); + } + + /** + * Getter for a todo application's type. + */ + public TaskType getType() { + return type; + } + + /** + * Set the deadline for the todo application. + */ + public void setDeadline(ApplicationDeadline deadline) { + this.deadline = deadline; + } + + /** + * Set the note content for the todo application. + */ + public void setNote(NoteContent note) { + this.note = note; + } + + /** + * Returns true if both interested internship have the same company name and job title. + * This defines a weaker notion of equality between two todo applications. + */ + public boolean isSameTodo(InternshipTodo otherInternship) { + if (otherInternship == this) { + return true; + } + return otherInternship != null + && otherInternship.getInternshipTitle().equals(getInternshipTitle()) + && otherInternship.getJobTitle().equals(getJobTitle()); + } + + /** + * Returns true if both interested internships have the same fields. + * This defines a stronger notion of equality between two todo applications. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof InternshipTodo)) { + return false; + } + + InternshipTodo otherCompany = (InternshipTodo) other; + boolean areEqual = otherCompany.getInternshipTitle().equals(getInternshipTitle()) + && otherCompany.getJobTitle().equals(getJobTitle()) + && otherCompany.getDeadline().equals(getDeadline()) + && otherCompany.getDate().equals(getDate()); + + if (otherCompany.getNote() != null ^ getNote() != null) { + return false; + } else if (otherCompany.getNote() == null && getNote() == null) { + return areEqual; + } else { + return areEqual && otherCompany.getNote().equals(getNote()); + } + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(title, jobTitle, deadline, note, date); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + + builder.append(getType()) + .append("; Create Date: ") + .append(getDate()); + + if (title.fullName != null) { + builder.append("; CompanyName: ") + .append(getInternshipTitle()) + .append("; Job Title: ") + .append(getJobTitle()); + } + + if (!deadline.fullName.equals(DateTimeFormatter.ofPattern("dd MMM yyyy, EEEE").format(LocalDate.MAX))) { + builder.append("; Deadline: ") + .append(getDeadline()); + } + + if (note != null) { + builder.append("; NoteList: ") + .append(getNote()); + } + + return builder.toString(); + } +} diff --git a/src/main/java/seedu/address/model/task/Note.java b/src/main/java/seedu/address/model/task/Note.java new file mode 100644 index 00000000000..a7d57c40c04 --- /dev/null +++ b/src/main/java/seedu/address/model/task/Note.java @@ -0,0 +1,131 @@ +package seedu.address.model.task; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Objects; + +import seedu.address.model.tag.TaskType; + +/** + * Represents a note object in the planner. + * Guarantees: immutable. + */ +public class Note { + + // Identity fields + private final LocalDate date; + private final TaskType type; + private NoteContent note; + + /** + * A Note constructor to create an instance of note with content {@code note}. + * Every field must be present and not null. + */ + public Note(NoteContent note) { + requireAllNonNull(note); + this.note = note; + this.date = LocalDate.now(); + this.type = TaskType.NOTE; + } + + /** + * A Note constructor to create an instance of note with content {@code note}, {@code date} and {@code type}. + * Every field must be present and not null. + * This constructor is used to create a {@code Note} from the respective Json data file entry. + */ + public Note(NoteContent note, LocalDate date, TaskType type) { + requireAllNonNull(note, date, type); + this.note = note; + this.date = date; + this.type = type; + } + + /** + * Getter for the note content. + */ + public NoteContent getNote() { + return note; + } + + /** + * Getter for the note's created date. + */ + public LocalDate getDate() { + return date; + } + + /** + * Getter for a note's created date, it produces string of date in an acceptable format for the date parser before + * being stored in the Json file. + */ + public String getJsonDate() { + return DateTimeFormatter.ofPattern("yyyy-MM-dd").format(date); + } + + /** + * Getter for a note's created date string, it produces string of date in format for the displayed date. + */ + public String getDateString() { + return DateTimeFormatter.ofPattern("dd MMM yyyy, EEEE").format(date); + } + + /** + * Getter for the note's type. + */ + public TaskType getType() { + return type; + } + + /** + * Set the note content for the note. + */ + public void setNote(NoteContent note) { + this.note = note; + } + + /** + * Returns true if both note have the same note content. + * This defines a weaker notion of equality between two notes. + */ + public boolean isSameNote(Note otherNote) { + if (otherNote == this) { + return true; + } + + return otherNote != null + && otherNote.getNote().equals(getNote()); + } + + /** + * Returns true if both interested internships have the same fields. + * This defines a stronger notion of equality between two notes. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Note)) { + return false; + } + + Note otherCompany = (Note) other; + return otherCompany.getNote().equals(getNote()) + && otherCompany.getDate().equals(getDate()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(note, date); + } + + @Override + public String toString() { + + return getType() + "; Create Date: " + getDate() + "; NoteList: " + getNote(); + } +} diff --git a/src/main/java/seedu/address/model/task/NoteContent.java b/src/main/java/seedu/address/model/task/NoteContent.java new file mode 100644 index 00000000000..c2447da96ef --- /dev/null +++ b/src/main/java/seedu/address/model/task/NoteContent.java @@ -0,0 +1,57 @@ +package seedu.address.model.task; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a NoteContent in {@code InternshipTodo} or {@code Note}. + * Guarantees: immutable; is valid as declared in {@link #isValidContent(String)} + */ +public class NoteContent { + + public static final String MESSAGE_CONSTRAINTS = "NoteList content can contain at most 55 words. " + + "It should not be blank."; + + /* + * The first character of the content must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "^.+$"; + + public final String content; + + /** + * Constructs a {@code NoteContent} with {@code note} + */ + public NoteContent(String note) { + requireNonNull(note); + checkArgument(isValidContent(note), MESSAGE_CONSTRAINTS); + content = note; + } + + /** + * Returns true if {@code test} is a valid name and is within 1 to 55 characters long. + */ + public static boolean isValidContent(String test) { + return test.length() <= 55 && test.matches(VALIDATION_REGEX); + } + + + @Override + public String toString() { + return content; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof seedu.address.model.task.NoteContent // instanceof handles nulls + && content.equals(((seedu.address.model.task.NoteContent) other).content)); // state check + } + + @Override + public int hashCode() { + return content.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/task/TitleContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/task/TitleContainsKeywordsPredicate.java new file mode 100644 index 00000000000..cc2fd4d9828 --- /dev/null +++ b/src/main/java/seedu/address/model/task/TitleContainsKeywordsPredicate.java @@ -0,0 +1,37 @@ +package seedu.address.model.task; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.util.StringUtil; + +/** + * Tests that an {@code Todo}'s {@code Title} and {@code JobTitle} matches any of the keywords given. + */ +public class TitleContainsKeywordsPredicate implements Predicate { + private final List keywords; + + /** + * Creates a {@code TitleContainsKeywordsPredicate} instance using the provided {@code keyword}. + */ + public TitleContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(InternshipTodo todo) { + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase( + todo.getInternshipTitle().toString() + + " " + todo.getJobTitle().toString(), + keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TitleContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((TitleContainsKeywordsPredicate) other).keywords)); // state check + } + +} diff --git a/src/main/java/seedu/address/model/task/UniqueNoteList.java b/src/main/java/seedu/address/model/task/UniqueNoteList.java new file mode 100644 index 00000000000..dbaef1feeb9 --- /dev/null +++ b/src/main/java/seedu/address/model/task/UniqueNoteList.java @@ -0,0 +1,140 @@ +package seedu.address.model.task; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.address.model.task.exceptions.DuplicateNoteException; +import seedu.address.model.task.exceptions.NoteNotFoundException; + +/** + * A list of Note that enforces uniqueness between its elements and does not allow nulls. + * A Note is considered unique by comparing using {@code Note#isSameNote(Note)}. As such, adding and updating of + * notes uses Note#isSameNote(Note) for equality to ensure that the note being added or updated is unique in terms of + * identity in the UniqueNoteList. However, the removal of a note uses Note#equals(Object) to ensure that the Note with + * exactly the same fields will be removed. + * Supports a minimal set of list operations. + * + * @see Note#isSameNote(Note) + */ +public class UniqueNoteList implements Iterable { + private final ObservableList internalNoteList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableNoteList = + FXCollections.unmodifiableObservableList(internalNoteList); + + /** + * Returns true if the list contains an equivalent note as the given {@code toCheck}. + */ + public boolean containsNote(Note toCheck) { + requireNonNull(toCheck); + return internalNoteList.stream().anyMatch(toCheck::isSameNote); + } + + /** + * Adds a note {@code toAdd} to the list. + * The note must not already exist in the list. + */ + public void addNote(Note toAdd) { + requireNonNull(toAdd); + if (containsNote(toAdd)) { + throw new DuplicateNoteException(); + } + internalNoteList.add(toAdd); + } + + /** + * Replaces the note {@code target} in the list with {@code editedNote}. + * {@code target} must exist in the list. + * The identity of {@code editedNote} must not be the same as another existing note in the list. + */ + public void setNotes(Note target, Note editedNote) { + requireAllNonNull(target, editedNote); + + int index = internalNoteList.indexOf(target); + if (index == -1) { + throw new NoteNotFoundException(); + } + + if (!target.isSameNote(editedNote) && containsNote(editedNote)) { + throw new DuplicateNoteException(); + } + + internalNoteList.set(index, editedNote); + } + + /** + * Replaces the contents of this list with {@code replacement}. + * {@code replacement} must not contain duplicate notes. + */ + public void setNotes(UniqueNoteList replacement) { + requireNonNull(replacement); + internalNoteList.setAll(replacement.internalNoteList); + } + + /** + * Replaces the contents of this list with {@code notes}. + * {@code notes} must not contain duplicate notes. + */ + public void setNotes(List notes) { + requireAllNonNull(notes); + if (!notesAreUnique(notes)) { + throw new DuplicateNoteException(); + } + + internalNoteList.setAll(notes); + } + + /** + * Removes the equivalent note from the list. + * The note must exist in the list. + */ + public void remove(Note toRemove) { + requireNonNull(toRemove); + if (!internalNoteList.remove(toRemove)) { + throw new NoteNotFoundException(); + } + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList asUnmodifiableObservableList() { + return internalUnmodifiableNoteList; + } + + @Override + public Iterator iterator() { + return internalNoteList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueNoteList // instanceof handles nulls + && internalNoteList.equals(((UniqueNoteList) other).internalNoteList)); + } + + @Override + public int hashCode() { + return internalNoteList.hashCode(); + } + + /** + * Returns true if {@code notes} contains only unique notes. + */ + private boolean notesAreUnique(List notes) { + for (int i = 0; i < notes.size() - 1; i++) { + for (int j = i + 1; j < notes.size(); j++) { + if (notes.get(i).isSameNote(notes.get(j))) { + return false; + } + } + } + return true; + } + +} diff --git a/src/main/java/seedu/address/model/task/UniqueTodoList.java b/src/main/java/seedu/address/model/task/UniqueTodoList.java new file mode 100644 index 00000000000..f37968aa2b3 --- /dev/null +++ b/src/main/java/seedu/address/model/task/UniqueTodoList.java @@ -0,0 +1,140 @@ +package seedu.address.model.task; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.address.model.task.exceptions.DuplicateTodoException; +import seedu.address.model.task.exceptions.TodoNotFoundException; + +/** + * A list of InternshipTodo that enforces uniqueness between its elements and does not allow nulls. + * An InternshipTodo is considered unique by comparing using {@code InternshipTodo#isSameTodo(InternshipTodo)}. + * As such, adding and updating of todos uses InternshipTodo#isSameTodo(InternshipTodo) for equality to ensure that the + * todo being added or updated is unique in terms of identity in the UniqueTodoList. However, the removal of a todo uses + * InternshipTodo#equals(Object) to ensure that the InternshipTodo with exactly the same fields will be removed. + * Supports a minimal set of list operations. + * + * @see InternshipTodo#isSameTodo(InternshipTodo) + */ +public class UniqueTodoList implements Iterable { + private final ObservableList internalTodoList = + FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableTodoList = + FXCollections.unmodifiableObservableList(internalTodoList); + + /** + * Returns true if the list contains an equivalent todo as the given argument. + */ + public boolean containsTodo(InternshipTodo toCheck) { + requireNonNull(toCheck); + return internalTodoList.stream().anyMatch(toCheck::isSameTodo); + } + + /** + * Adds a todo to the list. + * The todo must not already exist in the list. + */ + public void addTodo(InternshipTodo toAdd) { + requireNonNull(toAdd); + if (containsTodo(toAdd)) { + throw new DuplicateTodoException(); + } + internalTodoList.add(toAdd); + } + + /** + * Replaces the todo {@code target} in the list with {@code editedTodo}. + * {@code target} must exist in the list. + * The todo identity of {@code editedTodo} must not be the same as another existing todo in the list. + */ + public void setTodo(InternshipTodo target, InternshipTodo editedTodo) { + requireAllNonNull(target, editedTodo); + + int index = internalTodoList.indexOf(target); + if (index == -1) { + throw new TodoNotFoundException(); + } + + if (!target.isSameTodo(editedTodo) && containsTodo(editedTodo)) { + throw new DuplicateTodoException(); + } + + internalTodoList.set(index, editedTodo); + } + + /** + * Replaces the all the todo in list {@code target} with {@code replacement}. + * The todos in {@code replacement} should be unique. + */ + public void setTodo(UniqueTodoList replacement) { + requireNonNull(replacement); + internalTodoList.setAll(replacement.internalTodoList); + } + + /** + * Replaces the contents of this list with {@code todo}. + * {@code todo} must not contain duplicate todo tasks. + */ + public void setTodo(List todo) { + requireAllNonNull(todo); + if (!todoAreUnique(todo)) { + throw new DuplicateTodoException(); + } + + internalTodoList.setAll(todo); + } + + /** + * Removes the equivalent todo from the list. + * The todo must exist in the list. + */ + public void remove(InternshipTodo toRemove) { + requireNonNull(toRemove); + if (!internalTodoList.remove(toRemove)) { + throw new TodoNotFoundException(); + } + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList asUnmodifiableObservableList() { + return internalUnmodifiableTodoList; + } + + @Override + public Iterator iterator() { + return internalTodoList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueTodoList // instanceof handles nulls + && internalTodoList.equals(((UniqueTodoList) other).internalTodoList)); + } + + @Override + public int hashCode() { + return internalTodoList.hashCode(); + } + + /** + * Returns true if {@code todo} contains only unique todo. + */ + private boolean todoAreUnique(List todo) { + for (int i = 0; i < todo.size() - 1; i++) { + for (int j = i + 1; j < todo.size(); j++) { + if (todo.get(i).isSameTodo(todo.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/seedu/address/model/task/exceptions/DuplicateNoteException.java b/src/main/java/seedu/address/model/task/exceptions/DuplicateNoteException.java new file mode 100644 index 00000000000..98f14f79fb0 --- /dev/null +++ b/src/main/java/seedu/address/model/task/exceptions/DuplicateNoteException.java @@ -0,0 +1,15 @@ +package seedu.address.model.task.exceptions; + +/** + * Signals that the operation will result in duplicate Notes (Notes are considered duplicates if they have the same + * content). + */ +public class DuplicateNoteException extends RuntimeException { + + /** + * Creates an instance of DuplicateNoteException. + */ + public DuplicateNoteException() { + super("Operation would result in duplicate notes"); + } +} diff --git a/src/main/java/seedu/address/model/task/exceptions/DuplicateTodoException.java b/src/main/java/seedu/address/model/task/exceptions/DuplicateTodoException.java new file mode 100644 index 00000000000..cb81baf3516 --- /dev/null +++ b/src/main/java/seedu/address/model/task/exceptions/DuplicateTodoException.java @@ -0,0 +1,15 @@ +package seedu.address.model.task.exceptions; + +/** + * Signals that the operation will result in duplicate Todos (Todos are considered duplicates if they have the same + * title or company name). + */ +public class DuplicateTodoException extends RuntimeException { + + /** + * Creates an instance of DuplicateTodoException. + */ + public DuplicateTodoException() { + super("Operation would result in duplicate todos"); + } +} diff --git a/src/main/java/seedu/address/model/task/exceptions/NoteNotFoundException.java b/src/main/java/seedu/address/model/task/exceptions/NoteNotFoundException.java new file mode 100644 index 00000000000..3a5c927f537 --- /dev/null +++ b/src/main/java/seedu/address/model/task/exceptions/NoteNotFoundException.java @@ -0,0 +1,14 @@ +package seedu.address.model.task.exceptions; + +/** + * Signals that the operation will result requesting unavailable Note. + */ +public class NoteNotFoundException extends RuntimeException { + + /** + * Creates an instance of NoteNotFoundException. + */ + public NoteNotFoundException() { + super("Note not found!"); + } +} diff --git a/src/main/java/seedu/address/model/task/exceptions/TodoNotFoundException.java b/src/main/java/seedu/address/model/task/exceptions/TodoNotFoundException.java new file mode 100644 index 00000000000..3ca3a768506 --- /dev/null +++ b/src/main/java/seedu/address/model/task/exceptions/TodoNotFoundException.java @@ -0,0 +1,14 @@ +package seedu.address.model.task.exceptions; + +/** + * Signals that the operation will result requesting unavailable Todo. + */ +public class TodoNotFoundException extends RuntimeException { + + /** + * Creates an instance of TodoNotFoundException. + */ + public TodoNotFoundException() { + super("Todo not found!"); + } +} diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java index 1806da4facf..6ff9c3cccb4 100644 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java @@ -1,60 +1,155 @@ package seedu.address.model.util; +import java.time.LocalDate; import java.util.Arrays; import java.util.Set; import java.util.stream.Collectors; import seedu.address.model.AddressBook; +import seedu.address.model.NoteList; import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; +import seedu.address.model.ReadOnlyNote; +import seedu.address.model.ReadOnlyTodoList; +import seedu.address.model.TodoList; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.application.JobTitle; +import seedu.address.model.application.Location; +import seedu.address.model.application.ProgrammingLanguage; +import seedu.address.model.application.Qualification; +import seedu.address.model.application.Rating; +import seedu.address.model.application.Reflection; +import seedu.address.model.application.Review; +import seedu.address.model.application.Salary; +import seedu.address.model.contact.Contact; +import seedu.address.model.contact.Email; +import seedu.address.model.contact.Phone; +import seedu.address.model.documents.CoverLetterLink; +import seedu.address.model.documents.Documents; +import seedu.address.model.documents.ResumeLink; +import seedu.address.model.tag.TaskType; +import seedu.address.model.task.ApplicationDeadline; +import seedu.address.model.task.InternshipTodo; +import seedu.address.model.task.Note; +import seedu.address.model.task.NoteContent; /** * Contains utility methods for populating {@code AddressBook} with sample data. */ public class SampleDataUtil { - public static Person[] getSamplePersons() { - return new Person[] { - new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), - new Address("Blk 30 Geylang Street 29, #06-40"), - getTagSet("friends")), - new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), - new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), - getTagSet("colleagues", "friends")), - new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), - new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), - getTagSet("neighbours")), - new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), - new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), - getTagSet("family")), - new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), - new Address("Blk 47 Tampines Street 20, #17-35"), - getTagSet("classmates")), - new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), - new Address("Blk 45 Aljunied Street 85, #11-31"), - getTagSet("colleagues")) + public static InternshipApplication[] getSampleInternshipApplications() { + return new InternshipApplication[] { + new InternshipApplication(new CompanyName("Facebook"), new JobTitle("Software Engineer")), + new InternshipApplication(new CompanyName("Grab"), new JobTitle("Backend Developer"), + getReviewSet("Competitive environment"), + new Contact(new Phone("98765432"), new Email("career@grab.com")), InternshipStatus.RECEIVED, + false, + new Documents(new ResumeLink("https://myowndrive.com/resume_grab"), + new CoverLetterLink("https://myowndrive.com/cover_letter_grab"))), + new InternshipApplication(new CompanyName("Foodpanda"), new JobTitle("Fullstack Developer"), + getReviewSet("Interesting people", "Interesting atmosphere"), + getProgrammingLanguageSet("Java", "C++"), + getQualificationSet("Test Qualification", "AWS Cloud Certificate"), + new Location("Holland Village"), new Salary("9999 SGD"), + getNoteSet("Great food available nearby"), + new Rating("5/5"), + getReflectionSet("Can improve on coding speed", "Learn more about the company"), + new Contact(new Phone("12345678"), new Email("hr@foodpanda.com")), InternshipStatus.PENDING, + false, new InterviewDate("2024-01-03 12:31 PM"), + new Documents(new ResumeLink("https://example.com/resume_foodpanda"), + new CoverLetterLink("https://example.com/cover_letter_foodpanda"))) + }; + } + + public static InternshipTodo[] getSampleTodo() { + return new InternshipTodo[] { + new InternshipTodo(new CompanyName("Google"), new JobTitle("SE"), + new ApplicationDeadline(LocalDate.parse("2023-10-01")), + new NoteContent("no note"), LocalDate.parse("2023-03-11"), TaskType.TODO), + new InternshipTodo(new CompanyName("Meta"), new JobTitle("Web Dev"), + new ApplicationDeadline(LocalDate.parse("2023-07-10")), + new NoteContent("no note"), LocalDate.parse("2023-02-11"), TaskType.TODO), + new InternshipTodo(new CompanyName("Amazon"), new JobTitle("App Dev"), + new ApplicationDeadline(LocalDate.parse("2023-10-02")), + new NoteContent("no note"), LocalDate.parse("2023-01-11"), TaskType.TODO) + }; + } + + public static Note[] getSampleNotes() { + return new Note[] {new Note(new NoteContent("Vacation starts next week!")), + new Note(new NoteContent("Project should be finalized by this week!")), + new Note(new NoteContent("Write letter!")) }; } public static ReadOnlyAddressBook getSampleAddressBook() { AddressBook sampleAb = new AddressBook(); - for (Person samplePerson : getSamplePersons()) { - sampleAb.addPerson(samplePerson); + for (InternshipApplication sampleInternshipApplication : getSampleInternshipApplications()) { + sampleAb.addInternshipApplication(sampleInternshipApplication); } return sampleAb; } + public static ReadOnlyTodoList getSampleTodoList() { + TodoList sampleTd = new TodoList(); + for (InternshipTodo sampleTodo : getSampleTodo()) { + sampleTd.addTodo(sampleTodo); + } + return sampleTd; + } + + public static ReadOnlyNote getSampleNoteList() { + NoteList sampleNl = new NoteList(); + for (Note sampleNote : getSampleNotes()) { + sampleNl.addNote(sampleNote); + } + return sampleNl; + } + + /** + * Returns a review set containing the list of strings given. + */ + public static Set getReviewSet(String... strings) { + return Arrays.stream(strings) + .map(Review::new) + .collect(Collectors.toSet()); + } + /** - * Returns a tag set containing the list of strings given. + * Returns a programming language set containing the list of strings given. */ - public static Set getTagSet(String... strings) { + public static Set getProgrammingLanguageSet(String... strings) { return Arrays.stream(strings) - .map(Tag::new) + .map(ProgrammingLanguage::new) .collect(Collectors.toSet()); } + /** + * Returns a qualification set containing the list of strings given. + */ + public static Set getQualificationSet(String... strings) { + return Arrays.stream(strings) + .map(Qualification::new) + .collect(Collectors.toSet()); + } + + /** + * Returns a note set containing the list of strings given. + */ + public static Set getNoteSet(String... strings) { + return Arrays.stream(strings) + .map(seedu.address.model.application.Note::new) + .collect(Collectors.toSet()); + } + + /** + * Returns a reflection set containing the list of strings given. + */ + public static Set getReflectionSet(String... strings) { + return Arrays.stream(strings) + .map(Reflection::new) + .collect(Collectors.toSet()); + } } diff --git a/src/main/java/seedu/address/storage/JsonAdaptedInternshipApplication.java b/src/main/java/seedu/address/storage/JsonAdaptedInternshipApplication.java new file mode 100644 index 00000000000..5cafd7a1e30 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedInternshipApplication.java @@ -0,0 +1,323 @@ +package seedu.address.storage; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.application.JobTitle; +import seedu.address.model.application.Location; +import seedu.address.model.application.Note; +import seedu.address.model.application.ProgrammingLanguage; +import seedu.address.model.application.Qualification; +import seedu.address.model.application.Rating; +import seedu.address.model.application.Reflection; +import seedu.address.model.application.Review; +import seedu.address.model.application.Salary; +import seedu.address.model.contact.Contact; +import seedu.address.model.contact.Email; +import seedu.address.model.contact.Phone; +import seedu.address.model.documents.CoverLetterLink; +import seedu.address.model.documents.Documents; +import seedu.address.model.documents.ResumeLink; + +/** + * Jackson-friendly version of {@link seedu.address.model.application.InternshipApplication}. + */ +public class JsonAdaptedInternshipApplication { + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Internship application 's %s field is missing!"; + + private final String companyName; + private final String jobTitle; + private final List reviews = new ArrayList<>(); + private final List programmingLanguages = new ArrayList<>(); + private final List qualifications = new ArrayList<>(); + private final String location; + private final String salary; + private final List notes = new ArrayList<>(); + private final String rating; + private final List reflections = new ArrayList<>(); + private final List contact = new ArrayList<>(); + private final String status; + private final boolean archived; + private final String interviewDate; + private final List documents = new ArrayList<>(); + + /** + * Constructs a {@code JsonAdaptedInternshipApplication} with the given InternshipApplication details. + */ + @JsonCreator + public JsonAdaptedInternshipApplication(@JsonProperty("companyName") String companyName, + @JsonProperty("jobTitle") String jobTitle, + @JsonProperty("review") List reviews, + @JsonProperty("programmingLanguage") List programmingLanguages, + @JsonProperty("qualification") List qualifications, + @JsonProperty("location") String location, + @JsonProperty("salary") String salary, + @JsonProperty("note") List notes, + @JsonProperty("rating") String rating, + @JsonProperty("reflection") List reflections, + @JsonProperty("status") String status, + @JsonProperty("archived") boolean archived, + @JsonProperty("interviewDate") String interviewDate, + @JsonProperty("contact") List contact, + @JsonProperty("documents") List documents) { + this.companyName = companyName; + this.jobTitle = jobTitle; + if (reviews != null) { + this.reviews.addAll(reviews); + } + if (programmingLanguages != null) { + this.programmingLanguages.addAll(programmingLanguages); + } + if (qualifications != null) { + this.qualifications.addAll(qualifications); + } + this.location = location; + this.salary = salary; + if (notes != null) { + this.notes.addAll(notes); + } + this.rating = rating; + if (reflections != null) { + this.reflections.addAll(reflections); + } + if (contact != null) { + this.contact.addAll(contact); + } + this.status = status; + this.archived = archived; + this.interviewDate = interviewDate; + if (documents != null) { + this.documents.addAll(documents); + } + } + + /** + * Converts a given {@code InternshipApplication} into this class for Jackson use. + */ + public JsonAdaptedInternshipApplication(InternshipApplication source) { + companyName = source.getCompanyName().fullName; + jobTitle = source.getJobTitle().fullName; + reviews.addAll(source.getReviews().stream() + .map(Review::toString) + .collect(Collectors.toList())); + programmingLanguages.addAll(source.getProgrammingLanguages().stream() + .map(ProgrammingLanguage::toString) + .collect(Collectors.toList())); + qualifications.addAll(source.getQualifications().stream() + .map(Qualification::toString) + .collect(Collectors.toList())); + if (source.getLocation() != null) { + location = source.getLocation().value; + } else { + location = null; + } + if (source.getSalary() != null) { + salary = source.getSalary().value; + } else { + salary = null; + } + notes.addAll(source.getNotes().stream() + .map(Note::toString) + .collect(Collectors.toList())); + if (source.getRating() != null) { + rating = source.getRating().value; + } else { + rating = null; + } + reflections.addAll(source.getReflections().stream() + .map(Reflection::toString) + .collect(Collectors.toList())); + if (source.getContact() != null) { + contact.add(source.getContact().getPhone().value); + contact.add(source.getContact().getEmail().value); + } + status = source.getStatus().name(); + archived = source.isArchived(); + if (source.getInterviewDate() != null) { + interviewDate = source.getInterviewDate().toString(); + } else { + interviewDate = null; + } + if (source.getDocuments() != null) { + documents.add(source.getDocuments().getResumeLink().value); + documents.add(source.getDocuments().getCoverLetterLink().value); + } + } + + private CompanyName getModelCompanyName() throws IllegalValueException { + if (companyName == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + CompanyName.class.getSimpleName())); + } + if (!CompanyName.isValidCompanyName(companyName)) { + throw new IllegalValueException(CompanyName.MESSAGE_CONSTRAINTS); + } + return new CompanyName(companyName); + } + + private JobTitle getModelJobTitle() throws IllegalValueException { + if (jobTitle == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + JobTitle.class.getSimpleName())); + } + if (!JobTitle.isValidJobTitle(jobTitle)) { + throw new IllegalValueException(JobTitle.MESSAGE_CONSTRAINTS); + } + return new JobTitle(jobTitle); + } + + private Location getModelLocation() throws IllegalValueException { + if (!Location.isValidLocation(location)) { + throw new IllegalValueException(Location.MESSAGE_CONSTRAINTS); + } + return new Location(location); + } + + private Salary getModelSalary() throws IllegalValueException { + if (!Salary.isValidSalary(salary)) { + throw new IllegalValueException(Salary.MESSAGE_CONSTRAINTS); + } + return new Salary(salary); + } + + private Rating getModelRating() throws IllegalValueException { + if (!Rating.isValidRating(rating)) { + throw new IllegalValueException(Rating.MESSAGE_CONSTRAINTS); + } + return new Rating(rating); + } + + private List getReviewList() { + final List reviewList = new ArrayList<>(); + for (String review : reviews) { + reviewList.add(new Review(review)); + } + return reviewList; + } + + private List getProgrammingLanguageList() { + final List programmingLanguageList = new ArrayList<>(); + for (String programmingLanguage : programmingLanguages) { + programmingLanguageList.add(new ProgrammingLanguage(programmingLanguage)); + } + return programmingLanguageList; + } + + private List getQualificationList() { + final List qualificationList = new ArrayList<>(); + for (String qualification : qualifications) { + qualificationList.add(new Qualification(qualification)); + } + return qualificationList; + } + + private List getNoteList() { + final List noteList = new ArrayList<>(); + for (String note : notes) { + noteList.add(new Note(note)); + } + return noteList; + } + + private List getReflectionList() { + final List reflectionList = new ArrayList<>(); + for (String reflection : reflections) { + reflectionList.add(new Reflection(reflection)); + } + return reflectionList; + } + + private InternshipStatus getInternshipStatus() throws IllegalValueException { + if (status == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + InternshipStatus.class.getSimpleName())); + } + if (!InternshipStatus.isValidStatus(status)) { + throw new IllegalValueException(InternshipStatus.MESSAGE_CONSTRAINTS); + } + return InternshipStatus.valueOf(status); + } + + + private InterviewDate getInterviewDate() throws IllegalValueException { + return interviewDate == null ? null : new InterviewDate(interviewDate); + } + + private Contact getContact() throws IllegalValueException { + if (contact.size() == 1) { + throw new IllegalValueException(Contact.MESSAGE_CONSTRAINTS); + } else if (contact.size() == 2) { + if (!Phone.isValidPhone(contact.get(0))) { + throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS); + } + final Phone modelPhone = new Phone(contact.get(0)); + + if (!Email.isValidEmail(contact.get(1))) { + throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS); + } + final Email modelEmail = new Email(contact.get(1)); + return new Contact(modelPhone, modelEmail); + } else { + return null; + } + } + + private Documents getDocuments() throws IllegalValueException { + if (documents.size() == 1) { + throw new IllegalValueException(Documents.MESSAGE_CONSTRAINTS); + } else if (documents.size() == 2) { + if (!ResumeLink.isValidResumeLink(documents.get(0))) { + throw new IllegalValueException(ResumeLink.MESSAGE_CONSTRAINTS); + } + final ResumeLink modelResumeLink = new ResumeLink(documents.get(0)); + + if (!CoverLetterLink.isValidCoverLetterLink(documents.get(1))) { + throw new IllegalValueException(CoverLetterLink.MESSAGE_CONSTRAINTS); + } + final CoverLetterLink modelCoverLetterLink = new CoverLetterLink(documents.get(1)); + + return new Documents(modelResumeLink, modelCoverLetterLink); + } else { + return null; + } + } + + /** + * Converts this Jackson-friendly adapted InternshipApplication object + * into the model's {@code InternshipApplication} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted InternshipApplication. + */ + public InternshipApplication toModelType() throws IllegalValueException { + final CompanyName modelCompanyName = getModelCompanyName(); + final JobTitle modelJobTitle = getModelJobTitle(); + final Location modelLocation = getModelLocation(); + final Salary modelSalary = getModelSalary(); + final Rating modelRating = getModelRating(); + final Set modelReviews = new HashSet<>(getReviewList()); + final Set modelProgrammingLanguages = new HashSet<>(getProgrammingLanguageList()); + final Set modelQualifications = new HashSet<>(getQualificationList()); + final Set modelNotes = new HashSet<>(getNoteList()); + final Set modelReflections = new HashSet<>(getReflectionList()); + final InternshipStatus modelStatus = getInternshipStatus(); + final InterviewDate modelInterviewDate = getInterviewDate(); + final Contact modelContact = getContact(); + final Documents modelDocuments = getDocuments(); + + return new InternshipApplication(modelCompanyName, modelJobTitle, modelReviews, + modelProgrammingLanguages, modelQualifications, modelLocation, modelSalary, modelNotes, + modelRating, modelReflections, modelContact, modelStatus, archived, modelInterviewDate, + modelDocuments); + } +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java deleted file mode 100644 index a6321cec2ea..00000000000 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ /dev/null @@ -1,109 +0,0 @@ -package seedu.address.storage; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Jackson-friendly version of {@link Person}. - */ -class JsonAdaptedPerson { - - public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!"; - - private final String name; - private final String phone; - private final String email; - private final String address; - private final List tagged = new ArrayList<>(); - - /** - * Constructs a {@code JsonAdaptedPerson} with the given person details. - */ - @JsonCreator - public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone, - @JsonProperty("email") String email, @JsonProperty("address") String address, - @JsonProperty("tagged") List tagged) { - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - if (tagged != null) { - this.tagged.addAll(tagged); - } - } - - /** - * Converts a given {@code Person} into this class for Jackson use. - */ - public JsonAdaptedPerson(Person source) { - name = source.getName().fullName; - phone = source.getPhone().value; - email = source.getEmail().value; - address = source.getAddress().value; - tagged.addAll(source.getTags().stream() - .map(JsonAdaptedTag::new) - .collect(Collectors.toList())); - } - - /** - * Converts this Jackson-friendly adapted person object into the model's {@code Person} object. - * - * @throws IllegalValueException if there were any data constraints violated in the adapted person. - */ - public Person toModelType() throws IllegalValueException { - final List personTags = new ArrayList<>(); - for (JsonAdaptedTag tag : tagged) { - personTags.add(tag.toModelType()); - } - - if (name == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); - } - if (!Name.isValidName(name)) { - throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS); - } - final Name modelName = new Name(name); - - if (phone == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName())); - } - if (!Phone.isValidPhone(phone)) { - throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS); - } - final Phone modelPhone = new Phone(phone); - - if (email == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName())); - } - if (!Email.isValidEmail(email)) { - throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS); - } - final Email modelEmail = new Email(email); - - if (address == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); - } - if (!Address.isValidAddress(address)) { - throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); - } - final Address modelAddress = new Address(address); - - final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); - } - -} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/seedu/address/storage/JsonAdaptedTag.java deleted file mode 100644 index 0df22bdb754..00000000000 --- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java +++ /dev/null @@ -1,48 +0,0 @@ -package seedu.address.storage; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.tag.Tag; - -/** - * Jackson-friendly version of {@link Tag}. - */ -class JsonAdaptedTag { - - private final String tagName; - - /** - * Constructs a {@code JsonAdaptedTag} with the given {@code tagName}. - */ - @JsonCreator - public JsonAdaptedTag(String tagName) { - this.tagName = tagName; - } - - /** - * Converts a given {@code Tag} into this class for Jackson use. - */ - public JsonAdaptedTag(Tag source) { - tagName = source.tagName; - } - - @JsonValue - public String getTagName() { - return tagName; - } - - /** - * Converts this Jackson-friendly adapted tag object into the model's {@code Tag} object. - * - * @throws IllegalValueException if there were any data constraints violated in the adapted tag. - */ - public Tag toModelType() throws IllegalValueException { - if (!Tag.isValidTagName(tagName)) { - throw new IllegalValueException(Tag.MESSAGE_CONSTRAINTS); - } - return new Tag(tagName); - } - -} diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java index 5efd834091d..6b7657f013d 100644 --- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java +++ b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java @@ -11,7 +11,7 @@ import seedu.address.commons.exceptions.IllegalValueException; import seedu.address.model.AddressBook; import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; +import seedu.address.model.application.InternshipApplication; /** * An Immutable AddressBook that is serializable to JSON format. @@ -19,16 +19,18 @@ @JsonRootName(value = "addressbook") class JsonSerializableAddressBook { - public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s)."; + public static final String MESSAGE_DUPLICATE_APPLICATION = "Internship Application list " + + "contains duplicate InternshipApplication(s)."; - private final List persons = new ArrayList<>(); + private final List applications = new ArrayList<>(); /** - * Constructs a {@code JsonSerializableAddressBook} with the given persons. + * Constructs a {@code JsonSerializableAddressBook} with the given applications. */ @JsonCreator - public JsonSerializableAddressBook(@JsonProperty("persons") List persons) { - this.persons.addAll(persons); + public JsonSerializableAddressBook(@JsonProperty("applications") + List applications) { + this.applications.addAll(applications); } /** @@ -37,7 +39,8 @@ public JsonSerializableAddressBook(@JsonProperty("persons") List readUserPrefs() throws DataConversionException, IOException; @@ -29,4 +33,22 @@ public interface Storage extends AddressBookStorage, UserPrefsStorage { @Override void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; + @Override + Path getTodoListFilePath(); + + @Override + Optional readTodoList() throws DataConversionException, IOException; + + @Override + void saveTodoList(ReadOnlyTodoList todoList) throws IOException; + + @Override + Path getNoteListFilePath(); + + @Override + Optional readNoteList() throws DataConversionException, IOException; + + @Override + void saveNoteList(ReadOnlyNote notes) throws IOException; + } diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java index 6cfa0162164..9bbefccb6c4 100644 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ b/src/main/java/seedu/address/storage/StorageManager.java @@ -8,8 +8,12 @@ import seedu.address.commons.core.LogsCenter; import seedu.address.commons.exceptions.DataConversionException; import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyNote; +import seedu.address.model.ReadOnlyTodoList; import seedu.address.model.ReadOnlyUserPrefs; import seedu.address.model.UserPrefs; +import seedu.address.storage.task.note.NoteStorage; +import seedu.address.storage.task.todo.TodoListStorage; /** * Manages storage of AddressBook data in local storage. @@ -19,13 +23,18 @@ public class StorageManager implements Storage { private static final Logger logger = LogsCenter.getLogger(StorageManager.class); private AddressBookStorage addressBookStorage; private UserPrefsStorage userPrefsStorage; + private TodoListStorage todoListStorage; + private NoteStorage noteStorage; /** * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}. */ - public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) { + public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage, + TodoListStorage todoListStorage, NoteStorage noteStorage) { this.addressBookStorage = addressBookStorage; this.userPrefsStorage = userPrefsStorage; + this.todoListStorage = todoListStorage; + this.noteStorage = noteStorage; } // ================ UserPrefs methods ============================== @@ -75,4 +84,59 @@ public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) thro addressBookStorage.saveAddressBook(addressBook, filePath); } + // ================ TodoList methods ============================== + @Override + public Path getTodoListFilePath() { + return todoListStorage.getTodoListFilePath(); + } + + @Override + public Optional readTodoList() throws DataConversionException, IOException { + return readTodoList(todoListStorage.getTodoListFilePath()); + } + + @Override + public Optional readTodoList(Path filePath) throws DataConversionException, IOException { + logger.fine("Attempting to read data from file: " + filePath); + return todoListStorage.readTodoList(filePath); + } + + @Override + public void saveTodoList(ReadOnlyTodoList todoList) throws IOException { + saveTodoList(todoList, todoListStorage.getTodoListFilePath()); + } + + @Override + public void saveTodoList(ReadOnlyTodoList todoList, Path filePath) throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + todoListStorage.saveTodoList(todoList, filePath); + } + + // ================ NoteList methods ============================== + @Override + public Path getNoteListFilePath() { + return noteStorage.getNoteListFilePath(); + } + + @Override + public Optional readNoteList() throws DataConversionException, IOException { + return readNoteList(noteStorage.getNoteListFilePath()); + } + + @Override + public Optional readNoteList(Path filePath) throws DataConversionException, IOException { + logger.fine("Attempting to read data from file: " + filePath); + return noteStorage.readNoteList(filePath); + } + + @Override + public void saveNoteList(ReadOnlyNote note) throws IOException { + saveNoteList(note, noteStorage.getNoteListFilePath()); + } + + @Override + public void saveNoteList(ReadOnlyNote note, Path filePath) throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + noteStorage.saveNoteList(note, filePath); + } } diff --git a/src/main/java/seedu/address/storage/task/note/JsonAdaptedNote.java b/src/main/java/seedu/address/storage/task/note/JsonAdaptedNote.java new file mode 100644 index 00000000000..38f3667f622 --- /dev/null +++ b/src/main/java/seedu/address/storage/task/note/JsonAdaptedNote.java @@ -0,0 +1,77 @@ +package seedu.address.storage.task.note; + +import java.time.LocalDate; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.tag.TaskType; +import seedu.address.model.task.Note; +import seedu.address.model.task.NoteContent; + +/** + * Jackson-friendly version of {@link Note}. + */ +public class JsonAdaptedNote { + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Note 's %s field is missing!"; + + private final String type; + private final String date; + private final String note; + + /** + * Constructs a {@code JsonAdaptedNote} with the given Note details. + */ + @JsonCreator + public JsonAdaptedNote(@JsonProperty("type") String type, + @JsonProperty("date") String date, + @JsonProperty("note") String note) { + this.type = type; + this.date = date; + this.note = note; + } + + /** + * Converts a given {@code Note} into this class for Jackson use. + */ + public JsonAdaptedNote(Note source) { + type = source.getType().toString(); + date = source.getJsonDate(); + note = source.getNote().content; + } + + /** + * Converts this Jackson-friendly adapted Note object + * into the model's {@code Note} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted Note. + */ + public Note toModelType() throws IllegalValueException { + if (type == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + TaskType.class.getSimpleName())); + } + if (!TaskType.isValidNote(type)) { + throw new IllegalValueException(String.format(TaskType.MESSAGE_CONSTRAINTS, type)); + } + final TaskType type = TaskType.NOTE; + + if (date == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "Date created")); + } + + final LocalDate dateCreated = LocalDate.parse(date); + + if (note == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + NoteContent.class.getSimpleName())); + } + if (!NoteContent.isValidContent(note)) { + throw new IllegalValueException(NoteContent.MESSAGE_CONSTRAINTS); + } + final NoteContent content = new NoteContent(note); + + return new Note(content, dateCreated, type); + } +} diff --git a/src/main/java/seedu/address/storage/task/note/JsonNoteListStorage.java b/src/main/java/seedu/address/storage/task/note/JsonNoteListStorage.java new file mode 100644 index 00000000000..7b968fb1597 --- /dev/null +++ b/src/main/java/seedu/address/storage/task/note/JsonNoteListStorage.java @@ -0,0 +1,80 @@ +package seedu.address.storage.task.note; + +import static java.util.Objects.requireNonNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.commons.util.FileUtil; +import seedu.address.commons.util.JsonUtil; +import seedu.address.model.ReadOnlyNote; + +/** + * A class to access NoteList data stored as a json file on the hard disk. + */ +public class JsonNoteListStorage implements NoteStorage { + + private static final Logger logger = LogsCenter.getLogger(JsonNoteListStorage.class); + + private Path filePath; + + public JsonNoteListStorage(Path filePath) { + this.filePath = filePath; + } + + public Path getNoteListFilePath() { + return filePath; + } + + @Override + public Optional readNoteList() throws DataConversionException { + return readNoteList(filePath); + } + + /** + * Similar to {@link #readNoteList()}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readNoteList(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional jsonNoteList = JsonUtil.readJsonFile( + filePath, JsonSerializableNoteList.class); + if (!jsonNoteList.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonNoteList.get().toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + @Override + public void saveNoteList(ReadOnlyNote noteList) throws IOException { + saveNoteList(noteList, filePath); + } + + /** + * Similar to {@link #saveNoteList(ReadOnlyNote)}. + * + * @param filePath location of the data. Cannot be null. + */ + public void saveNoteList(ReadOnlyNote noteList, Path filePath) throws IOException { + requireNonNull(noteList); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonSerializableNoteList(noteList), filePath); + } + +} diff --git a/src/main/java/seedu/address/storage/task/note/JsonSerializableNoteList.java b/src/main/java/seedu/address/storage/task/note/JsonSerializableNoteList.java new file mode 100644 index 00000000000..465f82e44b3 --- /dev/null +++ b/src/main/java/seedu/address/storage/task/note/JsonSerializableNoteList.java @@ -0,0 +1,62 @@ +package seedu.address.storage.task.note; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.NoteList; +import seedu.address.model.ReadOnlyNote; +import seedu.address.model.task.Note; + +/** + * An Immutable NoteList that is serializable to JSON format. + */ +@JsonRootName(value = "notelist") +class JsonSerializableNoteList { + + public static final String MESSAGE_DUPLICATE_NOTE = "Note list contains duplicate Note(s)."; + + private final List notes = new ArrayList<>(); + + /** + * Constructs a {@code JsonSerializableNoteList} with the given notes. + */ + @JsonCreator + public JsonSerializableNoteList(@JsonProperty("notes") + List notes) { + this.notes.addAll(notes); + } + + /** + * Converts a given {@code ReadOnlyNote} into this class for Jackson use. + * + * @param source future changes to this will not affect the created {@code JsonSerializableNote}. + */ + public JsonSerializableNoteList(ReadOnlyNote source) { + notes.addAll(source.getNoteList().stream().map( + JsonAdaptedNote::new).collect(Collectors.toList())); + } + + /** + * Converts this note list into the model's {@code Note List} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public NoteList toModelType() throws IllegalValueException { + NoteList todoList = new NoteList(); + for (JsonAdaptedNote jsonAdaptedNote : notes) { + Note note = jsonAdaptedNote.toModelType(); + if (todoList.hasNote(note)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_NOTE); + } + todoList.addNote(note); + } + return todoList; + } + +} diff --git a/src/main/java/seedu/address/storage/task/note/NoteStorage.java b/src/main/java/seedu/address/storage/task/note/NoteStorage.java new file mode 100644 index 00000000000..4283f43a71f --- /dev/null +++ b/src/main/java/seedu/address/storage/task/note/NoteStorage.java @@ -0,0 +1,47 @@ +package seedu.address.storage.task.note; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.model.ReadOnlyNote; + +/** + * Represents a storage for {@link seedu.address.model.NoteList}. + */ +public interface NoteStorage { + + /** + * Returns the file path of the data file. + */ + Path getNoteListFilePath(); + + /** + * Returns AddressBook data as a {@link ReadOnlyNote}. + * Returns {@code Optional.empty()} if storage file is not found. + * + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + Optional readNoteList() throws DataConversionException, IOException; + + /** + * @see #getNoteListFilePath() + */ + Optional readNoteList(Path filePath) throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyNote} to the storage. + * + * @param noteList cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveNoteList(ReadOnlyNote noteList) throws IOException; + + /** + * @see #saveNoteList(ReadOnlyNote) + */ + void saveNoteList(ReadOnlyNote noteList, Path filePath) throws IOException; + +} diff --git a/src/main/java/seedu/address/storage/task/todo/JsonAdaptedTodo.java b/src/main/java/seedu/address/storage/task/todo/JsonAdaptedTodo.java new file mode 100644 index 00000000000..de0f4319ecf --- /dev/null +++ b/src/main/java/seedu/address/storage/task/todo/JsonAdaptedTodo.java @@ -0,0 +1,147 @@ +package seedu.address.storage.task.todo; + +import java.time.LocalDate; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.JobTitle; +import seedu.address.model.tag.TaskType; +import seedu.address.model.task.ApplicationDeadline; +import seedu.address.model.task.InternshipTodo; +import seedu.address.model.task.NoteContent; + +/** + * Jackson-friendly version of {@link InternshipTodo}. + */ +public class JsonAdaptedTodo { + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Internship application 's %s field is missing!"; + + private final String title; + private final String jobTitle; + private final String deadline; + private final String note; + private final String date; + private final String type; + + /** + * Constructs a {@code JsonAdaptedInternshipTodo} with the given InternshipTodo details. + */ + @JsonCreator + public JsonAdaptedTodo(@JsonProperty("title") String title, + @JsonProperty("jobTitle") String jobTitle, + @JsonProperty("deadline") String deadline, + @JsonProperty("note") String note, + @JsonProperty("date") String date, + @JsonProperty("type") String type) { + this.title = title; + this.jobTitle = jobTitle; + this.deadline = deadline; + this.note = note; + this.date = date; + this.type = type; + } + + /** + * Converts a given {@code InternshipTodo} into this class for Jackson use. + */ + public JsonAdaptedTodo(InternshipTodo source) { + title = source.getInternshipTitle().fullName; + jobTitle = source.getJobTitle().fullName; + deadline = source.getJsonDeadline(); + if (source.getNote() != null) { + note = source.getNote().content; + } else { + note = null; + } + date = source.getJsonDate(); + type = source.getType().toString(); + } + + /** + * Converts this Jackson-friendly adapted InternshipTodo object + * into the model's {@code InternshipTodo} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted InternshipTodo. + */ + public InternshipTodo toModelType() throws IllegalValueException { + final CompanyName modelTitle = titleToModelType(); + + final JobTitle modelJobTitle = jobToModelType(); + + final LocalDate modelDate = dateToModelType(); + + final TaskType modelType = typeToModelType(); + + final ApplicationDeadline modelDeadline = deadlineToModelType(); + + if (note != null) { + final NoteContent modelContent = noteToModelType(); + + return new InternshipTodo( + modelTitle, modelJobTitle, modelDeadline, modelContent, modelDate, modelType); + } + return new InternshipTodo( + modelTitle, modelJobTitle, modelDeadline, modelDate, modelType); + } + + private CompanyName titleToModelType() throws IllegalValueException { + if (title == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + CompanyName.class.getSimpleName())); + } + if (!CompanyName.isValidCompanyName(title)) { + throw new IllegalValueException(CompanyName.MESSAGE_CONSTRAINTS); + } + return new CompanyName(title); + } + + private JobTitle jobToModelType() throws IllegalValueException { + if (jobTitle == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + JobTitle.class.getSimpleName())); + } + if (!JobTitle.isValidJobTitle(jobTitle)) { + throw new IllegalValueException(JobTitle.MESSAGE_CONSTRAINTS); + } + return new JobTitle(jobTitle); + } + + private LocalDate dateToModelType() throws IllegalValueException { + if (date == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "Date created")); + } + return LocalDate.parse(date); + } + + private TaskType typeToModelType() throws IllegalValueException { + if (type == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + TaskType.class.getSimpleName())); + } + if (!TaskType.isValidTodo(type)) { + throw new IllegalValueException(String.format(TaskType.MESSAGE_CONSTRAINTS, type)); + } + return TaskType.TODO; + } + + private ApplicationDeadline deadlineToModelType() throws IllegalValueException { + if (deadline == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + ApplicationDeadline.class.getSimpleName())); + } + if (!ApplicationDeadline.isValidDate(LocalDate.parse(deadline))) { + throw new IllegalValueException(ApplicationDeadline.MESSAGE_CONSTRAINTS); + } + return new ApplicationDeadline(LocalDate.parse(deadline)); + } + + private NoteContent noteToModelType() throws IllegalValueException { + if (!NoteContent.isValidContent(note)) { + throw new IllegalValueException(NoteContent.MESSAGE_CONSTRAINTS); + } + return new NoteContent(note); + } +} diff --git a/src/main/java/seedu/address/storage/task/todo/JsonSerializableTodoList.java b/src/main/java/seedu/address/storage/task/todo/JsonSerializableTodoList.java new file mode 100644 index 00000000000..bed19d0a0ca --- /dev/null +++ b/src/main/java/seedu/address/storage/task/todo/JsonSerializableTodoList.java @@ -0,0 +1,62 @@ +package seedu.address.storage.task.todo; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.ReadOnlyTodoList; +import seedu.address.model.TodoList; +import seedu.address.model.task.InternshipTodo; + +/** + * An Immutable TodoList that is serializable to JSON format. + */ +@JsonRootName(value = "todolist") +class JsonSerializableTodoList { + + public static final String MESSAGE_DUPLICATE_TODO = "Internship todo list contains duplicate InternshipTodo(s)."; + + private final List todos = new ArrayList<>(); + + /** + * Constructs a {@code JsonSerializableTodoList} with the given todo tasks. + */ + @JsonCreator + public JsonSerializableTodoList(@JsonProperty("todos") + List todos) { + this.todos.addAll(todos); + } + + /** + * Converts a given {@code ReadOnlyTodoList} into this class for Jackson use. + * + * @param source future changes to this will not affect the created {@code JsonSerializableTodoList}. + */ + public JsonSerializableTodoList(ReadOnlyTodoList source) { + todos.addAll(source.getTodoList().stream().map( + JsonAdaptedTodo::new).collect(Collectors.toList())); + } + + /** + * Converts this todo list into the model's {@code TodoList} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public TodoList toModelType() throws IllegalValueException { + TodoList todoList = new TodoList(); + for (JsonAdaptedTodo jsonAdaptedTodo : todos) { + InternshipTodo todo = jsonAdaptedTodo.toModelType(); + if (todoList.hasTodo(todo)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_TODO); + } + todoList.addTodo(todo); + } + return todoList; + } + +} diff --git a/src/main/java/seedu/address/storage/task/todo/JsonTodoListStorage.java b/src/main/java/seedu/address/storage/task/todo/JsonTodoListStorage.java new file mode 100644 index 00000000000..3f09084a9b5 --- /dev/null +++ b/src/main/java/seedu/address/storage/task/todo/JsonTodoListStorage.java @@ -0,0 +1,80 @@ +package seedu.address.storage.task.todo; + +import static java.util.Objects.requireNonNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.commons.util.FileUtil; +import seedu.address.commons.util.JsonUtil; +import seedu.address.model.ReadOnlyTodoList; + +/** + * A class to access TodoList data stored as a json file on the hard disk. + */ +public class JsonTodoListStorage implements TodoListStorage { + + private static final Logger logger = LogsCenter.getLogger(JsonTodoListStorage.class); + + private Path filePath; + + public JsonTodoListStorage(Path filePath) { + this.filePath = filePath; + } + + public Path getTodoListFilePath() { + return filePath; + } + + @Override + public Optional readTodoList() throws DataConversionException { + return readTodoList(filePath); + } + + /** + * Similar to {@link #readTodoList()}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readTodoList(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional jsonTodoList = JsonUtil.readJsonFile( + filePath, JsonSerializableTodoList.class); + if (!jsonTodoList.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonTodoList.get().toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + @Override + public void saveTodoList(ReadOnlyTodoList todoList) throws IOException { + saveTodoList(todoList, filePath); + } + + /** + * Similar to {@link #saveTodoList(ReadOnlyTodoList)}. + * + * @param filePath location of the data. Cannot be null. + */ + public void saveTodoList(ReadOnlyTodoList todoList, Path filePath) throws IOException { + requireNonNull(todoList); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonSerializableTodoList(todoList), filePath); + } + +} diff --git a/src/main/java/seedu/address/storage/task/todo/TodoListStorage.java b/src/main/java/seedu/address/storage/task/todo/TodoListStorage.java new file mode 100644 index 00000000000..2fca23b93a2 --- /dev/null +++ b/src/main/java/seedu/address/storage/task/todo/TodoListStorage.java @@ -0,0 +1,47 @@ +package seedu.address.storage.task.todo; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.model.ReadOnlyTodoList; + +/** + * Represents a storage for {@link seedu.address.model.TodoList}. + */ +public interface TodoListStorage { + + /** + * Returns the file path of the data file. + */ + Path getTodoListFilePath(); + + /** + * Returns TodoList data as a {@link ReadOnlyTodoList}. + * Returns {@code Optional.empty()} if storage file is not found. + * + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + Optional readTodoList() throws DataConversionException, IOException; + + /** + * @see #getTodoListFilePath() + */ + Optional readTodoList(Path filePath) throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyTodoList} to the storage. + * + * @param todoList cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveTodoList(ReadOnlyTodoList todoList) throws IOException; + + /** + * @see #saveTodoList(ReadOnlyTodoList) + */ + void saveTodoList(ReadOnlyTodoList todoList, Path filePath) throws IOException; + +} diff --git a/src/main/java/seedu/address/ui/ApplicationCard.java b/src/main/java/seedu/address/ui/ApplicationCard.java new file mode 100644 index 00000000000..75b73265a29 --- /dev/null +++ b/src/main/java/seedu/address/ui/ApplicationCard.java @@ -0,0 +1,150 @@ +package seedu.address.ui; + +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.address.logic.commands.ArchiveCommand; +import seedu.address.logic.commands.DeleteCommand; +import seedu.address.logic.commands.UnarchiveCommand; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InterviewDate; +import seedu.address.ui.popups.PopupEditInternship; + +/** + * A UI component that displays information of a {@code InternshipApplication}. + */ +public class ApplicationCard extends UiPart { + private static final String FXML = "ApplicationListCard.fxml"; + public final InternshipApplication application; + private PopupEditInternship popupEditInternship; + private int index; + private final MainWindow mainWindow; + + /** + * NoteList: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + @FXML + private HBox cardPane; + @FXML + private Label companyName; + @FXML + private Label jobTitle; + @FXML + private Label name; + @FXML + private Label id; + @FXML + private Label internshipStatus; + @FXML + private Label interviewDate; + @FXML + private Button editButton; + + /** + * Creates a {@code InternshipApplicationCode} with the given {@code InternshipApplication} and index to display. + */ + public ApplicationCard(InternshipApplication application, int displayedIndex, MainWindow mainWindow) { + super(FXML); + this.application = application; + this.mainWindow = mainWindow; + this.index = displayedIndex; + this.popupEditInternship = new PopupEditInternship(mainWindow); + id.setText(displayedIndex + ". "); + companyName.setText(application.getCompanyName().fullName); + jobTitle.setText(application.getJobTitle().fullName); + internshipStatus.setText(application.getStatus().name()); + InterviewDate interviewDateStr = application.getInterviewDate(); + if (interviewDateStr != null) { + interviewDate.setText(interviewDateStr.toString()); + interviewDate.setVisible(true); + interviewDate.setManaged(true); + } + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ApplicationCard)) { + return false; + } + + // state check + ApplicationCard card = (ApplicationCard) other; + return id.getText().equals(card.id.getText()) + && application.equals(card.application); + } + + /** + * Handles the edit internship button clicked event. + */ + @FXML + private void handleEditInternshipClicked() { + if (!popupEditInternship.isShowing()) { + popupEditInternship.show(index, application); + } else { + popupEditInternship.focus(); + } + } + + /** + * Handles the delete internship button clicked event. + */ + @FXML + private void handleDeleteInternshipClicked() { + try { + mainWindow.executeCommand(makeDeleteCommand()); + } catch (CommandException e) { + e.printStackTrace(); + } catch (ParseException e) { + e.printStackTrace(); + } + } + + /** + * Make delete command. + */ + private String makeDeleteCommand() { + String commandText = DeleteCommand.COMMAND_WORD + " " + index; + return commandText; + } + + /** + * Handles the archive internship button clicked event. + */ + @FXML + private void handleArchiveInternshipClicked() { + try { + mainWindow.executeCommand(makeArchiveCommand()); + } catch (CommandException e) { + e.printStackTrace(); + } catch (ParseException e) { + e.printStackTrace(); + } + } + + /** + * Makes archive command. + */ + private String makeArchiveCommand() { + String commandText; + if (application.isArchived()) { + commandText = UnarchiveCommand.COMMAND_WORD + " " + index; + } else { + commandText = ArchiveCommand.COMMAND_WORD + " " + index; + } + return commandText; + } +} diff --git a/src/main/java/seedu/address/ui/ApplicationListPanel.java b/src/main/java/seedu/address/ui/ApplicationListPanel.java new file mode 100644 index 00000000000..82178f0bf02 --- /dev/null +++ b/src/main/java/seedu/address/ui/ApplicationListPanel.java @@ -0,0 +1,86 @@ +package seedu.address.ui; + +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import seedu.address.commons.core.LogsCenter; +import seedu.address.model.application.InternshipApplication; +import seedu.address.ui.popups.ControlBox; + +/** + * Panel containing the list of applications. + */ +public class ApplicationListPanel extends UiPart { + private static final String FXML = "ApplicationListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(ApplicationListPanel.class); + + private ViewContentPanel viewContentPanel; + private InternshipApplication currentApplication; + private ControlBox controlBox; + private final MainWindow mainWindow; + + @FXML + private ListView applicationListView; + + @FXML + private VBox container; + + /** + * Creates a {@code ViewContentPanel} with the given {@code ObservableList}. + */ + public ApplicationListPanel(ObservableList applicationList, MainWindow mainWindow, + ViewContentPanel viewContentPanel) { + super(FXML); + this.viewContentPanel = viewContentPanel; + this.mainWindow = mainWindow; + controlBox = new ControlBox(mainWindow); + container.getChildren().add(0, controlBox.getRoot()); + applicationListView.setItems(applicationList); + applicationListView.setCellFactory(listView -> new ApplicationListViewCell()); + } + + /** + * Handles mouse clicks for applicationListView to show the corresponding {@code InternshipApplication} + * in the {@code ViewContentPanel} + * + * @param arg0 mouse click event + */ + @FXML public void handleMouseClick(MouseEvent arg0) { + InternshipApplication applicationSelected = applicationListView.getSelectionModel().getSelectedItem(); + this.currentApplication = applicationSelected; + viewContentPanel.setInternshipApplication(applicationSelected); + } + + public VBox getContainer() { + return container; + } + + /** + * Custom {@code ListCell} that displays the graphics of an {@code InternshipApplication} + * using a {@code ApplicationCard}. + */ + class ApplicationListViewCell extends ListCell { + @Override + protected void updateItem(InternshipApplication application, boolean empty) { + super.updateItem(application, empty); + + if (empty || application == null) { + setGraphic(null); + setText(null); + viewContentPanel.clearPanel(); + } else { + setGraphic(new ApplicationCard(application, getIndex() + 1, mainWindow).getRoot()); + if (currentApplication != null && application.isSameApplication(currentApplication)) { + viewContentPanel.setInternshipApplication(application); + } + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/CommandBox.java b/src/main/java/seedu/address/ui/CommandBox.java index 9e75478664b..27d719d251b 100644 --- a/src/main/java/seedu/address/ui/CommandBox.java +++ b/src/main/java/seedu/address/ui/CommandBox.java @@ -2,6 +2,7 @@ import javafx.collections.ObservableList; import javafx.fxml.FXML; +import javafx.scene.control.Button; import javafx.scene.control.TextField; import javafx.scene.layout.Region; import seedu.address.logic.commands.CommandResult; @@ -21,6 +22,9 @@ public class CommandBox extends UiPart { @FXML private TextField commandTextField; + @FXML + private Button enterButton; + /** * Creates a {@code CommandBox} with the given {@code CommandExecutor}. */ @@ -29,6 +33,14 @@ public CommandBox(CommandExecutor commandExecutor) { this.commandExecutor = commandExecutor; // calls #setStyleToDefault() whenever there is a change to the text of the command box. commandTextField.textProperty().addListener((unused1, unused2, unused3) -> setStyleToDefault()); + enterButton.setText("ENTER"); + } + + /** + * Focus command prompt. + */ + public void focusCommandPrompt() { + commandTextField.requestFocus(); } /** @@ -43,12 +55,18 @@ private void handleCommandEntered() { try { commandExecutor.execute(commandText); - commandTextField.setText(""); } catch (CommandException | ParseException e) { setStyleToIndicateCommandFailure(); } } + /** + * Clear commandTextField for next input. + */ + public void clearCommandTextField() { + commandTextField.setText(""); + } + /** * Sets the command box style to use the default style. */ diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/address/ui/HelpWindow.java index 3f16b2fcf26..4c42cd62a37 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/seedu/address/ui/HelpWindow.java @@ -15,7 +15,7 @@ */ public class HelpWindow extends UiPart { - public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html"; + public static final String USERGUIDE_URL = "https://ay2223s2-cs2103t-w15-4.github.io/tp/UserGuide.html"; public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL; private static final Logger logger = LogsCenter.getLogger(HelpWindow.class); diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index 9106c3aa6e5..69dc899098f 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -1,14 +1,12 @@ package seedu.address.ui; +import java.util.ArrayList; import java.util.logging.Logger; -import javafx.event.ActionEvent; import javafx.fxml.FXML; -import javafx.scene.control.MenuItem; -import javafx.scene.control.TextInputControl; -import javafx.scene.input.KeyCombination; -import javafx.scene.input.KeyEvent; +import javafx.scene.layout.GridPane; import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; import javafx.stage.Stage; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; @@ -16,10 +14,14 @@ import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.tag.TaskType; +import seedu.address.ui.popups.PopupAddInternship; +import seedu.address.ui.popups.PopupEditInternship; +import seedu.address.ui.task.note.NoteListPanel; +import seedu.address.ui.task.todo.TodoListPanel; /** - * The Main Window. Provides the basic application layout containing - * a menu bar and space where other JavaFX elements can be placed. + * The Main Window. Provides the basic application layout where other JavaFX elements can be placed. */ public class MainWindow extends UiPart { @@ -31,24 +33,39 @@ public class MainWindow extends UiPart { private Logic logic; // Independent Ui parts residing in this Ui container - private PersonListPanel personListPanel; - private ResultDisplay resultDisplay; + private ApplicationListPanel applicationListPanel; + private QuickAccessToolbar quickAccessToolbar; private HelpWindow helpWindow; + private TodoListPanel todoListPanel; + private NoteListPanel noteListPanel; + private ViewContentPanel viewContentPanel; + private StatsInformationListPanel statsInformationListPanel; + private MixedPanel mixedPanel; + private CommandBox commandBox; + private ReminderWindow reminderWindow; + private PopupAddInternship popupAddInternship; + private ArrayList popupEditInternships = new ArrayList<>(); @FXML private StackPane commandBoxPlaceholder; @FXML - private MenuItem helpMenuItem; + private StackPane applicationListPanelPlaceholder; @FXML - private StackPane personListPanelPlaceholder; + private StackPane viewContentPanelPlaceholder; @FXML - private StackPane resultDisplayPlaceholder; + private StackPane statsInformationListPanelPlaceholder; @FXML - private StackPane statusbarPlaceholder; + private StackPane quickAccessToolbarPlaceholder; + + @FXML + private VBox mainContainer; + + @FXML + private GridPane headerGridPane; /** * Creates a {@code MainWindow} with the given {@code Stage} and {@code Logic}. @@ -62,65 +79,71 @@ public MainWindow(Stage primaryStage, Logic logic) { // Configure the UI setWindowDefaultSize(logic.getGuiSettings()); + quickAccessToolbar = new QuickAccessToolbar(this::executeCommand); + quickAccessToolbarPlaceholder.getChildren().add(quickAccessToolbar.getRoot()); - setAccelerators(); + reminderWindow = new ReminderWindow(new Stage(), logic.getReminderApplication(), this); helpWindow = new HelpWindow(); - } - - public Stage getPrimaryStage() { - return primaryStage; - } - - private void setAccelerators() { - setAccelerator(helpMenuItem, KeyCombination.valueOf("F1")); + headerGridPane.maxWidthProperty().bind(primaryStage.widthProperty()); + commandBoxPlaceholder.maxWidthProperty().bind(primaryStage.widthProperty()); } /** - * Sets the accelerator of a MenuItem. - * @param keyCombination the KeyCombination value of the accelerator + * Getter for primary stage. + * + * @return current primary stage */ - private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { - menuItem.setAccelerator(keyCombination); - - /* - * TODO: the code below can be removed once the bug reported here - * https://bugs.openjdk.java.net/browse/JDK-8131666 - * is fixed in later version of SDK. - * - * According to the bug report, TextInputControl (TextField, TextArea) will - * consume function-key events. Because CommandBox contains a TextField, and - * ResultDisplay contains a TextArea, thus some accelerators (e.g F1) will - * not work when the focus is in them because the key event is consumed by - * the TextInputControl(s). - * - * For now, we add following event filter to capture such key events and open - * help window purposely so to support accelerators even when focus is - * in CommandBox or ResultDisplay. - */ - getRoot().addEventFilter(KeyEvent.KEY_PRESSED, event -> { - if (event.getTarget() instanceof TextInputControl && keyCombination.match(event)) { - menuItem.getOnAction().handle(new ActionEvent()); - event.consume(); - } - }); + public Stage getPrimaryStage() { + return primaryStage; } /** * Fills up all the placeholders of this window. */ void fillInnerParts() { - personListPanel = new PersonListPanel(logic.getFilteredPersonList()); - personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); - - resultDisplay = new ResultDisplay(); - resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); + commandBox = new CommandBox(this::executeCommand); + commandBoxPlaceholder.getChildren().add(commandBox.getRoot()); + commandBox.focusCommandPrompt(); + viewContentPanel = new ViewContentPanel(); + viewContentPanelPlaceholder.getChildren().add(viewContentPanel.getRoot()); + + // Initialise statsInformationListPanel + statsInformationListPanel = new StatsInformationListPanel(logic.getStatsManager()); + statsInformationListPanelPlaceholder.getChildren().add(statsInformationListPanel.getRoot()); + + applicationListPanel = new ApplicationListPanel(logic.getSortedFilteredInternshipList(), this, + viewContentPanel); + todoListPanel = new TodoListPanel(logic.getFilteredTodoList(), viewContentPanel); + noteListPanel = new NoteListPanel(logic.getFilteredNoteList(), viewContentPanel); + mixedPanel = new MixedPanel(logic.getFilteredTodoList(), logic.getFilteredNoteList(), viewContentPanel); + applicationListPanelPlaceholder.getChildren().addAll(todoListPanel.getRoot(), noteListPanel.getRoot(), + mixedPanel.getRoot(), applicationListPanel.getRoot()); + + setHeightConstraints(); + } - StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath()); - statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot()); + private void setHeightConstraints() { + applicationListPanel.getContainer().maxHeightProperty().bind(primaryStage.heightProperty().multiply(0.9)); + todoListPanel.getContainer().maxHeightProperty().bind(primaryStage.heightProperty().multiply(0.9)); + noteListPanel.getContainer().maxHeightProperty().bind(primaryStage.heightProperty().multiply(0.9)); + mixedPanel.getContainer().maxHeightProperty().bind(primaryStage.heightProperty().multiply(0.9)); + + viewContentPanel.getContainer().maxHeightProperty().bind( + primaryStage.heightProperty().multiply(0.75 * 0.73)); + viewContentPanel.getContainer().prefHeightProperty().bind( + primaryStage.heightProperty().multiply(0.75 * 0.73)); + statsInformationListPanel.getContainer().maxHeightProperty().bind( + primaryStage.heightProperty().multiply(0.75 * 0.22)); + statsInformationListPanel.getContainer().prefHeightProperty().bind( + primaryStage.heightProperty().multiply(0.75 * 0.22)); + } - CommandBox commandBox = new CommandBox(this::executeCommand); - commandBoxPlaceholder.getChildren().add(commandBox.getRoot()); + private void changePanelPlaceholder(MainWindow m, TaskType type) { + m.getApplicationListPanel().getRoot().setVisible(type == TaskType.NONE); + m.getTodoListPanel().getRoot().setVisible(type == TaskType.TODO); + m.getNoteListPanel().getRoot().setVisible(type == TaskType.NOTE); + m.getMixedPanel().getRoot().setVisible(type == TaskType.BOTH); } /** @@ -147,6 +170,21 @@ public void handleHelp() { } } + /** + * Opens the reminder window or focuses on it if it's already opened. + */ + @FXML + public void handleReminder() { + if (!reminderWindow.isShowing()) { + reminderWindow.show(); + } else { + reminderWindow.focus(); + } + } + + /** + * Shows the primary stage. + */ void show() { primaryStage.show(); } @@ -160,11 +198,62 @@ private void handleExit() { (int) primaryStage.getX(), (int) primaryStage.getY()); logic.setGuiSettings(guiSettings); helpWindow.hide(); + reminderWindow.hide(); primaryStage.hide(); + popupAddInternship.hide(); + for (PopupEditInternship e : popupEditInternships) { + e.hide(); + } } - public PersonListPanel getPersonListPanel() { - return personListPanel; + /** + * Getter for application list panel. + * + * @return application list panel + */ + public ApplicationListPanel getApplicationListPanel() { + return applicationListPanel; + } + + /** + * Getter for todo list panel. + * + * @return todo list panel + */ + public TodoListPanel getTodoListPanel() { + return todoListPanel; + } + + /** + * Getter for note list panel. + * + * @return note list panel + */ + public NoteListPanel getNoteListPanel() { + return noteListPanel; + } + + /** + * Getter for mix panel. + * + * @return person list panel + */ + public MixedPanel getMixedPanel() { + return mixedPanel; + } + + /** + * Set add internship popup + */ + public void setPopupAddInternship(PopupAddInternship popupAddInternship) { + this.popupAddInternship = popupAddInternship; + } + + /** + * Add edit internship popup into the array + */ + public void setPopupEditInternships(PopupEditInternship popupEditInternship) { + this.popupEditInternships.add(popupEditInternship); } /** @@ -172,11 +261,18 @@ public PersonListPanel getPersonListPanel() { * * @see seedu.address.logic.Logic#execute(String) */ - private CommandResult executeCommand(String commandText) throws CommandException, ParseException { + public CommandResult executeCommand(String commandText) throws CommandException, ParseException { try { CommandResult commandResult = logic.execute(commandText); logger.info("Result: " + commandResult.getFeedbackToUser()); - resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser()); + changePanelPlaceholder(this, commandResult.getType()); + commandBox.clearCommandTextField(); + ResultDialog.displayResultDialog(commandResult.getFeedbackToUser(), primaryStage); + if (reminderWindow.isShowing()) { + reminderWindow.hide(); + } + reminderWindow = new ReminderWindow(new Stage(), logic.getReminderApplication(), this); + statsInformationListPanel.updateDisplay(); if (commandResult.isShowHelp()) { handleHelp(); @@ -186,10 +282,14 @@ private CommandResult executeCommand(String commandText) throws CommandException handleExit(); } + if (commandResult.isRemind()) { + handleReminder(); + } + commandBox.focusCommandPrompt(); return commandResult; } catch (CommandException | ParseException e) { logger.info("Invalid command: " + commandText); - resultDisplay.setFeedbackToUser(e.getMessage()); + ResultDialog.displayResultDialog(e.getMessage(), primaryStage); throw e; } } diff --git a/src/main/java/seedu/address/ui/MixedPanel.java b/src/main/java/seedu/address/ui/MixedPanel.java new file mode 100644 index 00000000000..86f8113ae7b --- /dev/null +++ b/src/main/java/seedu/address/ui/MixedPanel.java @@ -0,0 +1,126 @@ +package seedu.address.ui; + +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import seedu.address.commons.core.LogsCenter; +import seedu.address.model.task.InternshipTodo; +import seedu.address.model.task.Note; +import seedu.address.ui.task.note.NoteCard; +import seedu.address.ui.task.todo.TodoCard; + +/** + * Panel containing the list of notes and todos. + */ +public class MixedPanel extends UiPart { + private static final String FXML = "task/MixedPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(MixedPanel.class); + + private ViewContentPanel viewContentPanel; + private InternshipTodo currentTodo; + private Note currentNote; + + @FXML + private ListView todoListView; + + @FXML + private ListView noteListView; + + @FXML + private VBox container; + + /** + * Creates a {@code MixedPanel} with the given two {@code ObservableList}. + */ + public MixedPanel(ObservableList todoList, ObservableList noteList, + ViewContentPanel viewContentPanel) { + super(FXML); + this.viewContentPanel = viewContentPanel; + todoListView.setItems(todoList); + todoListView.setCellFactory(listView -> new TodoListViewCell()); + noteListView.setItems(noteList); + noteListView.setCellFactory(listView -> new NoteListViewCell()); + logger.info("Mixed panel updated."); + } + + /** + * Handles mouse clicks for todoListView to show the corresponding {@code InternshipTodo} + * in the {@code ViewContentPanel} + * + * @param arg0 mouse click event + */ + @FXML public void handleTodoMouseClick(MouseEvent arg0) { + InternshipTodo todoSelected = todoListView.getSelectionModel().getSelectedItem(); + this.currentTodo = todoSelected; + viewContentPanel.setInternshipTodo(todoSelected); + } + + /** + * Handles mouse clicks for noteListView to show the corresponding {@code Note} + * in the {@code ViewContentPanel} + * + * @param arg0 mouse click event + */ + @FXML public void handleNoteMouseClick(MouseEvent arg0) { + Note noteSelected = noteListView.getSelectionModel().getSelectedItem(); + this.currentNote = noteSelected; + viewContentPanel.setNote(noteSelected); + } + + /** + * Getter for the vertical box with id container. + * + * @return VBox with id container + */ + public VBox getContainer() { + return container; + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code ApplicationTodo} using a {@code TodoCard}. + */ + class TodoListViewCell extends ListCell { + @Override + protected void updateItem(InternshipTodo todo, boolean empty) { + super.updateItem(todo, empty); + + if (empty || todo == null) { + setGraphic(null); + setText(null); + viewContentPanel.clearPanel(); + } else { + setGraphic(new TodoCard(todo, getIndex() + 1).getRoot()); + if (currentTodo != null && todo.isSameTodo(currentTodo)) { + viewContentPanel.setInternshipTodo(todo); + } + } + } + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code NoteList} using a {@code NoteCard}. + */ + class NoteListViewCell extends ListCell { + @Override + public void updateItem(Note note, boolean empty) { + super.updateItem(note, empty); + + if (empty || note == null) { + setGraphic(null); + setText(null); + viewContentPanel.clearPanel(); + } else { + setGraphic(new NoteCard(note, getIndex() + 1).getRoot()); + if (currentNote != null && note.isSameNote(currentNote)) { + viewContentPanel.setNote(note); + } + } + } + } +} diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java deleted file mode 100644 index 7fc927bc5d9..00000000000 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ /dev/null @@ -1,77 +0,0 @@ -package seedu.address.ui; - -import java.util.Comparator; - -import javafx.fxml.FXML; -import javafx.scene.control.Label; -import javafx.scene.layout.FlowPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Region; -import seedu.address.model.person.Person; - -/** - * An UI component that displays information of a {@code Person}. - */ -public class PersonCard extends UiPart { - - private static final String FXML = "PersonListCard.fxml"; - - /** - * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. - * As a consequence, UI elements' variable names cannot be set to such keywords - * or an exception will be thrown by JavaFX during runtime. - * - * @see The issue on AddressBook level 4 - */ - - public final Person person; - - @FXML - private HBox cardPane; - @FXML - private Label name; - @FXML - private Label id; - @FXML - private Label phone; - @FXML - private Label address; - @FXML - private Label email; - @FXML - private FlowPane tags; - - /** - * Creates a {@code PersonCode} with the given {@code Person} and index to display. - */ - public PersonCard(Person person, int displayedIndex) { - super(FXML); - this.person = person; - id.setText(displayedIndex + ". "); - name.setText(person.getName().fullName); - phone.setText(person.getPhone().value); - address.setText(person.getAddress().value); - email.setText(person.getEmail().value); - person.getTags().stream() - .sorted(Comparator.comparing(tag -> tag.tagName)) - .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof PersonCard)) { - return false; - } - - // state check - PersonCard card = (PersonCard) other; - return id.getText().equals(card.id.getText()) - && person.equals(card.person); - } -} diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java deleted file mode 100644 index f4c501a897b..00000000000 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ /dev/null @@ -1,49 +0,0 @@ -package seedu.address.ui; - -import java.util.logging.Logger; - -import javafx.collections.ObservableList; -import javafx.fxml.FXML; -import javafx.scene.control.ListCell; -import javafx.scene.control.ListView; -import javafx.scene.layout.Region; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; - -/** - * Panel containing the list of persons. - */ -public class PersonListPanel extends UiPart { - private static final String FXML = "PersonListPanel.fxml"; - private final Logger logger = LogsCenter.getLogger(PersonListPanel.class); - - @FXML - private ListView personListView; - - /** - * Creates a {@code PersonListPanel} with the given {@code ObservableList}. - */ - public PersonListPanel(ObservableList personList) { - super(FXML); - personListView.setItems(personList); - personListView.setCellFactory(listView -> new PersonListViewCell()); - } - - /** - * Custom {@code ListCell} that displays the graphics of a {@code Person} using a {@code PersonCard}. - */ - class PersonListViewCell extends ListCell { - @Override - protected void updateItem(Person person, boolean empty) { - super.updateItem(person, empty); - - if (empty || person == null) { - setGraphic(null); - setText(null); - } else { - setGraphic(new PersonCard(person, getIndex() + 1).getRoot()); - } - } - } - -} diff --git a/src/main/java/seedu/address/ui/QuickAccessToolbar.java b/src/main/java/seedu/address/ui/QuickAccessToolbar.java new file mode 100644 index 00000000000..8060c825a19 --- /dev/null +++ b/src/main/java/seedu/address/ui/QuickAccessToolbar.java @@ -0,0 +1,106 @@ +package seedu.address.ui; + +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.layout.Region; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * The UI component that is responsible for receiving user command inputs. + */ +public class QuickAccessToolbar extends UiPart { + + public static final String ERROR_STYLE_CLASS = "error"; + private static final String FXML = "QuickAccessToolbar.fxml"; + + private final CommandExecutor commandExecutor; + + @FXML + private Button homeButton; + + @FXML + private Button notificationButton; + + @FXML + private Button helpButton; + + /** + * Creates a {@code QuickAccessToolBar} with 3 buttons homeButton, notificationButton and helpButton. + */ + public QuickAccessToolbar(CommandExecutor commandExecutor) { + super(FXML); + this.commandExecutor = commandExecutor; + } + + /** + * Handles the home button pressed event. + */ + @FXML + private void redirectToHome() { + try { + setFocusButton(homeButton); + commandExecutor.execute("list"); + } catch (CommandException | ParseException e) { + setStyleToIndicateCommandFailure(homeButton); + } + } + + /** + * Handles the notification button pressed event. + */ + @FXML + private void redirectToReminder() { + try { + setFocusButton(notificationButton); + commandExecutor.execute("remind"); + } catch (CommandException | ParseException e) { + setStyleToIndicateCommandFailure(notificationButton); + } + } + + /** + * Handles the help button pressed event. + */ + @FXML + private void redirectToHelp() { + try { + setFocusButton(helpButton); + commandExecutor.execute("help"); + } catch (CommandException | ParseException e) { + setStyleToIndicateCommandFailure(helpButton); + } + } + + private void setFocusButton(Button focusButton) { + focusButton.requestFocus(); + } + + /** + * Sets the button style to indicate a failed command. + */ + private void setStyleToIndicateCommandFailure(Button button) { + ObservableList styleClass = button.getStyleClass(); + + if (styleClass.contains(ERROR_STYLE_CLASS)) { + return; + } + + styleClass.add(ERROR_STYLE_CLASS); + } + + /** + * Represents a function that can execute commands. + */ + @FunctionalInterface + public interface CommandExecutor { + /** + * Executes the command and returns the result. + * + * @see seedu.address.logic.Logic#execute(String) + */ + CommandResult execute(String commandText) throws CommandException, ParseException; + } +} diff --git a/src/main/java/seedu/address/ui/ReminderApplicationCard.java b/src/main/java/seedu/address/ui/ReminderApplicationCard.java new file mode 100644 index 00000000000..b060b61c35e --- /dev/null +++ b/src/main/java/seedu/address/ui/ReminderApplicationCard.java @@ -0,0 +1,98 @@ +package seedu.address.ui; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.contact.Contact; +import seedu.address.ui.popups.PopupEditInternship; + +/** + * A UI component that displays information of a {@code InternshipApplication} for ReminderWindow. + */ +public class ReminderApplicationCard extends UiPart { + private static final String FXML = "ReminderApplicationCard.fxml"; + public final InternshipApplication application; + private PopupEditInternship popupEditInternship; + private final MainWindow mainWindow; + + /** + * NoteList: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + @FXML + private HBox cardPane; + @FXML + private Label companyName; + @FXML + private Label jobTitle; + @FXML + private Label name; + @FXML + private Label id; + @FXML + private Label phone; + @FXML + private Label address; + @FXML + private Label email; + @FXML + private FlowPane tags; + @FXML + private Label internshipStatus; + @FXML + private Label interviewDate; + + + /** + * Creates a {@code ApplicationCard} with the given {@code InternshipApplication}. + */ + public ReminderApplicationCard(InternshipApplication application, MainWindow mainWindow) { + super(FXML); + this.application = application; + this.mainWindow = mainWindow; + this.popupEditInternship = new PopupEditInternship(mainWindow); + companyName.setText(application.getCompanyName().fullName); + jobTitle.setText(application.getJobTitle().fullName); + internshipStatus.setText(application.getStatus().name()); + Contact companyContact = application.getContact(); + if (companyContact != null) { + email.setText(companyContact.getEmail().value); + phone.setText(companyContact.getPhone().value); + email.setVisible(true); + phone.setVisible(true); + email.setManaged(true); + phone.setManaged(true); + } + InterviewDate interviewDateStr = application.getInterviewDate(); + if (interviewDateStr != null) { + interviewDate.setText(interviewDateStr.toString()); + interviewDate.setVisible(true); + interviewDate.setManaged(true); + } + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ApplicationCard)) { + return false; + } + + // state check + ApplicationCard card = (ApplicationCard) other; + return application.equals(card.application); + } + +} diff --git a/src/main/java/seedu/address/ui/ReminderWindow.java b/src/main/java/seedu/address/ui/ReminderWindow.java new file mode 100644 index 00000000000..4f3b292eaaf --- /dev/null +++ b/src/main/java/seedu/address/ui/ReminderWindow.java @@ -0,0 +1,80 @@ +package seedu.address.ui; + +import java.util.logging.Logger; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.stage.Stage; +import seedu.address.commons.core.LogsCenter; +import seedu.address.model.application.InternshipApplication; + +/** + * Controller for a reminder window. + */ +public class ReminderWindow extends UiPart { + + public static final String EMPTY_REMINDER = "There are no reminders at the moment!"; + private static final Logger logger = LogsCenter.getLogger(ReminderWindow.class); + + private static final String FXML = "ReminderWindow.fxml"; + + @FXML + private Label reminderApplication; + + /** + * Creates a {@code ReminderWindow} with the given {@code InternshipApplication} + */ + public ReminderWindow(Stage root, InternshipApplication application, MainWindow mainWindow) { + super(FXML, root); + if (application == null) { + reminderApplication.setText(EMPTY_REMINDER); + } else { + reminderApplication.setGraphic(new ReminderApplicationCard(application, mainWindow).getRoot()); + } + } + + /** + * Shows the reminder window. + * @throws IllegalStateException + *
    + *
  • + * if this method is called on a thread other than the JavaFX Application Thread. + *
  • + *
  • + * if this method is called during animation or layout processing. + *
  • + *
  • + * if this method is called on the primary stage. + *
  • + *
  • + * if {@code dialogStage} is already showing. + *
  • + *
+ */ + public void show() { + logger.fine("Showing the reminder."); + getRoot().show(); + getRoot().centerOnScreen(); + } + + /** + * Returns true if the reminder window is currently being shown. + */ + public boolean isShowing() { + return getRoot().isShowing(); + } + + /** + * Hides the reminder window. + */ + public void hide() { + getRoot().hide(); + } + + /** + * Focuses on the reminder window. + */ + public void focus() { + getRoot().requestFocus(); + } +} diff --git a/src/main/java/seedu/address/ui/ResultDialog.java b/src/main/java/seedu/address/ui/ResultDialog.java new file mode 100644 index 00000000000..d53c86220fd --- /dev/null +++ b/src/main/java/seedu/address/ui/ResultDialog.java @@ -0,0 +1,77 @@ +package seedu.address.ui; + +import javafx.animation.KeyFrame; +import javafx.animation.Timeline; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.scene.control.Alert; +import javafx.scene.control.ButtonType; +import javafx.scene.image.ImageView; +import javafx.scene.layout.Region; +import javafx.scene.paint.Color; +import javafx.stage.Modality; +import javafx.stage.Stage; +import javafx.stage.StageStyle; +import javafx.util.Duration; + +/** + * A UI component that is responsible for showing the result of user requests. + */ +public class ResultDialog { + + private static final ImageView warningImgPath = new ImageView("/images/info_icon.png"); + private static String resultMessage; + private static Stage primaryStage; + + /** + * Creates a {@code ResultDialog} with the given {@code String} in the given {@code Stage}. + */ + public static void displayResultDialog(String resultMsg, Stage stage) { + resultMessage = resultMsg; + primaryStage = stage; + showResult(); + } + + private static void showResult() { + final Alert result = new Alert(Alert.AlertType.INFORMATION); + + setDialogStyle(result); + + displayDialog(result); + } + + private static void setDialogStyle(Alert result) { + result.initOwner(primaryStage); + result.getDialogPane().getStylesheets().add("view/ResultDialog.css"); + result.initStyle(StageStyle.TRANSPARENT); + result.initModality(Modality.NONE); + + result.setTitle("Notification"); + result.setHeaderText(null); + result.setX(primaryStage.getX() + 0.35 * primaryStage.getWidth()); + result.setY(0.78 * primaryStage.getHeight()); + result.setWidth(600); + result.setHeight(250); + result.setGraphic(warningImgPath); + result.setContentText(resultMessage); + + result.getDialogPane().getScene().setFill(Color.TRANSPARENT); + result.getDialogPane().lookupButton(ButtonType.OK).setVisible(false); + result.getDialogPane().lookup(".button-bar").setVisible(false); + result.getDialogPane().setMinHeight(Region.USE_PREF_SIZE); + } + + private static void displayDialog(Alert result) { + Timeline timer = new Timeline(new KeyFrame(Duration.seconds(5), new EventHandler() { + @Override + public void handle(ActionEvent event) { + result.setResult(ButtonType.OK); + result.close(); + } + })); + + timer.setCycleCount(1); + timer.play(); + result.showAndWait(); + } +} diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/seedu/address/ui/ResultDisplay.java deleted file mode 100644 index 7d98e84eedf..00000000000 --- a/src/main/java/seedu/address/ui/ResultDisplay.java +++ /dev/null @@ -1,28 +0,0 @@ -package seedu.address.ui; - -import static java.util.Objects.requireNonNull; - -import javafx.fxml.FXML; -import javafx.scene.control.TextArea; -import javafx.scene.layout.Region; - -/** - * A ui for the status bar that is displayed at the header of the application. - */ -public class ResultDisplay extends UiPart { - - private static final String FXML = "ResultDisplay.fxml"; - - @FXML - private TextArea resultDisplay; - - public ResultDisplay() { - super(FXML); - } - - public void setFeedbackToUser(String feedbackToUser) { - requireNonNull(feedbackToUser); - resultDisplay.setText(feedbackToUser); - } - -} diff --git a/src/main/java/seedu/address/ui/StatsInformationCard.java b/src/main/java/seedu/address/ui/StatsInformationCard.java new file mode 100644 index 00000000000..ce7b61dfd4b --- /dev/null +++ b/src/main/java/seedu/address/ui/StatsInformationCard.java @@ -0,0 +1,77 @@ +package seedu.address.ui; + +import javafx.fxml.FXML; +import javafx.geometry.Pos; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import javafx.scene.text.TextAlignment; +import seedu.address.model.statstics.StatsInformation; + +/** + * A UI component that displays information of a {@code StatsInformation}. + */ +public class StatsInformationCard extends UiPart { + + private static final String FXML = "StatsInformationCard.fxml"; + + /** + * NoteList: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + + public final StatsInformation statsInformation; + + @FXML + private HBox cardPane; + @FXML + private Label description; + @FXML + private Label stats; + + /** + * Creates a {@code StatsInformationCard} with the given {@code StatsInformation} to display. + */ + public StatsInformationCard(StatsInformation statsInformation) { + super(FXML); + this.statsInformation = statsInformation; + updateStatsInformation(); + description.setText(statsInformation.getDescription()); + Number s = statsInformation.getStatsInformation(); + stats.setText(s.toString()); + + cardPane.setAlignment(Pos.CENTER); + description.setTextAlignment(TextAlignment.CENTER); + stats.setTextAlignment(TextAlignment.CENTER); + } + + /** + * Updates underlying statistics information. + */ + public void updateStatsInformation() { + statsInformation.updateStatsInformation(); + Number updatedStatsInformation = statsInformation.getStatsInformation(); + stats.setText(updatedStatsInformation.toString()); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof StatsInformationCard)) { + return false; + } + + // state check + StatsInformationCard card = (StatsInformationCard) other; + return this.statsInformation.equals(card.statsInformation); + } +} + diff --git a/src/main/java/seedu/address/ui/StatsInformationListPanel.java b/src/main/java/seedu/address/ui/StatsInformationListPanel.java new file mode 100644 index 00000000000..c4dac832a7d --- /dev/null +++ b/src/main/java/seedu/address/ui/StatsInformationListPanel.java @@ -0,0 +1,85 @@ +package seedu.address.ui; + +import java.util.logging.Logger; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.address.commons.core.LogsCenter; +import seedu.address.model.statstics.StatsInformation; +import seedu.address.model.statstics.StatsManager; + +/** + * Panel containing the list of statistics information. + */ +public class StatsInformationListPanel extends UiPart { + private static final String FXML = "StatsInformationListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(ApplicationListPanel.class); + + private final StatsManager statsManager; + + @FXML + private ListView statsInformationListView; + + @FXML + private HBox container; + + /** + * Creates a {@code StatdsInformationListPanel} with the given {@code StatsManager}. + */ + public StatsInformationListPanel(StatsManager statsManager) { + super(FXML); + this.statsManager = statsManager; + statsInformationListView.setItems(statsManager.getFilteredStatsInformations()); + statsInformationListView.setCellFactory(listView -> new StatsInformationListViewCell()); + + + statsInformationListView.setMaxWidth(Region.USE_COMPUTED_SIZE); + } + + public HBox getContainer() { + return container; + } + + /** + * Updates the UI display of the list of statistics information. + */ + public void updateDisplay() { + ObservableList l = statsManager.getStatsInformations(); + ObservableList tempList = FXCollections.observableArrayList(); + int len = l.size(); + for (int i = 0; i < len; i++) { + StatsInformation s = l.get(0); + l.remove(0); + tempList.add(s); + } + for (int i = 0; i < len; i++) { + l.add(tempList.get(i)); + } + statsInformationListView.setItems(statsManager.getFilteredStatsInformations()); + statsInformationListView.setCellFactory(listView -> new StatsInformationListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code StatsInformation} + * using a {@code StatsInformationCard}. + */ + class StatsInformationListViewCell extends ListCell { + @Override + protected void updateItem(StatsInformation statsInformation, boolean empty) { + super.updateItem(statsInformation, empty); + + if (empty || statsInformation == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new StatsInformationCard(statsInformation).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/seedu/address/ui/StatusBarFooter.java deleted file mode 100644 index b577f829423..00000000000 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ /dev/null @@ -1,28 +0,0 @@ -package seedu.address.ui; - -import java.nio.file.Path; -import java.nio.file.Paths; - -import javafx.fxml.FXML; -import javafx.scene.control.Label; -import javafx.scene.layout.Region; - -/** - * A ui for the status bar that is displayed at the footer of the application. - */ -public class StatusBarFooter extends UiPart { - - private static final String FXML = "StatusBarFooter.fxml"; - - @FXML - private Label saveLocationStatus; - - /** - * Creates a {@code StatusBarFooter} with the given {@code Path}. - */ - public StatusBarFooter(Path saveLocation) { - super(FXML); - saveLocationStatus.setText(Paths.get(".").resolve(saveLocation).toString()); - } - -} diff --git a/src/main/java/seedu/address/ui/Ui.java b/src/main/java/seedu/address/ui/Ui.java index 17aa0b494fe..e28dae7e990 100644 --- a/src/main/java/seedu/address/ui/Ui.java +++ b/src/main/java/seedu/address/ui/Ui.java @@ -7,7 +7,9 @@ */ public interface Ui { - /** Starts the UI (and the App). */ + /** + * Starts the UI (and the App). + */ void start(Stage primaryStage); } diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/address/ui/UiManager.java index fdf024138bc..bb14fffb03f 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/seedu/address/ui/UiManager.java @@ -20,7 +20,7 @@ public class UiManager implements Ui { public static final String ALERT_DIALOG_PANE_FIELD_ID = "alertDialogPane"; private static final Logger logger = LogsCenter.getLogger(UiManager.class); - private static final String ICON_APPLICATION = "/images/address_book_32.png"; + private static final String ICON_APPLICATION = "/images/intern_ease.png"; private Logic logic; private MainWindow mainWindow; @@ -42,6 +42,7 @@ public void start(Stage primaryStage) { try { mainWindow = new MainWindow(primaryStage, logic); mainWindow.show(); //This should be called before creating other UI parts + mainWindow.handleReminder(); mainWindow.fillInnerParts(); } catch (Throwable e) { diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/seedu/address/ui/UiPart.java index fc820e01a9c..db90b7bda57 100644 --- a/src/main/java/seedu/address/ui/UiPart.java +++ b/src/main/java/seedu/address/ui/UiPart.java @@ -9,7 +9,7 @@ import seedu.address.MainApp; /** - * Represents a distinct part of the UI. e.g. Windows, dialogs, panels, status bars, etc. + * Represents a distinct part of the UI. e.g. Windows, dialogs, panels, etc. * It contains a scene graph with a root node of type {@code T}. */ public abstract class UiPart { @@ -45,6 +45,7 @@ public UiPart(URL fxmlFileUrl, T root) { /** * Constructs a UiPart with the specified FXML file within {@link #FXML_FILE_FOLDER} and root object. + * * @see #UiPart(URL, T) */ public UiPart(String fxmlFileName, T root) { @@ -60,6 +61,7 @@ public T getRoot() { /** * Loads the object hierarchy from a FXML document. + * * @param location Location of the FXML document. * @param root Specifies the root of the object hierarchy. */ diff --git a/src/main/java/seedu/address/ui/ViewContentPanel.java b/src/main/java/seedu/address/ui/ViewContentPanel.java new file mode 100644 index 00000000000..b6fb10ce7b0 --- /dev/null +++ b/src/main/java/seedu/address/ui/ViewContentPanel.java @@ -0,0 +1,259 @@ +package seedu.address.ui; + +import java.awt.Desktop; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Comparator; +import java.util.Set; +import java.util.logging.Logger; + +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.geometry.Pos; +import javafx.scene.control.Hyperlink; +import javafx.scene.control.Label; +import javafx.scene.control.ListView; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.Pane; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import javafx.scene.text.TextFlow; +import seedu.address.commons.core.LogsCenter; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipApplicationAttribute; +import seedu.address.model.documents.Documents; +import seedu.address.model.task.InternshipTodo; +import seedu.address.model.task.Note; + +/** + * Panel containing the detailed content of selected internship application. + */ +public class ViewContentPanel extends UiPart { + + private static final String FXML = "ViewContentPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(ViewContentPanel.class); + + @FXML + private VBox container; + @FXML + private Pane contentContainer; + @FXML + private VBox headingContainer; + + @FXML + private AnchorPane emptyAnchorPane; + @FXML + private Label emptyLabel; + + @FXML + private Text title; + @FXML + private TextFlow titleTextFlow; + @FXML + private Text description; + @FXML + private Label sideLabel; + @FXML + private ListView pane; + + /** + * Creates a {@code ViewContentPanel}. + */ + public ViewContentPanel() { + super(FXML); + emptyLabel.setMaxWidth(Double.MAX_VALUE); + AnchorPane.setLeftAnchor(emptyLabel, 0.0); + AnchorPane.setRightAnchor(emptyLabel, 0.0); + AnchorPane.setTopAnchor(emptyLabel, 0.0); + AnchorPane.setBottomAnchor(emptyLabel, 0.0); + emptyLabel.setAlignment(Pos.CENTER); + } + + /** + * Getter for the underlying container. + */ + public VBox getContainer() { + return container; + } + + private void addLabel(String labelString, InternshipApplicationAttribute attribute) { + if (attribute != null && attribute.toString() != null && !attribute.toString().isBlank()) { + Label label = new Label(labelString + ": " + attribute.toString()); + label.getStyleClass().add("cell_small_label"); + label.setWrapText(true); + label.maxWidthProperty().bind(pane.widthProperty()); + pane.getItems().add(label); + } + } + + private void addFlowPane(String labelString, Set attributesSet) { + if (attributesSet.size() > 0) { + VBox flowPaneContainer = new VBox(); + flowPaneContainer.setSpacing(3); + Label label = new Label(labelString + ":"); + label.getStyleClass().add("cell_small_label"); + VBox vbox = new VBox(2); + vbox.getStyleClass().add("multiple-items"); + vbox.getChildren().clear(); + attributesSet.stream() + .sorted(Comparator.comparing(InternshipApplicationAttribute::toString)) + .forEach(review -> vbox.getChildren().add(new Label(review.toString()))); + flowPaneContainer.getChildren().addAll(label, vbox); + pane.getItems().add(flowPaneContainer); + } + } + + private void displayEmptyPanel() { + emptyAnchorPane.setVisible(true); + emptyAnchorPane.setManaged(true); + contentContainer.setVisible(false); + contentContainer.setManaged(false); + } + + private void showActualPanel() { + emptyAnchorPane.setVisible(false); + emptyAnchorPane.setManaged(false); + contentContainer.setVisible(true); + contentContainer.setManaged(true); + } + + private void showCompulsoryAttributes(InternshipApplication internshipApplication) { + title.setText(internshipApplication.getCompanyName().fullName); + description.setText(internshipApplication.getJobTitle().fullName); + if (!internshipApplication.isArchived()) { + sideLabel.setText(internshipApplication.getStatus().label); + sideLabel.setStyle("-fx-background-color: " + internshipApplication.getStatus().labelColour); + } else { + sideLabel.setText("Archived"); + sideLabel.setStyle("-fx-background-color: #808080"); + } + } + + private void showOptionalAttributes(InternshipApplication internshipApplication) { + pane.getItems().clear(); + if (internshipApplication.getContact() != null) { + addLabel("Company Phone", internshipApplication.getContact().getPhone()); + addLabel("Company Email", internshipApplication.getContact().getEmail()); + } + if (internshipApplication.getDocuments() != null) { + addDocuments(internshipApplication.getDocuments()); + } + addLabel("Interview Date", internshipApplication.getInterviewDate()); + addLabel("Location", internshipApplication.getLocation()); + addLabel("Salary", internshipApplication.getSalary()); + addLabel("Rating", internshipApplication.getRating()); + addFlowPane("Reviews", internshipApplication.getReviews()); + addFlowPane("Programming Languages", internshipApplication.getProgrammingLanguages()); + addFlowPane("Qualifications", internshipApplication.getQualifications()); + addFlowPane("Notes", internshipApplication.getNotes()); + addFlowPane("Reflections", internshipApplication.getReflections()); + } + + private void addDocuments(Documents documents) { + FlowPane resumePane = new FlowPane(); + resumePane.getStyleClass().add("link-items"); + Label resumeLabel = new Label("Resume Link: "); + resumeLabel.getStyleClass().add("cell_small_label"); + Hyperlink resumeHyperlink = new Hyperlink(documents.getResumeLink().value); + setHyperlink(resumePane, resumeLabel, resumeHyperlink); + + FlowPane coverLetterPane = new FlowPane(); + coverLetterPane.getStyleClass().add("link-items"); + Label coverLetterLabel = new Label("Cover Letter Link: "); + coverLetterLabel.getStyleClass().add("cell_small_label"); + Hyperlink coverLetterLink = new Hyperlink(documents.getCoverLetterLink().value); + setHyperlink(coverLetterPane, coverLetterLabel, coverLetterLink); + + pane.getItems().addAll(resumePane, coverLetterPane); + } + + private void setHyperlink(FlowPane flowPane, Label label, Hyperlink hyperlink) { + hyperlink.getStyleClass().add("link-item"); + hyperlink.setOnAction(new EventHandler() { + @Override + public void handle(ActionEvent t) { + try { + Desktop.getDesktop().browse(new URI(hyperlink.getText())); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + }); + flowPane.getChildren().addAll(label, hyperlink); + } + + private void addInternshipTodoDetails(InternshipTodo internshipTodo) { + Label deadline = new Label("Deadline: " + internshipTodo.getDeadline()); + deadline.getStyleClass().add("cell_small_label"); + deadline.maxWidthProperty().bind(pane.widthProperty()); + + Label createdOn = new Label("Created on: " + internshipTodo.getDateString()); + createdOn.getStyleClass().add("cell_small_label"); + createdOn.maxWidthProperty().bind(pane.widthProperty()); + + if (internshipTodo.getNote() != null) { + Label note = new Label("Note: " + internshipTodo.getNote()); + note.getStyleClass().add("cell_small_label"); + note.maxWidthProperty().bind(pane.widthProperty()); + pane.getItems().addAll(deadline, note, createdOn); + } else { + pane.getItems().addAll(deadline, createdOn); + } + } + + /** + * Clears the {@code ViewContentPanel}. + */ + public void clearPanel() { + displayEmptyPanel(); + } + + public void setInternshipApplication(InternshipApplication internshipApplication) { + if (internshipApplication != null) { + showActualPanel(); + showCompulsoryAttributes(internshipApplication); + showOptionalAttributes(internshipApplication); + } else { + displayEmptyPanel(); + } + } + + /** + * Sets and displays the particulars of the selected {@code InternshipTodo} in the {@code ViewContentPanel}. + */ + public void setInternshipTodo(InternshipTodo internshipTodo) { + if (internshipTodo != null) { + showActualPanel(); + title.setText(internshipTodo.getInternshipTitle().fullName); + description.setText(internshipTodo.getJobTitle().fullName); + sideLabel.setText(internshipTodo.getType().name()); + sideLabel.setStyle("-fx-background-color: #53A3D8"); + pane.getItems().clear(); + addInternshipTodoDetails(internshipTodo); + } else { + displayEmptyPanel(); + } + } + + /** + * Sets and displays the particulars of the selected {@code Note} in the {@code ViewContentPanel}. + */ + public void setNote(Note note) { + if (note != null) { + showActualPanel(); + title.setText(note.getNote().content); + description.setText(note.getDateString()); + sideLabel.setText(note.getType().name()); + sideLabel.setStyle("-fx-background-color: #588157"); + pane.getItems().clear(); + } else { + displayEmptyPanel(); + } + } +} diff --git a/src/main/java/seedu/address/ui/popups/ControlBox.java b/src/main/java/seedu/address/ui/popups/ControlBox.java new file mode 100644 index 00000000000..de7cf57ecbf --- /dev/null +++ b/src/main/java/seedu/address/ui/popups/ControlBox.java @@ -0,0 +1,36 @@ +package seedu.address.ui.popups; + +import javafx.fxml.FXML; +import javafx.scene.layout.HBox; +import seedu.address.ui.MainWindow; +import seedu.address.ui.UiPart; + +/** + * Hbox containing button + */ +public class ControlBox extends UiPart { + private static final String FXML = "ControlBox.fxml"; + private final PopupAddInternship popupInternship; + private final MainWindow mainWindow; + + /** + * Creates a {@code ControlBox}. + */ + public ControlBox(MainWindow mainWindow) { + super(FXML); + this.mainWindow = mainWindow; + popupInternship = new PopupAddInternship(mainWindow); + } + + /** + * Handles the add internship button clicked event. + */ + @FXML + private void handleAddInternshipClicked() { + if (!popupInternship.isShowing()) { + popupInternship.show(); + } else { + popupInternship.focus(); + } + } +} diff --git a/src/main/java/seedu/address/ui/popups/PopupAddInternship.java b/src/main/java/seedu/address/ui/popups/PopupAddInternship.java new file mode 100644 index 00000000000..82ed248365e --- /dev/null +++ b/src/main/java/seedu/address/ui/popups/PopupAddInternship.java @@ -0,0 +1,392 @@ +package seedu.address.ui.popups; + +import java.util.logging.Logger; + +import javafx.fxml.FXML; +import javafx.scene.control.TextField; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.AddCommand; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.parser.CliSyntax; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.ui.MainWindow; +import seedu.address.ui.UiPart; + +/** + * Pop up for add internship + */ + +public class PopupAddInternship extends UiPart { + private static MainWindow mainWindow; + private static final Logger logger = LogsCenter.getLogger(PopupAddInternship.class); + private static final String FXML = "PopupAddInternship.fxml"; + + private static final String ILLEGAL_REGEX = ".*[a-z]/.*"; + + @FXML + private TextField companyName; + @FXML + private TextField jobTitle; + @FXML + private TextField place; + @FXML + private TextField salary; + @FXML + private TextField rating; + @FXML + private VBox qualificationVBox; + @FXML + private TextField qualification; + @FXML + private VBox programmingLanguageVBox; + @FXML + private TextField programmingLanguage; + @FXML + private VBox reviewVBox; + @FXML + private TextField review; + @FXML + private VBox noteVBox; + @FXML + private TextField note; + @FXML + private VBox reflectionVBox; + @FXML + private TextField reflection; + + /** + * Creates a new popup add internship. + */ + public PopupAddInternship(Stage root) { + super(FXML, root); + } + /** + * Creates a new popup add internship. + */ + public PopupAddInternship(MainWindow mainWindow) { + this(new Stage()); + this.mainWindow = mainWindow; + mainWindow.setPopupAddInternship(this); + } + + /** + * Shows the help window. + * @throws IllegalStateException + *
    + *
  • + * if this method is called on a thread other than the JavaFX Application Thread. + *
  • + *
  • + * if this method is called during animation or layout processing. + *
  • + *
  • + * if this method is called on the primary stage. + *
  • + *
  • + * if {@code dialogStage} is already showing. + *
  • + *
+ */ + public void show() { + logger.fine("Showing add internship popup"); + cleanNode(); + getRoot().show(); + getRoot().centerOnScreen(); + } + + /** + * Clean the text field and child of javaFX node. + */ + private void cleanNode() { + companyName.setText(""); + jobTitle.setText(""); + place.setText(""); + salary.setText(""); + rating.setText(""); + + qualification.setText(""); + int qVBoxSize = qualificationVBox.getChildren().size(); + if (qVBoxSize > 2) { + qualificationVBox.getChildren().remove(2, qVBoxSize); + } + + programmingLanguage.setText(""); + int plVBoxSize = programmingLanguageVBox.getChildren().size(); + if (plVBoxSize > 2) { + programmingLanguageVBox.getChildren().remove(2, plVBoxSize); + } + + review.setText(""); + int rVBoxSize = reviewVBox.getChildren().size(); + if (rVBoxSize > 2) { + reviewVBox.getChildren().remove(2, rVBoxSize); + } + + note.setText(""); + int nVBoxSize = noteVBox.getChildren().size(); + if (nVBoxSize > 2) { + noteVBox.getChildren().remove(2, nVBoxSize); + } + + reflection.setText(""); + int rfVBoxSize = reflectionVBox.getChildren().size(); + if (rfVBoxSize > 2) { + reflectionVBox.getChildren().remove(2, rfVBoxSize); + } + } + + /** + * Returns true if the popup window is currently being shown. + */ + public boolean isShowing() { + return getRoot().isShowing(); + } + + /** + * Hides the popup window. + */ + public void hide() { + getRoot().hide(); + } + + + /** + * Focuses on the popup window. + */ + public void focus() { + getRoot().requestFocus(); + } + + + /** + * Validate user input. + */ + private boolean isValid() { + if (companyName.getText().matches(ILLEGAL_REGEX) + || jobTitle.getText().matches(ILLEGAL_REGEX) + || place.getText().matches(ILLEGAL_REGEX) + || salary.getText().matches(ILLEGAL_REGEX) + || rating.getText().matches(ILLEGAL_REGEX)) { + return false; + } + + for (int i = 1; i < qualificationVBox.getChildren().size(); i += 1) { + TextField q = (TextField) qualificationVBox.getChildren().get(i); + if (q.getText().matches(ILLEGAL_REGEX)) { + return false; + } + } + + for (int i = 1; i < programmingLanguageVBox.getChildren().size(); i += 1) { + TextField pl = (TextField) programmingLanguageVBox.getChildren().get(i); + if (pl.getText().matches(ILLEGAL_REGEX)) { + return false; + } + } + + for (int i = 1; i < reviewVBox.getChildren().size(); i += 1) { + TextField r = (TextField) reviewVBox.getChildren().get(i); + if (r.getText().matches(ILLEGAL_REGEX)) { + return false; + } + } + + for (int i = 1; i < noteVBox.getChildren().size(); i += 1) { + TextField n = (TextField) noteVBox.getChildren().get(i); + if (n.getText().matches(ILLEGAL_REGEX)) { + return false; + } + } + + for (int i = 1; i < reflectionVBox.getChildren().size(); i += 1) { + TextField rf = (TextField) reflectionVBox.getChildren().get(i); + if (rf.getText().matches(ILLEGAL_REGEX)) { + return false; + } + } + return true; + } + + /** + * Constructing command text. + */ + private String makeCommandText() { + String commandText = AddCommand.COMMAND_WORD + + " " + CliSyntax.PREFIX_COMPANY_NAME.getPrefix() + + companyName.getText() + + " " + CliSyntax.PREFIX_JOB_TITLE.getPrefix() + + jobTitle.getText(); + + if (!place.getText().isBlank()) { + commandText += " " + CliSyntax.PREFIX_LOCATION.getPrefix() + place.getText(); + } + + if (!salary.getText().isBlank()) { + commandText += " " + CliSyntax.PREFIX_SALARY.getPrefix() + salary.getText(); + } + + if (!rating.getText().isBlank()) { + commandText += " " + CliSyntax.PREFIX_RATING.getPrefix() + rating.getText(); + } + + for (int i = 1; i < qualificationVBox.getChildren().size(); i += 1) { + TextField q = (TextField) qualificationVBox.getChildren().get(i); + if (!q.getText().isBlank()) { + commandText += " " + CliSyntax.PREFIX_QUALIFICATION.getPrefix() + q.getText(); + } + } + + for (int i = 1; i < programmingLanguageVBox.getChildren().size(); i += 1) { + TextField pl = (TextField) programmingLanguageVBox.getChildren().get(i); + if (!pl.getText().isBlank()) { + commandText += " " + CliSyntax.PREFIX_PROGRAMMING_LANGUAGE.getPrefix() + pl.getText(); + } + } + + for (int i = 1; i < reviewVBox.getChildren().size(); i += 1) { + TextField r = (TextField) reviewVBox.getChildren().get(i); + if (!r.getText().isBlank()) { + commandText += " " + CliSyntax.PREFIX_REVIEW.getPrefix() + r.getText(); + } + } + + for (int i = 1; i < noteVBox.getChildren().size(); i += 1) { + TextField n = (TextField) noteVBox.getChildren().get(i); + if (!n.getText().isBlank()) { + commandText += " " + CliSyntax.PREFIX_NOTE.getPrefix() + n.getText(); + } + } + + for (int i = 1; i < reflectionVBox.getChildren().size(); i += 1) { + TextField rf = (TextField) reflectionVBox.getChildren().get(i); + if (!rf.getText().isBlank()) { + commandText += " " + CliSyntax.PREFIX_REFLECTION.getPrefix() + rf.getText(); + } + } + return commandText; + } + + /** + * Handles the add internship button clicked event. + */ + @FXML + private void handleAddInternship() { + if (!isValid()) { + return; + } + + String commandText = makeCommandText(); + + try { + mainWindow.executeCommand(commandText); + } catch (CommandException e) { + e.printStackTrace(); + } catch (ParseException e) { + e.printStackTrace(); + } finally { + hide(); + } + } + + /** + * Handles the delete qualification button clicked event. + */ + @FXML + private void handleDelQualification() { + if (qualificationVBox.getChildren().size() > 2) { + qualificationVBox.getChildren().remove(qualificationVBox.getChildren().size() - 1); + } + } + + /** + * Handles the delete programming language button clicked event. + */ + @FXML + private void handleDelProgrammingLanguage() { + if (programmingLanguageVBox.getChildren().size() > 2) { + programmingLanguageVBox.getChildren().remove(programmingLanguageVBox.getChildren().size() - 1); + } + } + + /** + * Handles the delete review button clicked event. + */ + @FXML + private void handleDelReview() { + if (reviewVBox.getChildren().size() > 2) { + reviewVBox.getChildren().remove(reviewVBox.getChildren().size() - 1); + } + } + + /** + * Handles the delete note button clicked event. + */ + @FXML + private void handleDelNote() { + if (noteVBox.getChildren().size() > 2) { + noteVBox.getChildren().remove(noteVBox.getChildren().size() - 1); + } + } + + /** + * Handles the delete reflection button clicked event. + */ + @FXML + private void handleDelReflection() { + if (reflectionVBox.getChildren().size() > 2) { + reflectionVBox.getChildren().remove(reflectionVBox.getChildren().size() - 1); + } + } + + /** + * Handles the add qualification button clicked event. + */ + @FXML + private void handleAddQualification() { + TextField moreQualification = new TextField(); + moreQualification.setPromptText("More qualification..."); + qualificationVBox.getChildren().add(moreQualification); + } + + /** + * Handles the add programming language button clicked event. + */ + @FXML + private void handleAddProgrammingLanguage() { + TextField morePL = new TextField(); + morePL.setPromptText("More programming language..."); + programmingLanguageVBox.getChildren().add(morePL); + } + + /** + * Handles the add review button clicked event. + */ + @FXML + private void handleAddReview() { + TextField moreReview = new TextField(); + moreReview.setPromptText("More review..."); + reviewVBox.getChildren().add(moreReview); + } + + /** + * Handles the add note button clicked event. + */ + @FXML + private void handleAddNote() { + TextField moreNote = new TextField(); + moreNote.setPromptText("More note..."); + noteVBox.getChildren().add(moreNote); + } + + /** + * Handles the add reflection button clicked event. + */ + @FXML + private void handleAddReflection() { + TextField moreReflection = new TextField(); + moreReflection.setPromptText("More reflection..."); + reflectionVBox.getChildren().add(moreReflection); + } +} diff --git a/src/main/java/seedu/address/ui/popups/PopupEditInternship.java b/src/main/java/seedu/address/ui/popups/PopupEditInternship.java new file mode 100644 index 00000000000..c8b9be25ba6 --- /dev/null +++ b/src/main/java/seedu/address/ui/popups/PopupEditInternship.java @@ -0,0 +1,513 @@ +package seedu.address.ui.popups; + +import java.util.logging.Logger; + +import javafx.fxml.FXML; +import javafx.scene.control.TextField; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.EditCommand; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.parser.CliSyntax; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.application.InternshipApplication; +import seedu.address.ui.MainWindow; +import seedu.address.ui.UiPart; + +/** + * Pop up for add internship + */ + +public class PopupEditInternship extends UiPart { + private static MainWindow mainWindow; + private static final Logger logger = LogsCenter.getLogger(PopupEditInternship.class); + private static final String FXML = "PopupEditInternship.fxml"; + + private static final String ILLEGAL_REGEX = ".*[a-z]/.*"; + private int index; + private InternshipApplication internship; + + @FXML + private TextField companyName; + @FXML + private TextField jobTitle; + @FXML + private TextField place; + @FXML + private TextField salary; + @FXML + private TextField rating; + @FXML + private VBox qualificationVBox; + @FXML + private TextField qualification; + @FXML + private VBox programmingLanguageVBox; + @FXML + private TextField programmingLanguage; + @FXML + private VBox reviewVBox; + @FXML + private TextField review; + @FXML + private VBox noteVBox; + @FXML + private TextField note; + @FXML + private VBox reflectionVBox; + @FXML + private TextField reflection; + + /** + * Creates a new popup add internship. + */ + public PopupEditInternship(Stage root) { + super(FXML, root); + } + /** + * Creates a new popup add internship. + */ + public PopupEditInternship(MainWindow mainWindow) { + this(new Stage()); + this.mainWindow = mainWindow; + mainWindow.setPopupEditInternships(this); + } + + /** + * Shows the help window. + * @throws IllegalStateException + *
    + *
  • + * if this method is called on a thread other than the JavaFX Application Thread. + *
  • + *
  • + * if this method is called during animation or layout processing. + *
  • + *
  • + * if this method is called on the primary stage. + *
  • + *
  • + * if {@code dialogStage} is already showing. + *
  • + *
+ */ + public void show(int index, InternshipApplication internship) { + logger.fine("Showing edit internship popup"); + this.index = index; + this.internship = internship; + cleanNode(); + populateNode(); + getRoot().show(); + getRoot().centerOnScreen(); + } + + /** + * Clean the text field and child of javaFX node. + */ + private void cleanNode() { + companyName.setText(""); + jobTitle.setText(""); + place.setText(""); + salary.setText(""); + rating.setText(""); + + qualification.setText(""); + int qVBoxSize = qualificationVBox.getChildren().size(); + if (qVBoxSize > 2) { + qualificationVBox.getChildren().remove(2, qVBoxSize); + } + + programmingLanguage.setText(""); + int plVBoxSize = programmingLanguageVBox.getChildren().size(); + if (plVBoxSize > 2) { + programmingLanguageVBox.getChildren().remove(2, plVBoxSize); + } + + review.setText(""); + int rVBoxSize = reviewVBox.getChildren().size(); + if (rVBoxSize > 2) { + reviewVBox.getChildren().remove(2, rVBoxSize); + } + + note.setText(""); + int nVBoxSize = noteVBox.getChildren().size(); + if (nVBoxSize > 2) { + noteVBox.getChildren().remove(2, nVBoxSize); + } + + reflection.setText(""); + int rfVBoxSize = reflectionVBox.getChildren().size(); + if (rfVBoxSize > 2) { + reflectionVBox.getChildren().remove(2, rfVBoxSize); + } + } + + /** + * Populate the text field and child of javaFX node with initial internship values. + */ + private void populateNode() { + if (internship.getCompanyName() != null && internship.getCompanyName().fullName != null) { + companyName.setText(internship.getCompanyName().fullName); + } + if (internship.getJobTitle() != null && internship.getJobTitle().fullName != null) { + jobTitle.setText(internship.getJobTitle().fullName); + } + if (internship.getLocation() != null && internship.getLocation().value != null) { + place.setText(internship.getLocation().value); + } + if (internship.getSalary() != null && internship.getSalary().value != null) { + salary.setText(internship.getSalary().value); + } + if (internship.getRating() != null && internship.getRating().value != null) { + rating.setText(internship.getRating().value); + } + + if (!internship.getQualifications().isEmpty()) { + qualificationVBox.getChildren().remove(1); + internship.getQualifications().stream() + .forEach(q -> qualificationVBox.getChildren().add(new TextField(q.value))); + } + + if (!internship.getProgrammingLanguages().isEmpty()) { + programmingLanguageVBox.getChildren().remove(1); + internship.getProgrammingLanguages().stream() + .forEach(plg -> programmingLanguageVBox.getChildren().add(new TextField(plg.value))); + } + + if (!internship.getReviews().isEmpty()) { + reviewVBox.getChildren().remove(1); + internship.getReviews().stream() + .forEach(r -> reviewVBox.getChildren().add(new TextField(r.value))); + } + + if (!internship.getNotes().isEmpty()) { + noteVBox.getChildren().remove(1); + internship.getNotes().stream() + .forEach(n -> noteVBox.getChildren().add(new TextField(n.value))); + } + + if (!internship.getReflections().isEmpty()) { + reflectionVBox.getChildren().remove(1); + internship.getReflections().stream() + .forEach(rf -> reflectionVBox.getChildren().add(new TextField(rf.value))); + } + } + + /** + * Returns true if the popup window is currently being shown. + */ + public boolean isShowing() { + return getRoot().isShowing(); + } + + /** + * Hides the popup window. + */ + public void hide() { + getRoot().hide(); + } + + + /** + * Focuses on the popup window. + */ + public void focus() { + getRoot().requestFocus(); + } + + + /** + * Validate user input. + */ + private boolean isValid() { + if (companyName.getText().matches(ILLEGAL_REGEX) + || jobTitle.getText().matches(ILLEGAL_REGEX) + || place.getText().matches(ILLEGAL_REGEX) + || salary.getText().matches(ILLEGAL_REGEX) + || rating.getText().matches(ILLEGAL_REGEX)) { + return false; + } + + for (int i = 1; i < qualificationVBox.getChildren().size(); i += 1) { + TextField q = (TextField) qualificationVBox.getChildren().get(i); + if (q.getText().matches(ILLEGAL_REGEX)) { + return false; + } + } + + for (int i = 1; i < programmingLanguageVBox.getChildren().size(); i += 1) { + TextField pl = (TextField) programmingLanguageVBox.getChildren().get(i); + if (pl.getText().matches(ILLEGAL_REGEX)) { + return false; + } + } + + for (int i = 1; i < reviewVBox.getChildren().size(); i += 1) { + TextField r = (TextField) reviewVBox.getChildren().get(i); + if (r.getText().matches(ILLEGAL_REGEX)) { + return false; + } + } + + for (int i = 1; i < noteVBox.getChildren().size(); i += 1) { + TextField n = (TextField) noteVBox.getChildren().get(i); + if (n.getText().matches(ILLEGAL_REGEX)) { + return false; + } + } + + for (int i = 1; i < reflectionVBox.getChildren().size(); i += 1) { + TextField rf = (TextField) reflectionVBox.getChildren().get(i); + if (rf.getText().matches(ILLEGAL_REGEX)) { + return false; + } + } + return true; + } + + /** + * Constructing command text. + */ + private String makeCommandText() { + String commandText = EditCommand.COMMAND_WORD + " " + index; + + if (!companyName.getText().isBlank()) { + commandText += " " + CliSyntax.PREFIX_COMPANY_NAME.getPrefix() + companyName.getText(); + } + + if (!jobTitle.getText().isBlank()) { + commandText += " " + CliSyntax.PREFIX_JOB_TITLE.getPrefix() + jobTitle.getText(); + } + + commandText += " " + CliSyntax.PREFIX_LOCATION.getPrefix() + place.getText(); + commandText += " " + CliSyntax.PREFIX_SALARY.getPrefix() + salary.getText(); + commandText += " " + CliSyntax.PREFIX_RATING.getPrefix() + rating.getText(); + + + int validQualification = 0; + for (int i = 1; i < qualificationVBox.getChildren().size(); i += 1) { + TextField q = (TextField) qualificationVBox.getChildren().get(i); + if (!q.getText().isBlank()) { + validQualification += 1; + commandText += " " + CliSyntax.PREFIX_QUALIFICATION.getPrefix() + q.getText(); + } + } + + if (validQualification == 0) { + commandText += " " + CliSyntax.PREFIX_QUALIFICATION.getPrefix(); + } else { + for (int i = 1; i < qualificationVBox.getChildren().size(); i += 1) { + TextField q = (TextField) qualificationVBox.getChildren().get(i); + if (!q.getText().isBlank()) { + commandText += " " + CliSyntax.PREFIX_QUALIFICATION.getPrefix() + q.getText(); + } + } + } + + int validProgrammingLanguage = 0; + for (int i = 1; i < programmingLanguageVBox.getChildren().size(); i += 1) { + TextField p = (TextField) programmingLanguageVBox.getChildren().get(i); + if (!p.getText().isBlank()) { + validProgrammingLanguage += 1; + commandText += " " + CliSyntax.PREFIX_PROGRAMMING_LANGUAGE.getPrefix() + p.getText(); + } + } + + if (validProgrammingLanguage == 0) { + commandText += " " + CliSyntax.PREFIX_PROGRAMMING_LANGUAGE.getPrefix(); + } else { + for (int i = 1; i < programmingLanguageVBox.getChildren().size(); i += 1) { + TextField p = (TextField) programmingLanguageVBox.getChildren().get(i); + if (!p.getText().isBlank()) { + commandText += " " + CliSyntax.PREFIX_PROGRAMMING_LANGUAGE.getPrefix() + p.getText(); + } + } + } + + int validReview = 0; + for (int i = 1; i < reviewVBox.getChildren().size(); i += 1) { + TextField r = (TextField) reviewVBox.getChildren().get(i); + if (!r.getText().isBlank()) { + validReview += 1; + commandText += " " + CliSyntax.PREFIX_REVIEW.getPrefix() + r.getText(); + } + } + + if (validReview == 0) { + commandText += " " + CliSyntax.PREFIX_REVIEW.getPrefix(); + } else { + for (int i = 1; i < reviewVBox.getChildren().size(); i += 1) { + TextField r = (TextField) reviewVBox.getChildren().get(i); + if (!r.getText().isBlank()) { + commandText += " " + CliSyntax.PREFIX_REVIEW.getPrefix() + r.getText(); + } + } + } + + int validNote = 0; + for (int i = 1; i < noteVBox.getChildren().size(); i += 1) { + TextField n = (TextField) noteVBox.getChildren().get(i); + if (!n.getText().isBlank()) { + validNote += 1; + commandText += " " + CliSyntax.PREFIX_NOTE.getPrefix() + n.getText(); + } + } + + if (validNote == 0) { + commandText += " " + CliSyntax.PREFIX_NOTE.getPrefix(); + } else { + for (int i = 1; i < noteVBox.getChildren().size(); i += 1) { + TextField n = (TextField) noteVBox.getChildren().get(i); + if (!n.getText().isBlank()) { + commandText += " " + CliSyntax.PREFIX_NOTE.getPrefix() + n.getText(); + } + } + } + + int validReflection = 0; + for (int i = 1; i < reflectionVBox.getChildren().size(); i += 1) { + TextField rf = (TextField) reflectionVBox.getChildren().get(i); + if (!rf.getText().isBlank()) { + validReflection += 1; + commandText += " " + CliSyntax.PREFIX_REFLECTION.getPrefix() + rf.getText(); + } + } + + if (validReflection == 0) { + commandText += " " + CliSyntax.PREFIX_REFLECTION.getPrefix(); + } else { + for (int i = 1; i < reflectionVBox.getChildren().size(); i += 1) { + TextField rf = (TextField) reflectionVBox.getChildren().get(i); + if (!rf.getText().isBlank()) { + commandText += " " + CliSyntax.PREFIX_REFLECTION.getPrefix() + rf.getText(); + } + } + } + + return commandText; + } + + /** + * Handles the add internship button clicked event. + */ + @FXML + private void handleEditInternship() { + if (!isValid()) { + return; + } + + String commandText = makeCommandText(); + + try { + System.out.println(commandText); + mainWindow.executeCommand(commandText); + } catch (CommandException e) { + e.printStackTrace(); + } catch (ParseException e) { + e.printStackTrace(); + } finally { + hide(); + } + } + + /** + * Handles the add qualification button clicked event. + */ + @FXML + private void handleAddQualification() { + TextField moreQualification = new TextField(); + moreQualification.setPromptText("More qualification..."); + qualificationVBox.getChildren().add(moreQualification); + } + + /** + * Handles the add programming language button clicked event. + */ + @FXML + private void handleAddProgrammingLanguage() { + TextField morePL = new TextField(); + morePL.setPromptText("More programming language..."); + programmingLanguageVBox.getChildren().add(morePL); + } + + /** + * Handles the add review button clicked event. + */ + @FXML + private void handleAddReview() { + TextField moreReview = new TextField(); + moreReview.setPromptText("More review..."); + reviewVBox.getChildren().add(moreReview); + } + + /** + * Handles the add note button clicked event. + */ + @FXML + private void handleAddNote() { + TextField moreNote = new TextField(); + moreNote.setPromptText("More note..."); + noteVBox.getChildren().add(moreNote); + } + + /** + * Handles the add reflection button clicked event. + */ + @FXML + private void handleAddReflection() { + TextField moreReflection = new TextField(); + moreReflection.setPromptText("More reflection..."); + reflectionVBox.getChildren().add(moreReflection); + } + + /** + * Handles the delete qualification button clicked event. + */ + @FXML + private void handleDelQualification() { + if (qualificationVBox.getChildren().size() > 2) { + qualificationVBox.getChildren().remove(qualificationVBox.getChildren().size() - 1); + } + } + + /** + * Handles the delete programming language button clicked event. + */ + @FXML + private void handleDelProgrammingLanguage() { + if (programmingLanguageVBox.getChildren().size() > 2) { + programmingLanguageVBox.getChildren().remove(programmingLanguageVBox.getChildren().size() - 1); + } + } + + /** + * Handles the delete review button clicked event. + */ + @FXML + private void handleDelReview() { + if (reviewVBox.getChildren().size() > 2) { + reviewVBox.getChildren().remove(reviewVBox.getChildren().size() - 1); + } + } + + /** + * Handles the delete note button clicked event. + */ + @FXML + private void handleDelNote() { + if (noteVBox.getChildren().size() > 2) { + noteVBox.getChildren().remove(noteVBox.getChildren().size() - 1); + } + } + + /** + * Handles the delete reflection button clicked event. + */ + @FXML + private void handleDelReflection() { + if (reflectionVBox.getChildren().size() > 2) { + reflectionVBox.getChildren().remove(reflectionVBox.getChildren().size() - 1); + } + } +} diff --git a/src/main/java/seedu/address/ui/task/note/NoteCard.java b/src/main/java/seedu/address/ui/task/note/NoteCard.java new file mode 100644 index 00000000000..9e0fb048907 --- /dev/null +++ b/src/main/java/seedu/address/ui/task/note/NoteCard.java @@ -0,0 +1,65 @@ +package seedu.address.ui.task.note; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.address.model.task.Note; +import seedu.address.ui.UiPart; + +/** + * A UI component that displays information of a {@code NoteList}. + */ +public class NoteCard extends UiPart { + + private static final String FXML = "task/NoteListCard.fxml"; + + /** + * NoteList: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + */ + + public final Note note; + + @FXML + private HBox cardPane; + @FXML + private Label id; + @FXML + private Label noteContent; + @FXML + private Label date; + @FXML + private Label tags; + + /** + * Creates a {@code NoteCard} with the given {@code NoteList} and index to display. + */ + public NoteCard(Note note, int displayedIndex) { + super(FXML); + this.note = note; + id.setText("#" + displayedIndex + " "); + noteContent.setText(note.getNote().content); + date.setText("Created by: " + note.getDateString()); + tags.setText(note.getType().toString()); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof NoteCard)) { + return false; + } + + // state check + NoteCard card = (NoteCard) other; + return id.getText().equals(card.id.getText()) + && note.equals(card.note); + } +} diff --git a/src/main/java/seedu/address/ui/task/note/NoteListPanel.java b/src/main/java/seedu/address/ui/task/note/NoteListPanel.java new file mode 100644 index 00000000000..10e9b985fb6 --- /dev/null +++ b/src/main/java/seedu/address/ui/task/note/NoteListPanel.java @@ -0,0 +1,85 @@ +package seedu.address.ui.task.note; + +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import seedu.address.commons.core.LogsCenter; +import seedu.address.model.task.Note; +import seedu.address.ui.UiPart; +import seedu.address.ui.ViewContentPanel; + +/** + * Panel containing the list of notes. + */ +public class NoteListPanel extends UiPart { + private static final String FXML = "task/NoteListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(NoteListPanel.class); + + private ViewContentPanel viewContentPanel; + private Note currentNote; + + @FXML + private ListView noteListView; + + @FXML + private VBox container; + + /** + * Creates a {@code NoteListPanel} with the given {@code ObservableList}. + */ + public NoteListPanel(ObservableList noteList, ViewContentPanel viewContentPanel) { + super(FXML); + this.viewContentPanel = viewContentPanel; + noteListView.setItems(noteList); + noteListView.setCellFactory(listView -> new NoteListViewCell()); + logger.info("Note List updated."); + } + + /** + * Handles mouse clicks for noteListView to show the corresponding {@code Note} + * in the {@code ViewContentPanel} + * @param arg0 mouse click event + */ + @FXML public void handleMouseClick(MouseEvent arg0) { + Note noteSelected = noteListView.getSelectionModel().getSelectedItem(); + this.currentNote = noteSelected; + viewContentPanel.setNote(noteSelected); + } + + /** + * Getter for the vertical box with id container. + * + * @return VBox with id container + */ + public VBox getContainer() { + return container; + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code NoteList} using a {@code NoteCard}. + */ + class NoteListViewCell extends ListCell { + @Override + public void updateItem(Note note, boolean empty) { + super.updateItem(note, empty); + + if (empty || note == null) { + setGraphic(null); + setText(null); + viewContentPanel.clearPanel(); + } else { + setGraphic(new NoteCard(note, getIndex() + 1).getRoot()); + if (currentNote != null && note.isSameNote(currentNote)) { + viewContentPanel.setNote(note); + } + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/task/todo/TodoCard.java b/src/main/java/seedu/address/ui/task/todo/TodoCard.java new file mode 100644 index 00000000000..b11b2b222a0 --- /dev/null +++ b/src/main/java/seedu/address/ui/task/todo/TodoCard.java @@ -0,0 +1,81 @@ +package seedu.address.ui.task.todo; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.address.model.task.InternshipTodo; +import seedu.address.model.task.NoteContent; +import seedu.address.ui.UiPart; + +/** + * An UI component that displays information of a {@code Todo}. + */ +public class TodoCard extends UiPart { + + private static final String FXML = "task/TodoListCard.fxml"; + + /** + * NoteList: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + */ + + public final InternshipTodo todo; + + @FXML + private HBox cardPane; + @FXML + private Label id; + @FXML + private Label companyName; + @FXML + private Label jobTitle; + @FXML + private Label deadline; + @FXML + private Label note; + @FXML + private Label createDate; + @FXML + private Label tags; + + /** + * Creates a {@code TodoCard} with the given {@code InternshipTodo} and index to display. + */ + public TodoCard(InternshipTodo todo, int displayedIndex) { + super(FXML); + this.todo = todo; + id.setText("#" + displayedIndex + " "); + companyName.setText(todo.getInternshipTitle().fullName); + jobTitle.setText("Job: " + todo.getJobTitle().fullName); + deadline.setText("Deadline: " + todo.getDeadline().fullName); + createDate.setText("Created by: " + todo.getDateString()); + tags.setText(todo.getType().toString()); + NoteContent noteContent = todo.getNote(); + if (noteContent != null) { + note.setText("Note: " + noteContent.content); + note.setVisible(true); + } else { + note.setVisible(false); + } + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof TodoCard)) { + return false; + } + + // state check + TodoCard card = (TodoCard) other; + return id.getText().equals(card.id.getText()) + && todo.equals(card.todo); + } +} diff --git a/src/main/java/seedu/address/ui/task/todo/TodoListPanel.java b/src/main/java/seedu/address/ui/task/todo/TodoListPanel.java new file mode 100644 index 00000000000..11edaf59187 --- /dev/null +++ b/src/main/java/seedu/address/ui/task/todo/TodoListPanel.java @@ -0,0 +1,85 @@ +package seedu.address.ui.task.todo; + +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import seedu.address.commons.core.LogsCenter; +import seedu.address.model.task.InternshipTodo; +import seedu.address.ui.UiPart; +import seedu.address.ui.ViewContentPanel; + +/** + * Panel containing the list of todos. + */ +public class TodoListPanel extends UiPart { + private static final String FXML = "task/TodoListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(TodoListPanel.class); + + private ViewContentPanel viewContentPanel; + private InternshipTodo currentTodo; + + @FXML + private ListView todoListView; + + @FXML + private VBox container; + + /** + * Creates a {@code TodoListPanel} with the given {@code ObservableList}. + */ + public TodoListPanel(ObservableList todoList, ViewContentPanel viewContentPanel) { + super(FXML); + this.viewContentPanel = viewContentPanel; + todoListView.setItems(todoList); + todoListView.setCellFactory(listView -> new TodoListViewCell()); + logger.info("Todo List updated."); + } + + /** + * Handles mouse clicks for todoListView to show the corresponding {@code InternshipTodo} + * in the {@code ViewContentPanel} + * @param arg0 mouse click event + */ + @FXML public void handleMouseClick(MouseEvent arg0) { + InternshipTodo todoSelected = todoListView.getSelectionModel().getSelectedItem(); + this.currentTodo = todoSelected; + viewContentPanel.setInternshipTodo(todoSelected); + } + + /** + * Getter for the vertical box with id container. + * + * @return VBox with id container + */ + public VBox getContainer() { + return container; + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code InternshipTodo} using a {@code TodoCard}. + */ + class TodoListViewCell extends ListCell { + @Override + protected void updateItem(InternshipTodo todo, boolean empty) { + super.updateItem(todo, empty); + + if (empty || todo == null) { + setGraphic(null); + setText(null); + viewContentPanel.clearPanel(); + } else { + setGraphic(new TodoCard(todo, getIndex() + 1).getRoot()); + if (currentTodo != null && todo.isSameTodo(currentTodo)) { + viewContentPanel.setInternshipTodo(todo); + } + } + } + } + +} diff --git a/src/main/resources/images/add_button.png b/src/main/resources/images/add_button.png new file mode 100644 index 00000000000..6f45d3c386e Binary files /dev/null and b/src/main/resources/images/add_button.png differ diff --git a/src/main/resources/images/add_internship.png b/src/main/resources/images/add_internship.png new file mode 100644 index 00000000000..50815caa89a Binary files /dev/null and b/src/main/resources/images/add_internship.png differ diff --git a/src/main/resources/images/address_book_32.png b/src/main/resources/images/address_book_32.png deleted file mode 100644 index 29810cf1fd9..00000000000 Binary files a/src/main/resources/images/address_book_32.png and /dev/null differ diff --git a/src/main/resources/images/archive.png b/src/main/resources/images/archive.png new file mode 100644 index 00000000000..63429608d01 Binary files /dev/null and b/src/main/resources/images/archive.png differ diff --git a/src/main/resources/images/delete.png b/src/main/resources/images/delete.png new file mode 100644 index 00000000000..38557a7fd24 Binary files /dev/null and b/src/main/resources/images/delete.png differ diff --git a/src/main/resources/images/edit_internship.png b/src/main/resources/images/edit_internship.png new file mode 100644 index 00000000000..3a90c9565fd Binary files /dev/null and b/src/main/resources/images/edit_internship.png differ diff --git a/src/main/resources/images/help.png b/src/main/resources/images/help.png new file mode 100644 index 00000000000..c43b2ba218f Binary files /dev/null and b/src/main/resources/images/help.png differ diff --git a/src/main/resources/images/home.png b/src/main/resources/images/home.png new file mode 100644 index 00000000000..4d298b7c5c9 Binary files /dev/null and b/src/main/resources/images/home.png differ diff --git a/src/main/resources/images/icon-warning.png b/src/main/resources/images/icon-warning.png new file mode 100644 index 00000000000..968f0ee6ea5 Binary files /dev/null and b/src/main/resources/images/icon-warning.png differ diff --git a/src/main/resources/images/info_icon.png b/src/main/resources/images/info_icon.png index f8cef714095..80e3cf0a8bb 100644 Binary files a/src/main/resources/images/info_icon.png and b/src/main/resources/images/info_icon.png differ diff --git a/src/main/resources/images/intern_ease.png b/src/main/resources/images/intern_ease.png new file mode 100644 index 00000000000..78f237922ab Binary files /dev/null and b/src/main/resources/images/intern_ease.png differ diff --git a/src/main/resources/images/notification.png b/src/main/resources/images/notification.png new file mode 100644 index 00000000000..c84917f2d93 Binary files /dev/null and b/src/main/resources/images/notification.png differ diff --git a/src/main/resources/view/ApplicationListCard.css b/src/main/resources/view/ApplicationListCard.css new file mode 100644 index 00000000000..f9315946687 --- /dev/null +++ b/src/main/resources/view/ApplicationListCard.css @@ -0,0 +1,76 @@ +.center { + text-align: center; + -fx-border-color: rgba(255, 255, 255, 0.3); +} + +#cardPane { + -fx-background-color: #143D4D; + -fx-background-radius: 8; + -fx-effect: dropshadow(gaussian, rgba(24, 87, 87, 1.0), 0, 0, 5, 5); + -fx-text-fill: #DBD8D0; +} + +#cardPane:hover { + -fx-background-color: #1884A8; + -fx-background-radius: 8; + -fx-text-fill: whitesmoke; +} + +#cardPane *.text { + -fx-fill: white; + -fx-family: "Segoe UI Light"; +} + +#id { + -fx-text-fill: white; + -fx-font-size: 16px; + -fx-font-family: "Segoe UI Semibold"; +} +#companyName { + -fx-text-fill: white; + -fx-font-size: 16px; + -fx-font-family: "Segoe UI Semibold"; +} + +.primary-button { + -fx-background-color: #55CEFF; + -fx-background-radius: 8; + -fx-text-fill: #2d3f4d; +} + +.primary-button:hover { + -fx-background-color: #3769FF; +} + +.primary-button:pressed { + -fx-background-color: #0072A0; +} + +.delete-button { + -fx-background-color: #B80F0A; + -fx-background-radius: 8; + -fx-text-fill: #2d3f4d; +} + +.delete-button:hover { + -fx-background-color: #420D09; +} + +.delete-button:pressed { + -fx-background-color: #0072A0; +} + +.archive-button { + -fx-background-color: #EEE8AA; + -fx-background-radius: 8; + -fx-text-fill: #2D3F4D; +} + +.archive-button:hover { + -fx-background-color: #EEDD82; +} + +.archive-button:pressed { + -fx-background-color: #DAA520; +} + diff --git a/src/main/resources/view/ApplicationListCard.fxml b/src/main/resources/view/ApplicationListCard.fxml new file mode 100644 index 00000000000..e29d4c60fd3 --- /dev/null +++ b/src/main/resources/view/ApplicationListCard.fxml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/ApplicationListPanel.fxml b/src/main/resources/view/ApplicationListPanel.fxml new file mode 100644 index 00000000000..41707178eca --- /dev/null +++ b/src/main/resources/view/ApplicationListPanel.fxml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/main/resources/view/CommandBox.css b/src/main/resources/view/CommandBox.css new file mode 100644 index 00000000000..24e443b0d0b --- /dev/null +++ b/src/main/resources/view/CommandBox.css @@ -0,0 +1,42 @@ +#commandTextField { + -fx-control-inner-background: #463f3a; + -fx-text-box-border: transparent; + -fx-background-radius: 10px; + + -fx-prompt-text-fill: #bcbcbc; + -fx-prompt-text-size: 15px; + + -fx-text-fill: whitesmoke; + -fx-font-size: 15px; + -fx-font-size: 18px; + -fx-font-family: consolas; +} + +#enterButton { + -fx-background-color: #029fed; + -fx-background-radius: 18px; + + -fx-text-fill: whitesmoke; + -fx-font-size: 18px; + -fx-font-weight: bold; + -fx-font-family: stencil; +} + +#commandTextField:focused { + -fx-text-box-border: #7bb5d1; + -fx-faint-focus-color: #7bb5d122; +} + +#commandTextField:hover { + -fx-text-box-border: whitesmoke; + -fx-faint-focus-color: #ffffff22; +} + +#enterButton:hover { + -fx-background-color: #04557d; +} + +#enterButton:clicked { + -fx-background-color: #043852; +} + diff --git a/src/main/resources/view/CommandBox.fxml b/src/main/resources/view/CommandBox.fxml index 09f6d6fe9e4..49aa4aabf58 100644 --- a/src/main/resources/view/CommandBox.fxml +++ b/src/main/resources/view/CommandBox.fxml @@ -1,9 +1,25 @@ + - - - - - + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/resources/view/HelpWindow.fxml b/src/main/resources/view/HelpWindow.fxml index 5dea0adef70..db0e15a66d7 100644 --- a/src/main/resources/view/HelpWindow.fxml +++ b/src/main/resources/view/HelpWindow.fxml @@ -9,7 +9,7 @@ - + diff --git a/src/main/resources/view/MainWindow.css b/src/main/resources/view/MainWindow.css new file mode 100644 index 00000000000..3119336cd82 --- /dev/null +++ b/src/main/resources/view/MainWindow.css @@ -0,0 +1,16 @@ +.root { + -fx-background-color: #02203d; +} + +#productLabel { + -fx-text-fill: whitesmoke; + -fx-font-size: 24; + -fx-font-family: impact; +} + +.separator *.line { + -fx-orientation: vertical; + -fx-border-style: solid; + -fx-border-width: 1; + -fx-border-color: rgba(255, 255, 255, 0.3); +} diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index a431648f6c0..60f681c44c6 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -1,60 +1,70 @@ - - - - - + + + + + + + - - - - + + + + - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml deleted file mode 100644 index f08ea32ad55..00000000000 --- a/src/main/resources/view/PersonListCard.fxml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/view/PersonListPanel.fxml b/src/main/resources/view/PersonListPanel.fxml deleted file mode 100644 index 8836d323cc5..00000000000 --- a/src/main/resources/view/PersonListPanel.fxml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/src/main/resources/view/PopupAddInternship.css b/src/main/resources/view/PopupAddInternship.css new file mode 100644 index 00000000000..671d8f9fff4 --- /dev/null +++ b/src/main/resources/view/PopupAddInternship.css @@ -0,0 +1,27 @@ +.primary-button { + -fx-background-color: #55CEFF; + -fx-background-radius: 8; + -fx-text-fill: #2d3f4d; +} + +.primary-button:hover { + -fx-background-color: #3769FF; +} + +.primary-button:pressed { + -fx-background-color: #0072A0; +} + +.add-button { + -fx-background-color: #B5B7BB; + -fx-background-radius: 8; + -fx-text-fill: #2d3f4d; +} + +.add-button:hover { + -fx-background-color: #A8A9AD; +} + +.add-button:pressed { + -fx-background-color: #757575; +} diff --git a/src/main/resources/view/PopupAddInternship.fxml b/src/main/resources/view/PopupAddInternship.fxml new file mode 100644 index 00000000000..a3a07e41306 --- /dev/null +++ b/src/main/resources/view/PopupAddInternship.fxml @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/PopupEditInternship.fxml b/src/main/resources/view/PopupEditInternship.fxml new file mode 100644 index 00000000000..10b029ce04f --- /dev/null +++ b/src/main/resources/view/PopupEditInternship.fxml @@ -0,0 +1,286 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/QuickAccessToolbar.css b/src/main/resources/view/QuickAccessToolbar.css new file mode 100644 index 00000000000..6c778a204f7 --- /dev/null +++ b/src/main/resources/view/QuickAccessToolbar.css @@ -0,0 +1,17 @@ +.button { + -fx-background-radius: 17; + -fx-background-color: transparent; +} + +#quickAccessToolbar { + -fx-background-color: rgba(200, 200, 200, 0.4); + -fx-background-radius: 500 0 0 500; +} + +.button:hover { + -fx-background-color: rgba(0, 0, 0, 0.3); +} + +.button:focused { + -fx-background-color: rgba(255, 255, 255, 0.2); +} diff --git a/src/main/resources/view/QuickAccessToolbar.fxml b/src/main/resources/view/QuickAccessToolbar.fxml new file mode 100644 index 00000000000..f4cc1273472 --- /dev/null +++ b/src/main/resources/view/QuickAccessToolbar.fxml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/ReminderApplicationCard.fxml b/src/main/resources/view/ReminderApplicationCard.fxml new file mode 100644 index 00000000000..c08471bb4a2 --- /dev/null +++ b/src/main/resources/view/ReminderApplicationCard.fxml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/ReminderWindow.css b/src/main/resources/view/ReminderWindow.css new file mode 100644 index 00000000000..9079c2282fc --- /dev/null +++ b/src/main/resources/view/ReminderWindow.css @@ -0,0 +1,8 @@ +#reminderApplicationContainer { + -fx-background-color: #02203d; +} + +#reminderApplication *.text { + -fx-fill: white; + -fx-family: "Segoe UI semibold"; +} diff --git a/src/main/resources/view/ReminderWindow.fxml b/src/main/resources/view/ReminderWindow.fxml new file mode 100644 index 00000000000..56217bbda24 --- /dev/null +++ b/src/main/resources/view/ReminderWindow.fxml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/ResultDialog.css b/src/main/resources/view/ResultDialog.css new file mode 100644 index 00000000000..617d4711a08 --- /dev/null +++ b/src/main/resources/view/ResultDialog.css @@ -0,0 +1,17 @@ +.dialog-pane { + -fx-background-color: #edede9; + -fx-background-radius: 8; + -fx-effect: dropshadow( gaussian , rgba(0,0,0,0.7) , 10,0,0,1); +} + +.dialog-pane > .content.label { + -fx-font-size: 14px; + -fx-font-weight: bold; + -fx-font-family: Georgia; + -fx-text-fill: #023047; + -fx-wrap-text: true; +} + +.dialog-pane:hover { + -fx-background-color: #cecece; +} diff --git a/src/main/resources/view/StatsInformationCard.fxml b/src/main/resources/view/StatsInformationCard.fxml new file mode 100644 index 00000000000..b0951e3516d --- /dev/null +++ b/src/main/resources/view/StatsInformationCard.fxml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/StatsInformationListPanel.css b/src/main/resources/view/StatsInformationListPanel.css new file mode 100644 index 00000000000..12ea0cc7ad2 --- /dev/null +++ b/src/main/resources/view/StatsInformationListPanel.css @@ -0,0 +1,35 @@ +.list-view { + -fx-background-color: #170945; +} + +.list-view .scroll-bar:vertical { + width: 0; + -fx-background-color: transparent; +} + +.list-view .scroll-bar:horizontal { + width: 0; + height: 1; + -fx-background-color: transparent; +} + +.list-cell:filled:even { + -fx-background-color: #062c33; +} + +.list-cell:filled:odd { + -fx-background-color: #022126; +} + +.list-cell:filled:selected { + -fx-background-color: #424d5f; +} + +.list-cell:filled:selected #cardPane { + -fx-border-color: #3e7b91; + -fx-border-width: 1; +} + +.list-cell .label { + -fx-text-fill: #daeaed; +} diff --git a/src/main/resources/view/StatsInformationListPanel.fxml b/src/main/resources/view/StatsInformationListPanel.fxml new file mode 100644 index 00000000000..0d8e4bb9560 --- /dev/null +++ b/src/main/resources/view/StatsInformationListPanel.fxml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/main/resources/view/StatsPanel.fxml b/src/main/resources/view/StatsPanel.fxml new file mode 100644 index 00000000000..b2705effbdb --- /dev/null +++ b/src/main/resources/view/StatsPanel.fxml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/ViewContentPanel.css b/src/main/resources/view/ViewContentPanel.css new file mode 100644 index 00000000000..e6959955ed4 --- /dev/null +++ b/src/main/resources/view/ViewContentPanel.css @@ -0,0 +1,105 @@ +#container { + -fx-border-width: 0; + -fx-background-radius: 10; + -fx-background-color: #143D4D; + -fx-padding: 15; +} + +.list-cell { + -fx-background-color: transparent; + -fx-padding: 5 0; +} +.list-view { + -fx-background-color: transparent; +} + +#container .label { + -fx-text-fill: white; + -fx-font-family: "Segoe UI Semibold"; +} + +.title { + -fx-fill: white; + -fx-font-weight: 700; + -fx-font-size: 25px; +} + +.description { + -fx-fill: white; + -fx-font-weight: 600; + -fx-font-size: 18px; +} + +#sideLabel { + -fx-background-color: #D40E2F; + -fx-padding: 7.5 15; + -fx-font-size: 16px; + -fx-background-radius: 22.5; +} + +#emptyLabel { + -fx-font-size: 20px; +} + +.cell_small_label { + -fx-font-size: 15px; + -fx-wrap-text: true; +} + +.scroll-bar { + -fx-background-color: transparent; +} + +.scroll-bar .thumb { + -fx-background-color: derive(#1d1d1d, 50%); + -fx-background-insets: 3; +} + +.scroll-bar .increment-button, .scroll-bar .decrement-button { + -fx-background-color: transparent; + -fx-padding: 0 0 0 0; +} + +.scroll-bar .increment-arrow, .scroll-bar .decrement-arrow { + -fx-shape: " "; +} + +.scroll-bar:vertical .increment-arrow, .scroll-bar:vertical .decrement-arrow { + -fx-padding: 1 8 1 8; +} + +.scroll-bar:horizontal .increment-arrow, .scroll-bar:horizontal .decrement-arrow { + -fx-padding: 8 1 8 1; +} + +.list-view .corner { + -fx-background-color: transparent; +} + +.multiple-items { + -fx-hgap: 7; + -fx-vgap: 3; +} + +.multiple-items .label { + -fx-text-fill: white; + -fx-background-color: #3e7b91; + -fx-padding: 1 3 1 3; + -fx-border-radius: 2; + -fx-background-radius: 2; + -fx-font-size: 13px; +} + +.link-items { + -fx-hgap: 0; +} + +.link-item { + -fx-font-size: 16px; + -fx-border-color: transparent; + -fx-padding: 0; +} + +.link-item:visited { + -fx-text-fill: #0095C8; +} diff --git a/src/main/resources/view/ViewContentPanel.fxml b/src/main/resources/view/ViewContentPanel.fxml new file mode 100644 index 00000000000..63070fdfb86 --- /dev/null +++ b/src/main/resources/view/ViewContentPanel.fxml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/task/ListPanel.css b/src/main/resources/view/task/ListPanel.css new file mode 100644 index 00000000000..e6223455f7a --- /dev/null +++ b/src/main/resources/view/task/ListPanel.css @@ -0,0 +1,29 @@ +.list-view { + -fx-background-color: #02203d; + -fx-background-radius: 10; +} + +.scroll-bar { + -fx-background-color: transparent; + -fx-padding: 0, 2, 5, 2; + -fx-width: 1; +} + +.scroll-bar .thumb { + -fx-background-color: rgba(255, 255, 255, 0.5); + -fx-background-radius: 20; +} + +.scroll-bar:vertical .increment-arrow, .scroll-bar .decrement-arrow { + -fx-shape: null; + -fx-background-color: transparent; +} + +.list-cell { + -fx-background-color: #02203d; + -fx-padding: 5; +} + +#container { + -fx-background-color: #02203d; +} diff --git a/src/main/resources/view/task/MixedPanel.fxml b/src/main/resources/view/task/MixedPanel.fxml new file mode 100644 index 00000000000..41219543be2 --- /dev/null +++ b/src/main/resources/view/task/MixedPanel.fxml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/task/NoteListCard.css b/src/main/resources/view/task/NoteListCard.css new file mode 100644 index 00000000000..0fb28ff9a13 --- /dev/null +++ b/src/main/resources/view/task/NoteListCard.css @@ -0,0 +1,37 @@ +#tags { + -fx-background-color: #588157; + -fx-font-weight: bold; + -fx-background-radius: 6; + -fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.3), 0.5, 0.5, 2, 2); +} + +#date { + -fx-font-style: italic; + -fx-text-fill: #3d3c38; + -fx-font-family: courier; + -fx-font-size: 10px; +} + +#cardPane { + -fx-background-color: #369fc2; + -fx-background-radius: 8; + -fx-effect: dropshadow(gaussian, rgba(24, 87, 87, 1.0), 0, 0, 5, 5); + -fx-text-fill: #dbd8d0; +} + +#id { + -fx-font-size: 18px; + -fx-font-family: verdana; + -fx-font-weight: bold; +} + +#noteContent { + -fx-font-size: 18px; + -fx-font-family: candara; +} + +#cardPane : hover { + -fx-background-color: #1884a8; + -fx-background-radius: 8; + -fx-text-fill: whitesmoke; +} diff --git a/src/main/resources/view/task/NoteListCard.fxml b/src/main/resources/view/task/NoteListCard.fxml new file mode 100644 index 00000000000..a3f513e5ef7 --- /dev/null +++ b/src/main/resources/view/task/NoteListCard.fxml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + +
+
diff --git a/src/main/resources/view/task/NoteListPanel.fxml b/src/main/resources/view/task/NoteListPanel.fxml new file mode 100644 index 00000000000..e3e3ef80d54 --- /dev/null +++ b/src/main/resources/view/task/NoteListPanel.fxml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/main/resources/view/task/TodoListCard.css b/src/main/resources/view/task/TodoListCard.css new file mode 100644 index 00000000000..a1397834f26 --- /dev/null +++ b/src/main/resources/view/task/TodoListCard.css @@ -0,0 +1,37 @@ +#tags { + -fx-background-color: #53A3D8; + -fx-font-weight: bold; + -fx-background-radius: 6; + -fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.3), 0.5, 0.5, 2, 2); +} + +#createDate { + -fx-font-style: italic; + -fx-text-fill: #3d3c38; + -fx-font-family: courier; + -fx-font-size: 10px; +} + +#cardPane { + -fx-background-color: #eb505a; + -fx-background-radius: 8; + -fx-effect: dropshadow(gaussian, rgba(128, 30, 30, 1.0), 0, 0, 5, 5); + -fx-text-fill: #dbd8d0; +} + +#id, #companyName { + -fx-font-size: 18px; + -fx-font-family: verdana; + -fx-font-weight: bold; +} + +#jobTitle, #deadline, #note { + -fx-font-size: 16px; + -fx-font-family: candara; +} + +#cardPane : hover { + -fx-background-color: #bf2a34; + -fx-background-radius: 8; + -fx-text-fill: whitesmoke; +} diff --git a/src/main/resources/view/task/TodoListCard.fxml b/src/main/resources/view/task/TodoListCard.fxml new file mode 100644 index 00000000000..be65f479ab2 --- /dev/null +++ b/src/main/resources/view/task/TodoListCard.fxml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +
+
+ + + +
+
diff --git a/src/main/resources/view/task/TodoListPanel.fxml b/src/main/resources/view/task/TodoListPanel.fxml new file mode 100644 index 00000000000..3f3fbe2dfd5 --- /dev/null +++ b/src/main/resources/view/task/TodoListPanel.fxml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json index f10eddee12e..441c3bfeae8 100644 --- a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json +++ b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json @@ -2,8 +2,8 @@ "_comment": "AddressBook save file which contains the same Person values as in TypicalPersons#getTypicalAddressBook()", "persons" : [ { "name" : "Alice Pauline", - "phone" : "94351253", - "email" : "alice@example.com", + "phone" : "11111111", + "email" : "amy@example.com", "address" : "123, Jurong West Ave 6, #08-111", "tagged" : [ "friends" ] }, { @@ -38,8 +38,8 @@ "tagged" : [ ] }, { "name" : "George Best", - "phone" : "9482442", - "email" : "anna@example.com", + "phone" : "11111111", + "email" : "amy@example.com", "address" : "4th street", "tagged" : [ ] } ] diff --git a/src/test/java/seedu/address/commons/util/AppUtilTest.java b/src/test/java/seedu/address/commons/util/AppUtilTest.java index 594de1e6365..cb0f9a10a70 100644 --- a/src/test/java/seedu/address/commons/util/AppUtilTest.java +++ b/src/test/java/seedu/address/commons/util/AppUtilTest.java @@ -9,7 +9,7 @@ public class AppUtilTest { @Test public void getImage_exitingImage() { - assertNotNull(AppUtil.getImage("/images/address_book_32.png")); + assertNotNull(AppUtil.getImage("/images/home.png")); } @Test diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/address/logic/LogicManagerTest.java index ad923ac249a..4ca46da8257 100644 --- a/src/test/java/seedu/address/logic/LogicManagerTest.java +++ b/src/test/java/seedu/address/logic/LogicManagerTest.java @@ -1,162 +1,162 @@ -package seedu.address.logic; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.AMY; - -import java.io.IOException; -import java.nio.file.Path; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; -import seedu.address.storage.JsonAddressBookStorage; -import seedu.address.storage.JsonUserPrefsStorage; -import seedu.address.storage.StorageManager; -import seedu.address.testutil.PersonBuilder; - -public class LogicManagerTest { - private static final IOException DUMMY_IO_EXCEPTION = new IOException("dummy exception"); - - @TempDir - public Path temporaryFolder; - - private Model model = new ModelManager(); - private Logic logic; - - @BeforeEach - public void setUp() { - JsonAddressBookStorage addressBookStorage = - new JsonAddressBookStorage(temporaryFolder.resolve("addressBook.json")); - JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("userPrefs.json")); - StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage); - logic = new LogicManager(model, storage); - } - - @Test - public void execute_invalidCommandFormat_throwsParseException() { - String invalidCommand = "uicfhmowqewca"; - assertParseException(invalidCommand, MESSAGE_UNKNOWN_COMMAND); - } - - @Test - public void execute_commandExecutionError_throwsCommandException() { - String deleteCommand = "delete 9"; - assertCommandException(deleteCommand, MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - @Test - public void execute_validCommand_success() throws Exception { - String listCommand = ListCommand.COMMAND_WORD; - assertCommandSuccess(listCommand, ListCommand.MESSAGE_SUCCESS, model); - } - - @Test - public void execute_storageThrowsIoException_throwsCommandException() { - // Setup LogicManager with JsonAddressBookIoExceptionThrowingStub - JsonAddressBookStorage addressBookStorage = - new JsonAddressBookIoExceptionThrowingStub(temporaryFolder.resolve("ioExceptionAddressBook.json")); - JsonUserPrefsStorage userPrefsStorage = - new JsonUserPrefsStorage(temporaryFolder.resolve("ioExceptionUserPrefs.json")); - StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage); - logic = new LogicManager(model, storage); - - // Execute add command - String addCommand = AddCommand.COMMAND_WORD + NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY - + ADDRESS_DESC_AMY; - Person expectedPerson = new PersonBuilder(AMY).withTags().build(); - ModelManager expectedModel = new ModelManager(); - expectedModel.addPerson(expectedPerson); - String expectedMessage = LogicManager.FILE_OPS_ERROR_MESSAGE + DUMMY_IO_EXCEPTION; - assertCommandFailure(addCommand, CommandException.class, expectedMessage, expectedModel); - } - - @Test - public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredPersonList().remove(0)); - } - - /** - * Executes the command and confirms that - * - no exceptions are thrown
- * - the feedback message is equal to {@code expectedMessage}
- * - the internal model manager state is the same as that in {@code expectedModel}
- * @see #assertCommandFailure(String, Class, String, Model) - */ - private void assertCommandSuccess(String inputCommand, String expectedMessage, - Model expectedModel) throws CommandException, ParseException { - CommandResult result = logic.execute(inputCommand); - assertEquals(expectedMessage, result.getFeedbackToUser()); - assertEquals(expectedModel, model); - } - - /** - * Executes the command, confirms that a ParseException is thrown and that the result message is correct. - * @see #assertCommandFailure(String, Class, String, Model) - */ - private void assertParseException(String inputCommand, String expectedMessage) { - assertCommandFailure(inputCommand, ParseException.class, expectedMessage); - } - - /** - * Executes the command, confirms that a CommandException is thrown and that the result message is correct. - * @see #assertCommandFailure(String, Class, String, Model) - */ - private void assertCommandException(String inputCommand, String expectedMessage) { - assertCommandFailure(inputCommand, CommandException.class, expectedMessage); - } - - /** - * Executes the command, confirms that the exception is thrown and that the result message is correct. - * @see #assertCommandFailure(String, Class, String, Model) - */ - private void assertCommandFailure(String inputCommand, Class expectedException, - String expectedMessage) { - Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - assertCommandFailure(inputCommand, expectedException, expectedMessage, expectedModel); - } - - /** - * Executes the command and confirms that - * - the {@code expectedException} is thrown
- * - the resulting error message is equal to {@code expectedMessage}
- * - the internal model manager state is the same as that in {@code expectedModel}
- * @see #assertCommandSuccess(String, String, Model) - */ - private void assertCommandFailure(String inputCommand, Class expectedException, - String expectedMessage, Model expectedModel) { - assertThrows(expectedException, expectedMessage, () -> logic.execute(inputCommand)); - assertEquals(expectedModel, model); - } - - /** - * A stub class to throw an {@code IOException} when the save method is called. - */ - private static class JsonAddressBookIoExceptionThrowingStub extends JsonAddressBookStorage { - private JsonAddressBookIoExceptionThrowingStub(Path filePath) { - super(filePath); - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { - throw DUMMY_IO_EXCEPTION; - } - } -} +//package seedu.address.logic; +// +//import static org.junit.jupiter.api.Assertions.assertEquals; +//import static seedu.address.commons.core.Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX; +//import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +//import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; +//import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; +//import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; +//import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; +//import static seedu.address.testutil.Assert.assertThrows; +//import static seedu.address.testutil.TypicalPersons.AMY; +// +//import java.io.IOException; +//import java.nio.file.Path; +// +//import org.junit.jupiter.api.BeforeEach; +//import org.junit.jupiter.api.Test; +//import org.junit.jupiter.api.io.TempDir; +// +//import seedu.address.logic.commands.AddCommand; +//import seedu.address.logic.commands.CommandResult; +//import seedu.address.logic.commands.ListCommand; +//import seedu.address.logic.commands.exceptions.CommandException; +//import seedu.address.logic.parser.exceptions.ParseException; +//import seedu.address.model.Model; +//import seedu.address.model.ModelManager; +//import seedu.address.model.ReadOnlyAddressBook; +//import seedu.address.model.UserPrefs; +//import seedu.address.model.person.Person; +//import seedu.address.storage.JsonAddressBookStorage; +//import seedu.address.storage.JsonUserPrefsStorage; +//import seedu.address.storage.StorageManager; +//import seedu.address.testutil.PersonBuilder; +// +//public class LogicManagerTest { +// private static final IOException DUMMY_IO_EXCEPTION = new IOException("dummy exception"); +// +// @TempDir +// public Path temporaryFolder; +// +// private Model model = new ModelManager(); +// private Logic logic; +// +// @BeforeEach +// public void setUp() { +// JsonAddressBookStorage addressBookStorage = +// new JsonAddressBookStorage(temporaryFolder.resolve("addressBook.json")); +// JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("userPrefs.json")); +// StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage); +// logic = new LogicManager(model, storage); +// } +// +// @Test +// public void execute_invalidCommandFormat_throwsParseException() { +// String invalidCommand = "uicfhmowqewca"; +// assertParseException(invalidCommand, MESSAGE_UNKNOWN_COMMAND); +// } +// +// @Test +// public void execute_commandExecutionError_throwsCommandException() { +// String deleteCommand = "delete 9"; +// assertCommandException(deleteCommand, MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); +// } +// +// @Test +// public void execute_validCommand_success() throws Exception { +// String listCommand = ListCommand.COMMAND_WORD; +// assertCommandSuccess(listCommand, ListCommand.MESSAGE_SUCCESS, model); +// } +// +// @Test +// public void execute_storageThrowsIoException_throwsCommandException() { +// // Setup LogicManager with JsonAddressBookIoExceptionThrowingStub +// JsonAddressBookStorage addressBookStorage = +// new JsonAddressBookIoExceptionThrowingStub(temporaryFolder.resolve("ioExceptionAddressBook.json")); +// JsonUserPrefsStorage userPrefsStorage = +// new JsonUserPrefsStorage(temporaryFolder.resolve("ioExceptionUserPrefs.json")); +// StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage); +// logic = new LogicManager(model, storage); +// +// // Execute add command +// String addCommand = AddCommand.COMMAND_WORD + NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY +// + ADDRESS_DESC_AMY; +// Person expectedPerson = new PersonBuilder(AMY).withTags().build(); +// ModelManager expectedModel = new ModelManager(); +// expectedModel.addPerson(expectedPerson); +// String expectedMessage = LogicManager.FILE_OPS_ERROR_MESSAGE + DUMMY_IO_EXCEPTION; +// assertCommandFailure(addCommand, CommandException.class, expectedMessage, expectedModel); +// } +// +// @Test +// public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() { +// assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredPersonList().remove(0)); +// } +// +// /** +// * Executes the command and confirms that +// * - no exceptions are thrown
+// * - the feedback message is equal to {@code expectedMessage}
+// * - the internal model manager state is the same as that in {@code expectedModel}
+// * @see #assertCommandFailure(String, Class, String, Model) +// */ +// private void assertCommandSuccess(String inputCommand, String expectedMessage, +// Model expectedModel) throws CommandException, ParseException { +// CommandResult result = logic.execute(inputCommand); +// assertEquals(expectedMessage, result.getFeedbackToUser()); +// assertEquals(expectedModel, model); +// } +// +// /** +// * Executes the command, confirms that a ParseException is thrown and that the result message is correct. +// * @see #assertCommandFailure(String, Class, String, Model) +// */ +// private void assertParseException(String inputCommand, String expectedMessage) { +// assertCommandFailure(inputCommand, ParseException.class, expectedMessage); +// } +// +// /** +// * Executes the command, confirms that a CommandException is thrown and that the result message is correct. +// * @see #assertCommandFailure(String, Class, String, Model) +// */ +// private void assertCommandException(String inputCommand, String expectedMessage) { +// assertCommandFailure(inputCommand, CommandException.class, expectedMessage); +// } +// +// /** +// * Executes the command, confirms that the exception is thrown and that the result message is correct. +// * @see #assertCommandFailure(String, Class, String, Model) +// */ +// private void assertCommandFailure(String inputCommand, Class expectedException, +// String expectedMessage) { +// Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); +// assertCommandFailure(inputCommand, expectedException, expectedMessage, expectedModel); +// } +// +// /** +// * Executes the command and confirms that +// * - the {@code expectedException} is thrown
+// * - the resulting error message is equal to {@code expectedMessage}
+// * - the internal model manager state is the same as that in {@code expectedModel}
+// * @see #assertCommandSuccess(String, String, Model) +// */ +// private void assertCommandFailure(String inputCommand, Class expectedException, +// String expectedMessage, Model expectedModel) { +// assertThrows(expectedException, expectedMessage, () -> logic.execute(inputCommand)); +// assertEquals(expectedModel, model); +// } +// +// /** +// * A stub class to throw an {@code IOException} when the save method is called. +// */ +// private static class JsonAddressBookIoExceptionThrowingStub extends JsonAddressBookStorage { +// private JsonAddressBookIoExceptionThrowingStub(Path filePath) { +// super(filePath); +// } +// +// @Override +// public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { +// throw DUMMY_IO_EXCEPTION; +// } +// } +//} diff --git a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java b/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java index cb8714bb055..87f213e640a 100644 --- a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java +++ b/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java @@ -2,7 +2,9 @@ import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -10,8 +12,8 @@ import seedu.address.model.Model; import seedu.address.model.ModelManager; import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; -import seedu.address.testutil.PersonBuilder; +import seedu.address.model.application.InternshipApplication; +import seedu.address.testutil.InternshipBuilder; /** * Contains integration tests (interaction with the Model) for {@code AddCommand}. @@ -22,24 +24,25 @@ public class AddCommandIntegrationTest { @BeforeEach public void setUp() { - model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); } @Test - public void execute_newPerson_success() { - Person validPerson = new PersonBuilder().build(); + public void execute_newInternship_success() { + InternshipApplication validInternship = new InternshipBuilder().build(); - Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - expectedModel.addPerson(validPerson); + Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + expectedModel.addApplication(validInternship); - assertCommandSuccess(new AddCommand(validPerson), model, - String.format(AddCommand.MESSAGE_SUCCESS, validPerson), expectedModel); + assertCommandSuccess(new AddCommand(validInternship), model, + String.format(AddCommand.MESSAGE_SUCCESS, validInternship), expectedModel); } @Test - public void execute_duplicatePerson_throwsCommandException() { - Person personInList = model.getAddressBook().getPersonList().get(0); - assertCommandFailure(new AddCommand(personInList), model, AddCommand.MESSAGE_DUPLICATE_PERSON); + public void execute_duplicateInternship_throwsCommandException() { + InternshipApplication internshipInList = model.getAddressBook().getInternshipList().get(0); + assertCommandFailure(new AddCommand(internshipInList), model, AddCommand.MESSAGE_DUPLICATE_INTERNSHIP); } } diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java index 5865713d5dd..254dcdb5533 100644 --- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/AddCommandTest.java @@ -9,6 +9,8 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; +import java.util.List; import java.util.function.Predicate; import org.junit.jupiter.api.Test; @@ -19,64 +21,70 @@ import seedu.address.model.AddressBook; import seedu.address.model.Model; import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyNote; +import seedu.address.model.ReadOnlyTodoList; import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.person.Person; -import seedu.address.testutil.PersonBuilder; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.task.InternshipTodo; +import seedu.address.model.task.Note; +import seedu.address.testutil.InternshipBuilder; public class AddCommandTest { @Test - public void constructor_nullPerson_throwsNullPointerException() { + public void constructor_nullInternship_throwsNullPointerException() { assertThrows(NullPointerException.class, () -> new AddCommand(null)); } @Test - public void execute_personAcceptedByModel_addSuccessful() throws Exception { - ModelStubAcceptingPersonAdded modelStub = new ModelStubAcceptingPersonAdded(); - Person validPerson = new PersonBuilder().build(); + public void execute_internshipAcceptedByModel_addSuccessful() throws Exception { + ModelStubAcceptingInternshipAdded modelStub = new ModelStubAcceptingInternshipAdded(); + InternshipApplication validInternship = new InternshipBuilder().build(); - CommandResult commandResult = new AddCommand(validPerson).execute(modelStub); + CommandResult commandResult = new AddCommand(validInternship).execute(modelStub); - assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, validPerson), commandResult.getFeedbackToUser()); - assertEquals(Arrays.asList(validPerson), modelStub.personsAdded); + assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, validInternship), commandResult.getFeedbackToUser()); + assertEquals(Arrays.asList(validInternship), modelStub.internshipsAdded); } @Test - public void execute_duplicatePerson_throwsCommandException() { - Person validPerson = new PersonBuilder().build(); - AddCommand addCommand = new AddCommand(validPerson); - ModelStub modelStub = new ModelStubWithPerson(validPerson); + public void execute_duplicateInternship_throwsCommandException() { + InternshipApplication validInternship = new InternshipBuilder().build(); + AddCommand addCommand = new AddCommand(validInternship); + ModelStub modelStub = new ModelStubWithInternship(validInternship); - assertThrows(CommandException.class, AddCommand.MESSAGE_DUPLICATE_PERSON, () -> addCommand.execute(modelStub)); + assertThrows(CommandException.class, AddCommand.MESSAGE_DUPLICATE_INTERNSHIP, () + -> addCommand.execute(modelStub)); } @Test public void equals() { - Person alice = new PersonBuilder().withName("Alice").build(); - Person bob = new PersonBuilder().withName("Bob").build(); - AddCommand addAliceCommand = new AddCommand(alice); - AddCommand addBobCommand = new AddCommand(bob); + InternshipApplication bankOfAmerica = new InternshipBuilder().withCompanyName("Bank of America").build(); + InternshipApplication deutscheBank = new InternshipBuilder().withCompanyName("Deutsche Bank").build(); + AddCommand addBankOfAmericaCommand = new AddCommand(bankOfAmerica); + AddCommand addDeutscheBankCommand = new AddCommand(deutscheBank); // same object -> returns true - assertTrue(addAliceCommand.equals(addAliceCommand)); + assertTrue(addBankOfAmericaCommand.equals(addBankOfAmericaCommand)); // same values -> returns true - AddCommand addAliceCommandCopy = new AddCommand(alice); - assertTrue(addAliceCommand.equals(addAliceCommandCopy)); + AddCommand addBankOfAmericaCommandCopy = new AddCommand(bankOfAmerica); + assertTrue(addBankOfAmericaCommand.equals(addBankOfAmericaCommandCopy)); // different types -> returns false - assertFalse(addAliceCommand.equals(1)); + assertFalse(addBankOfAmericaCommand.equals(1)); // null -> returns false - assertFalse(addAliceCommand.equals(null)); + assertFalse(addBankOfAmericaCommand.equals(null)); // different person -> returns false - assertFalse(addAliceCommand.equals(addBobCommand)); + assertFalse(addBankOfAmericaCommand.equals(addDeutscheBankCommand)); } /** - * A default model stub that have all of the methods failing. + * A default model stub that have all the methods failing. */ + private class ModelStub implements Model { @Override public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { @@ -103,18 +111,52 @@ public Path getAddressBookFilePath() { throw new AssertionError("This method should not be called."); } + @Override + public Path getNoteListFilePath() { + throw new AssertionError("This method should not be called."); + } + + @Override + public Path getTodoListFilePath() { + throw new AssertionError("This method should not be called."); + } + @Override public void setAddressBookFilePath(Path addressBookFilePath) { throw new AssertionError("This method should not be called."); } @Override - public void addPerson(Person person) { + public void addApplication(InternshipApplication person) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addTodo(InternshipTodo todo) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addNote(Note note) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addApplications(List applications) { + throw new AssertionError("This method should not be called."); + } + + @Override + public InternshipApplication getReminder() { + throw new AssertionError("This method should not be called."); + } + @Override + public void updateReminder() { throw new AssertionError("This method should not be called."); } @Override - public void setAddressBook(ReadOnlyAddressBook newData) { + public void setInternEase(ReadOnlyAddressBook newData) { throw new AssertionError("This method should not be called."); } @@ -124,65 +166,167 @@ public ReadOnlyAddressBook getAddressBook() { } @Override - public boolean hasPerson(Person person) { + public ReadOnlyTodoList getTodoList() { + return null; + } + + @Override + public ReadOnlyNote getNoteList() { + return null; + } + + @Override + public boolean hasApplication(InternshipApplication application) { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean hasTodo(InternshipTodo todo) { + return false; + } + + @Override + public boolean hasNote(Note note) { + return false; + } + + @Override + public void deleteInternship(InternshipApplication target) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void deleteTodo(InternshipTodo target) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void deleteNote(Note target) { throw new AssertionError("This method should not be called."); } @Override - public void deletePerson(Person target) { + public void clearTodo(ReadOnlyTodoList internEase) { throw new AssertionError("This method should not be called."); } @Override - public void setPerson(Person target, Person editedPerson) { + public void clearNote(ReadOnlyNote internEase) { throw new AssertionError("This method should not be called."); } @Override - public ObservableList getFilteredPersonList() { + public void setApplication(InternshipApplication target, + InternshipApplication editedApplication) { throw new AssertionError("This method should not be called."); } @Override - public void updateFilteredPersonList(Predicate predicate) { + public void setTodo(InternshipTodo target, InternshipTodo editedTodo) { throw new AssertionError("This method should not be called."); } + + @Override + public void setNote(Note target, Note editedNote) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ObservableList getSortedFilteredInternshipList() { + throw new AssertionError("This method should not be called."); + } + + @Override + public ObservableList getFilteredTodoList() { + return null; + } + + @Override + public ObservableList getFilteredNoteList() { + return null; + } + + @Override + public List getCachedInternshipList() { + return null; + } + + @Override + public InternshipApplication getAndRemoveCachedApplication() { + return null; + } + + @Override + public void addInternshipToCache(InternshipApplication application) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addAllInternshipToCache(List application) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setEmptyInternshipCacheList() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateFilteredInternshipList(Predicate predicate) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateFilteredTodoList(Predicate predicate) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateFilteredNoteList(Predicate predicate) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateSortedFilteredInternshipList(Comparator comparator) { + throw new AssertionError("This method should not be called."); + } + } /** - * A Model stub that contains a single person. + * A Model stub that contains a single internship. */ - private class ModelStubWithPerson extends ModelStub { - private final Person person; + private class ModelStubWithInternship extends ModelStub { + private final InternshipApplication internship; - ModelStubWithPerson(Person person) { - requireNonNull(person); - this.person = person; + ModelStubWithInternship(InternshipApplication internship) { + requireNonNull(internship); + this.internship = internship; } @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return this.person.isSamePerson(person); + public boolean hasApplication(InternshipApplication internship) { + requireNonNull(internship); + return this.internship.isSameApplication(internship); } } /** - * A Model stub that always accept the person being added. + * A Model stub that always accept the internship application being added. */ - private class ModelStubAcceptingPersonAdded extends ModelStub { - final ArrayList personsAdded = new ArrayList<>(); + private class ModelStubAcceptingInternshipAdded extends ModelStub { + final ArrayList internshipsAdded = new ArrayList<>(); @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return personsAdded.stream().anyMatch(person::isSamePerson); + public boolean hasApplication(InternshipApplication internship) { + requireNonNull(internship); + return internshipsAdded.stream().anyMatch(internship::isSameApplication); } @Override - public void addPerson(Person person) { + public void addApplication(InternshipApplication person) { requireNonNull(person); - personsAdded.add(person); + internshipsAdded.add(person); } @Override diff --git a/src/test/java/seedu/address/logic/commands/AddInterviewDateCommandTest.java b/src/test/java/seedu/address/logic/commands/AddInterviewDateCommandTest.java new file mode 100644 index 00000000000..3d7eac73a6d --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/AddInterviewDateCommandTest.java @@ -0,0 +1,102 @@ +package seedu.address.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_CARL_KURZ; +import static seedu.address.logic.commands.CommandTestUtil.VALID_JOB_TITLE_WEB_DEVELOPER; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.address.model.application.InternshipStatus.PENDING; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_SEVENTH_APPLICATION; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InterviewDate; +import seedu.address.testutil.InternshipBuilder; +import seedu.address.testutil.InterviewDateBuilder; + +public class AddInterviewDateCommandTest { + + private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + + @Test + public void execute_allFieldsSpecifiedUnfilteredList_success() { + InterviewDate interviewDate = new InterviewDate("2023-04-09 12:00 PM"); + InternshipApplication initialApplication = new InternshipBuilder() + .withCompanyName(VALID_COMPANY_NAME_CARL_KURZ) + .withJobTitle(VALID_JOB_TITLE_WEB_DEVELOPER) + .withStatus(PENDING).build(); + InternshipApplication interviewDateAddedApplication = new InternshipBuilder() + .withCompanyName(VALID_COMPANY_NAME_CARL_KURZ) + .withJobTitle(VALID_JOB_TITLE_WEB_DEVELOPER) + .withStatus(PENDING) + .withInterviewDate(interviewDate).build(); + AddInterviewDateCommand addInterviewDateCommand = new AddInterviewDateCommand(INDEX_SEVENTH_APPLICATION, + interviewDate); + + String expectedMessage = String.format(AddInterviewDateCommand.MESSAGE_ADD_INTERVIEW_DATE_SUCCESS, + initialApplication + "\n" + interviewDate); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + expectedModel.setApplication(model.getSortedFilteredInternshipList().get(6), interviewDateAddedApplication); + + assertCommandSuccess(addInterviewDateCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + InterviewDate interviewDate = new InterviewDateBuilder().build(); + Index outOfBoundIndex = Index.fromOneBased(model.getSortedFilteredInternshipList().size() + 1); + AddInterviewDateCommand addInterviewDateCommand = new AddInterviewDateCommand(outOfBoundIndex, interviewDate); + + assertCommandFailure(addInterviewDateCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + @Test + public void execute_filteredList_success() { + showInternshipAtIndex(model, INDEX_SECOND_APPLICATION); + InternshipApplication applicationInFilteredList = + model.getSortedFilteredInternshipList().get(INDEX_FIRST_APPLICATION.getZeroBased()); + InternshipApplication initialApplication = + new InternshipBuilder(applicationInFilteredList).build(); + InterviewDate interviewDate = new InterviewDateBuilder().build(); + InternshipApplication interviewDateAddedApplication = + new InternshipBuilder(applicationInFilteredList).withInterviewDate(interviewDate).build(); + AddInterviewDateCommand addInterviewDateCommand = new AddInterviewDateCommand(INDEX_FIRST_APPLICATION, + interviewDate); + String expectedMessage = String.format(AddInterviewDateCommand.MESSAGE_ADD_INTERVIEW_DATE_SUCCESS, + initialApplication + "\n" + interviewDate); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + expectedModel.setApplication(model.getSortedFilteredInternshipList().get(0), interviewDateAddedApplication); + + assertCommandSuccess(addInterviewDateCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showInternshipAtIndex(model, INDEX_SECOND_APPLICATION); + InterviewDate interviewDate = new InterviewDateBuilder().build(); + Index outOfBoundIndex = INDEX_SECOND_APPLICATION; + + assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getInternshipList().size()); + AddInterviewDateCommand addInterviewDateCommand = new AddInterviewDateCommand(outOfBoundIndex, interviewDate); + + assertCommandFailure(addInterviewDateCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } +} diff --git a/src/test/java/seedu/address/logic/commands/ArchiveCommandTest.java b/src/test/java/seedu/address/logic/commands/ArchiveCommandTest.java new file mode 100644 index 00000000000..b6bed053863 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/ArchiveCommandTest.java @@ -0,0 +1,146 @@ +package seedu.address.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static seedu.address.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_BANK_OF_AMERICA; +import static seedu.address.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_META; +import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_META; +import static seedu.address.logic.commands.CommandTestUtil.VALID_JOB_TITLE_SOFTWARE_ENGINEER; +import static seedu.address.logic.commands.CommandTestUtil.VALID_JOB_TITLE_SOFTWARE_TESTER; +import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_META; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.address.model.application.InternshipStatus.ACCEPTED; +import static seedu.address.model.application.InternshipStatus.DECLINED; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +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.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.contact.Contact; +import seedu.address.model.contact.Email; +import seedu.address.model.contact.Phone; +import seedu.address.testutil.InternshipBuilder; + +/** + * Contains integration tests (interaction with the Model) and unit tests for + * {@code DeleteCommand}. + */ +public class ArchiveCommandTest { + + private Model model; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + } + + @Test + public void execute_validIndexUnfilteredList_success() { + ArchiveCommand archiveCommand = new ArchiveCommand(Index.fromOneBased(2)); + + InternshipApplication archivedApplication = new InternshipBuilder() + .withCompanyName(VALID_COMPANY_NAME_BANK_OF_AMERICA) + .withJobTitle(VALID_JOB_TITLE_SOFTWARE_ENGINEER) + .withInterviewDate(new InterviewDate("2023-04-09 12:00 PM")) + .withStatus(DECLINED) + .withIsArchived(true).build(); + + String expectedMessage = String.format(ArchiveCommand.MESSAGE_ARCHIVE_APPLICATION_SUCCESS, archivedApplication); + + ModelManager expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + expectedModel.setApplication(model.getSortedFilteredInternshipList().get(1), archivedApplication); + + try { + CommandResult archivedMessage = archiveCommand.execute(model); + assertCommandSuccess(archivedMessage, model, expectedMessage, expectedModel); + + } catch (CommandException ce) { + fail(ce.toString()); + } + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(model.getSortedFilteredInternshipList().size() + 1); + ArchiveCommand archiveCommand = new ArchiveCommand(outOfBoundIndex); + + assertCommandFailure(archiveCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + @Test + public void execute_validIndexFilteredList_success() { + showInternshipAtIndex(model, Index.fromOneBased(1)); + ArchiveCommand archiveCommand = new ArchiveCommand(Index.fromOneBased(1)); + + InternshipApplication archivedApplication = new InternshipBuilder() + .withCompanyName(VALID_COMPANY_NAME_META) + .withJobTitle(VALID_JOB_TITLE_SOFTWARE_TESTER) + .withContact(new Contact(new Phone(VALID_PHONE_META), new Email(VALID_EMAIL_META))) + .withInterviewDate(new InterviewDate("2023-04-01 08:00 PM")) + .withStatus(ACCEPTED) + .withIsArchived(true).build(); + + String expectedMessage = String.format(ArchiveCommand.MESSAGE_ARCHIVE_APPLICATION_SUCCESS, archivedApplication); + + ModelManager expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + expectedModel.setApplication(model.getSortedFilteredInternshipList().get(0), archivedApplication); + + try { + CommandResult archivedMessage = archiveCommand.execute(model); + assertCommandSuccess(archivedMessage, model, expectedMessage, expectedModel); + + } catch (CommandException ce) { + fail(ce.toString()); + } + } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showInternshipAtIndex(model, Index.fromOneBased(3)); + + Index outOfBoundIndex = Index.fromOneBased(3); + // ensures that outOfBoundIndex is still in bounds of internships list + assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getInternshipList().size()); + + ArchiveCommand archiveCommand = new ArchiveCommand(outOfBoundIndex); + + assertCommandFailure(archiveCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + @Test + public void equals() { + ArchiveCommand archiveFirstCommand = new ArchiveCommand(Index.fromOneBased(3)); + ArchiveCommand archiveSecondCommand = new ArchiveCommand(Index.fromOneBased(5)); + + // same object -> returns true + assertTrue(archiveFirstCommand.equals(archiveFirstCommand)); + + // same values -> returns true + ArchiveCommand archiveFirstCommandCopy = new ArchiveCommand(Index.fromOneBased(3)); + assertTrue(archiveFirstCommand.equals(archiveFirstCommandCopy)); + + // different types -> returns false + assertFalse(archiveFirstCommandCopy.equals(1)); + + // null -> returns false + assertFalse(archiveFirstCommandCopy.equals(null)); + + // different person -> returns false + assertFalse(archiveFirstCommandCopy.equals(archiveSecondCommand)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/ClearByCommandTest.java b/src/test/java/seedu/address/logic/commands/ClearByCommandTest.java new file mode 100644 index 00000000000..2eb2ecf3950 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/ClearByCommandTest.java @@ -0,0 +1,202 @@ +package seedu.address.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static seedu.address.logic.commands.ClearByCommand.MESSAGE_CLEAR_SUCCESS; +import static seedu.address.logic.commands.ClearByCommand.MESSAGE_EMPTY_FILTERED_LIST; +import static seedu.address.logic.commands.ClearByCommand.MESSAGE_NULL; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalInternships.getTypicalInternships; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.JobTitle; + +/** + * Contains integration tests (interaction with the Model) and unit tests for {@code ClearByCommand}. + */ +public class ClearByCommandTest { + + private Model model; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + } + + @Test + public void execute_clearByCompanyName_success() { + CompanyName companyName = new CompanyName("Alice Wonder"); + + ClearByCommand clearByCommand = new ClearByCommand(companyName); + + CommandResult expectedMessage = new CommandResult( + String.format(MESSAGE_CLEAR_SUCCESS, "Company name", companyName.fullName)); + + ModelManager expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + + List filteredList = getTypicalInternships().stream() + .filter(a -> (a.getCompanyName()).equals(companyName)) + .collect(Collectors.toList()); + + for (InternshipApplication ia : filteredList) { + expectedModel.deleteInternship(ia); + } + + try { + CommandResult clearByMessage = clearByCommand.execute(model); + assertCommandSuccess(clearByMessage, model, expectedMessage, expectedModel); + + } catch (CommandException ce) { + fail(ce.toString()); + } + } + + @Test + public void execute_clearByJobTitle_success() { + JobTitle jobTitle = new JobTitle("Software Engineer"); + + ClearByCommand clearByCommand = new ClearByCommand(jobTitle); + + CommandResult expectedMessage = new CommandResult( + String.format(MESSAGE_CLEAR_SUCCESS, "Job title", jobTitle.fullName)); + + ModelManager expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + + List filteredList = getTypicalInternships().stream() + .filter(a -> (a.getJobTitle()).equals(jobTitle)) + .collect(Collectors.toList()); + + for (InternshipApplication ia : filteredList) { + expectedModel.deleteInternship(ia); + } + + try { + CommandResult clearByMessage = clearByCommand.execute(model); + assertCommandSuccess(clearByMessage, model, expectedMessage, expectedModel); + + } catch (CommandException ce) { + fail(ce.toString()); + } + } + + @Test + public void execute_clearByStatus_success() { + InternshipStatus status = InternshipStatus.PENDING; + + ClearByCommand clearByCommand = new ClearByCommand(status); + + CommandResult expectedMessage = new CommandResult( + String.format(MESSAGE_CLEAR_SUCCESS, "Status", status)); + + ModelManager expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + + List filteredList = getTypicalInternships().stream() + .filter(a -> (a.getStatus().equals(status) && !a.isArchived())) + .collect(Collectors.toList()); + + for (InternshipApplication ia : filteredList) { + expectedModel.deleteInternship(ia); + } + + try { + CommandResult clearByMessage = clearByCommand.execute(model); + assertCommandSuccess(clearByMessage, model, expectedMessage, expectedModel); + + } catch (CommandException ce) { + fail(ce.toString()); + } + } + + @Test + public void execute_clearByEmptyList_error() { + CompanyName companyName = new CompanyName("Never Found"); + + ClearByCommand clearByCommand = new ClearByCommand(companyName); + + CommandResult expectedMessage = new CommandResult( + String.format(MESSAGE_EMPTY_FILTERED_LIST, "Company name", companyName.fullName)); + + ModelManager expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + + List filteredList = getTypicalInternships().stream() + .filter(a -> (a.getCompanyName()).equals(companyName)) + .collect(Collectors.toList()); + + for (InternshipApplication ia : filteredList) { + expectedModel.deleteInternship(ia); + } + + try { + CommandResult clearByMessage = clearByCommand.execute(model); + assertCommandSuccess(clearByMessage, model, expectedMessage, expectedModel); + + } catch (CommandException ce) { + fail(ce.toString()); + } + } + + @Test + public void execute_clearByEmptyFilteredList_error() { + model = new ModelManager(); + InternshipStatus status = InternshipStatus.PENDING; + + ClearByCommand clearByCommand = new ClearByCommand(status); + + CommandResult expectedMessage = new CommandResult(MESSAGE_NULL); + + ModelManager expectedModel = new ModelManager(); + try { + CommandResult clearByMessage = clearByCommand.execute(model); + assertCommandSuccess(clearByMessage, model, expectedMessage, expectedModel); + + } catch (CommandException ce) { + fail(ce.toString()); + } + } + + @Test + public void equals() { + ClearByCommand clearByFirstCommand = new ClearByCommand(new CompanyName("Company 1")); + ClearByCommand clearBySecondCommand = new ClearByCommand(new JobTitle("Developer")); + ClearByCommand clearByThirdCommand = new ClearByCommand(InternshipStatus.PENDING); + + // same object -> returns true + assertTrue(clearByFirstCommand.equals(clearByFirstCommand)); + assertTrue(clearByThirdCommand.equals(clearByThirdCommand)); + + // same values -> returns true + ClearByCommand clearByFirstCommandCopy = new ClearByCommand(new CompanyName("Company 1")); + assertTrue(clearByFirstCommand.equals(clearByFirstCommandCopy)); + + // different types -> returns false + assertFalse(clearByFirstCommand.equals(1)); + + // null -> returns false + assertFalse(clearByFirstCommand.equals(null)); + + // different application -> returns false + assertFalse(clearByFirstCommand.equals(clearBySecondCommand)); + assertFalse(clearBySecondCommand.equals(clearByThirdCommand)); + assertFalse(clearByThirdCommand.equals(clearByFirstCommand)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java b/src/test/java/seedu/address/logic/commands/ClearCommandTest.java index 80d9110c03a..b0edb413136 100644 --- a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/ClearCommandTest.java @@ -1,8 +1,11 @@ package seedu.address.logic.commands; import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import seedu.address.model.AddressBook; @@ -10,23 +13,37 @@ import seedu.address.model.ModelManager; import seedu.address.model.UserPrefs; +/** + * Contains integration tests (interaction with the Model) for {@code ClearCommand}. + */ public class ClearCommandTest { + private Model model; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + } + @Test - public void execute_emptyAddressBook_success() { - Model model = new ModelManager(); + public void execute_emptyInternEase_success() { + model = new ModelManager(); Model expectedModel = new ModelManager(); - assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); + assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_NULL, expectedModel); } + @Test - public void execute_nonEmptyAddressBook_success() { - Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - expectedModel.setAddressBook(new AddressBook()); + public void execute_nonEmptyInternEase_success() { + Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + expectedModel.setInternEase(new AddressBook()); + + ClearCommand clearCommand = new ClearCommand(); + CommandResult result = clearCommand.execute(model); - assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); + assertCommandSuccess(result, model, ClearCommand.MESSAGE_SUCCESS, expectedModel); } } diff --git a/src/test/java/seedu/address/logic/commands/CommandResultTest.java b/src/test/java/seedu/address/logic/commands/CommandResultTest.java index 4f3eb46e9ef..ef82cba04d0 100644 --- a/src/test/java/seedu/address/logic/commands/CommandResultTest.java +++ b/src/test/java/seedu/address/logic/commands/CommandResultTest.java @@ -14,7 +14,8 @@ public void equals() { // same values -> returns true assertTrue(commandResult.equals(new CommandResult("feedback"))); - assertTrue(commandResult.equals(new CommandResult("feedback", false, false))); + assertTrue(commandResult.equals(new CommandResult( + "feedback", false, false, false))); // same object -> returns true assertTrue(commandResult.equals(commandResult)); @@ -29,10 +30,16 @@ public void equals() { assertFalse(commandResult.equals(new CommandResult("different"))); // different showHelp value -> returns false - assertFalse(commandResult.equals(new CommandResult("feedback", true, false))); + assertFalse(commandResult.equals(new CommandResult( + "feedback", true, false, false))); // different exit value -> returns false - assertFalse(commandResult.equals(new CommandResult("feedback", false, true))); + assertFalse(commandResult.equals(new CommandResult( + "feedback", false, true, false))); + + // different showReminder value -> returns false + assertFalse(commandResult.equals(new CommandResult( + "feedback", false, false, true))); } @Test @@ -46,9 +53,12 @@ public void hashcode() { assertNotEquals(commandResult.hashCode(), new CommandResult("different").hashCode()); // different showHelp value -> returns different hashcode - assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false).hashCode()); + assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false, false).hashCode()); // different exit value -> returns different hashcode - assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true).hashCode()); + assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true, false).hashCode()); + + // different showReminder value -> returns different hashcode + assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, false, true).hashCode()); } } diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java index 643a1d08069..68780c4a0f7 100644 --- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java +++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java @@ -2,11 +2,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COVER_LETTER; 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.logic.parser.CliSyntax.PREFIX_RESUME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_STATUS; import static seedu.address.testutil.Assert.assertThrows; import java.util.ArrayList; @@ -14,59 +15,141 @@ import java.util.List; import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.contact.EditContactCommand; +import seedu.address.logic.commands.documents.EditDocumentsCommand; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.AddressBook; import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.model.person.Person; -import seedu.address.testutil.EditPersonDescriptorBuilder; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.NameContainsKeywordsPredicate; +import seedu.address.testutil.EditContactDescriptorBuilder; +import seedu.address.testutil.EditDocumentsDescriptorBuilder; +import seedu.address.testutil.EditInternshipDescriptorBuilder; /** * Contains helper methods for testing commands. */ public class CommandTestUtil { - public static final String VALID_NAME_AMY = "Amy Bee"; - public static final String VALID_NAME_BOB = "Bob Choo"; - public static final String VALID_PHONE_AMY = "11111111"; - public static final String VALID_PHONE_BOB = "22222222"; - public static final String VALID_EMAIL_AMY = "amy@example.com"; - public static final String VALID_EMAIL_BOB = "bob@example.com"; - public static final String VALID_ADDRESS_AMY = "Block 312, Amy Street 1"; - public static final String VALID_ADDRESS_BOB = "Block 123, Bobby Street 3"; - public static final String VALID_TAG_HUSBAND = "husband"; - public static final String VALID_TAG_FRIEND = "friend"; - - public static final String NAME_DESC_AMY = " " + PREFIX_NAME + VALID_NAME_AMY; - public static final String NAME_DESC_BOB = " " + PREFIX_NAME + VALID_NAME_BOB; - public static final String PHONE_DESC_AMY = " " + PREFIX_PHONE + VALID_PHONE_AMY; - public static final String PHONE_DESC_BOB = " " + PREFIX_PHONE + VALID_PHONE_BOB; - public static final String EMAIL_DESC_AMY = " " + PREFIX_EMAIL + VALID_EMAIL_AMY; - public static final String EMAIL_DESC_BOB = " " + PREFIX_EMAIL + VALID_EMAIL_BOB; - public static final String ADDRESS_DESC_AMY = " " + PREFIX_ADDRESS + VALID_ADDRESS_AMY; - public static final String ADDRESS_DESC_BOB = " " + PREFIX_ADDRESS + VALID_ADDRESS_BOB; - public static final String TAG_DESC_FRIEND = " " + PREFIX_TAG + VALID_TAG_FRIEND; - public static final String TAG_DESC_HUSBAND = " " + PREFIX_TAG + VALID_TAG_HUSBAND; - - public static final String INVALID_NAME_DESC = " " + PREFIX_NAME + "James&"; // '&' not allowed in names + public static final String VALID_COMPANY_NAME_GOOGLE = "Google"; + public static final String VALID_COMPANY_NAME_NETFLIX = "Netflix"; + public static final String VALID_COMPANY_NAME_ORACLE = "Oracle"; + public static final String VALID_COMPANY_NAME_BANK_OF_AMERICA = "Bank of America"; + public static final String VALID_COMPANY_NAME_CARL_KURZ = "Carl Kurz"; + public static final String VALID_COMPANY_NAME_META = "Meta"; + public static final String VALID_COMPANY_NAME_AMAZON = "Amazon"; + public static final String VALID_COMPANY_NAME_DEUTSCHE_BANK = "Deutsche Bank"; + public static final String VALID_JOB_TITLE_PRODUCT_MANAGER = "Product Manager"; + public static final String VALID_JOB_TITLE_NETWORK_ENGINEER = "Network Engineer"; + public static final String VALID_JOB_TITLE_DATA_ENGINEER = "Data Engineer"; + public static final String VALID_JOB_TITLE_SOFTWARE_ENGINEER = "Software Engineer"; + public static final String VALID_JOB_TITLE_SOFTWARE_TESTER = "Software Tester"; + public static final String VALID_JOB_TITLE_CLOUD_ENGINEER = "Cloud Engineer"; + public static final String VALID_JOB_TITLE_WEB_DEVELOPER = "Web Developer"; + public static final String VALID_LOCATION_CLEMENTI = "Clementi"; + public static final String VALID_SALARY_META = "6000 SGD"; + public static final String VALID_SALARY_GOOGLE = "1000 SGD"; + public static final String VALID_RATING_META = "4/5"; + public static final String VALID_RATING_GOOGLE = "5*"; + public static final String VALID_QUALIFICATION_CS = "CS undergrad"; + public static final String VALID_QUALIFICATION_CAP = "CAP 4.5+"; + public static final String VALID_PROGRAMMING_LANGUAGE_C = "C"; + public static final String VALID_PROGRAMMING_LANGUAGE_PYTHON = "Python"; + public static final String VALID_REVIEW_ENV = "Nice environment"; + public static final String VALID_NOTES_CAMPUS = "Recruiter coming campus"; + public static final String VALID_REFLECTION_RESEARCH = "Research recruiter LinkedIn"; + public static final String VALID_PHONE_COMPANY_A = "33333333"; + public static final String VALID_PHONE_COMPANY_B = "55555555"; + public static final String VALID_PHONE_BANK_OF_AMERICA = "33333333"; + public static final String VALID_PHONE_META = "55555555"; + public static final String VALID_PHONE_AMAZON = "66666666"; + + public static final String VALID_EMAIL_BANK_OF_AMERICA = "example@bankofamerica.com"; + public static final String VALID_EMAIL_META = "meta@example.com"; + public static final String VALID_EMAIL_AMAZON = "example@amazon.com"; + public static final String VALID_RESUME_LINK_GOOGLE = "https://drive.example.com/resume_google"; + public static final String VALID_RESUME_LINK_NETFLIX = "https://drive.example.com/resume_netflix"; + public static final String VALID_RESUME_LINK_ORACLE = "https://drive.example.com/resume_oracle"; + public static final String VALID_COVER_LETTER_LINK_GOOGLE = "https://drive.example.com/coverletter_google"; + public static final String VALID_COVER_LETTER_LINK_NETFLIX = "https://drive.example.com/coverletter_netflix"; + public static final String VALID_COVER_LETTER_LINK_ORACLE = "https://drive.example.com/coverletter_oracle"; + public static final String VALID_STATUS_PENDING = "PENDING"; + public static final String VALID_STATUS_ACCEPTED = "ACCEPTED"; + + + public static final String COMPANY_NAME_DESC_META = " " + PREFIX_COMPANY_NAME + VALID_COMPANY_NAME_META; + public static final String COMPANY_NAME_DESC_GOOGLE = " " + PREFIX_COMPANY_NAME + VALID_COMPANY_NAME_GOOGLE; + public static final String PHONE_DESC_COMPANY_A = " " + PREFIX_PHONE + VALID_PHONE_COMPANY_A; + public static final String PHONE_DESC_COMPANY_B = " " + PREFIX_PHONE + VALID_PHONE_COMPANY_B; + public static final String RESUME_DESC_GOOGLE = " " + PREFIX_RESUME + VALID_RESUME_LINK_GOOGLE; + public static final String RESUME_DESC_NETFLIX = " " + PREFIX_RESUME + VALID_RESUME_LINK_NETFLIX; + public static final String COVER_LETTER_DESC_GOOGLE = " " + PREFIX_COVER_LETTER + VALID_COVER_LETTER_LINK_GOOGLE; + public static final String COVER_LETTER_DESC_NETFLIX = " " + PREFIX_COVER_LETTER + VALID_COVER_LETTER_LINK_NETFLIX; + public static final String PHONE_DESC_BANK_OF_AMERICA = " " + PREFIX_PHONE + VALID_PHONE_BANK_OF_AMERICA; + public static final String EMAIL_DESC_BANK_OF_AMERICA = " " + PREFIX_EMAIL + VALID_EMAIL_BANK_OF_AMERICA; + public static final String PHONE_DESC_META = " " + PREFIX_PHONE + VALID_PHONE_META; + public static final String EMAIL_DESC_META = " " + PREFIX_EMAIL + VALID_EMAIL_META; + public static final String STATUS_DESC_PENDING = " " + PREFIX_STATUS + VALID_STATUS_PENDING; + public static final String STATUS_DESC_ACCEPTED = " " + PREFIX_STATUS + VALID_STATUS_ACCEPTED; + public static final String INVALID_PHONE_DESC = " " + PREFIX_PHONE + "911a"; // 'a' not allowed in phones public static final String INVALID_EMAIL_DESC = " " + PREFIX_EMAIL + "bob!yahoo"; // missing '@' symbol - public static final String INVALID_ADDRESS_DESC = " " + PREFIX_ADDRESS; // empty string not allowed for addresses - public static final String INVALID_TAG_DESC = " " + PREFIX_TAG + "hubby*"; // '*' not allowed in tags + + // protocol missing in URL + public static final String INVALID_RESUME_DESC = " " + PREFIX_RESUME + "example.com/resume_google"; + // missing domain name + public static final String INVALID_COVER_LETTER_DESC = " " + PREFIX_COVER_LETTER + "https://-/cover_letter_google"; + public static final String INVALID_STATUS_DESC = " " + PREFIX_STATUS + "DELETED"; public static final String PREAMBLE_WHITESPACE = "\t \r \n"; public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble"; - public static final EditCommand.EditPersonDescriptor DESC_AMY; - public static final EditCommand.EditPersonDescriptor DESC_BOB; + public static final EditCommand.EditInternshipDescriptor DESC_META; + public static final EditCommand.EditInternshipDescriptor DESC_GOOGLE; + + public static final EditDocumentsCommand.EditDocumentsDescriptor DESC_DOCUMENTS_GOOGLE; + public static final EditDocumentsCommand.EditDocumentsDescriptor DESC_DOCUMENTS_NETFLIX; + + public static final EditContactCommand.EditContactDescriptor DESC_BANK_OF_AMERICA_CONTACT; + public static final EditContactCommand.EditContactDescriptor DESC_META_CONTACT; static { - DESC_AMY = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY) - .withPhone(VALID_PHONE_AMY).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY) - .withTags(VALID_TAG_FRIEND).build(); - DESC_BOB = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB) - .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB) - .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build(); + DESC_META = new EditInternshipDescriptorBuilder() + .withCompanyName(VALID_COMPANY_NAME_META) + .withJobTitle(VALID_JOB_TITLE_SOFTWARE_ENGINEER) + .withLocation(VALID_LOCATION_CLEMENTI) + .withSalary(VALID_SALARY_META) + .withRating(VALID_RATING_META) + .withQualifications(VALID_QUALIFICATION_CS, VALID_QUALIFICATION_CAP) + .withProgrammingLanguages(VALID_PROGRAMMING_LANGUAGE_C, VALID_PROGRAMMING_LANGUAGE_PYTHON) + .withReviews(VALID_REVIEW_ENV) + .withNotes(VALID_NOTES_CAMPUS) + .withReflections(VALID_REFLECTION_RESEARCH) + .build(); + DESC_GOOGLE = new EditInternshipDescriptorBuilder() + .withCompanyName(VALID_COMPANY_NAME_GOOGLE) + .withJobTitle(VALID_JOB_TITLE_SOFTWARE_TESTER) + .withLocation(VALID_LOCATION_CLEMENTI) + .withSalary(VALID_SALARY_GOOGLE) + .withRating(VALID_RATING_GOOGLE) + .withQualifications(VALID_QUALIFICATION_CS, VALID_QUALIFICATION_CAP) + .withProgrammingLanguages(VALID_PROGRAMMING_LANGUAGE_C, VALID_PROGRAMMING_LANGUAGE_PYTHON) + .withReviews(VALID_REVIEW_ENV) + .withNotes(VALID_NOTES_CAMPUS) + .withReflections(VALID_REFLECTION_RESEARCH) + .build(); + DESC_DOCUMENTS_GOOGLE = new EditDocumentsDescriptorBuilder() + .withResumeLink(VALID_RESUME_LINK_GOOGLE) + .withCoverLetterLink(VALID_COVER_LETTER_LINK_GOOGLE) + .build(); + DESC_DOCUMENTS_NETFLIX = new EditDocumentsDescriptorBuilder() + .withResumeLink(VALID_RESUME_LINK_NETFLIX) + .withCoverLetterLink(VALID_COVER_LETTER_LINK_NETFLIX) + .build(); + DESC_BANK_OF_AMERICA_CONTACT = new EditContactDescriptorBuilder().withPhone(VALID_PHONE_BANK_OF_AMERICA) + .withEmail(VALID_EMAIL_BANK_OF_AMERICA).build(); + DESC_META_CONTACT = new EditContactDescriptorBuilder().withPhone(VALID_PHONE_META) + .withEmail(VALID_EMAIL_META).build(); } /** @@ -75,7 +158,7 @@ public class CommandTestUtil { * - the {@code actualModel} matches {@code expectedModel} */ public static void assertCommandSuccess(Command command, Model actualModel, CommandResult expectedCommandResult, - Model expectedModel) { + Model expectedModel) { try { CommandResult result = command.execute(actualModel); assertEquals(expectedCommandResult, result); @@ -95,34 +178,59 @@ public static void assertCommandSuccess(Command command, Model actualModel, Stri assertCommandSuccess(command, actualModel, expectedCommandResult, expectedModel); } + /** + * Compares the given {@code result}, confirms that
+ * - the {@link CommandResult} matches {@code expectedCommandResult}
+ * - the {@code actualModel} matches {@code expectedModel} + */ + + public static void assertCommandSuccess(CommandResult result, Model actualModel, + CommandResult expectedCommandResult, Model expectedModel) { + assertEquals(expectedCommandResult, result); + assertEquals(expectedModel, actualModel); + } + + /** + * Convenience wrapper to {@link #assertCommandSuccess(CommandResult, Model, CommandResult, Model)} + * that takes a string {@code expectedMessage}. + */ + + public static void assertCommandSuccess(CommandResult result, Model actualModel, String expectedMessage, + Model expectedModel) { + CommandResult expectedCommandResult = new CommandResult(expectedMessage); + assertCommandSuccess(result, actualModel, expectedCommandResult, expectedModel); + } + /** * Executes the given {@code command}, confirms that
* - a {@code CommandException} is thrown
* - the CommandException message matches {@code expectedMessage}
- * - the address book, filtered person list and selected person in {@code actualModel} remain unchanged + * - the address book, filtered internship list and selected internship in {@code actualModel} remain unchanged */ public static void assertCommandFailure(Command command, Model actualModel, String expectedMessage) { // we are unable to defensively copy the model for comparison later, so we can // only do so by copying its components. AddressBook expectedAddressBook = new AddressBook(actualModel.getAddressBook()); - List expectedFilteredList = new ArrayList<>(actualModel.getFilteredPersonList()); + List expectedFilteredList = new ArrayList<>( + actualModel.getSortedFilteredInternshipList()); assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel)); assertEquals(expectedAddressBook, actualModel.getAddressBook()); - assertEquals(expectedFilteredList, actualModel.getFilteredPersonList()); + assertEquals(expectedFilteredList, actualModel.getSortedFilteredInternshipList()); } + /** - * Updates {@code model}'s filtered list to show only the person at the given {@code targetIndex} in the + * Updates {@code model}'s filtered list to show only the internship at the given {@code targetIndex} in the * {@code model}'s address book. */ - public static void showPersonAtIndex(Model model, Index targetIndex) { - assertTrue(targetIndex.getZeroBased() < model.getFilteredPersonList().size()); + public static void showInternshipAtIndex(Model model, Index targetIndex) { + assertTrue(targetIndex.getZeroBased() < model.getSortedFilteredInternshipList().size()); - Person person = model.getFilteredPersonList().get(targetIndex.getZeroBased()); - final String[] splitName = person.getName().fullName.split("\\s+"); - model.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(splitName[0]))); + InternshipApplication internship = model.getSortedFilteredInternshipList().get(targetIndex.getZeroBased()); + final String[] splitName = internship.getCompanyName().fullName.split("\\s+"); + model.updateFilteredInternshipList(new NameContainsKeywordsPredicate(Arrays.asList(splitName[0]))); - assertEquals(1, model.getFilteredPersonList().size()); + assertEquals(1, model.getSortedFilteredInternshipList().size()); } - } + diff --git a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java index 45a8c910ba1..99a6c70a490 100644 --- a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java @@ -2,90 +2,112 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; 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.ModelManager; import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; +import seedu.address.model.application.InternshipApplication; /** - * Contains integration tests (interaction with the Model) and unit tests for - * {@code DeleteCommand}. + * Contains integration tests (interaction with the Model) and unit tests for {@code DeleteCommand}. */ public class DeleteCommandTest { - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + private Model model; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + } @Test public void execute_validIndexUnfilteredList_success() { - Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON); + InternshipApplication internshipToDelete = model.getSortedFilteredInternshipList() + .get(Index.fromOneBased(3).getZeroBased()); + DeleteCommand deleteCommand = new DeleteCommand(Index.fromOneBased(3)); - String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); + String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_APPLICATION_SUCCESS, internshipToDelete); - ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - expectedModel.deletePerson(personToDelete); + ModelManager expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + expectedModel.deleteInternship(internshipToDelete); - assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + try { + CommandResult deleteMessage = deleteCommand.execute(model); + assertCommandSuccess(deleteMessage, model, expectedMessage, expectedModel); + + } catch (CommandException ce) { + fail(ce.toString()); + } } @Test public void execute_invalidIndexUnfilteredList_throwsCommandException() { - Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); + Index outOfBoundIndex = Index.fromOneBased(model.getSortedFilteredInternshipList().size() + 1); DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex); - assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); } @Test public void execute_validIndexFilteredList_success() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); + showInternshipAtIndex(model, Index.fromOneBased(1)); + + InternshipApplication internshipToDelete = model.getSortedFilteredInternshipList() + .get(Index.fromOneBased(1).getZeroBased()); + DeleteCommand deleteCommand = new DeleteCommand(Index.fromOneBased(1)); - Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON); + String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_APPLICATION_SUCCESS, internshipToDelete); - String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); + ModelManager expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + expectedModel.deleteInternship(internshipToDelete); - Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - expectedModel.deletePerson(personToDelete); - showNoPerson(expectedModel); + try { + CommandResult deleteMessage = deleteCommand.execute(model); + assertCommandSuccess(deleteMessage, model, expectedMessage, expectedModel); - assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + } catch (CommandException ce) { + fail(ce.toString()); + } } @Test public void execute_invalidIndexFilteredList_throwsCommandException() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); + showInternshipAtIndex(model, Index.fromOneBased(3)); - Index outOfBoundIndex = INDEX_SECOND_PERSON; - // ensures that outOfBoundIndex is still in bounds of address book list - assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size()); + Index outOfBoundIndex = Index.fromOneBased(3); + // ensures that outOfBoundIndex is still in bounds of internships list + assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getInternshipList().size()); DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex); - assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); } @Test public void equals() { - DeleteCommand deleteFirstCommand = new DeleteCommand(INDEX_FIRST_PERSON); - DeleteCommand deleteSecondCommand = new DeleteCommand(INDEX_SECOND_PERSON); + DeleteCommand deleteFirstCommand = new DeleteCommand(Index.fromOneBased(3)); + DeleteCommand deleteSecondCommand = new DeleteCommand(Index.fromOneBased(5)); // same object -> returns true assertTrue(deleteFirstCommand.equals(deleteFirstCommand)); // same values -> returns true - DeleteCommand deleteFirstCommandCopy = new DeleteCommand(INDEX_FIRST_PERSON); + DeleteCommand deleteFirstCommandCopy = new DeleteCommand(Index.fromOneBased(3)); assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy)); // different types -> returns false @@ -97,13 +119,4 @@ public void equals() { // different person -> returns false assertFalse(deleteFirstCommand.equals(deleteSecondCommand)); } - - /** - * Updates {@code model}'s filtered list to show no one. - */ - private void showNoPerson(Model model) { - model.updateFilteredPersonList(p -> false); - - assertTrue(model.getFilteredPersonList().isEmpty()); - } } diff --git a/src/test/java/seedu/address/logic/commands/EditCommandTest.java b/src/test/java/seedu/address/logic/commands/EditCommandTest.java index 214c6c2507b..b13c9c5f6f5 100644 --- a/src/test/java/seedu/address/logic/commands/EditCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/EditCommandTest.java @@ -2,130 +2,155 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static seedu.address.logic.commands.CommandTestUtil.DESC_GOOGLE; +import static seedu.address.logic.commands.CommandTestUtil.DESC_META; +import static seedu.address.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_META; +import static seedu.address.logic.commands.CommandTestUtil.VALID_QUALIFICATION_CAP; import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.address.model.application.InternshipStatus.ACCEPTED; +import static seedu.address.testutil.InternshipBuilder.DEFAULT_EMAIL; +import static seedu.address.testutil.InternshipBuilder.DEFAULT_PHONE; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_APPLICATION; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; import org.junit.jupiter.api.Test; import seedu.address.commons.core.Messages; import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; +import seedu.address.logic.commands.EditCommand.EditInternshipDescriptor; import seedu.address.model.AddressBook; import seedu.address.model.Model; import seedu.address.model.ModelManager; import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; -import seedu.address.testutil.EditPersonDescriptorBuilder; -import seedu.address.testutil.PersonBuilder; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.contact.Contact; +import seedu.address.model.contact.Email; +import seedu.address.model.contact.Phone; +import seedu.address.testutil.EditInternshipDescriptorBuilder; +import seedu.address.testutil.InternshipBuilder; /** * Contains integration tests (interaction with the Model) and unit tests for EditCommand. */ public class EditCommandTest { - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); @Test public void execute_allFieldsSpecifiedUnfilteredList_success() { - Person editedPerson = new PersonBuilder().build(); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(editedPerson).build(); - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, descriptor); + InternshipApplication editedInternship = new InternshipBuilder() + .withContact(new Contact(new Phone(DEFAULT_PHONE), new Email(DEFAULT_EMAIL))) + .withStatus(ACCEPTED) + .withInterviewDate(new InterviewDate("2023-04-01 08:00 PM")) + .build(); + EditInternshipDescriptor descriptor = new EditInternshipDescriptorBuilder(editedInternship).build(); + EditCommand editCommand = new EditCommand(INDEX_FIRST_APPLICATION, descriptor); - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); + String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_INTERNSHIP_SUCCESS, editedInternship); - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson); + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(), + model.getTodoList(), model.getNoteList()); + expectedModel.setApplication(model.getSortedFilteredInternshipList().get(0), editedInternship); assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); } @Test public void execute_someFieldsSpecifiedUnfilteredList_success() { - Index indexLastPerson = Index.fromOneBased(model.getFilteredPersonList().size()); - Person lastPerson = model.getFilteredPersonList().get(indexLastPerson.getZeroBased()); + Index indexLastInternship = Index.fromOneBased(model.getSortedFilteredInternshipList().size()); + InternshipApplication lastInternship = model.getSortedFilteredInternshipList() + .get(indexLastInternship.getZeroBased()); - PersonBuilder personInList = new PersonBuilder(lastPerson); - Person editedPerson = personInList.withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB) - .withTags(VALID_TAG_HUSBAND).build(); + InternshipBuilder internshipInList = new InternshipBuilder(lastInternship); + InternshipApplication editedInternship = internshipInList.withCompanyName(VALID_COMPANY_NAME_META) + .withQualifications(VALID_QUALIFICATION_CAP).build(); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB) - .withPhone(VALID_PHONE_BOB).withTags(VALID_TAG_HUSBAND).build(); - EditCommand editCommand = new EditCommand(indexLastPerson, descriptor); + EditInternshipDescriptor descriptor = new EditInternshipDescriptorBuilder() + .withCompanyName(VALID_COMPANY_NAME_META) + .withQualifications(VALID_QUALIFICATION_CAP).build(); + EditCommand editCommand = new EditCommand(indexLastInternship, descriptor); - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); + String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_INTERNSHIP_SUCCESS, editedInternship); - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - expectedModel.setPerson(lastPerson, editedPerson); + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + expectedModel.setApplication(lastInternship, editedInternship); assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); } @Test public void execute_noFieldSpecifiedUnfilteredList_success() { - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, new EditPersonDescriptor()); - Person editedPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); + EditCommand editCommand = new EditCommand(INDEX_FIRST_APPLICATION, new EditInternshipDescriptor()); + InternshipApplication editedInternship = model.getSortedFilteredInternshipList() + .get(INDEX_FIRST_APPLICATION.getZeroBased()); - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); + String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_INTERNSHIP_SUCCESS, editedInternship); - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); } @Test public void execute_filteredList_success() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); + showInternshipAtIndex(model, INDEX_FIRST_APPLICATION); - Person personInFilteredList = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - Person editedPerson = new PersonBuilder(personInFilteredList).withName(VALID_NAME_BOB).build(); - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, - new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build()); + InternshipApplication internshipInFilteredList = model.getSortedFilteredInternshipList() + .get(INDEX_FIRST_APPLICATION.getZeroBased()); + InternshipApplication editedInternship = new InternshipBuilder(internshipInFilteredList) + .withCompanyName(VALID_COMPANY_NAME_META).build(); + EditCommand editCommand = new EditCommand(INDEX_FIRST_APPLICATION, + new EditInternshipDescriptorBuilder().withCompanyName(VALID_COMPANY_NAME_META).build()); - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); + String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_INTERNSHIP_SUCCESS, editedInternship); - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson); + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + expectedModel.setApplication(model.getSortedFilteredInternshipList().get(0), editedInternship); assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); } @Test - public void execute_duplicatePersonUnfilteredList_failure() { - Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(firstPerson).build(); - EditCommand editCommand = new EditCommand(INDEX_SECOND_PERSON, descriptor); + public void execute_duplicateInternshipUnfilteredList_failure() { + InternshipApplication firstInternship = model.getSortedFilteredInternshipList() + .get(INDEX_FIRST_APPLICATION.getZeroBased()); + EditInternshipDescriptor descriptor = new EditInternshipDescriptorBuilder(firstInternship).build(); + EditCommand editCommand = new EditCommand(INDEX_SECOND_APPLICATION, descriptor); - assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_PERSON); + assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_INTERNSHIP); } @Test - public void execute_duplicatePersonFilteredList_failure() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); + public void execute_duplicateInternshipFilteredList_failure() { + showInternshipAtIndex(model, INDEX_FIRST_APPLICATION); - // edit person in filtered list into a duplicate in address book - Person personInList = model.getAddressBook().getPersonList().get(INDEX_SECOND_PERSON.getZeroBased()); - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, - new EditPersonDescriptorBuilder(personInList).build()); + // edit internship in filtered list into a duplicate in address book + InternshipApplication internshipInList = model.getAddressBook().getInternshipList() + .get(INDEX_SECOND_APPLICATION.getZeroBased()); + EditCommand editCommand = new EditCommand(INDEX_FIRST_APPLICATION, + new EditInternshipDescriptorBuilder(internshipInList).build()); - assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_PERSON); + assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_INTERNSHIP); } @Test - public void execute_invalidPersonIndexUnfilteredList_failure() { - Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build(); + public void execute_invalidInternshipIndexUnfilteredList_failure() { + Index outOfBoundIndex = Index.fromOneBased(model.getSortedFilteredInternshipList().size() + 1); + EditInternshipDescriptor descriptor = new EditInternshipDescriptorBuilder() + .withCompanyName(VALID_COMPANY_NAME_META).build(); EditCommand editCommand = new EditCommand(outOfBoundIndex, descriptor); - assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); } /** @@ -133,25 +158,25 @@ public void execute_invalidPersonIndexUnfilteredList_failure() { * but smaller than size of address book */ @Test - public void execute_invalidPersonIndexFilteredList_failure() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - Index outOfBoundIndex = INDEX_SECOND_PERSON; + public void execute_invalidInternshipIndexFilteredList_failure() { + showInternshipAtIndex(model, INDEX_FIRST_APPLICATION); + Index outOfBoundIndex = INDEX_SECOND_APPLICATION; // ensures that outOfBoundIndex is still in bounds of address book list - assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size()); + assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getInternshipList().size()); EditCommand editCommand = new EditCommand(outOfBoundIndex, - new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build()); + new EditInternshipDescriptorBuilder().withCompanyName(VALID_COMPANY_NAME_META).build()); - assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); } @Test public void equals() { - final EditCommand standardCommand = new EditCommand(INDEX_FIRST_PERSON, DESC_AMY); + final EditCommand standardCommand = new EditCommand(INDEX_FIRST_APPLICATION, DESC_META); // same values -> returns true - EditPersonDescriptor copyDescriptor = new EditPersonDescriptor(DESC_AMY); - EditCommand commandWithSameValues = new EditCommand(INDEX_FIRST_PERSON, copyDescriptor); + EditInternshipDescriptor copyDescriptor = new EditInternshipDescriptor(DESC_META); + EditCommand commandWithSameValues = new EditCommand(INDEX_FIRST_APPLICATION, copyDescriptor); assertTrue(standardCommand.equals(commandWithSameValues)); // same object -> returns true @@ -164,10 +189,10 @@ public void equals() { assertFalse(standardCommand.equals(new ClearCommand())); // different index -> returns false - assertFalse(standardCommand.equals(new EditCommand(INDEX_SECOND_PERSON, DESC_AMY))); + assertFalse(standardCommand.equals(new EditCommand(INDEX_SECOND_APPLICATION, DESC_META))); // different descriptor -> returns false - assertFalse(standardCommand.equals(new EditCommand(INDEX_FIRST_PERSON, DESC_BOB))); + assertFalse(standardCommand.equals(new EditCommand(INDEX_FIRST_APPLICATION, DESC_GOOGLE))); } } diff --git a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java b/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java index e0288792e72..74b3a02b7a5 100644 --- a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java +++ b/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java @@ -1,58 +1,58 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.testutil.EditPersonDescriptorBuilder; - -public class EditPersonDescriptorTest { - - @Test - public void equals() { - // same values -> returns true - EditPersonDescriptor descriptorWithSameValues = new EditPersonDescriptor(DESC_AMY); - assertTrue(DESC_AMY.equals(descriptorWithSameValues)); - - // same object -> returns true - assertTrue(DESC_AMY.equals(DESC_AMY)); - - // null -> returns false - assertFalse(DESC_AMY.equals(null)); - - // different types -> returns false - assertFalse(DESC_AMY.equals(5)); - - // different values -> returns false - assertFalse(DESC_AMY.equals(DESC_BOB)); - - // different name -> returns false - EditPersonDescriptor editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withName(VALID_NAME_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different phone -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withPhone(VALID_PHONE_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different email -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withEmail(VALID_EMAIL_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different address -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withAddress(VALID_ADDRESS_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different tags -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withTags(VALID_TAG_HUSBAND).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - } -} +//package seedu.address.logic.commands; +// +//import static org.junit.jupiter.api.Assertions.assertFalse; +//import static org.junit.jupiter.api.Assertions.assertTrue; +//import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY; +//import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +// +//import org.junit.jupiter.api.Test; +// +//import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; +//import seedu.address.testutil.EditPersonDescriptorBuilder; +// +//public class EditPersonDescriptorTest { +// +// @Test +// public void equals() { +// // same values -> returns true +// EditPersonDescriptor descriptorWithSameValues = new EditPersonDescriptor(DESC_AMY); +// assertTrue(DESC_AMY.equals(descriptorWithSameValues)); +// +// // same object -> returns true +// assertTrue(DESC_AMY.equals(DESC_AMY)); +// +// // null -> returns false +// assertFalse(DESC_AMY.equals(null)); +// +// // different types -> returns false +// assertFalse(DESC_AMY.equals(5)); +// +// // different values -> returns false +// assertFalse(DESC_AMY.equals(DESC_BOB)); +// +// // different name -> returns false +// EditPersonDescriptor editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withName(VALID_NAME_BOB).build(); +// assertFalse(DESC_AMY.equals(editedAmy)); +// +// // different phone -> returns false +// editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withPhone(VALID_PHONE_BOB).build(); +// assertFalse(DESC_AMY.equals(editedAmy)); +// +// // different email -> returns false +// editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withEmail(VALID_EMAIL_BOB).build(); +// assertFalse(DESC_AMY.equals(editedAmy)); +// +// // different address -> returns false +// editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withAddress(VALID_ADDRESS_BOB).build(); +// assertFalse(DESC_AMY.equals(editedAmy)); +// +// // different tags -> returns false +// editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withTags(VALID_TAG_HUSBAND).build(); +// assertFalse(DESC_AMY.equals(editedAmy)); +// } +//} diff --git a/src/test/java/seedu/address/logic/commands/EditStatusCommandTest.java b/src/test/java/seedu/address/logic/commands/EditStatusCommandTest.java new file mode 100644 index 00000000000..32c61afa9c1 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/EditStatusCommandTest.java @@ -0,0 +1,137 @@ +package seedu.address.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static seedu.address.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_BANK_OF_AMERICA; +import static seedu.address.logic.commands.CommandTestUtil.VALID_JOB_TITLE_SOFTWARE_ENGINEER; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_APPLICATION; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +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.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.InterviewDate; +import seedu.address.testutil.InternshipBuilder; + +public class EditStatusCommandTest { + private Model model; + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + } + + @Test + public void execute_validIndexUnfilteredList_success() { + EditStatusCommand editStatusCommand = new EditStatusCommand(Index.fromOneBased(2), + InternshipStatus.ACCEPTED); + + InternshipApplication updatedApplication = new InternshipBuilder() + .withCompanyName(VALID_COMPANY_NAME_BANK_OF_AMERICA) + .withJobTitle(VALID_JOB_TITLE_SOFTWARE_ENGINEER) + .withStatus(InternshipStatus.ACCEPTED) + .withInterviewDate(new InterviewDate("2023-04-09 12:00 PM")).build(); + + String expectedMessage = String.format(EditStatusCommand.MESSAGE_UPDATE_STATUS_SUCCESS, updatedApplication); + + ModelManager expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + expectedModel.setApplication(model.getSortedFilteredInternshipList().get(1), updatedApplication); + + try { + CommandResult archivedMessage = editStatusCommand.execute(model); + assertCommandSuccess(archivedMessage, model, expectedMessage, expectedModel); + + } catch (CommandException ce) { + fail(ce.toString()); + } + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(model.getSortedFilteredInternshipList().size() + 1); + EditStatusCommand editStatusCommand = new EditStatusCommand(outOfBoundIndex, InternshipStatus.ACCEPTED); + + assertCommandFailure(editStatusCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + @Test + public void execute_validIndexFilteredList_success() { + showInternshipAtIndex(model, INDEX_SECOND_APPLICATION); + EditStatusCommand editStatusCommand = new EditStatusCommand(Index.fromOneBased(1), + InternshipStatus.ACCEPTED); + + InternshipApplication updatedApplication = new InternshipBuilder() + .withCompanyName(VALID_COMPANY_NAME_BANK_OF_AMERICA) + .withJobTitle(VALID_JOB_TITLE_SOFTWARE_ENGINEER) + .withStatus(InternshipStatus.ACCEPTED) + .withInterviewDate(new InterviewDate("2023-04-09 12:00 PM")).build(); + + String expectedMessage = String.format(EditStatusCommand.MESSAGE_UPDATE_STATUS_SUCCESS, updatedApplication); + + ModelManager expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + expectedModel.setApplication(model.getSortedFilteredInternshipList().get(0), updatedApplication); + + try { + CommandResult archivedMessage = editStatusCommand.execute(model); + assertCommandSuccess(archivedMessage, model, expectedMessage, expectedModel); + + } catch (CommandException ce) { + fail(ce.toString()); + } + } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showInternshipAtIndex(model, INDEX_SECOND_APPLICATION); + + Index outOfBoundIndex = INDEX_SECOND_APPLICATION; + // ensures that outOfBoundIndex is still in bounds of address book list + assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getInternshipList().size()); + + EditStatusCommand editStatusCommand = new EditStatusCommand(outOfBoundIndex, InternshipStatus.ACCEPTED); + + assertCommandFailure(editStatusCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + @Test + public void equals() { + final EditStatusCommand standardCommand = new EditStatusCommand(INDEX_FIRST_APPLICATION, + InternshipStatus.ACCEPTED); + + // same values -> returns true + EditStatusCommand commandWithSameValues = new EditStatusCommand(INDEX_FIRST_APPLICATION, + InternshipStatus.ACCEPTED); + assertTrue(standardCommand.equals(commandWithSameValues)); + + // same object -> returns true + assertTrue(standardCommand.equals(standardCommand)); + + // null -> returns false + assertFalse(standardCommand.equals(null)); + + // different types -> returns false + assertFalse(standardCommand.equals(new ClearCommand())); + + // different index -> returns false + assertFalse(standardCommand.equals(new EditStatusCommand(INDEX_SECOND_APPLICATION, InternshipStatus.ACCEPTED))); + + // different status -> returns false + assertFalse(standardCommand.equals(new EditStatusCommand(INDEX_FIRST_APPLICATION, InternshipStatus.REJECTED))); + } +} diff --git a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java b/src/test/java/seedu/address/logic/commands/ExitCommandTest.java index 9533c473875..4f586039897 100644 --- a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/ExitCommandTest.java @@ -8,13 +8,22 @@ import seedu.address.model.Model; import seedu.address.model.ModelManager; +/** + * Contains integration tests (interaction with the Model) for {@code ExitCommand}. + */ public class ExitCommandTest { + private Model model = new ModelManager(); private Model expectedModel = new ModelManager(); @Test public void execute_exit_success() { - CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); - assertCommandSuccess(new ExitCommand(), model, expectedCommandResult, expectedModel); + ExitCommand exitCommand = new ExitCommand(); + CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, + false, true, false); + CommandResult mockConfirmExit = exitCommand.execute(model); + assertCommandSuccess(mockConfirmExit, model, expectedCommandResult, expectedModel); } } + + diff --git a/src/test/java/seedu/address/logic/commands/FindCommandTest.java b/src/test/java/seedu/address/logic/commands/FindCommandTest.java index 9b15db28bbb..6ffac8fade9 100644 --- a/src/test/java/seedu/address/logic/commands/FindCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/FindCommandTest.java @@ -3,12 +3,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.commons.core.Messages.MESSAGE_PERSONS_LISTED_OVERVIEW; +import static seedu.address.commons.core.Messages.MESSAGE_APPLICATION_LISTED_OVERVIEW; import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.CARL; -import static seedu.address.testutil.TypicalPersons.ELLE; -import static seedu.address.testutil.TypicalPersons.FIONA; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalInternships.ALICE; +import static seedu.address.testutil.TypicalInternships.FIONA; +import static seedu.address.testutil.TypicalInternships.META; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; import java.util.Arrays; import java.util.Collections; @@ -18,14 +20,16 @@ import seedu.address.model.Model; import seedu.address.model.ModelManager; import seedu.address.model.UserPrefs; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.application.NameContainsKeywordsPredicate; /** * Contains integration tests (interaction with the Model) for {@code FindCommand}. */ public class FindCommandTest { - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - private Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + private Model model = new ModelManager( + getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + private Model expectedModel = new ModelManager( + getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); @Test public void equals() { @@ -55,23 +59,23 @@ public void equals() { } @Test - public void execute_zeroKeywords_noPersonFound() { - String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 0); + public void execute_zeroKeywords_noApplicationFound() { + String expectedMessage = String.format(MESSAGE_APPLICATION_LISTED_OVERVIEW, 0); NameContainsKeywordsPredicate predicate = preparePredicate(" "); FindCommand command = new FindCommand(predicate); - expectedModel.updateFilteredPersonList(predicate); + expectedModel.updateFilteredInternshipList(predicate); assertCommandSuccess(command, model, expectedMessage, expectedModel); - assertEquals(Collections.emptyList(), model.getFilteredPersonList()); + assertEquals(Collections.emptyList(), model.getSortedFilteredInternshipList()); } @Test - public void execute_multipleKeywords_multiplePersonsFound() { - String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 3); - NameContainsKeywordsPredicate predicate = preparePredicate("Kurz Elle Kunz"); + public void execute_multipleKeywords_multipleApplicationsFound() { + String expectedMessage = String.format(MESSAGE_APPLICATION_LISTED_OVERVIEW, 3); + NameContainsKeywordsPredicate predicate = preparePredicate("Meta Alice Kunz"); FindCommand command = new FindCommand(predicate); - expectedModel.updateFilteredPersonList(predicate); + expectedModel.updateFilteredInternshipList(predicate); assertCommandSuccess(command, model, expectedMessage, expectedModel); - assertEquals(Arrays.asList(CARL, ELLE, FIONA), model.getFilteredPersonList()); + assertEquals(Arrays.asList(META, ALICE, FIONA), model.getSortedFilteredInternshipList()); } /** diff --git a/src/test/java/seedu/address/logic/commands/FindDateCommandTest.java b/src/test/java/seedu/address/logic/commands/FindDateCommandTest.java new file mode 100644 index 00000000000..df5c74fa2f6 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/FindDateCommandTest.java @@ -0,0 +1,139 @@ +package seedu.address.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.commons.core.Messages.MESSAGE_APPLICATION_LISTED_OVERVIEW; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalInternships.ALICE; +import static seedu.address.testutil.TypicalInternships.BANK_OF_AMERICA; +import static seedu.address.testutil.TypicalInternships.BENSON; +import static seedu.address.testutil.TypicalInternships.META; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import java.util.Arrays; +import java.util.Collections; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.application.AfterDatePredicate; +import seedu.address.model.application.BeforeDatePredicate; +import seedu.address.model.application.BetweenDatePredicate; +import seedu.address.model.application.DatePredicate; +import seedu.address.model.application.InterviewDate; + +public class FindDateCommandTest { + private Model model = new ModelManager( + getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + private Model expectedModel = new ModelManager( + getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + + @Test + public void equals() { + AfterDatePredicate firstPredicate = new AfterDatePredicate(new InterviewDate("2023-03-15 08:00 AM")); + AfterDatePredicate secondPredicate = new AfterDatePredicate(new InterviewDate("2023-03-15 11:00 AM")); + BeforeDatePredicate thirdPredicate = new BeforeDatePredicate(new InterviewDate("2023-03-15 08:00 AM")); + + FindDateCommand findFirstCommand = new FindDateCommand(firstPredicate); + FindDateCommand findSecondCommand = new FindDateCommand(secondPredicate); + FindDateCommand findThirdCommand = new FindDateCommand(thirdPredicate); + + // same object -> returns true + assertTrue(findFirstCommand.equals(findFirstCommand)); + + // same values -> returns true + FindDateCommand findFirstCommandCopy = new FindDateCommand(firstPredicate); + assertTrue(findFirstCommand.equals(findFirstCommandCopy)); + + // different types -> returns false + assertFalse(findFirstCommand.equals(1)); + + // null -> returns false + assertFalse(findFirstCommand.equals(null)); + + // different date -> returns false + assertFalse(findFirstCommand.equals(findSecondCommand)); + + // different type of predicate -> returns false + assertFalse(findFirstCommand.equals(findThirdCommand)); + } + + @Test + public void execute_afterDatePredicate_multipleApplicationsFound() { + String expectedMessage = String.format(MESSAGE_APPLICATION_LISTED_OVERVIEW, 3); + DatePredicate predicate = new AfterDatePredicate(new InterviewDate("2023-03-15 08:00 AM")); + FindDateCommand command = new FindDateCommand(predicate); + expectedModel.updateFilteredInternshipList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(META, BANK_OF_AMERICA, ALICE), model.getSortedFilteredInternshipList()); + } + + @Test + public void execute_afterDatePredicate_noApplicationsFound() { + String expectedMessage = String.format(MESSAGE_APPLICATION_LISTED_OVERVIEW, 0); + DatePredicate predicate = new AfterDatePredicate(new InterviewDate("2023-07-15 08:00 AM")); + FindDateCommand command = new FindDateCommand(predicate); + expectedModel.updateFilteredInternshipList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getSortedFilteredInternshipList()); + } + + + @Test + public void execute_beforeDatePredicate_multipleApplicationsFound() { + String expectedMessage = String.format(MESSAGE_APPLICATION_LISTED_OVERVIEW, 2); + DatePredicate predicate = new BeforeDatePredicate(new InterviewDate("2023-03-27 08:00 AM")); + FindDateCommand command = new FindDateCommand(predicate); + expectedModel.updateFilteredInternshipList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(ALICE, BENSON), model.getSortedFilteredInternshipList()); + } + + @Test + public void execute_beforeDatePredicate_noApplicationsFound() { + String expectedMessage = String.format(MESSAGE_APPLICATION_LISTED_OVERVIEW, 0); + DatePredicate predicate = new BeforeDatePredicate(new InterviewDate("2023-02-27 07:00 AM")); + FindDateCommand command = new FindDateCommand(predicate); + expectedModel.updateFilteredInternshipList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getSortedFilteredInternshipList()); + } + + @Test + public void execute_betweenDatePredicate_multipleApplicationsFound() { + String expectedMessage = String.format(MESSAGE_APPLICATION_LISTED_OVERVIEW, 2); + DatePredicate predicate = new BetweenDatePredicate( + new InterviewDate("2023-03-31 08:00 AM"), new InterviewDate("2023-04-09 09:00 PM")); + FindDateCommand command = new FindDateCommand(predicate); + expectedModel.updateFilteredInternshipList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(META, BANK_OF_AMERICA), model.getSortedFilteredInternshipList()); + } + + @Test + public void execute_betweenDatePredicateEdgeCase_multipleApplicationsFound() { + String expectedMessage = String.format(MESSAGE_APPLICATION_LISTED_OVERVIEW, 1); + DatePredicate predicate = new BetweenDatePredicate( + new InterviewDate("2023-03-27 09:00 AM"), new InterviewDate("2023-04-01 08:00 PM")); + FindDateCommand command = new FindDateCommand(predicate); + expectedModel.updateFilteredInternshipList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(META), model.getSortedFilteredInternshipList()); + } + + @Test + public void execute_betweenDatePredicate_noApplicationsFound() { + String expectedMessage = String.format(MESSAGE_APPLICATION_LISTED_OVERVIEW, 0); + DatePredicate predicate = new BetweenDatePredicate( + new InterviewDate("2023-06-27 09:00 AM"), new InterviewDate("2023-06-28 08:00 PM")); + FindDateCommand command = new FindDateCommand(predicate); + expectedModel.updateFilteredInternshipList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getSortedFilteredInternshipList()); + } +} diff --git a/src/test/java/seedu/address/logic/commands/FindStatusCommandTest.java b/src/test/java/seedu/address/logic/commands/FindStatusCommandTest.java new file mode 100644 index 00000000000..81db485c706 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/FindStatusCommandTest.java @@ -0,0 +1,81 @@ +package seedu.address.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.commons.core.Messages.MESSAGE_APPLICATION_LISTED_OVERVIEW; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.model.application.InternshipStatus.ACCEPTED; +import static seedu.address.model.application.InternshipStatus.DECLINED; +import static seedu.address.model.application.InternshipStatus.PENDING; +import static seedu.address.model.application.InternshipStatus.RECEIVED; +import static seedu.address.testutil.TypicalInternships.ALICE; +import static seedu.address.testutil.TypicalInternships.BANK_OF_AMERICA; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import java.util.Arrays; +import java.util.Collections; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.application.StatusPredicate; + +/** + * Contains integration tests (interaction with the Model) for {@code FindStatusCommand}. + */ +public class FindStatusCommandTest { + private Model model = new ModelManager( + getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + private Model expectedModel = new ModelManager( + getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + + @Test + public void equals() { + StatusPredicate firstPredicate = new StatusPredicate(PENDING); + StatusPredicate secondPredicate = new StatusPredicate(ACCEPTED); + + FindStatusCommand findFirstCommand = new FindStatusCommand(firstPredicate); + FindStatusCommand findSecondCommand = new FindStatusCommand(secondPredicate); + + // same object -> returns true + assertTrue(findFirstCommand.equals(findFirstCommand)); + + // same values -> returns true + FindStatusCommand findFirstCommandCopy = new FindStatusCommand(firstPredicate); + assertTrue(findFirstCommand.equals(findFirstCommandCopy)); + + // different types -> returns false + assertFalse(findFirstCommand.equals(1)); + + // null -> returns false + assertFalse(findFirstCommand.equals(null)); + + // different person -> returns false + assertFalse(findFirstCommand.equals(findSecondCommand)); + } + + @Test + public void execute_oneStatus_multipleApplicationsFound() { + String expectedMessage = String.format(MESSAGE_APPLICATION_LISTED_OVERVIEW, 2); + StatusPredicate predicate = new StatusPredicate(DECLINED); + FindStatusCommand command = new FindStatusCommand(predicate); + expectedModel.updateFilteredInternshipList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(BANK_OF_AMERICA, ALICE), model.getSortedFilteredInternshipList()); + } + + @Test + public void execute_noMatchingStatus_noApplicationsFound() { + String expectedMessage = String.format(MESSAGE_APPLICATION_LISTED_OVERVIEW, 0); + StatusPredicate predicate = new StatusPredicate(RECEIVED); + FindStatusCommand command = new FindStatusCommand(predicate); + expectedModel.updateFilteredInternshipList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getSortedFilteredInternshipList()); + } +} diff --git a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java index 4904fc4352e..3b35c387c4c 100644 --- a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java @@ -14,7 +14,7 @@ public class HelpCommandTest { @Test public void execute_help_success() { - CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false); + CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false, false); assertCommandSuccess(new HelpCommand(), model, expectedCommandResult, expectedModel); } } diff --git a/src/test/java/seedu/address/logic/commands/ListArchivedCommandTest.java b/src/test/java/seedu/address/logic/commands/ListArchivedCommandTest.java new file mode 100644 index 00000000000..c0238aa0406 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/ListArchivedCommandTest.java @@ -0,0 +1,43 @@ +package seedu.address.logic.commands; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.index.Index; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; + +/** + * Contains integration tests (interaction with the Model) and unit tests for ListCommand. + */ +public class ListArchivedCommandTest { + + private Model model; + private Model expectedModel; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs(), model.getTodoList(), + model.getNoteList()); + expectedModel.updateFilteredInternshipList(Model.PREDICATE_SHOW_ARCHIVED_APPLICATIONS); + } + + @Test + public void execute_listIsNotFiltered_showsSameList() { + assertCommandSuccess(new ListArchivedCommand(), model, ListArchivedCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_listIsFiltered_showsEverything() { + showInternshipAtIndex(model, Index.fromOneBased(8)); + assertCommandSuccess(new ListArchivedCommand(), model, ListArchivedCommand.MESSAGE_SUCCESS, expectedModel); + } +} diff --git a/src/test/java/seedu/address/logic/commands/ListCommandTest.java b/src/test/java/seedu/address/logic/commands/ListCommandTest.java index 435ff1f7275..e688fc95f39 100644 --- a/src/test/java/seedu/address/logic/commands/ListCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/ListCommandTest.java @@ -1,9 +1,11 @@ package seedu.address.logic.commands; import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_APPLICATION; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -22,8 +24,10 @@ public class ListCommandTest { @BeforeEach public void setUp() { - model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + model = new ModelManager( + getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + expectedModel = new ModelManager( + model.getAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); } @Test @@ -33,7 +37,7 @@ public void execute_listIsNotFiltered_showsSameList() { @Test public void execute_listIsFiltered_showsEverything() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); + showInternshipAtIndex(model, INDEX_FIRST_APPLICATION); assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel); } } diff --git a/src/test/java/seedu/address/logic/commands/RevertAllCommandTest.java b/src/test/java/seedu/address/logic/commands/RevertAllCommandTest.java new file mode 100644 index 00000000000..b294d6ae216 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/RevertAllCommandTest.java @@ -0,0 +1,67 @@ +package seedu.address.logic.commands; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.logic.commands.RevertAllCommand.MESSAGE_SUCCESS; +import static seedu.address.logic.commands.RevertCommand.MESSAGE_NO_APPLICATIONS; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.index.Index; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.application.InternshipApplication; + +/** + * Contains integration tests (interaction with the Model) for {@code RevertAllCommand}. + */ +public class RevertAllCommandTest { + + private Model model; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + } + + @Test + public void execute_validCacheList_success() { + InternshipApplication ia1 = model.getSortedFilteredInternshipList().get(Index.fromOneBased(3).getZeroBased()); + InternshipApplication ia2 = model.getSortedFilteredInternshipList().get(Index.fromOneBased(1).getZeroBased()); + model.deleteInternship(ia1); + model.addInternshipToCache(ia1); + model.deleteInternship(ia2); + model.addInternshipToCache(ia2); + RevertAllCommand revertAllCommand = new RevertAllCommand(); + + ModelManager expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + expectedModel.deleteInternship(ia1); + expectedModel.addInternshipToCache(ia1); + expectedModel.deleteInternship(ia2); + expectedModel.addInternshipToCache(ia2); + expectedModel.addApplication(ia1); + expectedModel.addApplication(ia2); + + CommandResult revertMessage = revertAllCommand.execute(model); + assertCommandSuccess(revertMessage, model, MESSAGE_SUCCESS, expectedModel); + + } + + @Test + public void execute_emptyCacheList_success() { + model = new ModelManager(); + + RevertAllCommand revertAllCommand = new RevertAllCommand(); + + ModelManager expectedModel = new ModelManager(); + + CommandResult revertMessage = revertAllCommand.execute(model); + assertCommandSuccess(revertMessage, model, MESSAGE_NO_APPLICATIONS, expectedModel); + } + +} diff --git a/src/test/java/seedu/address/logic/commands/RevertCommandTest.java b/src/test/java/seedu/address/logic/commands/RevertCommandTest.java new file mode 100644 index 00000000000..f508b5515ac --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/RevertCommandTest.java @@ -0,0 +1,91 @@ +package seedu.address.logic.commands; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.logic.commands.RevertCommand.MESSAGE_DUPLICATE_APPLICATION; +import static seedu.address.logic.commands.RevertCommand.MESSAGE_NO_APPLICATIONS; +import static seedu.address.logic.commands.RevertCommand.MESSAGE_SUCCESS; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.index.Index; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.application.InternshipApplication; + +/** + * Contains integration tests (interaction with the Model) for {@code RevertCommand}. + */ +public class RevertCommandTest { + private Model model; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + } + + @Test + public void execute_validCacheList_success() { + InternshipApplication ia1 = model.getSortedFilteredInternshipList().get(Index.fromOneBased(3).getZeroBased()); + InternshipApplication ia2 = model.getSortedFilteredInternshipList().get(Index.fromOneBased(1).getZeroBased()); + model.deleteInternship(ia1); + model.addInternshipToCache(ia1); + model.deleteInternship(ia2); + model.addInternshipToCache(ia2); + RevertCommand revertCommand = new RevertCommand(); + + String expectedMessage = String.format(MESSAGE_SUCCESS, ia2); + + ModelManager expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + expectedModel.deleteInternship(ia1); + expectedModel.addInternshipToCache(ia1); + expectedModel.deleteInternship(ia2); + expectedModel.addInternshipToCache(ia2); + expectedModel.addApplication(ia2); + + CommandResult revertMessage = revertCommand.execute(model); + assertCommandSuccess(revertMessage, model, expectedMessage, expectedModel); + + } + + @Test + public void execute_duplicateInternships_error() { + InternshipApplication ia1 = model.getSortedFilteredInternshipList().get(Index.fromOneBased(3).getZeroBased()); + InternshipApplication ia2 = model.getSortedFilteredInternshipList().get(Index.fromOneBased(1).getZeroBased()); + model.deleteInternship(ia1); + model.addInternshipToCache(ia1); + model.deleteInternship(ia2); + model.addInternshipToCache(ia2); + model.addApplication(ia2); + RevertCommand revertCommand = new RevertCommand(); + + ModelManager expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + expectedModel.deleteInternship(ia1); + expectedModel.addInternshipToCache(ia1); + expectedModel.deleteInternship(ia2); + expectedModel.addInternshipToCache(ia2); + expectedModel.addApplication(ia2); + + CommandResult revertMessage = revertCommand.execute(model); + assertCommandSuccess(revertMessage, model, MESSAGE_DUPLICATE_APPLICATION, expectedModel); + + } + + @Test + public void execute_emptyCacheList_success() { + model = new ModelManager(); + + RevertCommand revertCommand = new RevertCommand(); + + ModelManager expectedModel = new ModelManager(); + + CommandResult revertMessage = revertCommand.execute(model); + assertCommandSuccess(revertMessage, model, MESSAGE_NO_APPLICATIONS, expectedModel); + } +} diff --git a/src/test/java/seedu/address/logic/commands/SortCommandTest.java b/src/test/java/seedu/address/logic/commands/SortCommandTest.java new file mode 100644 index 00000000000..61da80d9018 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/SortCommandTest.java @@ -0,0 +1,115 @@ +package seedu.address.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.commons.core.Messages.MESSAGE_APPLICATION_LISTED_OVERVIEW; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalInternships.ALICE; +import static seedu.address.testutil.TypicalInternships.AMAZON; +import static seedu.address.testutil.TypicalInternships.BANK_OF_AMERICA; +import static seedu.address.testutil.TypicalInternships.BENSON; +import static seedu.address.testutil.TypicalInternships.CARL; +import static seedu.address.testutil.TypicalInternships.DANIEL; +import static seedu.address.testutil.TypicalInternships.ELLE; +import static seedu.address.testutil.TypicalInternships.FIONA; +import static seedu.address.testutil.TypicalInternships.GEORGE; +import static seedu.address.testutil.TypicalInternships.GOOGLE; +import static seedu.address.testutil.TypicalInternships.HARRY; +import static seedu.address.testutil.TypicalInternships.IAN; +import static seedu.address.testutil.TypicalInternships.JAMES; +import static seedu.address.testutil.TypicalInternships.META; +import static seedu.address.testutil.TypicalInternships.MICRON; +import static seedu.address.testutil.TypicalInternships.NETFLIX; +import static seedu.address.testutil.TypicalInternships.ORACLE; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.application.comparator.CompanyNameComparator; +import seedu.address.model.application.comparator.InterviewDateComparator; +import seedu.address.model.application.comparator.JobTitleComparator; +import seedu.address.model.application.comparator.StatusComparator; + +public class SortCommandTest { + private Model model = new ModelManager( + getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + private Model expectedModel = new ModelManager( + getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + + @Test + public void equals() { + CompanyNameComparator firstCmp = new CompanyNameComparator(); + JobTitleComparator secondCmp = new JobTitleComparator(); + + SortCommand sortFirstCommand = new SortCommand(firstCmp); + SortCommand sortSecondCommand = new SortCommand(secondCmp); + + // same object -> returns true + assertTrue(sortFirstCommand.equals(sortFirstCommand)); + + // same values -> returns true + SortCommand sortFirstCommandCopy = new SortCommand(firstCmp); + assertTrue(sortFirstCommand.equals(sortFirstCommandCopy)); + + // different types -> returns false + assertFalse(sortFirstCommand.equals(1)); + + // null -> returns false + assertFalse(sortFirstCommand.equals(null)); + + // different cmp -> returns false + assertFalse(sortFirstCommand.equals(sortSecondCommand)); + } + + @Test + public void execute_sortByCompanyName_sorted() { + String expectedMessage = String.format(MESSAGE_APPLICATION_LISTED_OVERVIEW, 17); + CompanyNameComparator cmp = new CompanyNameComparator(); + SortCommand command = new SortCommand(cmp); + expectedModel.updateSortedFilteredInternshipList(cmp); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals( + Arrays.asList( + ALICE, AMAZON, BANK_OF_AMERICA, BENSON, CARL, DANIEL, ELLE, JAMES, FIONA, GEORGE, GOOGLE, HARRY, + IAN, META, MICRON, NETFLIX, ORACLE), + model.getSortedFilteredInternshipList()); + } + + @Test + public void execute_sortByJobTitle_sorted() { + String expectedMessage = String.format(MESSAGE_APPLICATION_LISTED_OVERVIEW, 17); + JobTitleComparator cmp = new JobTitleComparator(); + SortCommand command = new SortCommand(cmp); + expectedModel.updateSortedFilteredInternshipList(cmp); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(expectedModel.getSortedFilteredInternshipList(), model.getSortedFilteredInternshipList()); + } + + @Test + public void execute_sortByInterviewDate_sorted() { + String expectedMessage = String.format(MESSAGE_APPLICATION_LISTED_OVERVIEW, 17); + InterviewDateComparator cmp = new InterviewDateComparator(); + SortCommand command = new SortCommand(cmp); + expectedModel.updateSortedFilteredInternshipList(cmp); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(expectedModel.getSortedFilteredInternshipList(), model.getSortedFilteredInternshipList()); + } + + @Test + public void execute_sortByStatus_sorted() { + String expectedMessage = String.format(MESSAGE_APPLICATION_LISTED_OVERVIEW, 17); + StatusComparator cmp = new StatusComparator(); + SortCommand command = new SortCommand(cmp); + expectedModel.updateSortedFilteredInternshipList(cmp); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(expectedModel.getSortedFilteredInternshipList(), model.getSortedFilteredInternshipList()); + } +} diff --git a/src/test/java/seedu/address/logic/commands/UnarchiveCommandTest.java b/src/test/java/seedu/address/logic/commands/UnarchiveCommandTest.java new file mode 100644 index 00000000000..eb258e03093 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/UnarchiveCommandTest.java @@ -0,0 +1,133 @@ +package seedu.address.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +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.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.application.InternshipApplication; +import seedu.address.testutil.InternshipBuilder; + +/** + * Contains integration tests (interaction with the Model) and unit tests for + * {@code DeleteCommand}. + */ +public class UnarchiveCommandTest { + + private Model model; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + } + + @Test + public void execute_validIndexUnfilteredList_success() { + UnarchiveCommand unarchiveCommand = new UnarchiveCommand(Index.fromOneBased(1)); + + InternshipApplication unarchivedApplication = new InternshipBuilder() + .withCompanyName("Microsoft") + .withJobTitle("Operating System Developer") + .withIsArchived(false).build(); + + String expectedMessage = String.format(UnarchiveCommand.MESSAGE_UNARCHIVE_APPLICATION_SUCCESS, + unarchivedApplication); + + ModelManager expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + model.updateFilteredInternshipList(Model.PREDICATE_SHOW_ARCHIVED_APPLICATIONS); + expectedModel.setApplication(model.getSortedFilteredInternshipList().get(0), unarchivedApplication); + + try { + CommandResult unarchivedMessage = unarchiveCommand.execute(model); + assertCommandSuccess(unarchivedMessage, model, expectedMessage, expectedModel); + + } catch (CommandException ce) { + fail(ce.toString()); + } + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(model.getSortedFilteredInternshipList().size() + 1); + UnarchiveCommand unarchiveCommand = new UnarchiveCommand(outOfBoundIndex); + + assertCommandFailure(unarchiveCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + @Test + public void execute_validIndexFilteredList_success() { + showInternshipAtIndex(model, Index.fromOneBased(1)); + UnarchiveCommand unarchiveCommand = new UnarchiveCommand(Index.fromOneBased(1)); + + InternshipApplication unarchivedApplication = new InternshipBuilder() + .withCompanyName("Microsoft") + .withJobTitle("Operating System Developer") + .withIsArchived(false).build(); + + String expectedMessage = String.format(UnarchiveCommand.MESSAGE_UNARCHIVE_APPLICATION_SUCCESS, + unarchivedApplication); + + ModelManager expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + model.updateFilteredInternshipList(Model.PREDICATE_SHOW_ARCHIVED_APPLICATIONS); + expectedModel.setApplication(model.getSortedFilteredInternshipList().get(0), unarchivedApplication); + + try { + CommandResult unarchivedMessage = unarchiveCommand.execute(model); + assertCommandSuccess(unarchivedMessage, model, expectedMessage, expectedModel); + + } catch (CommandException ce) { + fail(ce.toString()); + } + } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showInternshipAtIndex(model, Index.fromOneBased(3)); + + Index outOfBoundIndex = Index.fromOneBased(3); + // ensures that outOfBoundIndex is still in bounds of internships list + assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getInternshipList().size()); + + UnarchiveCommand unarchiveCommand = new UnarchiveCommand(outOfBoundIndex); + + assertCommandFailure(unarchiveCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + @Test + public void equals() { + UnarchiveCommand unarchiveFirstCommand = new UnarchiveCommand(Index.fromOneBased(3)); + UnarchiveCommand unarchiveSecondCommand = new UnarchiveCommand(Index.fromOneBased(5)); + + // same object -> returns true + assertTrue(unarchiveFirstCommand.equals(unarchiveFirstCommand)); + + // same values -> returns true + UnarchiveCommand unarchiveFirstCommandCopy = new UnarchiveCommand(Index.fromOneBased(3)); + assertTrue(unarchiveFirstCommand.equals(unarchiveFirstCommandCopy)); + + // different types -> returns false + assertFalse(unarchiveFirstCommand.equals(1)); + + // null -> returns false + assertFalse(unarchiveFirstCommand.equals(null)); + + // different person -> returns false + assertFalse(unarchiveFirstCommand.equals(unarchiveSecondCommand)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/contact/AddContactCommandTest.java b/src/test/java/seedu/address/logic/commands/contact/AddContactCommandTest.java new file mode 100644 index 00000000000..b08715b6544 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/contact/AddContactCommandTest.java @@ -0,0 +1,144 @@ +package seedu.address.logic.commands.contact; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_BANK_OF_AMERICA; +import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BANK_OF_AMERICA; +import static seedu.address.logic.commands.CommandTestUtil.VALID_JOB_TITLE_SOFTWARE_ENGINEER; +import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BANK_OF_AMERICA; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.address.model.application.InternshipStatus.DECLINED; +import static seedu.address.testutil.TypicalContacts.BANK_OF_AMERICA_CONTACT; +import static seedu.address.testutil.TypicalContacts.META_CONTACT; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_APPLICATION; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.ClearCommand; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.contact.Contact; +import seedu.address.model.contact.Email; +import seedu.address.model.contact.Phone; +import seedu.address.testutil.ContactBuilder; +import seedu.address.testutil.InternshipBuilder; + +/** + * Contains integration tests (interaction with the Model) and unit tests for AddContactCommand. + */ +public class AddContactCommandTest { + + private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + + @Test + public void execute_allFieldsSpecifiedUnfilteredList_success() { + Contact contact = new ContactBuilder().build(); + InternshipApplication initialApplication = new InternshipBuilder() + .withCompanyName(VALID_COMPANY_NAME_BANK_OF_AMERICA) + .withJobTitle(VALID_JOB_TITLE_SOFTWARE_ENGINEER) + .withStatus(DECLINED) + .withInterviewDate(new InterviewDate("2023-04-09 12:00 PM")).build(); + InternshipApplication contactAddedApplication = new InternshipBuilder() + .withCompanyName(VALID_COMPANY_NAME_BANK_OF_AMERICA) + .withJobTitle(VALID_JOB_TITLE_SOFTWARE_ENGINEER) + .withStatus(DECLINED) + .withInterviewDate(new InterviewDate("2023-04-09 12:00 PM")) + .withContact(contact).build(); + AddContactCommand addContactCommand = new AddContactCommand(INDEX_SECOND_APPLICATION, contact); + + String expectedMessage = String.format(AddContactCommand.MESSAGE_ADD_CONTACT_SUCCESS, initialApplication + + "\n" + contact); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + expectedModel.setApplication(model.getSortedFilteredInternshipList().get(1), contactAddedApplication); + + assertCommandSuccess(addContactCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Contact contact = new ContactBuilder().build(); + Index outOfBoundIndex = Index.fromOneBased(model.getSortedFilteredInternshipList().size() + 1); + AddContactCommand addContactCommand = new AddContactCommand(outOfBoundIndex, contact); + + assertCommandFailure(addContactCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + @Test + public void execute_filteredList_success() { + showInternshipAtIndex(model, INDEX_SECOND_APPLICATION); + + InternshipApplication applicationInFilteredList = + model.getSortedFilteredInternshipList().get(INDEX_FIRST_APPLICATION.getZeroBased()); + InternshipApplication initialApplication = + new InternshipBuilder(applicationInFilteredList).build(); + Contact contact = new ContactBuilder().build(); + InternshipApplication contactAddedApplication = + new InternshipBuilder(applicationInFilteredList).withContact(contact).build(); + AddContactCommand addContactCommand = new AddContactCommand(INDEX_FIRST_APPLICATION, contact); + + String expectedMessage = String.format(AddContactCommand.MESSAGE_ADD_CONTACT_SUCCESS, initialApplication + + "\n" + contact); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + expectedModel.setApplication(model.getSortedFilteredInternshipList().get(0), contactAddedApplication); + + assertCommandSuccess(addContactCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showInternshipAtIndex(model, INDEX_SECOND_APPLICATION); + + Contact contact = new ContactBuilder().build(); + Index outOfBoundIndex = INDEX_SECOND_APPLICATION; + // ensures that outOfBoundIndex is still in bounds of address book list + assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getInternshipList().size()); + + AddContactCommand addContactCommand = new AddContactCommand(outOfBoundIndex, contact); + + assertCommandFailure(addContactCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + @Test + public void equals() { + final AddContactCommand standardCommand = new AddContactCommand(INDEX_FIRST_APPLICATION, BANK_OF_AMERICA_CONTACT + ); + + // same values -> returns true + Contact copyContact = new Contact(new Phone(VALID_PHONE_BANK_OF_AMERICA), + new Email(VALID_EMAIL_BANK_OF_AMERICA)); + AddContactCommand commandWithSameValues = new AddContactCommand(INDEX_FIRST_APPLICATION, copyContact); + assertTrue(standardCommand.equals(commandWithSameValues)); + + // same object -> returns true + assertTrue(standardCommand.equals(standardCommand)); + + // null -> returns false + assertFalse(standardCommand.equals(null)); + + // different types -> returns false + assertFalse(standardCommand.equals(new ClearCommand())); + + // different index -> returns false + assertFalse(standardCommand.equals(new AddContactCommand(INDEX_SECOND_APPLICATION, BANK_OF_AMERICA_CONTACT))); + + // different contact -> returns false + assertFalse(standardCommand.equals(new AddContactCommand(INDEX_FIRST_APPLICATION, META_CONTACT))); + } +} diff --git a/src/test/java/seedu/address/logic/commands/contact/DeleteContactCommandTest.java b/src/test/java/seedu/address/logic/commands/contact/DeleteContactCommandTest.java new file mode 100644 index 00000000000..e9dfe08a354 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/contact/DeleteContactCommandTest.java @@ -0,0 +1,124 @@ +package seedu.address.logic.commands.contact; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_META; +import static seedu.address.logic.commands.CommandTestUtil.VALID_JOB_TITLE_SOFTWARE_TESTER; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.address.model.application.InternshipStatus.ACCEPTED; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_APPLICATION; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InterviewDate; +import seedu.address.testutil.InternshipBuilder; + +/** + * Contains integration tests (interaction with the Model) and unit tests for + * {@code DeleteContactCommand}. + */ +public class DeleteContactCommandTest { + + private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + + @Test + public void execute_validIndexUnfilteredList_success() { + InternshipApplication applicationToDeleteContact = + model.getSortedFilteredInternshipList().get(INDEX_FIRST_APPLICATION.getZeroBased()); + DeleteContactCommand deleteContactCommand = new DeleteContactCommand(INDEX_FIRST_APPLICATION); + + InternshipApplication contactDeletedApplication = new InternshipBuilder() + .withCompanyName(VALID_COMPANY_NAME_META) + .withJobTitle(VALID_JOB_TITLE_SOFTWARE_TESTER) + .withInterviewDate(new InterviewDate("2023-04-01 08:00 PM")) + .withStatus(ACCEPTED).build(); + String expectedMessage = String.format(DeleteContactCommand.MESSAGE_DELETE_CONTACT_SUCCESS, + applicationToDeleteContact); + + ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + expectedModel.setApplication(applicationToDeleteContact, contactDeletedApplication); + + assertCommandSuccess(deleteContactCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(model.getSortedFilteredInternshipList().size() + 1); + DeleteContactCommand deleteContactCommand = new DeleteContactCommand(outOfBoundIndex); + + assertCommandFailure(deleteContactCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + @Test + public void execute_validIndexFilteredList_success() { + showInternshipAtIndex(model, INDEX_FIRST_APPLICATION); + + InternshipApplication applicationToDeleteContact = + model.getSortedFilteredInternshipList().get(INDEX_FIRST_APPLICATION.getZeroBased()); + DeleteContactCommand deleteContactCommand = new DeleteContactCommand(INDEX_FIRST_APPLICATION); + + InternshipApplication contactDeletedApplication = new InternshipBuilder() + .withCompanyName(VALID_COMPANY_NAME_META) + .withJobTitle(VALID_JOB_TITLE_SOFTWARE_TESTER) + .withInterviewDate(new InterviewDate("2023-04-01 08:00 PM")) + .withStatus(ACCEPTED).build(); + + String expectedMessage = String.format(DeleteContactCommand.MESSAGE_DELETE_CONTACT_SUCCESS, + applicationToDeleteContact); + + Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + expectedModel.setApplication(applicationToDeleteContact, contactDeletedApplication); + + assertCommandSuccess(deleteContactCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showInternshipAtIndex(model, INDEX_FIRST_APPLICATION); + + Index outOfBoundIndex = INDEX_SECOND_APPLICATION; + // ensures that outOfBoundIndex is still in bounds of address book list + assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getInternshipList().size()); + + DeleteContactCommand deleteContactCommand = new DeleteContactCommand(outOfBoundIndex); + + assertCommandFailure(deleteContactCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + @Test + public void equals() { + DeleteContactCommand deleteFirstCommand = new DeleteContactCommand(INDEX_FIRST_APPLICATION); + DeleteContactCommand deleteSecondCommand = new DeleteContactCommand(INDEX_SECOND_APPLICATION); + + // same object -> returns true + assertTrue(deleteFirstCommand.equals(deleteFirstCommand)); + + // same values -> returns true + DeleteContactCommand deleteFirstCommandCopy = new DeleteContactCommand(INDEX_FIRST_APPLICATION); + assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy)); + + // different types -> returns false + assertFalse(deleteFirstCommand.equals(1)); + + // null -> returns false + assertFalse(deleteFirstCommand.equals(null)); + + // different person -> returns false + assertFalse(deleteFirstCommand.equals(deleteSecondCommand)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/contact/EditContactCommandTest.java b/src/test/java/seedu/address/logic/commands/contact/EditContactCommandTest.java new file mode 100644 index 00000000000..37245535970 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/contact/EditContactCommandTest.java @@ -0,0 +1,184 @@ +package seedu.address.logic.commands.contact; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.DESC_BANK_OF_AMERICA_CONTACT; +import static seedu.address.logic.commands.CommandTestUtil.DESC_META_CONTACT; +import static seedu.address.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_AMAZON; +import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_META; +import static seedu.address.logic.commands.CommandTestUtil.VALID_JOB_TITLE_CLOUD_ENGINEER; +import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMAZON; +import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BANK_OF_AMERICA; +import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_META; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.address.model.application.InternshipStatus.ACCEPTED; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_APPLICATION; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.ClearCommand; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.contact.Contact; +import seedu.address.model.contact.Email; +import seedu.address.model.contact.Phone; +import seedu.address.testutil.ContactBuilder; +import seedu.address.testutil.EditContactDescriptorBuilder; +import seedu.address.testutil.InternshipBuilder; + +/** + * Contains integration tests (interaction with the Model) and unit tests for EditContactCommand. + */ +public class EditContactCommandTest { + + private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + + @Test + public void execute_allFieldsSpecifiedUnfilteredList_success() { + Contact editedContact = new ContactBuilder().build(); + EditContactCommand.EditContactDescriptor descriptor = new EditContactDescriptorBuilder(editedContact).build(); + InternshipApplication applicationWithEditedContact = new InternshipBuilder().withCompanyName("Meta") + .withJobTitle("Software Tester").withInterviewDate(new InterviewDate("2023-04-01 08:00 PM")) + .withStatus(ACCEPTED).withContact(editedContact).build(); + EditContactCommand editContactCommand = new EditContactCommand(INDEX_FIRST_APPLICATION, descriptor); + + String expectedMessage = String.format(EditContactCommand.MESSAGE_EDIT_CONTACT_SUCCESS, + applicationWithEditedContact); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + expectedModel.setApplication(model.getSortedFilteredInternshipList().get(0), applicationWithEditedContact); + + assertCommandSuccess(editContactCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_someFieldsSpecifiedUnfilteredList_success() { + Index indexLastApplication = Index.fromOneBased(model.getSortedFilteredInternshipList().size()); + InternshipApplication lastApplication = model.getSortedFilteredInternshipList() + .get(indexLastApplication.getZeroBased()); + + InternshipBuilder internshipInList = new InternshipBuilder(lastApplication); + InternshipApplication editedApplication = internshipInList.withCompanyName(VALID_COMPANY_NAME_AMAZON) + .withJobTitle(VALID_JOB_TITLE_CLOUD_ENGINEER) + .withContact(new Contact(new Phone(VALID_PHONE_AMAZON), new Email("example2@amazon.com"))) + .build(); + + EditContactCommand.EditContactDescriptor descriptor = new EditContactDescriptorBuilder() + .withPhone(VALID_PHONE_AMAZON).withEmail("example2@amazon.com").build(); + EditContactCommand editContactCommand = new EditContactCommand(indexLastApplication, descriptor); + + String expectedMessage = String.format(EditContactCommand.MESSAGE_EDIT_CONTACT_SUCCESS, editedApplication); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + expectedModel.setApplication(lastApplication, editedApplication); + + assertCommandSuccess(editContactCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_noFieldSpecifiedUnfilteredList_success() { + EditContactCommand editContactCommand = new EditContactCommand(INDEX_FIRST_APPLICATION, + new EditContactCommand.EditContactDescriptor()); + InternshipApplication editedApplication = + model.getSortedFilteredInternshipList().get(INDEX_FIRST_APPLICATION.getZeroBased()); + + String expectedMessage = String.format(EditContactCommand.MESSAGE_EDIT_CONTACT_SUCCESS, editedApplication); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + + assertCommandSuccess(editContactCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_filteredList_success() { + showInternshipAtIndex(model, INDEX_FIRST_APPLICATION); + + InternshipApplication applicationInFilteredList = + model.getSortedFilteredInternshipList().get(INDEX_FIRST_APPLICATION.getZeroBased()); + Contact editedContact = new ContactBuilder().build(); + InternshipApplication editedApplication = new InternshipBuilder(applicationInFilteredList) + .withContact(editedContact).build(); + EditContactCommand editCommand = new EditContactCommand(INDEX_FIRST_APPLICATION, + new EditContactDescriptorBuilder().withPhone(VALID_PHONE_META).withEmail(VALID_EMAIL_META).build()); + + String expectedMessage = String.format(EditContactCommand.MESSAGE_EDIT_CONTACT_SUCCESS, editedApplication); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + expectedModel.setApplication(model.getSortedFilteredInternshipList().get(0), editedApplication); + + assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidApplicationIndexUnfilteredList_failure() { + Index outOfBoundIndex = Index.fromOneBased(model.getSortedFilteredInternshipList().size() + 1); + EditContactCommand.EditContactDescriptor descriptor = + new EditContactDescriptorBuilder().withPhone(VALID_PHONE_BANK_OF_AMERICA).build(); + EditContactCommand editContactCommand = new EditContactCommand(outOfBoundIndex, descriptor); + + assertCommandFailure(editContactCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + /** + * Edit filtered list where index is larger than size of filtered list, + * but smaller than size of address book + */ + @Test + public void execute_invalidPersonIndexFilteredList_failure() { + showInternshipAtIndex(model, INDEX_FIRST_APPLICATION); + Index outOfBoundIndex = INDEX_SECOND_APPLICATION; + // ensures that outOfBoundIndex is still in bounds of address book list + assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getInternshipList().size()); + + EditContactCommand editContactCommand = new EditContactCommand(outOfBoundIndex, + new EditContactDescriptorBuilder().withPhone(VALID_PHONE_BANK_OF_AMERICA).build()); + + assertCommandFailure(editContactCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + @Test + public void equals() { + final EditContactCommand standardCommand = new EditContactCommand(INDEX_FIRST_APPLICATION, + DESC_BANK_OF_AMERICA_CONTACT); + + // same values -> returns true + EditContactCommand.EditContactDescriptor copyDescriptor = + new EditContactCommand.EditContactDescriptor(DESC_BANK_OF_AMERICA_CONTACT); + EditContactCommand commandWithSameValues = new EditContactCommand(INDEX_FIRST_APPLICATION, copyDescriptor); + assertEquals(standardCommand, commandWithSameValues); + + // same object -> returns true + assertEquals(standardCommand, standardCommand); + + // null -> returns false + assertNotEquals(null, standardCommand); + + // different types -> returns false + assertNotEquals(standardCommand, new ClearCommand()); + + // different index -> returns false + assertNotEquals(standardCommand, new EditContactCommand(INDEX_SECOND_APPLICATION, + DESC_BANK_OF_AMERICA_CONTACT)); + + // different descriptor -> returns false + assertNotEquals(standardCommand, new EditContactCommand(INDEX_FIRST_APPLICATION, DESC_META_CONTACT)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/contact/EditContactDescriptorTest.java b/src/test/java/seedu/address/logic/commands/contact/EditContactDescriptorTest.java new file mode 100644 index 00000000000..58f70cccf39 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/contact/EditContactDescriptorTest.java @@ -0,0 +1,46 @@ +package seedu.address.logic.commands.contact; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static seedu.address.logic.commands.CommandTestUtil.DESC_BANK_OF_AMERICA_CONTACT; +import static seedu.address.logic.commands.CommandTestUtil.DESC_META_CONTACT; +import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_META; +import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_META; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.EditContactDescriptorBuilder; + +public class EditContactDescriptorTest { + + @Test + public void equals() { + // same values -> returns true + EditContactCommand.EditContactDescriptor descriptorWithSameValues = + new EditContactCommand.EditContactDescriptor(DESC_BANK_OF_AMERICA_CONTACT); + assertEquals(DESC_BANK_OF_AMERICA_CONTACT, descriptorWithSameValues); + + // same object -> returns true + assertEquals(DESC_BANK_OF_AMERICA_CONTACT, DESC_BANK_OF_AMERICA_CONTACT); + + // null -> returns false + assertNotEquals(null, DESC_BANK_OF_AMERICA_CONTACT); + + // different types -> returns false + assertNotEquals(5, DESC_BANK_OF_AMERICA_CONTACT); + + // different values -> returns false + assertNotEquals(DESC_BANK_OF_AMERICA_CONTACT, DESC_META_CONTACT); + + // different phone -> returns false + EditContactCommand.EditContactDescriptor editedGoogleDocuments = + new EditContactDescriptorBuilder(DESC_BANK_OF_AMERICA_CONTACT) + .withPhone(VALID_PHONE_META).build(); + assertNotEquals(DESC_BANK_OF_AMERICA_CONTACT, editedGoogleDocuments); + + // different email -> returns false + editedGoogleDocuments = new EditContactDescriptorBuilder(DESC_BANK_OF_AMERICA_CONTACT) + .withEmail(VALID_EMAIL_META).build(); + assertNotEquals(DESC_BANK_OF_AMERICA_CONTACT, editedGoogleDocuments); + } +} diff --git a/src/test/java/seedu/address/logic/commands/documents/AddDocumentsCommandTest.java b/src/test/java/seedu/address/logic/commands/documents/AddDocumentsCommandTest.java new file mode 100644 index 00000000000..a5544b81ee8 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/documents/AddDocumentsCommandTest.java @@ -0,0 +1,141 @@ +package seedu.address.logic.commands.documents; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_GOOGLE; +import static seedu.address.logic.commands.CommandTestUtil.VALID_COVER_LETTER_LINK_GOOGLE; +import static seedu.address.logic.commands.CommandTestUtil.VALID_JOB_TITLE_PRODUCT_MANAGER; +import static seedu.address.logic.commands.CommandTestUtil.VALID_RESUME_LINK_GOOGLE; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.address.testutil.TypicalDocuments.DOCUMENTS_GOOGLE; +import static seedu.address.testutil.TypicalDocuments.DOCUMENTS_NETFLIX; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIFTH_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_FOURTH_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_APPLICATION; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.ClearCommand; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.documents.CoverLetterLink; +import seedu.address.model.documents.Documents; +import seedu.address.model.documents.ResumeLink; +import seedu.address.testutil.DocumentsBuilder; +import seedu.address.testutil.InternshipBuilder; + +/** + * Contains integration tests (interaction with the Model) and unit tests for AddDocumentsCommand. + */ +public class AddDocumentsCommandTest { + + private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + + @Test + public void execute_allFieldsSpecifiedUnfilteredList_success() { + Documents documents = new DocumentsBuilder().build(); + InternshipApplication initialApplication = new InternshipBuilder() + .withCompanyName(VALID_COMPANY_NAME_GOOGLE) + .withJobTitle(VALID_JOB_TITLE_PRODUCT_MANAGER).build(); + InternshipApplication documentsAddedApplication = new InternshipBuilder() + .withCompanyName(VALID_COMPANY_NAME_GOOGLE) + .withJobTitle(VALID_JOB_TITLE_PRODUCT_MANAGER) + .withDocuments(documents).build(); + AddDocumentsCommand addDocumentsCommand = new AddDocumentsCommand(INDEX_FOURTH_APPLICATION, documents); + + String expectedMessage = String.format(AddDocumentsCommand.MESSAGE_ADD_DOCUMENTS_SUCCESS, initialApplication + + "\n" + documents); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + expectedModel.setApplication(model.getSortedFilteredInternshipList().get(3), documentsAddedApplication); + + assertCommandSuccess(addDocumentsCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Documents documents = new DocumentsBuilder().build(); + Index outOfBoundIndex = Index.fromOneBased(model.getSortedFilteredInternshipList().size() + 1); + AddDocumentsCommand addDocumentsCommand = new AddDocumentsCommand(outOfBoundIndex, documents); + + assertCommandFailure(addDocumentsCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + @Test + public void execute_filteredList_success() { + showInternshipAtIndex(model, INDEX_FOURTH_APPLICATION); + + InternshipApplication applicationInFilteredList = + model.getSortedFilteredInternshipList().get(INDEX_FIRST_APPLICATION.getZeroBased()); + InternshipApplication initialApplication = + new InternshipBuilder(applicationInFilteredList).build(); + Documents documents = new DocumentsBuilder().build(); + InternshipApplication documentsAddedApplication = + new InternshipBuilder(applicationInFilteredList).withDocuments(documents).build(); + AddDocumentsCommand addDocumentsCommand = new AddDocumentsCommand(INDEX_FIRST_APPLICATION, documents); + + String expectedMessage = String.format(AddDocumentsCommand.MESSAGE_ADD_DOCUMENTS_SUCCESS, initialApplication + + "\n" + documents); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + expectedModel.setApplication(model.getSortedFilteredInternshipList().get(0), documentsAddedApplication); + + assertCommandSuccess(addDocumentsCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showInternshipAtIndex(model, INDEX_FOURTH_APPLICATION); + + Documents documents = new DocumentsBuilder().build(); + Index outOfBoundIndex = INDEX_SECOND_APPLICATION; + // ensures that outOfBoundIndex is still in bounds of address book list + assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getInternshipList().size()); + + AddDocumentsCommand addDocumentsCommand = new AddDocumentsCommand(outOfBoundIndex, documents); + + assertCommandFailure(addDocumentsCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + @Test + public void equals() { + final AddDocumentsCommand standardCommand = new AddDocumentsCommand(INDEX_FOURTH_APPLICATION, DOCUMENTS_GOOGLE + ); + + // same values -> returns true + Documents copyDocuments = new Documents(new ResumeLink(VALID_RESUME_LINK_GOOGLE), + new CoverLetterLink(VALID_COVER_LETTER_LINK_GOOGLE)); + AddDocumentsCommand commandWithSameValues = new AddDocumentsCommand(INDEX_FOURTH_APPLICATION, copyDocuments); + assertEquals(standardCommand, commandWithSameValues); + + // same object -> returns true + assertEquals(standardCommand, standardCommand); + + // null -> returns false + assertNotEquals(null, standardCommand); + + // different types -> returns false + assertNotEquals(standardCommand, new ClearCommand()); + + // different index -> returns false + assertNotEquals(standardCommand, new AddDocumentsCommand(INDEX_FIFTH_APPLICATION, DOCUMENTS_GOOGLE)); + + // different documents -> returns false + assertNotEquals(standardCommand, new AddDocumentsCommand(INDEX_FOURTH_APPLICATION, DOCUMENTS_NETFLIX)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/documents/DeleteDocumentsCommandTest.java b/src/test/java/seedu/address/logic/commands/documents/DeleteDocumentsCommandTest.java new file mode 100644 index 00000000000..22d73da6731 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/documents/DeleteDocumentsCommandTest.java @@ -0,0 +1,121 @@ +package seedu.address.logic.commands.documents; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_NETFLIX; +import static seedu.address.logic.commands.CommandTestUtil.VALID_JOB_TITLE_NETWORK_ENGINEER; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIFTH_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_FOURTH_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_APPLICATION; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.application.InternshipApplication; +import seedu.address.testutil.InternshipBuilder; + +/** + * Contains integration tests (interaction with the Model) and unit tests for + * {@code DeleteDocumentsCommand}. + */ +public class DeleteDocumentsCommandTest { + + private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + + @Test + public void execute_validIndexUnfilteredList_success() { + InternshipApplication applicationToDeleteDocuments = + model.getSortedFilteredInternshipList().get(INDEX_FIFTH_APPLICATION.getZeroBased()); + DeleteDocumentsCommand deleteDocumentsCommand = new DeleteDocumentsCommand(INDEX_FIFTH_APPLICATION); + + InternshipApplication documentsDeletedApplication = new InternshipBuilder() + .withCompanyName(VALID_COMPANY_NAME_NETFLIX) + .withJobTitle(VALID_JOB_TITLE_NETWORK_ENGINEER).build(); + String expectedMessage = String.format(DeleteDocumentsCommand.MESSAGE_DELETE_DOCUMENTS_SUCCESS, + applicationToDeleteDocuments); + + ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + expectedModel.setApplication(applicationToDeleteDocuments, documentsDeletedApplication); + + assertCommandSuccess(deleteDocumentsCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(model.getSortedFilteredInternshipList().size() + 1); + DeleteDocumentsCommand deleteDocumentsCommand = new DeleteDocumentsCommand(outOfBoundIndex); + + assertCommandFailure(deleteDocumentsCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + @Test + public void execute_validIndexFilteredList_success() { + showInternshipAtIndex(model, INDEX_FIFTH_APPLICATION); + + InternshipApplication applicationToDeleteDocuments = + model.getSortedFilteredInternshipList().get(INDEX_FIRST_APPLICATION.getZeroBased()); + DeleteDocumentsCommand deleteDocumentsCommand = new DeleteDocumentsCommand(INDEX_FIRST_APPLICATION); + + InternshipApplication contactDeletedApplication = new InternshipBuilder() + .withCompanyName(VALID_COMPANY_NAME_NETFLIX) + .withJobTitle(VALID_JOB_TITLE_NETWORK_ENGINEER).build(); + + String expectedMessage = String.format(DeleteDocumentsCommand.MESSAGE_DELETE_DOCUMENTS_SUCCESS, + applicationToDeleteDocuments); + + Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + expectedModel.setApplication(applicationToDeleteDocuments, contactDeletedApplication); + + assertCommandSuccess(deleteDocumentsCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showInternshipAtIndex(model, INDEX_FIRST_APPLICATION); + + Index outOfBoundIndex = INDEX_SECOND_APPLICATION; + // ensures that outOfBoundIndex is still in bounds of address book list + assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getInternshipList().size()); + + DeleteDocumentsCommand deleteDocumentsCommand = new DeleteDocumentsCommand(outOfBoundIndex); + + assertCommandFailure(deleteDocumentsCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + @Test + public void equals() { + DeleteDocumentsCommand deleteFourthCommand = new DeleteDocumentsCommand(INDEX_FOURTH_APPLICATION); + DeleteDocumentsCommand deleteFifthCommand = new DeleteDocumentsCommand(INDEX_FIFTH_APPLICATION); + + // same object -> returns true + assertEquals(deleteFourthCommand, deleteFourthCommand); + + // same values -> returns true + DeleteDocumentsCommand deleteFourthCommandCopy = new DeleteDocumentsCommand(INDEX_FOURTH_APPLICATION); + assertEquals(deleteFourthCommand, deleteFourthCommandCopy); + + // different types -> returns false + assertNotEquals(1, deleteFourthCommand); + + // null -> returns false + assertNotEquals(null, deleteFourthCommand); + + // different person -> returns false + assertNotEquals(deleteFourthCommand, deleteFifthCommand); + } +} diff --git a/src/test/java/seedu/address/logic/commands/documents/EditDocumentsCommandTest.java b/src/test/java/seedu/address/logic/commands/documents/EditDocumentsCommandTest.java new file mode 100644 index 00000000000..130ca5686ad --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/documents/EditDocumentsCommandTest.java @@ -0,0 +1,192 @@ +package seedu.address.logic.commands.documents; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.DESC_DOCUMENTS_GOOGLE; +import static seedu.address.logic.commands.CommandTestUtil.DESC_DOCUMENTS_NETFLIX; +import static seedu.address.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_NETFLIX; +import static seedu.address.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_ORACLE; +import static seedu.address.logic.commands.CommandTestUtil.VALID_COVER_LETTER_LINK_NETFLIX; +import static seedu.address.logic.commands.CommandTestUtil.VALID_JOB_TITLE_DATA_ENGINEER; +import static seedu.address.logic.commands.CommandTestUtil.VALID_JOB_TITLE_NETWORK_ENGINEER; +import static seedu.address.logic.commands.CommandTestUtil.VALID_RESUME_LINK_GOOGLE; +import static seedu.address.logic.commands.CommandTestUtil.VALID_RESUME_LINK_NETFLIX; +import static seedu.address.logic.commands.CommandTestUtil.VALID_RESUME_LINK_ORACLE; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIFTH_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_FOURTH_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_APPLICATION; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.ClearCommand; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.documents.CoverLetterLink; +import seedu.address.model.documents.Documents; +import seedu.address.model.documents.ResumeLink; +import seedu.address.testutil.DocumentsBuilder; +import seedu.address.testutil.EditDocumentsDescriptorBuilder; +import seedu.address.testutil.InternshipBuilder; + +/** + * Contains integration tests (interaction with the Model) and unit tests for EditDocumentsCommand. + */ +public class EditDocumentsCommandTest { + + private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + + @Test + public void execute_allFieldsSpecifiedUnfilteredList_success() { + Documents editedDocuments = new DocumentsBuilder().build(); + EditDocumentsCommand.EditDocumentsDescriptor descriptor = + new EditDocumentsDescriptorBuilder(editedDocuments).build(); + InternshipApplication applicationWithEditedDocuments = new InternshipBuilder() + .withCompanyName(VALID_COMPANY_NAME_NETFLIX) + .withJobTitle(VALID_JOB_TITLE_NETWORK_ENGINEER).withDocuments(editedDocuments).build(); + EditDocumentsCommand editDocumentsCommand = new EditDocumentsCommand(INDEX_FIFTH_APPLICATION, descriptor); + + String expectedMessage = String.format(EditDocumentsCommand.MESSAGE_EDIT_DOCUMENTS_SUCCESS, + applicationWithEditedDocuments); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + expectedModel.setApplication(model.getSortedFilteredInternshipList().get(4), applicationWithEditedDocuments); + + assertCommandSuccess(editDocumentsCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_someFieldsSpecifiedUnfilteredList_success() { + Index indexSecondLastApplication = Index.fromOneBased(model.getSortedFilteredInternshipList().size() - 1); + InternshipApplication lastApplication = model.getSortedFilteredInternshipList() + .get(indexSecondLastApplication.getZeroBased()); + + InternshipBuilder internshipInList = new InternshipBuilder(lastApplication); + InternshipApplication editedApplication = internshipInList.withCompanyName(VALID_COMPANY_NAME_ORACLE) + .withJobTitle(VALID_JOB_TITLE_DATA_ENGINEER) + .withDocuments(new Documents(new ResumeLink(VALID_RESUME_LINK_ORACLE), + new CoverLetterLink("https://drive.example.com/coverletter_oracle_2"))) + .build(); + + EditDocumentsCommand.EditDocumentsDescriptor descriptor = new EditDocumentsDescriptorBuilder() + .withResumeLink(VALID_RESUME_LINK_ORACLE) + .withCoverLetterLink("https://drive.example.com/coverletter_oracle_2").build(); + EditDocumentsCommand editDocumentsCommand = new EditDocumentsCommand(indexSecondLastApplication, descriptor); + + String expectedMessage = String.format(EditDocumentsCommand.MESSAGE_EDIT_DOCUMENTS_SUCCESS, editedApplication); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + expectedModel.setApplication(lastApplication, editedApplication); + + assertCommandSuccess(editDocumentsCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_noFieldSpecifiedUnfilteredList_success() { + EditDocumentsCommand editDocumentsCommand = new EditDocumentsCommand(INDEX_FIFTH_APPLICATION, + new EditDocumentsCommand.EditDocumentsDescriptor()); + InternshipApplication editedApplication = + model.getSortedFilteredInternshipList().get(INDEX_FIFTH_APPLICATION.getZeroBased()); + + String expectedMessage = String.format(EditDocumentsCommand.MESSAGE_EDIT_DOCUMENTS_SUCCESS, editedApplication); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + + assertCommandSuccess(editDocumentsCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_filteredList_success() { + showInternshipAtIndex(model, INDEX_FIFTH_APPLICATION); + + InternshipApplication applicationInFilteredList = + model.getSortedFilteredInternshipList().get(INDEX_FIRST_APPLICATION.getZeroBased()); + Documents editedDocuments = new DocumentsBuilder().build(); + InternshipApplication editedApplication = new InternshipBuilder(applicationInFilteredList) + .withDocuments(editedDocuments).build(); + EditDocumentsCommand editCommand = new EditDocumentsCommand(INDEX_FIRST_APPLICATION, + new EditDocumentsDescriptorBuilder() + .withResumeLink(VALID_RESUME_LINK_NETFLIX) + .withCoverLetterLink(VALID_COVER_LETTER_LINK_NETFLIX) + .build()); + + String expectedMessage = String.format(EditDocumentsCommand.MESSAGE_EDIT_DOCUMENTS_SUCCESS, editedApplication); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(), + getTypicalTodoList(), getTypicalNoteList()); + expectedModel.setApplication(model.getSortedFilteredInternshipList().get(0), editedApplication); + + assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidApplicationIndexUnfilteredList_failure() { + Index outOfBoundIndex = Index.fromOneBased(model.getSortedFilteredInternshipList().size() + 1); + EditDocumentsCommand.EditDocumentsDescriptor descriptor = + new EditDocumentsDescriptorBuilder().withResumeLink(VALID_RESUME_LINK_GOOGLE).build(); + EditDocumentsCommand editDocumentsCommand = new EditDocumentsCommand(outOfBoundIndex, descriptor); + + assertCommandFailure(editDocumentsCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + /** + * Edit filtered list where index is larger than size of filtered list, + * but smaller than size of address book + */ + @Test + public void execute_invalidPersonIndexFilteredList_failure() { + showInternshipAtIndex(model, INDEX_FIRST_APPLICATION); + Index outOfBoundIndex = INDEX_SECOND_APPLICATION; + // ensures that outOfBoundIndex is still in bounds of address book list + assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getInternshipList().size()); + + EditDocumentsCommand editDocumentsCommand = new EditDocumentsCommand(outOfBoundIndex, + new EditDocumentsDescriptorBuilder().withResumeLink(VALID_RESUME_LINK_GOOGLE).build()); + + assertCommandFailure(editDocumentsCommand, model, Messages.MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX); + } + + @Test + public void equals() { + final EditDocumentsCommand standardCommand = new EditDocumentsCommand(INDEX_FOURTH_APPLICATION, + DESC_DOCUMENTS_GOOGLE); + + // same values -> returns true + EditDocumentsCommand.EditDocumentsDescriptor copyDescriptor = + new EditDocumentsCommand.EditDocumentsDescriptor(DESC_DOCUMENTS_GOOGLE); + EditDocumentsCommand commandWithSameValues = new EditDocumentsCommand(INDEX_FOURTH_APPLICATION, copyDescriptor); + assertEquals(standardCommand, commandWithSameValues); + + // same object -> returns true + assertEquals(standardCommand, standardCommand); + + // null -> returns false + assertNotEquals(null, standardCommand); + + // different types -> returns false + assertNotEquals(standardCommand, new ClearCommand()); + + // different index -> returns false + assertNotEquals(standardCommand, new EditDocumentsCommand(INDEX_FIFTH_APPLICATION, + DESC_DOCUMENTS_GOOGLE)); + + // different descriptor -> returns false + assertNotEquals(standardCommand, new EditDocumentsCommand(INDEX_FOURTH_APPLICATION, DESC_DOCUMENTS_NETFLIX)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/documents/EditDocumentsDescriptorTest.java b/src/test/java/seedu/address/logic/commands/documents/EditDocumentsDescriptorTest.java new file mode 100644 index 00000000000..e1e3433e9be --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/documents/EditDocumentsDescriptorTest.java @@ -0,0 +1,46 @@ +package seedu.address.logic.commands.documents; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static seedu.address.logic.commands.CommandTestUtil.DESC_DOCUMENTS_GOOGLE; +import static seedu.address.logic.commands.CommandTestUtil.DESC_DOCUMENTS_NETFLIX; +import static seedu.address.logic.commands.CommandTestUtil.VALID_COVER_LETTER_LINK_NETFLIX; +import static seedu.address.logic.commands.CommandTestUtil.VALID_RESUME_LINK_NETFLIX; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.EditDocumentsDescriptorBuilder; + +public class EditDocumentsDescriptorTest { + + @Test + public void equals() { + // same values -> returns true + EditDocumentsCommand.EditDocumentsDescriptor descriptorWithSameValues = + new EditDocumentsCommand.EditDocumentsDescriptor(DESC_DOCUMENTS_GOOGLE); + assertEquals(DESC_DOCUMENTS_GOOGLE, descriptorWithSameValues); + + // same object -> returns true + assertEquals(DESC_DOCUMENTS_GOOGLE, DESC_DOCUMENTS_GOOGLE); + + // null -> returns false + assertNotEquals(null, DESC_DOCUMENTS_GOOGLE); + + // different types -> returns false + assertNotEquals(5, DESC_DOCUMENTS_GOOGLE); + + // different values -> returns false + assertNotEquals(DESC_DOCUMENTS_GOOGLE, DESC_DOCUMENTS_NETFLIX); + + // different resume link -> returns false + EditDocumentsCommand.EditDocumentsDescriptor editedGoogleDocuments = + new EditDocumentsDescriptorBuilder(DESC_DOCUMENTS_GOOGLE) + .withResumeLink(VALID_RESUME_LINK_NETFLIX).build(); + assertNotEquals(DESC_DOCUMENTS_GOOGLE, editedGoogleDocuments); + + // different cover letter link -> returns false + editedGoogleDocuments = new EditDocumentsDescriptorBuilder(DESC_DOCUMENTS_GOOGLE) + .withCoverLetterLink(VALID_COVER_LETTER_LINK_NETFLIX).build(); + assertNotEquals(DESC_DOCUMENTS_GOOGLE, editedGoogleDocuments); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/FindTaskCommandTest.java b/src/test/java/seedu/address/logic/commands/task/FindTaskCommandTest.java new file mode 100644 index 00000000000..df28165db22 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/FindTaskCommandTest.java @@ -0,0 +1,105 @@ +package seedu.address.logic.commands.task; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.NOTE_5; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.GOOGLE_T; +import static seedu.address.testutil.TypicalTodos.META_T; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.task.ContentContainsKeywordsPredicate; +import seedu.address.model.task.TitleContainsKeywordsPredicate; + +/** + * Contains integration tests (interaction with the Model) for {@code FindTaskCommand}. + */ +public class FindTaskCommandTest { + + private Model model; + private Model expectedModel; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + } + + @Test + public void execute_zeroKeywords_noTaskFound() { + String expectedMessage = String.format(Messages.MESSAGE_RESULT_LISTED_OVERVIEW, 0); + + TitleContainsKeywordsPredicate predicateT = prepareTitlePredicate(" "); + ContentContainsKeywordsPredicate predicateC = prepareContentPredicate(" "); + + FindTaskCommand command = new FindTaskCommand(predicateT, predicateC); + + expectedModel.updateFilteredTodoList(predicateT); + expectedModel.updateFilteredNoteList(predicateC); + + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getFilteredTodoList()); + assertEquals(Collections.emptyList(), model.getFilteredNoteList()); + } + + @Test + public void execute_multipleKeywords_multipleTasksFound() { + String expectedMessage = String.format(Messages.MESSAGE_RESULT_LISTED_OVERVIEW, 3); + + TitleContainsKeywordsPredicate predicateT = prepareTitlePredicate("META GOOGLE Day"); + ContentContainsKeywordsPredicate predicateC = prepareContentPredicate("META GOOGLE Day"); + + FindTaskCommand command = new FindTaskCommand(predicateT, predicateC); + + expectedModel.updateFilteredTodoList(predicateT); + expectedModel.updateFilteredNoteList(predicateC); + + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(META_T, GOOGLE_T), model.getFilteredTodoList()); + assertEquals(List.of(NOTE_5), model.getFilteredNoteList()); + } + + private TitleContainsKeywordsPredicate prepareTitlePredicate(String userInput) { + return new TitleContainsKeywordsPredicate(Arrays.asList(userInput.split("\\s+"))); + } + + private ContentContainsKeywordsPredicate prepareContentPredicate(String userInput) { + return new ContentContainsKeywordsPredicate(Arrays.asList(userInput.split("\\s+"))); + } + + @Test + public void equals() { + FindTaskCommand findTaskOneCommand = new FindTaskCommand(prepareTitlePredicate("one"), + prepareContentPredicate("one")); + FindTaskCommand findTaskTwoCommand = new FindTaskCommand(prepareTitlePredicate("one"), + prepareContentPredicate("two")); + FindTaskCommand findTaskThreeCommand = new FindTaskCommand(prepareTitlePredicate("three"), + prepareContentPredicate("one")); + FindTaskCommand findTaskSameCommand = new FindTaskCommand(prepareTitlePredicate("one"), + prepareContentPredicate("one")); + + // same object -> returns true + assertTrue(findTaskOneCommand.equals(findTaskOneCommand)); + + assertFalse(findTaskOneCommand.equals(findTaskTwoCommand)); + + assertFalse(findTaskThreeCommand.equals(findTaskOneCommand)); + + assertTrue(findTaskOneCommand.equals(findTaskSameCommand)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/ListTaskCommandTest.java b/src/test/java/seedu/address/logic/commands/task/ListTaskCommandTest.java new file mode 100644 index 00000000000..df07200f3c2 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/ListTaskCommandTest.java @@ -0,0 +1,55 @@ +package seedu.address.logic.commands.task; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.NoteList; +import seedu.address.model.TodoList; +import seedu.address.model.UserPrefs; + +/** +* Contains integration tests (interaction with the Model) for {@code ListTaskCommand}. +*/ +public class ListTaskCommandTest { + + private Model model; + private Model expectedModel; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + } + + @Test + public void execute_successTaskList() { + assertCommandSuccess(new ListTaskCommand(), model, ListTaskCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_nullTodo_successTaskList() { + Model todoNullModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), new TodoList(), + getTypicalNoteList()); + Model expectedTodoModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), new TodoList(), + getTypicalNoteList()); + assertCommandSuccess(new ListTaskCommand(), todoNullModel, ListTaskCommand.MESSAGE_SUCCESS, expectedTodoModel); + } + + @Test + public void execute_nullLists_alertNullLists() { + Model newNullModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), new TodoList(), new NoteList()); + Model expectedNullModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), + new TodoList(), new NoteList()); + assertCommandSuccess(new ListTaskCommand(), newNullModel, + ListTaskCommand.MESSAGE_NO_APPLICATIONS, expectedNullModel); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/note/AddNoteCommandTest.java b/src/test/java/seedu/address/logic/commands/task/note/AddNoteCommandTest.java new file mode 100644 index 00000000000..181404c18bb --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/note/AddNoteCommandTest.java @@ -0,0 +1,48 @@ +package seedu.address.logic.commands.task.note; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.NoteList; +import seedu.address.model.UserPrefs; +import seedu.address.model.task.Note; +import seedu.address.testutil.NoteBuilder; + +/** + * Contains integration tests (interaction with the Model) for {@code AddNoteCommand}. + */ +public class AddNoteCommandTest { + private Model model; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + } + + @Test + public void execute_noteAcceptedByModel_addSuccessful() { + Note validNote = new NoteBuilder().build(); + NoteList noteList = getTypicalNoteList(); + noteList.addNote(validNote); + Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), + getTypicalTodoList(), noteList); + + assertCommandSuccess(new AddNoteCommand(validNote), model, + String.format(AddNoteCommand.MESSAGE_SUCCESS, validNote), expectedModel); + } + + @Test + public void execute_duplicateNote_throwsCommandException() { + Note validNote = model.getFilteredNoteList().get(0); + + assertCommandFailure(new AddNoteCommand(validNote), model, AddNoteCommand.MESSAGE_DUPLICATE_NOTE); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/note/ClearNoteCommandTest.java b/src/test/java/seedu/address/logic/commands/task/note/ClearNoteCommandTest.java new file mode 100644 index 00000000000..010bafcaf23 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/note/ClearNoteCommandTest.java @@ -0,0 +1,49 @@ +package seedu.address.logic.commands.task.note; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.CommandResult; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.NoteList; +import seedu.address.model.UserPrefs; + +/** + * Contains integration tests (interaction with the Model) for {@code ClearNoteCommand}. + */ +public class ClearNoteCommandTest { + + private Model model; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + } + + @Test + public void execute_emptyNoteList_success() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), new NoteList()); + Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + new NoteList()); + + assertCommandSuccess(new ClearNoteCommand(), model, ClearNoteCommand.MESSAGE_NULL, expectedModel); + } + + + @Test + public void execute_nonEmptyNoteList_success() { + Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + new NoteList()); + + ClearNoteCommand clearNoteCommand = new ClearNoteCommand(); + CommandResult result = clearNoteCommand.execute(model); + + assertCommandSuccess(result, model, ClearNoteCommand.MESSAGE_SUCCESS, expectedModel); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/note/DeleteNoteCommandTest.java b/src/test/java/seedu/address/logic/commands/task/note/DeleteNoteCommandTest.java new file mode 100644 index 00000000000..1cbaf125680 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/note/DeleteNoteCommandTest.java @@ -0,0 +1,63 @@ +package seedu.address.logic.commands.task.note; + +import static org.junit.jupiter.api.Assertions.fail; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.task.Note; + +/** + * Contains integration tests (interaction with the Model) for {@code DeleteNoteCommand}. + */ +public class DeleteNoteCommandTest { + + private Model model; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + } + + @Test + public void execute_validNoteList_success() { + Note noteToDelete = model.getFilteredNoteList() + .get(Index.fromOneBased(3).getZeroBased()); + DeleteNoteCommand deleteNoteCommand = new DeleteNoteCommand(Index.fromOneBased(3)); + + String expectedMessage = String.format(DeleteNoteCommand.MESSAGE_DELETE_NOTE_SUCCESS, noteToDelete); + + ModelManager expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + expectedModel.deleteNote(noteToDelete); + + try { + CommandResult deleteMessage = deleteNoteCommand.execute(model); + assertCommandSuccess(deleteMessage, model, expectedMessage, expectedModel); + + } catch (CommandException ce) { + fail(ce.toString()); + } + + } + + @Test + public void execute_invalidIndex_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredNoteList().size() + 1); + DeleteNoteCommand deleteNoteCommand = new DeleteNoteCommand(outOfBoundIndex); + + assertCommandFailure(deleteNoteCommand, model, Messages.MESSAGE_INVALID_NOTE_DISPLAYED_INDEX); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/note/ListNoteCommandTest.java b/src/test/java/seedu/address/logic/commands/task/note/ListNoteCommandTest.java new file mode 100644 index 00000000000..22c3ae7995f --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/note/ListNoteCommandTest.java @@ -0,0 +1,45 @@ +package seedu.address.logic.commands.task.note; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.NoteList; +import seedu.address.model.UserPrefs; + +/** + * Contains integration tests (interaction with the Model) and unit tests for ListNoteCommand. + */ +public class ListNoteCommandTest { + private Model model; + private Model expectedModel; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + } + + @Test + public void execute_successNoteList() { + assertCommandSuccess(new ListNoteCommand(), model, ListNoteCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_nullNoteList_alertNullNoteList() { + Model nullNoteModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + new NoteList()); + Model expectedNoteModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + new NoteList()); + assertCommandSuccess(new ListNoteCommand(), nullNoteModel, + ListNoteCommand.MESSAGE_NO_NOTE, expectedNoteModel); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/todo/AddTodoCommandTest.java b/src/test/java/seedu/address/logic/commands/task/todo/AddTodoCommandTest.java new file mode 100644 index 00000000000..0f58b70ea90 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/todo/AddTodoCommandTest.java @@ -0,0 +1,49 @@ +package seedu.address.logic.commands.task.todo; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.TodoList; +import seedu.address.model.UserPrefs; +import seedu.address.model.task.InternshipTodo; +import seedu.address.testutil.InternshipTodoBuilder; + +/** + * Contains integration tests (interaction with the Model) for {@code AddTodoCommand}. + */ +public class AddTodoCommandTest { + + private Model model; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + } + + @Test + public void execute_todoAcceptedByModel_addSuccessful() { + InternshipTodo validTodo = new InternshipTodoBuilder().build(); + TodoList todoList = getTypicalTodoList(); + todoList.addTodo(validTodo); + Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), + todoList, getTypicalNoteList()); + + assertCommandSuccess(new AddTodoCommand(validTodo), model, + String.format(AddTodoCommand.MESSAGE_SUCCESS, validTodo), expectedModel); + } + + @Test + public void execute_duplicateTodo_throwsCommandException() { + InternshipTodo validTodo = model.getFilteredTodoList().get(0); + + assertCommandFailure(new AddTodoCommand(validTodo), model, AddTodoCommand.MESSAGE_DUPLICATE_TODO); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/todo/ClearTodoCommandTest.java b/src/test/java/seedu/address/logic/commands/task/todo/ClearTodoCommandTest.java new file mode 100644 index 00000000000..b13db6f38fd --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/todo/ClearTodoCommandTest.java @@ -0,0 +1,49 @@ +package seedu.address.logic.commands.task.todo; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.CommandResult; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.TodoList; +import seedu.address.model.UserPrefs; + +/** + * Contains integration tests (interaction with the Model) for {@code ClearTodoCommand}. + */ +public class ClearTodoCommandTest { + + private Model model; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + } + + @Test + public void execute_emptyTodoList_success() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), new TodoList(), getTypicalNoteList()); + Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), new TodoList(), + getTypicalNoteList()); + + assertCommandSuccess(new ClearTodoCommand(), model, ClearTodoCommand.MESSAGE_NULL, expectedModel); + } + + + @Test + public void execute_nonEmptyTodoList_success() { + Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), new TodoList(), + getTypicalNoteList()); + + ClearTodoCommand clearTodoCommand = new ClearTodoCommand(); + CommandResult result = clearTodoCommand.execute(model); + + assertCommandSuccess(result, model, ClearTodoCommand.MESSAGE_SUCCESS, expectedModel); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/todo/DeleteTodoCommandTest.java b/src/test/java/seedu/address/logic/commands/task/todo/DeleteTodoCommandTest.java new file mode 100644 index 00000000000..efc99bb00cb --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/todo/DeleteTodoCommandTest.java @@ -0,0 +1,63 @@ +package seedu.address.logic.commands.task.todo; + +import static org.junit.jupiter.api.Assertions.fail; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.task.InternshipTodo; + +/** + * Contains integration tests (interaction with the Model) for {@code DeleteTodoCommand}. + */ +public class DeleteTodoCommandTest { + + private Model model; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + } + + @Test + public void execute_validTodoList_success() { + InternshipTodo todoToDelete = model.getFilteredTodoList() + .get(Index.fromOneBased(3).getZeroBased()); + DeleteTodoCommand deleteTodoCommand = new DeleteTodoCommand(Index.fromOneBased(3)); + + String expectedMessage = String.format(DeleteTodoCommand.MESSAGE_DELETE_TODO_SUCCESS, todoToDelete); + + ModelManager expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + expectedModel.deleteTodo(todoToDelete); + + try { + CommandResult deleteMessage = deleteTodoCommand.execute(model); + assertCommandSuccess(deleteMessage, model, expectedMessage, expectedModel); + + } catch (CommandException ce) { + fail(ce.toString()); + } + + } + + @Test + public void execute_invalidIndex_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredTodoList().size() + 1); + DeleteTodoCommand deleteTodoCommand = new DeleteTodoCommand(outOfBoundIndex); + + assertCommandFailure(deleteTodoCommand, model, Messages.MESSAGE_INVALID_TODO_DISPLAYED_INDEX); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/todo/EditDeadlineCommandTest.java b/src/test/java/seedu/address/logic/commands/task/todo/EditDeadlineCommandTest.java new file mode 100644 index 00000000000..56c4895a8de --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/todo/EditDeadlineCommandTest.java @@ -0,0 +1,85 @@ +package seedu.address.logic.commands.task.todo; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import java.time.LocalDate; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.JobTitle; +import seedu.address.model.task.ApplicationDeadline; +import seedu.address.model.task.InternshipTodo; + +/** + * Contains integration tests (interaction with the Model) for {@code EditDeadlineCommand}. + */ +public class EditDeadlineCommandTest { + + private Model model; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + } + + @Test + public void execute_editTodoDeadline_success() { + EditDeadlineCommand editDeadlineCommand = new EditDeadlineCommand( + Index.fromOneBased(3), new ApplicationDeadline(LocalDate.parse("2023-09-09"))); + InternshipTodo editedTodo = model.getFilteredTodoList().get(Index.fromOneBased(3).getZeroBased()); + editedTodo.setDeadline(new ApplicationDeadline(LocalDate.parse("2023-09-09"))); + + String expectedMessage = String.format(EditDeadlineCommand.MESSAGE_UPDATE_STATUS_SUCCESS, editedTodo); + + Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + expectedModel.setTodo(model.getFilteredTodoList().get(Index.fromOneBased(3).getZeroBased()), + editedTodo); + + assertCommandSuccess(editDeadlineCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_editTodoDeadline_successNullNote() { + InternshipTodo newTodo = new InternshipTodo(new CompanyName("Tesla"), new JobTitle("Tester"), + new ApplicationDeadline(LocalDate.parse("2023-05-05"))); + model.addTodo(newTodo); + + int targetIndex = model.getFilteredTodoList().size(); + EditDeadlineCommand editDeadlineCommand = new EditDeadlineCommand( + Index.fromOneBased(targetIndex), + new ApplicationDeadline(LocalDate.parse("2023-09-09"))); + + InternshipTodo editedTodo = model.getFilteredTodoList().get(Index.fromOneBased(targetIndex).getZeroBased()); + editedTodo.setDeadline(new ApplicationDeadline(LocalDate.parse("2023-09-09"))); + + String expectedMessage = String.format(EditDeadlineCommand.MESSAGE_UPDATE_STATUS_SUCCESS, editedTodo); + + Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), model.getTodoList(), + getTypicalNoteList()); + expectedModel.setTodo(model.getFilteredTodoList().get(Index.fromOneBased(targetIndex).getZeroBased()), + editedTodo); + + assertCommandSuccess(editDeadlineCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidTodoIndex_failure() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredTodoList().size() + 1); + EditDeadlineCommand editDeadlineCommand = new EditDeadlineCommand(outOfBoundIndex, + new ApplicationDeadline(LocalDate.parse("2023-09-09"))); + + assertCommandFailure(editDeadlineCommand, model, Messages.MESSAGE_INVALID_DISPLAYED_INDEX); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/todo/EditNoteContentCommandTest.java b/src/test/java/seedu/address/logic/commands/task/todo/EditNoteContentCommandTest.java new file mode 100644 index 00000000000..c45a457c13c --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/todo/EditNoteContentCommandTest.java @@ -0,0 +1,56 @@ +package seedu.address.logic.commands.task.todo; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.task.InternshipTodo; +import seedu.address.model.task.NoteContent; + +/** + * Contains integration tests (interaction with the Model) for {@code EditNoteContentCommand}. + */ +public class EditNoteContentCommandTest { + private Model model; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), getTypicalNoteList()); + } + + @Test + public void execute_editNoteContent_success() { + EditNoteContentCommand editNoteContentCommand = new EditNoteContentCommand( + Index.fromOneBased(3), new NoteContent("New note for test")); + InternshipTodo editedTodo = model.getFilteredTodoList().get(Index.fromOneBased(3).getZeroBased()); + editedTodo.setNote(new NoteContent("New note for test")); + + String expectedMessage = String.format(EditNoteContentCommand.MESSAGE_UPDATE_STATUS_SUCCESS, editedTodo); + + Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + expectedModel.setTodo(model.getFilteredTodoList().get(Index.fromOneBased(3).getZeroBased()), + editedTodo); + + assertCommandSuccess(editNoteContentCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidTodoIndex_failure() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredTodoList().size() + 1); + EditNoteContentCommand editNoteCommand = new EditNoteContentCommand(outOfBoundIndex, + new NoteContent("New note for test")); + + assertCommandFailure(editNoteCommand, model, Messages.MESSAGE_INVALID_DISPLAYED_INDEX); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/todo/ListTodoCommandTest.java b/src/test/java/seedu/address/logic/commands/task/todo/ListTodoCommandTest.java new file mode 100644 index 00000000000..c834867d3c4 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/todo/ListTodoCommandTest.java @@ -0,0 +1,45 @@ +package seedu.address.logic.commands.task.todo; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; +import static seedu.address.testutil.TypicalNotes.getTypicalNoteList; +import static seedu.address.testutil.TypicalTodos.getTypicalTodoList; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.TodoList; +import seedu.address.model.UserPrefs; + +/** + * Contains integration tests (interaction with the Model) for {@code ListTodoCommand}. + */ +public class ListTodoCommandTest { + private Model model; + private Model expectedModel; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTodoList(), + getTypicalNoteList()); + } + + @Test + public void execute_successTodoList() { + assertCommandSuccess(new ListTodoCommand(), model, ListTodoCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_nullTodoList_alertNullTodoList() { + Model nullTodoModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), new TodoList(), + getTypicalNoteList()); + Model expectedTodoModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), + new TodoList(), getTypicalNoteList()); + assertCommandSuccess(new ListTodoCommand(), nullTodoModel, + ListTodoCommand.MESSAGE_NO_APPLICATIONS, expectedTodoModel); + } +} diff --git a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java index 5cf487d7ebb..f6a73729ce7 100644 --- a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java @@ -1,141 +1,140 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY; -import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalPersons.AMY; -import static seedu.address.testutil.TypicalPersons.BOB; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -import seedu.address.testutil.PersonBuilder; - -public class AddCommandParserTest { - private AddCommandParser parser = new AddCommandParser(); - - @Test - public void parse_allFieldsPresent_success() { - Person expectedPerson = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND).build(); - - // whitespace only preamble - assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple names - last name accepted - assertParseSuccess(parser, NAME_DESC_AMY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple phones - last phone accepted - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_AMY + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple emails - last email accepted - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_AMY + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple addresses - last address accepted - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_AMY - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple tags - all accepted - Person expectedPersonMultipleTags = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND) - .build(); - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, new AddCommand(expectedPersonMultipleTags)); - } - - @Test - public void parse_optionalFieldsMissing_success() { - // zero tags - Person expectedPerson = new PersonBuilder(AMY).withTags().build(); - assertParseSuccess(parser, NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY + ADDRESS_DESC_AMY, - new AddCommand(expectedPerson)); - } - - @Test - public void parse_compulsoryFieldMissing_failure() { - String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); - - // missing name prefix - assertParseFailure(parser, VALID_NAME_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB, - expectedMessage); - - // missing phone prefix - assertParseFailure(parser, NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB, - expectedMessage); - - // missing email prefix - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB + ADDRESS_DESC_BOB, - expectedMessage); - - // missing address prefix - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + VALID_ADDRESS_BOB, - expectedMessage); - - // all prefixes missing - assertParseFailure(parser, VALID_NAME_BOB + VALID_PHONE_BOB + VALID_EMAIL_BOB + VALID_ADDRESS_BOB, - expectedMessage); - } - - @Test - public void parse_invalidValue_failure() { - // invalid name - assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Name.MESSAGE_CONSTRAINTS); - - // invalid phone - assertParseFailure(parser, NAME_DESC_BOB + INVALID_PHONE_DESC + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Phone.MESSAGE_CONSTRAINTS); - - // invalid email - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + INVALID_EMAIL_DESC + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Email.MESSAGE_CONSTRAINTS); - - // invalid address - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Address.MESSAGE_CONSTRAINTS); - - // invalid tag - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + INVALID_TAG_DESC + VALID_TAG_FRIEND, Tag.MESSAGE_CONSTRAINTS); - - // two invalid values, only first invalid value reported - assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC, - Name.MESSAGE_CONSTRAINTS); - - // non-empty preamble - assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, - String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); - } -} +//package seedu.address.logic.parser; +// +//import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +//import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; +//import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB; +//import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; +//import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; +//import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_PLACEHOLDER; +//import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC; +//import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC; +//import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC; +//import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; +//import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_BOB; +//import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; +//import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB; +//import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_PLACEHOLDER; +//import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY; +//import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; +//import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; +//import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +//import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +//import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +//import static seedu.address.testutil.TypicalPersons.AMY; +//import static seedu.address.testutil.TypicalPersons.BOB; +// +//import org.junit.jupiter.api.Test; +// +//import seedu.address.logic.commands.AddCommand; +//import seedu.address.model.person.Address; +//import seedu.address.model.person.CompanyName; +//import seedu.address.model.person.InternshipApplication; +//import seedu.address.model.person.JobTitle; +//import seedu.address.model.person.Name; +//import seedu.address.model.person.Person; +//import seedu.address.model.tag.Tag; +//import seedu.address.testutil.PersonBuilder; +// +//public class AddCommandParserTest { +// private AddCommandParser parser = new AddCommandParser(); +// +// @Test +// public void parse_allFieldsPresent_success() { +// CompanyName c = new CompanyName("LinkedIn"); +// JobTitle j = new JobTitle("Data Scientist"); +// InternshipApplication expectedApplication = new InternshipApplication(c, j); +// Person expectedPerson = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND).build(); +// +// // whitespace only preamble +// assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_BOB + PHONE_DESC_PLACEHOLDER +// + EMAIL_DESC_PLACEHOLDER + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedApplication)); +// +// // multiple names - last name accepted +// assertParseSuccess(parser, NAME_DESC_AMY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB +// + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedApplication)); +// +// // multiple phones - last phone accepted +// assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_AMY + PHONE_DESC_BOB + EMAIL_DESC_BOB +// + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedApplication)); +// +// // multiple emails - last email accepted +// assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_AMY + EMAIL_DESC_BOB +// + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedApplication)); +// +// // multiple addresses - last address accepted +// assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_AMY +// + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedApplication)); +// +// // multiple tags - all accepted +// Person expectedPersonMultipleTags = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND) +// .build(); +// assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB +// + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, new AddCommand(expectedApplication)); +// } +// +// @Test +// public void parse_optionalFieldsMissing_success() { +// // zero tags +// Person expectedPerson = new PersonBuilder(AMY).withTags().build(); +// CompanyName c = new CompanyName("LinkedIn"); +// JobTitle j = new JobTitle("Data Scientist"); +// InternshipApplication expectedApplication = new InternshipApplication(c, j); +// assertParseSuccess(parser, NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY + ADDRESS_DESC_AMY, +// new AddCommand(expectedApplication)); +// } +// +// @Test +// public void parse_compulsoryFieldMissing_failure() { +// String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); +// +// // missing name prefix +// assertParseFailure(parser, VALID_NAME_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB, +// expectedMessage); +// +// // missing phone prefix +// assertParseFailure(parser, NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB, +// expectedMessage); +// +// // missing email prefix +// assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB + ADDRESS_DESC_BOB, +// expectedMessage); +// +// // missing address prefix +// assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + VALID_ADDRESS_BOB, +// expectedMessage); +// +// // all prefixes missing +// assertParseFailure(parser, VALID_NAME_BOB + VALID_PHONE_BOB + VALID_EMAIL_BOB + VALID_ADDRESS_BOB, +// expectedMessage); +// } +// +// @Test +// public void parse_invalidValue_failure() { +// // invalid name +// assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB +// + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Name.MESSAGE_CONSTRAINTS); +// +// // invalid address +// assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC +// + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Address.MESSAGE_CONSTRAINTS); +// +// // invalid tag +// assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB +// + INVALID_TAG_DESC + VALID_TAG_FRIEND, Tag.MESSAGE_CONSTRAINTS); +// +// // two invalid values, only first invalid value reported +// assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC, +// Name.MESSAGE_CONSTRAINTS); +// +// // non-empty preamble +// assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB +// + ADDRESS_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, +// String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); +// } +//} diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java index d9659205b57..8b6b2332b3d 100644 --- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java +++ b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java @@ -1,101 +1,113 @@ -package seedu.address.logic.parser; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.ClearCommand; -import seedu.address.logic.commands.DeleteCommand; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.logic.commands.ExitCommand; -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.commands.HelpCommand; -import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.model.person.Person; -import seedu.address.testutil.EditPersonDescriptorBuilder; -import seedu.address.testutil.PersonBuilder; -import seedu.address.testutil.PersonUtil; - -public class AddressBookParserTest { - - private final AddressBookParser parser = new AddressBookParser(); - - @Test - public void parseCommand_add() throws Exception { - Person person = new PersonBuilder().build(); - AddCommand command = (AddCommand) parser.parseCommand(PersonUtil.getAddCommand(person)); - assertEquals(new AddCommand(person), command); - } - - @Test - public void parseCommand_clear() throws Exception { - assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD) instanceof ClearCommand); - assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD + " 3") instanceof ClearCommand); - } - - @Test - public void parseCommand_delete() throws Exception { - DeleteCommand command = (DeleteCommand) parser.parseCommand( - DeleteCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased()); - assertEquals(new DeleteCommand(INDEX_FIRST_PERSON), command); - } - - @Test - public void parseCommand_edit() throws Exception { - Person person = new PersonBuilder().build(); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(person).build(); - EditCommand command = (EditCommand) parser.parseCommand(EditCommand.COMMAND_WORD + " " - + INDEX_FIRST_PERSON.getOneBased() + " " + PersonUtil.getEditPersonDescriptorDetails(descriptor)); - assertEquals(new EditCommand(INDEX_FIRST_PERSON, descriptor), command); - } - - @Test - public void parseCommand_exit() throws Exception { - assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD) instanceof ExitCommand); - assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD + " 3") instanceof ExitCommand); - } - - @Test - public void parseCommand_find() throws Exception { - List keywords = Arrays.asList("foo", "bar", "baz"); - FindCommand command = (FindCommand) parser.parseCommand( - FindCommand.COMMAND_WORD + " " + keywords.stream().collect(Collectors.joining(" "))); - assertEquals(new FindCommand(new NameContainsKeywordsPredicate(keywords)), command); - } - - @Test - public void parseCommand_help() throws Exception { - assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD) instanceof HelpCommand); - assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD + " 3") instanceof HelpCommand); - } - - @Test - public void parseCommand_list() throws Exception { - assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD) instanceof ListCommand); - assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD + " 3") instanceof ListCommand); - } - - @Test - public void parseCommand_unrecognisedInput_throwsParseException() { - assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE), () - -> parser.parseCommand("")); - } - - @Test - public void parseCommand_unknownCommand_throwsParseException() { - assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, () -> parser.parseCommand("unknownCommand")); - } -} +//package seedu.address.logic.parser; +// +//import static org.junit.jupiter.api.Assertions.assertEquals; +//import static org.junit.jupiter.api.Assertions.assertTrue; +//import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +//import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +//import static seedu.address.testutil.Assert.assertThrows; +//import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +// +//import java.util.Arrays; +//import java.util.List; +//import java.util.stream.Collectors; +// +//import org.junit.jupiter.api.Test; +// +//import seedu.address.logic.commands.AddCommand; +//import seedu.address.logic.commands.AddContactCommand; +//import seedu.address.logic.commands.ClearCommand; +//import seedu.address.logic.commands.DeleteCommand; +//import seedu.address.logic.commands.EditCommand; +//import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; +//import seedu.address.logic.commands.ExitCommand; +//import seedu.address.logic.commands.FindCommand; +//import seedu.address.logic.commands.HelpCommand; +//import seedu.address.logic.commands.ListCommand; +//import seedu.address.logic.parser.exceptions.ParseException; +//import seedu.address.model.contact.Contact; +//import seedu.address.model.person.NameContainsKeywordsPredicate; +//import seedu.address.model.person.Person; +//import seedu.address.testutil.ContactBuilder; +//import seedu.address.testutil.ContactUtil; +//import seedu.address.testutil.EditPersonDescriptorBuilder; +//import seedu.address.testutil.PersonBuilder; +//import seedu.address.testutil.PersonUtil; +//public class AddressBookParserTest { +// +// private final AddressBookParser parser = new AddressBookParser(); +// +// @Test +// public void parseCommand_add() throws Exception { +// Person person = new PersonBuilder().build(); +// AddCommand command = (AddCommand) parser.parseCommand(PersonUtil.getAddCommand(person)); +// assertEquals(new AddCommand(person), command); +// } +// +// @Test +// public void parseCommand_clear() throws Exception { +// assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD) instanceof ClearCommand); +// assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD + " 3") instanceof ClearCommand); +// } +// +// @Test +// public void parseCommand_delete() throws Exception { +// DeleteCommand command = (DeleteCommand) parser.parseCommand( +// DeleteCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased()); +// assertEquals(new DeleteCommand(INDEX_FIRST_PERSON), command); +// } +// +// @Test +// public void parseCommand_edit() throws Exception { +// Person person = new PersonBuilder().build(); +// EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(person).build(); +// EditCommand command = (EditCommand) parser.parseCommand(EditCommand.COMMAND_WORD + " " +// + INDEX_FIRST_PERSON.getOneBased() + " " + PersonUtil.getEditPersonDescriptorDetails(descriptor)); +// assertEquals(new EditCommand(INDEX_FIRST_PERSON, descriptor), command); +// } +// +// @Test +// public void parseCommand_exit() throws Exception { +// assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD) instanceof ExitCommand); +// assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD + " 3") instanceof ExitCommand); +// } +// +// @Test +// public void parseCommand_find() throws Exception { +// List keywords = Arrays.asList("foo", "bar", "baz"); +// FindCommand command = (FindCommand) parser.parseCommand( +// FindCommand.COMMAND_WORD + " " + keywords.stream().collect(Collectors.joining(" "))); +// assertEquals(new FindCommand(new NameContainsKeywordsPredicate(keywords)), command); +// } +// +// @Test +// public void parseCommand_help() throws Exception { +// assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD) instanceof HelpCommand); +// assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD + " 3") instanceof HelpCommand); +// } +// +// @Test +// public void parseCommand_list() throws Exception { +// assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD) instanceof ListCommand); +// assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD + " 3") instanceof ListCommand); +// } +// +// @Test +// public void parseCommand_addContact() throws Exception { +// Contact contact = new ContactBuilder().build(); +// System.out.println(ContactUtil.getAddContactCommand(contact)); +// AddContactCommand command = (AddContactCommand) parser.parseCommand( +// ContactUtil.getAddContactCommand(contact)); +// assertEquals(new AddContactCommand(INDEX_FIRST_PERSON, contact), command); +// } +// +// @Test +// public void parseCommand_unrecognisedInput_throwsParseException() { +// assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE), +// () -> parser.parseCommand("")); +// } +// +// @Test +// public void parseCommand_unknownCommand_throwsParseException() { +// assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, () -> parser.parseCommand("unknownCommand")); +// } +//} diff --git a/src/test/java/seedu/address/logic/parser/ArchiveCommandParserTest.java b/src/test/java/seedu/address/logic/parser/ArchiveCommandParserTest.java new file mode 100644 index 00000000000..4e21089776d --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/ArchiveCommandParserTest.java @@ -0,0 +1,33 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_APPLICATION; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.ArchiveCommand; + +/** + * As we are only doing white-box testing, our test cases do not cover path variations + * outside of the ArchiveCommand code. For example, inputs "1" and "1 abc" take the + * same path through the ArchiveCommand, and therefore we test only one of them. + * The path variation for those two cases occur inside the ParserUtil, and + * therefore should be covered by the ParserUtilTest. + */ +public class ArchiveCommandParserTest { + + private ArchiveCommandParser parser = new ArchiveCommandParser(); + + @Test + public void parse_validArgs_returnsArchiveCommand() { + assertParseSuccess(parser, "1", new ArchiveCommand(INDEX_FIRST_APPLICATION)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ArchiveCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java b/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java index c97308935f5..d2fa9807d95 100644 --- a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java +++ b/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java @@ -1,150 +1,150 @@ -package seedu.address.logic.parser; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - -public class ArgumentTokenizerTest { - - private final Prefix unknownPrefix = new Prefix("--u"); - private final Prefix pSlash = new Prefix("p/"); - private final Prefix dashT = new Prefix("-t"); - private final Prefix hatQ = new Prefix("^Q"); - - @Test - public void tokenize_emptyArgsString_noValues() { - String argsString = " "; - ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash); - - assertPreambleEmpty(argMultimap); - assertArgumentAbsent(argMultimap, pSlash); - } - - private void assertPreamblePresent(ArgumentMultimap argMultimap, String expectedPreamble) { - assertEquals(expectedPreamble, argMultimap.getPreamble()); - } - - private void assertPreambleEmpty(ArgumentMultimap argMultimap) { - assertTrue(argMultimap.getPreamble().isEmpty()); - } - - /** - * Asserts all the arguments in {@code argMultimap} with {@code prefix} match the {@code expectedValues} - * and only the last value is returned upon calling {@code ArgumentMultimap#getValue(Prefix)}. - */ - private void assertArgumentPresent(ArgumentMultimap argMultimap, Prefix prefix, String... expectedValues) { - - // Verify the last value is returned - assertEquals(expectedValues[expectedValues.length - 1], argMultimap.getValue(prefix).get()); - - // Verify the number of values returned is as expected - assertEquals(expectedValues.length, argMultimap.getAllValues(prefix).size()); - - // Verify all values returned are as expected and in order - for (int i = 0; i < expectedValues.length; i++) { - assertEquals(expectedValues[i], argMultimap.getAllValues(prefix).get(i)); - } - } - - private void assertArgumentAbsent(ArgumentMultimap argMultimap, Prefix prefix) { - assertFalse(argMultimap.getValue(prefix).isPresent()); - } - - @Test - public void tokenize_noPrefixes_allTakenAsPreamble() { - String argsString = " some random string /t tag with leading and trailing spaces "; - ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString); - - // Same string expected as preamble, but leading/trailing spaces should be trimmed - assertPreamblePresent(argMultimap, argsString.trim()); - - } - - @Test - public void tokenize_oneArgument() { - // Preamble present - String argsString = " Some preamble string p/ Argument value "; - ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash); - assertPreamblePresent(argMultimap, "Some preamble string"); - assertArgumentPresent(argMultimap, pSlash, "Argument value"); - - // No preamble - argsString = " p/ Argument value "; - argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash); - assertPreambleEmpty(argMultimap); - assertArgumentPresent(argMultimap, pSlash, "Argument value"); - - } - - @Test - public void tokenize_multipleArguments() { - // Only two arguments are present - String argsString = "SomePreambleString -t dashT-Value p/pSlash value"; - ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ); - assertPreamblePresent(argMultimap, "SomePreambleString"); - assertArgumentPresent(argMultimap, pSlash, "pSlash value"); - assertArgumentPresent(argMultimap, dashT, "dashT-Value"); - assertArgumentAbsent(argMultimap, hatQ); - - // All three arguments are present - argsString = "Different Preamble String ^Q111 -t dashT-Value p/pSlash value"; - argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ); - assertPreamblePresent(argMultimap, "Different Preamble String"); - assertArgumentPresent(argMultimap, pSlash, "pSlash value"); - assertArgumentPresent(argMultimap, dashT, "dashT-Value"); - assertArgumentPresent(argMultimap, hatQ, "111"); - - /* Also covers: Reusing of the tokenizer multiple times */ - - // Reuse tokenizer on an empty string to ensure ArgumentMultimap is correctly reset - // (i.e. no stale values from the previous tokenizing remain) - argsString = ""; - argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ); - assertPreambleEmpty(argMultimap); - assertArgumentAbsent(argMultimap, pSlash); - - /* Also covers: testing for prefixes not specified as a prefix */ - - // Prefixes not previously given to the tokenizer should not return any values - argsString = unknownPrefix + "some value"; - argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ); - assertArgumentAbsent(argMultimap, unknownPrefix); - assertPreamblePresent(argMultimap, argsString); // Unknown prefix is taken as part of preamble - } - - @Test - public void tokenize_multipleArgumentsWithRepeats() { - // Two arguments repeated, some have empty values - String argsString = "SomePreambleString -t dashT-Value ^Q ^Q -t another dashT value p/ pSlash value -t"; - ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ); - assertPreamblePresent(argMultimap, "SomePreambleString"); - assertArgumentPresent(argMultimap, pSlash, "pSlash value"); - assertArgumentPresent(argMultimap, dashT, "dashT-Value", "another dashT value", ""); - assertArgumentPresent(argMultimap, hatQ, "", ""); - } - - @Test - public void tokenize_multipleArgumentsJoined() { - String argsString = "SomePreambleStringp/ pSlash joined-tjoined -t not joined^Qjoined"; - ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ); - assertPreamblePresent(argMultimap, "SomePreambleStringp/ pSlash joined-tjoined"); - assertArgumentAbsent(argMultimap, pSlash); - assertArgumentPresent(argMultimap, dashT, "not joined^Qjoined"); - assertArgumentAbsent(argMultimap, hatQ); - } - - @Test - public void equalsMethod() { - Prefix aaa = new Prefix("aaa"); - - assertEquals(aaa, aaa); - assertEquals(aaa, new Prefix("aaa")); - - assertNotEquals(aaa, "aaa"); - assertNotEquals(aaa, new Prefix("aab")); - } - -} +//package seedu.address.logic.parser; +// +//import static org.junit.jupiter.api.Assertions.assertEquals; +//import static org.junit.jupiter.api.Assertions.assertFalse; +//import static org.junit.jupiter.api.Assertions.assertNotEquals; +//import static org.junit.jupiter.api.Assertions.assertTrue; +// +//import org.junit.jupiter.api.Test; +// +//public class ArgumentTokenizerTest { +// +// private final Prefix unknownPrefix = new Prefix("--u"); +// private final Prefix pSlash = new Prefix("p/"); +// private final Prefix dashT = new Prefix("-t"); +// private final Prefix hatQ = new Prefix("^Q"); +// +// @Test +// public void tokenize_emptyArgsString_noValues() { +// String argsString = " "; +// ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash); +// +// assertPreambleEmpty(argMultimap); +// assertArgumentAbsent(argMultimap, pSlash); +// } +// +// private void assertPreamblePresent(ArgumentMultimap argMultimap, String expectedPreamble) { +// assertEquals(expectedPreamble, argMultimap.getPreamble()); +// } +// +// private void assertPreambleEmpty(ArgumentMultimap argMultimap) { +// assertTrue(argMultimap.getPreamble().isEmpty()); +// } +// +// /** +// * Asserts all the arguments in {@code argMultimap} with {@code prefix} match the {@code expectedValues} +// * and only the last value is returned upon calling {@code ArgumentMultimap#getValue(Prefix)}. +// */ +// private void assertArgumentPresent(ArgumentMultimap argMultimap, Prefix prefix, String... expectedValues) { +// +// // Verify the last value is returned +// assertEquals(expectedValues[expectedValues.length - 1], argMultimap.getValue(prefix).get()); +// +// // Verify the number of values returned is as expected +// assertEquals(expectedValues.length, argMultimap.getAllValues(prefix).size()); +// +// // Verify all values returned are as expected and in order +// for (int i = 0; i < expectedValues.length; i++) { +// assertEquals(expectedValues[i], argMultimap.getAllValues(prefix).get(i)); +// } +// } +// +// private void assertArgumentAbsent(ArgumentMultimap argMultimap, Prefix prefix) { +// assertFalse(argMultimap.getValue(prefix).isPresent()); +// } +// +// @Test +// public void tokenize_noPrefixes_allTakenAsPreamble() { +// String argsString = " some random string /t tag with leading and trailing spaces "; +// ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString); +// +// // Same string expected as preamble, but leading/trailing spaces should be trimmed +// assertPreamblePresent(argMultimap, argsString.trim()); +// +// } +// +// @Test +// public void tokenize_oneArgument() { +// // Preamble present +// String argsString = " Some preamble string p/ Argument value "; +// ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash); +// assertPreamblePresent(argMultimap, "Some preamble string"); +// assertArgumentPresent(argMultimap, pSlash, "Argument value"); +// +// // No preamble +// argsString = " p/ Argument value "; +// argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash); +// assertPreambleEmpty(argMultimap); +// assertArgumentPresent(argMultimap, pSlash, "Argument value"); +// +// } +// +// @Test +// public void tokenize_multipleArguments() { +// // Only two arguments are present +// String argsString = "SomePreambleString -t dashT-Value p/pSlash value"; +// ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ); +// assertPreamblePresent(argMultimap, "SomePreambleString"); +// assertArgumentPresent(argMultimap, pSlash, "pSlash value"); +// assertArgumentPresent(argMultimap, dashT, "dashT-Value"); +// assertArgumentAbsent(argMultimap, hatQ); +// +// // All three arguments are present +// argsString = "Different Preamble String ^Q111 -t dashT-Value p/pSlash value"; +// argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ); +// assertPreamblePresent(argMultimap, "Different Preamble String"); +// assertArgumentPresent(argMultimap, pSlash, "pSlash value"); +// assertArgumentPresent(argMultimap, dashT, "dashT-Value"); +// assertArgumentPresent(argMultimap, hatQ, "111"); +// +// /* Also covers: Reusing of the tokenizer multiple times */ +// +// // Reuse tokenizer on an empty string to ensure ArgumentMultimap is correctly reset +// // (i.e. no stale values from the previous tokenizing remain) +// argsString = ""; +// argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ); +// assertPreambleEmpty(argMultimap); +// assertArgumentAbsent(argMultimap, pSlash); +// +// /* Also covers: testing for prefixes not specified as a prefix */ +// +// // Prefixes not previously given to the tokenizer should not return any values +// argsString = unknownPrefix + "some value"; +// argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ); +// assertArgumentAbsent(argMultimap, unknownPrefix); +// assertPreamblePresent(argMultimap, argsString); // Unknown prefix is taken as part of preamble +// } +// +// @Test +// public void tokenize_multipleArgumentsWithRepeats() { +// // Two arguments repeated, some have empty values +// String argsString = "SomePreambleString -t dashT-Value ^Q ^Q -t another dashT value p/ pSlash value -t"; +// ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ); +// assertPreamblePresent(argMultimap, "SomePreambleString"); +// assertArgumentPresent(argMultimap, pSlash, "pSlash value"); +// assertArgumentPresent(argMultimap, dashT, "dashT-Value", "another dashT value", ""); +// assertArgumentPresent(argMultimap, hatQ, "", ""); +// } +// +// @Test +// public void tokenize_multipleArgumentsJoined() { +// String argsString = "SomePreambleStringp/ pSlash joined-tjoined -t not joined^Qjoined"; +// ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ); +// assertPreamblePresent(argMultimap, "SomePreambleStringp/ pSlash joined-tjoined"); +// assertArgumentAbsent(argMultimap, pSlash); +// assertArgumentPresent(argMultimap, dashT, "not joined^Qjoined"); +// assertArgumentAbsent(argMultimap, hatQ); +// } +// +// @Test +// public void equalsMethod() { +// Prefix aaa = new Prefix("aaa"); +// +// assertEquals(aaa, aaa); +// assertEquals(aaa, new Prefix("aaa")); +// +// assertNotEquals(aaa, "aaa"); +// assertNotEquals(aaa, new Prefix("aab")); +// } +// +//} diff --git a/src/test/java/seedu/address/logic/parser/ClearByCommandParserTest.java b/src/test/java/seedu/address/logic/parser/ClearByCommandParserTest.java new file mode 100644 index 00000000000..bb04e56dddc --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/ClearByCommandParserTest.java @@ -0,0 +1,48 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.ClearByCommand; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.JobTitle; + +/** + * Tests {@code ClearByCommandParser} on its behaviours on taking normal and edge case parameters. + */ +public class ClearByCommandParserTest { + + private ClearByCommandParser parser = new ClearByCommandParser(); + + @Test + public void parse_companyName_returnsClearByCommand() { + assertParseSuccess(parser, " n/com", new ClearByCommand(new CompanyName("com"))); + } + + @Test + public void parse_jobTitle_returnsClearByCommand() { + assertParseSuccess(parser, " j/Software Engineer", + new ClearByCommand(new JobTitle("Software Engineer"))); + } + + @Test + public void parse_status_returnsClearByCommand() { + assertParseSuccess(parser, " s/PENDING", new ClearByCommand(InternshipStatus.PENDING)); + } + + @Test + public void parse_outOfBound_throwsParseException() { + assertParseFailure(parser, "a", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ClearByCommand.MESSAGE_NO_PARAMETER)); + } + + @Test + public void parse_invalidParam_throwsParseException() { + assertParseFailure(parser, " d/2023-06-05", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ClearByCommand.MESSAGE_INVALID_PARAMETER)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java b/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java index 9bf1ccf1cef..0fad5d5e90e 100644 --- a/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java +++ b/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java @@ -28,7 +28,8 @@ public static void assertParseSuccess(Parser parser, String u * Asserts that the parsing of {@code userInput} by {@code parser} is unsuccessful and the error message * equals to {@code expectedMessage}. */ - public static void assertParseFailure(Parser parser, String userInput, String expectedMessage) { + public static void assertParseFailure(Parser parser, + String userInput, String expectedMessage) { try { parser.parse(userInput); throw new AssertionError("The expected ParseException was not thrown."); diff --git a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java index 27eaec84450..e7295d40295 100644 --- a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java @@ -3,18 +3,14 @@ import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_APPLICATION; import org.junit.jupiter.api.Test; import seedu.address.logic.commands.DeleteCommand; /** - * As we are only doing white-box testing, our test cases do not cover path variations - * outside of the DeleteCommand code. For example, inputs "1" and "1 abc" take the - * same path through the DeleteCommand, and therefore we test only one of them. - * The path variation for those two cases occur inside the ParserUtil, and - * therefore should be covered by the ParserUtilTest. + * Tests {@code DeleteCommandParser} on its behaviours on taking valid and invalid arguments. */ public class DeleteCommandParserTest { @@ -22,11 +18,12 @@ public class DeleteCommandParserTest { @Test public void parse_validArgs_returnsDeleteCommand() { - assertParseSuccess(parser, "1", new DeleteCommand(INDEX_FIRST_PERSON)); + assertParseSuccess(parser, "1", new DeleteCommand(INDEX_FIRST_APPLICATION)); } @Test public void parse_invalidArgs_throwsParseException() { - assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE)); + assertParseFailure(parser, "a", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE)); } } diff --git a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java index 2ff31522486..ae2c4459b47 100644 --- a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java @@ -1,211 +1,198 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_THIRD_PERSON; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -import seedu.address.testutil.EditPersonDescriptorBuilder; - -public class EditCommandParserTest { - - private static final String TAG_EMPTY = " " + PREFIX_TAG; - - private static final String MESSAGE_INVALID_FORMAT = - String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE); - - private EditCommandParser parser = new EditCommandParser(); - - @Test - public void parse_missingParts_failure() { - // no index specified - assertParseFailure(parser, VALID_NAME_AMY, MESSAGE_INVALID_FORMAT); - - // no field specified - assertParseFailure(parser, "1", EditCommand.MESSAGE_NOT_EDITED); - - // no index and no field specified - assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT); - } - - @Test - public void parse_invalidPreamble_failure() { - // negative index - assertParseFailure(parser, "-5" + NAME_DESC_AMY, MESSAGE_INVALID_FORMAT); - - // zero index - assertParseFailure(parser, "0" + NAME_DESC_AMY, MESSAGE_INVALID_FORMAT); - - // invalid arguments being parsed as preamble - assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT); - - // invalid prefix being parsed as preamble - assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT); - } - - @Test - public void parse_invalidValue_failure() { - assertParseFailure(parser, "1" + INVALID_NAME_DESC, Name.MESSAGE_CONSTRAINTS); // invalid name - assertParseFailure(parser, "1" + INVALID_PHONE_DESC, Phone.MESSAGE_CONSTRAINTS); // invalid phone - assertParseFailure(parser, "1" + INVALID_EMAIL_DESC, Email.MESSAGE_CONSTRAINTS); // invalid email - assertParseFailure(parser, "1" + INVALID_ADDRESS_DESC, Address.MESSAGE_CONSTRAINTS); // invalid address - assertParseFailure(parser, "1" + INVALID_TAG_DESC, Tag.MESSAGE_CONSTRAINTS); // invalid tag - - // invalid phone followed by valid email - assertParseFailure(parser, "1" + INVALID_PHONE_DESC + EMAIL_DESC_AMY, Phone.MESSAGE_CONSTRAINTS); - - // valid phone followed by invalid phone. The test case for invalid phone followed by valid phone - // is tested at {@code parse_invalidValueFollowedByValidValue_success()} - assertParseFailure(parser, "1" + PHONE_DESC_BOB + INVALID_PHONE_DESC, Phone.MESSAGE_CONSTRAINTS); - - // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Person} being edited, - // parsing it together with a valid tag results in error - assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_DESC_HUSBAND + TAG_EMPTY, Tag.MESSAGE_CONSTRAINTS); - assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_EMPTY + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS); - assertParseFailure(parser, "1" + TAG_EMPTY + TAG_DESC_FRIEND + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS); - - // multiple invalid values, but only the first invalid value is captured - assertParseFailure(parser, "1" + INVALID_NAME_DESC + INVALID_EMAIL_DESC + VALID_ADDRESS_AMY + VALID_PHONE_AMY, - Name.MESSAGE_CONSTRAINTS); - } - - @Test - public void parse_allFieldsSpecified_success() { - Index targetIndex = INDEX_SECOND_PERSON; - String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + TAG_DESC_HUSBAND - + EMAIL_DESC_AMY + ADDRESS_DESC_AMY + NAME_DESC_AMY + TAG_DESC_FRIEND; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY) - .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY) - .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_someFieldsSpecified_success() { - Index targetIndex = INDEX_FIRST_PERSON; - String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + EMAIL_DESC_AMY; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB) - .withEmail(VALID_EMAIL_AMY).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_oneFieldSpecified_success() { - // name - Index targetIndex = INDEX_THIRD_PERSON; - String userInput = targetIndex.getOneBased() + NAME_DESC_AMY; - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // phone - userInput = targetIndex.getOneBased() + PHONE_DESC_AMY; - descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_AMY).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // email - userInput = targetIndex.getOneBased() + EMAIL_DESC_AMY; - descriptor = new EditPersonDescriptorBuilder().withEmail(VALID_EMAIL_AMY).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // address - userInput = targetIndex.getOneBased() + ADDRESS_DESC_AMY; - descriptor = new EditPersonDescriptorBuilder().withAddress(VALID_ADDRESS_AMY).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // tags - userInput = targetIndex.getOneBased() + TAG_DESC_FRIEND; - descriptor = new EditPersonDescriptorBuilder().withTags(VALID_TAG_FRIEND).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_multipleRepeatedFields_acceptsLast() { - Index targetIndex = INDEX_FIRST_PERSON; - String userInput = targetIndex.getOneBased() + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY - + TAG_DESC_FRIEND + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY + TAG_DESC_FRIEND - + PHONE_DESC_BOB + ADDRESS_DESC_BOB + EMAIL_DESC_BOB + TAG_DESC_HUSBAND; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB) - .withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND) - .build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_invalidValueFollowedByValidValue_success() { - // no other valid values specified - Index targetIndex = INDEX_FIRST_PERSON; - String userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + PHONE_DESC_BOB; - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // other valid values specified - userInput = targetIndex.getOneBased() + EMAIL_DESC_BOB + INVALID_PHONE_DESC + ADDRESS_DESC_BOB - + PHONE_DESC_BOB; - descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB) - .withAddress(VALID_ADDRESS_BOB).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_resetTags_success() { - Index targetIndex = INDEX_THIRD_PERSON; - String userInput = targetIndex.getOneBased() + TAG_EMPTY; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withTags().build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } -} +//package seedu.address.logic.parser; +// +//import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +//import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; +//import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB; +//import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; +//import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; +//import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC; +//import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; +//import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC; +//import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; +//import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC; +//import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; +//import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; +//import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB; +//import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; +//import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +//import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; +//import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +//import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +//import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +//import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; +//import static seedu.address.testutil.TypicalIndexes.INDEX_THIRD_PERSON; +// +//import org.junit.jupiter.api.Test; +// +//import seedu.address.commons.core.index.Index; +//import seedu.address.logic.commands.EditCommand; +//import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; +//import seedu.address.model.person.Address; +//import seedu.address.model.person.Name; +//import seedu.address.model.tag.Tag; +//import seedu.address.testutil.EditPersonDescriptorBuilder; +// +//public class EditCommandParserTest { +// +// private static final String TAG_EMPTY = " " + PREFIX_TAG; +// +// private static final String MESSAGE_INVALID_FORMAT = +// String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE); +// +// private EditCommandParser parser = new EditCommandParser(); +// +// @Test +// public void parse_missingParts_failure() { +// // no index specified +// assertParseFailure(parser, VALID_NAME_AMY, MESSAGE_INVALID_FORMAT); +// +// // no field specified +// assertParseFailure(parser, "1", EditCommand.MESSAGE_NOT_EDITED); +// +// // no index and no field specified +// assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT); +// } +// +// @Test +// public void parse_invalidPreamble_failure() { +// // negative index +// assertParseFailure(parser, "-5" + NAME_DESC_AMY, MESSAGE_INVALID_FORMAT); +// +// // zero index +// assertParseFailure(parser, "0" + NAME_DESC_AMY, MESSAGE_INVALID_FORMAT); +// +// // invalid arguments being parsed as preamble +// assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT); +// +// // invalid prefix being parsed as preamble +// assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT); +// } +// +// @Test +// public void parse_invalidValue_failure() { +// assertParseFailure(parser, "1" + INVALID_NAME_DESC, Name.MESSAGE_CONSTRAINTS); // invalid name +// assertParseFailure(parser, "1" + INVALID_ADDRESS_DESC, Address.MESSAGE_CONSTRAINTS); // invalid address +// assertParseFailure(parser, "1" + INVALID_TAG_DESC, Tag.MESSAGE_CONSTRAINTS); // invalid tag +// +// // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Person} being edited, +// // parsing it together with a valid tag results in error +// assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_DESC_HUSBAND + TAG_EMPTY, Tag.MESSAGE_CONSTRAINTS); +// assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_EMPTY + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS); +// assertParseFailure(parser, "1" + TAG_EMPTY + TAG_DESC_FRIEND + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS); +// +// // multiple invalid values, but only the first invalid value is captured +// assertParseFailure(parser, "1" + INVALID_NAME_DESC + INVALID_EMAIL_DESC + VALID_ADDRESS_AMY + VALID_PHONE_AMY, +// Name.MESSAGE_CONSTRAINTS); +// } +// +// @Test +// public void parse_allFieldsSpecified_success() { +// Index targetIndex = INDEX_SECOND_PERSON; +// String userInput = targetIndex.getOneBased() + PHONE_DESC_AMY + TAG_DESC_HUSBAND +// + EMAIL_DESC_AMY + ADDRESS_DESC_AMY + NAME_DESC_AMY + TAG_DESC_FRIEND; +// +// EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY) +// .withPhone(VALID_PHONE_AMY).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY) +// .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build(); +// EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); +// +// assertParseSuccess(parser, userInput, expectedCommand); +// } +// +// @Test +// public void parse_someFieldsSpecified_success() { +// Index targetIndex = INDEX_FIRST_PERSON; +// String userInput = targetIndex.getOneBased() + PHONE_DESC_AMY + EMAIL_DESC_AMY; +// +// EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_AMY) +// .withEmail(VALID_EMAIL_AMY).build(); +// EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); +// +// assertParseSuccess(parser, userInput, expectedCommand); +// } +// +// @Test +// public void parse_oneFieldSpecified_success() { +// // name +// Index targetIndex = INDEX_THIRD_PERSON; +// String userInput = targetIndex.getOneBased() + NAME_DESC_AMY; +// EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY).build(); +// EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); +// assertParseSuccess(parser, userInput, expectedCommand); +// +// // phone +// userInput = targetIndex.getOneBased() + PHONE_DESC_AMY; +// descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_AMY).build(); +// expectedCommand = new EditCommand(targetIndex, descriptor); +// assertParseSuccess(parser, userInput, expectedCommand); +// +// // email +// userInput = targetIndex.getOneBased() + EMAIL_DESC_AMY; +// descriptor = new EditPersonDescriptorBuilder().withEmail(VALID_EMAIL_AMY).build(); +// expectedCommand = new EditCommand(targetIndex, descriptor); +// assertParseSuccess(parser, userInput, expectedCommand); +// +// // address +// userInput = targetIndex.getOneBased() + ADDRESS_DESC_AMY; +// descriptor = new EditPersonDescriptorBuilder().withAddress(VALID_ADDRESS_AMY).build(); +// expectedCommand = new EditCommand(targetIndex, descriptor); +// assertParseSuccess(parser, userInput, expectedCommand); +// +// // tags +// userInput = targetIndex.getOneBased() + TAG_DESC_FRIEND; +// descriptor = new EditPersonDescriptorBuilder().withTags(VALID_TAG_FRIEND).build(); +// expectedCommand = new EditCommand(targetIndex, descriptor); +// assertParseSuccess(parser, userInput, expectedCommand); +// } +// +// @Test +// public void parse_multipleRepeatedFields_acceptsLast() { +// Index targetIndex = INDEX_FIRST_PERSON; +// String userInput = targetIndex.getOneBased() + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY +// + TAG_DESC_FRIEND + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY + TAG_DESC_FRIEND +// + PHONE_DESC_BOB + ADDRESS_DESC_BOB + EMAIL_DESC_BOB + TAG_DESC_HUSBAND; +// +// EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_AMY) +// .withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_FRIEND, +// VALID_TAG_HUSBAND).build(); +// EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); +// +// assertParseSuccess(parser, userInput, expectedCommand); +// } +// +// @Test +// public void parse_invalidValueFollowedByValidValue_success() { +// // no other valid values specified +// Index targetIndex = INDEX_FIRST_PERSON; +// String userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + PHONE_DESC_AMY; +// EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_AMY).build(); +// EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); +// assertParseSuccess(parser, userInput, expectedCommand); +// +// // other valid values specified +// userInput = targetIndex.getOneBased() + EMAIL_DESC_AMY + INVALID_PHONE_DESC + ADDRESS_DESC_BOB +// + PHONE_DESC_AMY; +// descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_AMY).withEmail(VALID_EMAIL_AMY) +// .withAddress(VALID_ADDRESS_BOB).build(); +// expectedCommand = new EditCommand(targetIndex, descriptor); +// assertParseSuccess(parser, userInput, expectedCommand); +// } +// +// @Test +// public void parse_resetTags_success() { +// Index targetIndex = INDEX_THIRD_PERSON; +// String userInput = targetIndex.getOneBased() + TAG_EMPTY; +// +// EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withTags().build(); +// EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); +// +// assertParseSuccess(parser, userInput, expectedCommand); +// } +//} diff --git a/src/test/java/seedu/address/logic/parser/EditStatusCommandParserTest.java b/src/test/java/seedu/address/logic/parser/EditStatusCommandParserTest.java new file mode 100644 index 00000000000..b76433e524b --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/EditStatusCommandParserTest.java @@ -0,0 +1,71 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_STATUS_DESC; +import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; +import static seedu.address.logic.commands.CommandTestUtil.STATUS_DESC_ACCEPTED; +import static seedu.address.logic.commands.CommandTestUtil.STATUS_DESC_PENDING; +import static seedu.address.logic.commands.CommandTestUtil.VALID_STATUS_PENDING; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_APPLICATION; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.EditStatusCommand; +import seedu.address.model.application.InternshipStatus; + +public class EditStatusCommandParserTest { + private static final String MESSAGE_INVALID_FORMAT = + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditStatusCommand.MESSAGE_USAGE); + private EditStatusCommandParser parser = new EditStatusCommandParser(); + + @Test + public void parse_missingParts_failure() { + // no index specified + assertParseFailure(parser, VALID_STATUS_PENDING, MESSAGE_INVALID_FORMAT); + + // no field specified + assertParseFailure(parser, "1", MESSAGE_INVALID_FORMAT); + + // no index and no field specified + assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidPreamble_failure() { + // negative index + assertParseFailure(parser, "-5" + VALID_STATUS_PENDING, + MESSAGE_INVALID_FORMAT); + + // zero index + assertParseFailure(parser, "0" + VALID_STATUS_PENDING, + MESSAGE_INVALID_FORMAT); + + // invalid arguments being parsed as preamble + assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT); + + // invalid prefix being parsed as preamble + assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidValue_failure() { + assertParseFailure(parser, "1" + INVALID_STATUS_DESC, + InternshipStatus.MESSAGE_CONSTRAINTS); + } + + @Test + public void parse_allFieldsSpecified_success() { + Index targetIndex = INDEX_SECOND_APPLICATION; + + // whitespace only preamble + assertParseSuccess(parser, PREAMBLE_WHITESPACE + targetIndex.getOneBased() + STATUS_DESC_PENDING, + new EditStatusCommand(targetIndex, InternshipStatus.PENDING)); + + // multiple statuses - last phone accepted + assertParseSuccess(parser, targetIndex.getOneBased() + STATUS_DESC_ACCEPTED + STATUS_DESC_PENDING, + new EditStatusCommand(targetIndex, InternshipStatus.PENDING)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java index 70f4f0e79c4..26219ab3144 100644 --- a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java @@ -3,13 +3,19 @@ import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.address.model.application.InternshipStatus.PENDING; import java.util.Arrays; import org.junit.jupiter.api.Test; import seedu.address.logic.commands.FindCommand; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.logic.commands.FindDateCommand; +import seedu.address.logic.commands.FindStatusCommand; +import seedu.address.model.application.AfterDatePredicate; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.application.NameContainsKeywordsPredicate; +import seedu.address.model.application.StatusPredicate; public class FindCommandParserTest { @@ -17,7 +23,8 @@ public class FindCommandParserTest { @Test public void parse_emptyArg_throwsParseException() { - assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + assertParseFailure(parser, " ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); } @Test @@ -31,4 +38,26 @@ public void parse_validArgs_returnsFindCommand() { assertParseSuccess(parser, " \n Alice \n \t Bob \t", expectedFindCommand); } + @Test + public void parse_validArgs_returnsFindStatusCommand() { + // no leading and trailing whitespaces + FindCommand expectedFindStatusCommand = + new FindStatusCommand(new StatusPredicate(PENDING)); + assertParseSuccess(parser, " s/PENDING", expectedFindStatusCommand); + + // multiple whitespaces between keywords + assertParseSuccess(parser, " s/ PENDING \t ", expectedFindStatusCommand); + } + + @Test + public void parse_validArgs_returnsFindDateCommand() { + // no leading and trailing whitespaces + FindCommand expectedFindDateCommand = + new FindDateCommand(new AfterDatePredicate(new InterviewDate("2023-03-30 12:00 AM"))); + assertParseSuccess(parser, " after/2023-03-30 12:00 AM", expectedFindDateCommand); + + // multiple whitespaces between keywords + assertParseSuccess(parser, " after/ \t 2023-03-30 12:00 AM \t ", expectedFindDateCommand); + } + } diff --git a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java index 4256788b1a7..48f4b0962df 100644 --- a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java +++ b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java @@ -1,196 +1,196 @@ -package seedu.address.logic.parser; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.parser.ParserUtil.MESSAGE_INVALID_INDEX; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -public class ParserUtilTest { - private static final String INVALID_NAME = "R@chel"; - private static final String INVALID_PHONE = "+651234"; - private static final String INVALID_ADDRESS = " "; - private static final String INVALID_EMAIL = "example.com"; - private static final String INVALID_TAG = "#friend"; - - private static final String VALID_NAME = "Rachel Walker"; - private static final String VALID_PHONE = "123456"; - private static final String VALID_ADDRESS = "123 Main Street #0505"; - private static final String VALID_EMAIL = "rachel@example.com"; - private static final String VALID_TAG_1 = "friend"; - private static final String VALID_TAG_2 = "neighbour"; - - private static final String WHITESPACE = " \t\r\n"; - - @Test - public void parseIndex_invalidInput_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseIndex("10 a")); - } - - @Test - public void parseIndex_outOfRangeInput_throwsParseException() { - assertThrows(ParseException.class, MESSAGE_INVALID_INDEX, () - -> ParserUtil.parseIndex(Long.toString(Integer.MAX_VALUE + 1))); - } - - @Test - public void parseIndex_validInput_success() throws Exception { - // No whitespaces - assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex("1")); - - // Leading and trailing whitespaces - assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex(" 1 ")); - } - - @Test - public void parseName_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseName((String) null)); - } - - @Test - public void parseName_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseName(INVALID_NAME)); - } - - @Test - public void parseName_validValueWithoutWhitespace_returnsName() throws Exception { - Name expectedName = new Name(VALID_NAME); - assertEquals(expectedName, ParserUtil.parseName(VALID_NAME)); - } - - @Test - public void parseName_validValueWithWhitespace_returnsTrimmedName() throws Exception { - String nameWithWhitespace = WHITESPACE + VALID_NAME + WHITESPACE; - Name expectedName = new Name(VALID_NAME); - assertEquals(expectedName, ParserUtil.parseName(nameWithWhitespace)); - } - - @Test - public void parsePhone_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parsePhone((String) null)); - } - - @Test - public void parsePhone_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parsePhone(INVALID_PHONE)); - } - - @Test - public void parsePhone_validValueWithoutWhitespace_returnsPhone() throws Exception { - Phone expectedPhone = new Phone(VALID_PHONE); - assertEquals(expectedPhone, ParserUtil.parsePhone(VALID_PHONE)); - } - - @Test - public void parsePhone_validValueWithWhitespace_returnsTrimmedPhone() throws Exception { - String phoneWithWhitespace = WHITESPACE + VALID_PHONE + WHITESPACE; - Phone expectedPhone = new Phone(VALID_PHONE); - assertEquals(expectedPhone, ParserUtil.parsePhone(phoneWithWhitespace)); - } - - @Test - public void parseAddress_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseAddress((String) null)); - } - - @Test - public void parseAddress_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseAddress(INVALID_ADDRESS)); - } - - @Test - public void parseAddress_validValueWithoutWhitespace_returnsAddress() throws Exception { - Address expectedAddress = new Address(VALID_ADDRESS); - assertEquals(expectedAddress, ParserUtil.parseAddress(VALID_ADDRESS)); - } - - @Test - public void parseAddress_validValueWithWhitespace_returnsTrimmedAddress() throws Exception { - String addressWithWhitespace = WHITESPACE + VALID_ADDRESS + WHITESPACE; - Address expectedAddress = new Address(VALID_ADDRESS); - assertEquals(expectedAddress, ParserUtil.parseAddress(addressWithWhitespace)); - } - - @Test - public void parseEmail_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseEmail((String) null)); - } - - @Test - public void parseEmail_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseEmail(INVALID_EMAIL)); - } - - @Test - public void parseEmail_validValueWithoutWhitespace_returnsEmail() throws Exception { - Email expectedEmail = new Email(VALID_EMAIL); - assertEquals(expectedEmail, ParserUtil.parseEmail(VALID_EMAIL)); - } - - @Test - public void parseEmail_validValueWithWhitespace_returnsTrimmedEmail() throws Exception { - String emailWithWhitespace = WHITESPACE + VALID_EMAIL + WHITESPACE; - Email expectedEmail = new Email(VALID_EMAIL); - assertEquals(expectedEmail, ParserUtil.parseEmail(emailWithWhitespace)); - } - - @Test - public void parseTag_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseTag(null)); - } - - @Test - public void parseTag_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseTag(INVALID_TAG)); - } - - @Test - public void parseTag_validValueWithoutWhitespace_returnsTag() throws Exception { - Tag expectedTag = new Tag(VALID_TAG_1); - assertEquals(expectedTag, ParserUtil.parseTag(VALID_TAG_1)); - } - - @Test - public void parseTag_validValueWithWhitespace_returnsTrimmedTag() throws Exception { - String tagWithWhitespace = WHITESPACE + VALID_TAG_1 + WHITESPACE; - Tag expectedTag = new Tag(VALID_TAG_1); - assertEquals(expectedTag, ParserUtil.parseTag(tagWithWhitespace)); - } - - @Test - public void parseTags_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseTags(null)); - } - - @Test - public void parseTags_collectionWithInvalidTags_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, INVALID_TAG))); - } - - @Test - public void parseTags_emptyCollection_returnsEmptySet() throws Exception { - assertTrue(ParserUtil.parseTags(Collections.emptyList()).isEmpty()); - } - - @Test - public void parseTags_collectionWithValidTags_returnsTagSet() throws Exception { - Set actualTagSet = ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, VALID_TAG_2)); - Set expectedTagSet = new HashSet(Arrays.asList(new Tag(VALID_TAG_1), new Tag(VALID_TAG_2))); - - assertEquals(expectedTagSet, actualTagSet); - } -} +//package seedu.address.logic.parser; +// +//import static org.junit.jupiter.api.Assertions.assertEquals; +//import static org.junit.jupiter.api.Assertions.assertTrue; +//import static seedu.address.logic.parser.ParserUtil.MESSAGE_INVALID_INDEX; +//import static seedu.address.testutil.Assert.assertThrows; +//import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +// +//import java.util.Arrays; +//import java.util.Collections; +//import java.util.HashSet; +//import java.util.Set; +// +//import org.junit.jupiter.api.Test; +// +//import seedu.address.logic.parser.exceptions.ParseException; +//import seedu.address.model.contact.Email; +//import seedu.address.model.contact.Phone; +//import seedu.address.model.person.Address; +//import seedu.address.model.person.Name; +//import seedu.address.model.tag.Tag; +// +//public class ParserUtilTest { +// private static final String INVALID_NAME = "R@chel"; +// private static final String INVALID_PHONE = "+651234"; +// private static final String INVALID_ADDRESS = " "; +// private static final String INVALID_EMAIL = "example.com"; +// private static final String INVALID_TAG = "#friend"; +// +// private static final String VALID_NAME = "Rachel Walker"; +// private static final String VALID_PHONE = "123456"; +// private static final String VALID_ADDRESS = "123 Main Street #0505"; +// private static final String VALID_EMAIL = "rachel@example.com"; +// private static final String VALID_TAG_1 = "friend"; +// private static final String VALID_TAG_2 = "neighbour"; +// +// private static final String WHITESPACE = " \t\r\n"; +// +// @Test +// public void parseIndex_invalidInput_throwsParseException() { +// assertThrows(ParseException.class, () -> ParserUtil.parseIndex("10 a")); +// } +// +// @Test +// public void parseIndex_outOfRangeInput_throwsParseException() { +// assertThrows(ParseException.class, MESSAGE_INVALID_INDEX, () +// -> ParserUtil.parseIndex(Long.toString(Integer.MAX_VALUE + 1))); +// } +// +// @Test +// public void parseIndex_validInput_success() throws Exception { +// // No whitespaces +// assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex("1")); +// +// // Leading and trailing whitespaces +// assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex(" 1 ")); +// } +// +// @Test +// public void parseName_null_throwsNullPointerException() { +// assertThrows(NullPointerException.class, () -> ParserUtil.parseName((String) null)); +// } +// +// @Test +// public void parseName_invalidValue_throwsParseException() { +// assertThrows(ParseException.class, () -> ParserUtil.parseName(INVALID_NAME)); +// } +// +// @Test +// public void parseName_validValueWithoutWhitespace_returnsName() throws Exception { +// Name expectedName = new Name(VALID_NAME); +// assertEquals(expectedName, ParserUtil.parseName(VALID_NAME)); +// } +// +// @Test +// public void parseName_validValueWithWhitespace_returnsTrimmedName() throws Exception { +// String nameWithWhitespace = WHITESPACE + VALID_NAME + WHITESPACE; +// Name expectedName = new Name(VALID_NAME); +// assertEquals(expectedName, ParserUtil.parseName(nameWithWhitespace)); +// } +// +// @Test +// public void parsePhone_null_throwsNullPointerException() { +// assertThrows(NullPointerException.class, () -> ParserUtil.parsePhone((String) null)); +// } +// +// @Test +// public void parsePhone_invalidValue_throwsParseException() { +// assertThrows(ParseException.class, () -> ParserUtil.parsePhone(INVALID_PHONE)); +// } +// +// @Test +// public void parsePhone_validValueWithoutWhitespace_returnsPhone() throws Exception { +// Phone expectedPhone = new Phone(VALID_PHONE); +// assertEquals(expectedPhone, ParserUtil.parsePhone(VALID_PHONE)); +// } +// +// @Test +// public void parsePhone_validValueWithWhitespace_returnsTrimmedPhone() throws Exception { +// String phoneWithWhitespace = WHITESPACE + VALID_PHONE + WHITESPACE; +// Phone expectedPhone = new Phone(VALID_PHONE); +// assertEquals(expectedPhone, ParserUtil.parsePhone(phoneWithWhitespace)); +// } +// +// @Test +// public void parseAddress_null_throwsNullPointerException() { +// assertThrows(NullPointerException.class, () -> ParserUtil.parseAddress((String) null)); +// } +// +// @Test +// public void parseAddress_invalidValue_throwsParseException() { +// assertThrows(ParseException.class, () -> ParserUtil.parseAddress(INVALID_ADDRESS)); +// } +// +// @Test +// public void parseAddress_validValueWithoutWhitespace_returnsAddress() throws Exception { +// Address expectedAddress = new Address(VALID_ADDRESS); +// assertEquals(expectedAddress, ParserUtil.parseAddress(VALID_ADDRESS)); +// } +// +// @Test +// public void parseAddress_validValueWithWhitespace_returnsTrimmedAddress() throws Exception { +// String addressWithWhitespace = WHITESPACE + VALID_ADDRESS + WHITESPACE; +// Address expectedAddress = new Address(VALID_ADDRESS); +// assertEquals(expectedAddress, ParserUtil.parseAddress(addressWithWhitespace)); +// } +// +// @Test +// public void parseEmail_null_throwsNullPointerException() { +// assertThrows(NullPointerException.class, () -> ParserUtil.parseEmail((String) null)); +// } +// +// @Test +// public void parseEmail_invalidValue_throwsParseException() { +// assertThrows(ParseException.class, () -> ParserUtil.parseEmail(INVALID_EMAIL)); +// } +// +// @Test +// public void parseEmail_validValueWithoutWhitespace_returnsEmail() throws Exception { +// Email expectedEmail = new Email(VALID_EMAIL); +// assertEquals(expectedEmail, ParserUtil.parseEmail(VALID_EMAIL)); +// } +// +// @Test +// public void parseEmail_validValueWithWhitespace_returnsTrimmedEmail() throws Exception { +// String emailWithWhitespace = WHITESPACE + VALID_EMAIL + WHITESPACE; +// Email expectedEmail = new Email(VALID_EMAIL); +// assertEquals(expectedEmail, ParserUtil.parseEmail(emailWithWhitespace)); +// } +// +// @Test +// public void parseTag_null_throwsNullPointerException() { +// assertThrows(NullPointerException.class, () -> ParserUtil.parseTag(null)); +// } +// +// @Test +// public void parseTag_invalidValue_throwsParseException() { +// assertThrows(ParseException.class, () -> ParserUtil.parseTag(INVALID_TAG)); +// } +// +// @Test +// public void parseTag_validValueWithoutWhitespace_returnsTag() throws Exception { +// Tag expectedTag = new Tag(VALID_TAG_1); +// assertEquals(expectedTag, ParserUtil.parseTag(VALID_TAG_1)); +// } +// +// @Test +// public void parseTag_validValueWithWhitespace_returnsTrimmedTag() throws Exception { +// String tagWithWhitespace = WHITESPACE + VALID_TAG_1 + WHITESPACE; +// Tag expectedTag = new Tag(VALID_TAG_1); +// assertEquals(expectedTag, ParserUtil.parseTag(tagWithWhitespace)); +// } +// +// @Test +// public void parseTags_null_throwsNullPointerException() { +// assertThrows(NullPointerException.class, () -> ParserUtil.parseTags(null)); +// } +// +// @Test +// public void parseTags_collectionWithInvalidTags_throwsParseException() { +// assertThrows(ParseException.class, () -> ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, INVALID_TAG))); +// } +// +// @Test +// public void parseTags_emptyCollection_returnsEmptySet() throws Exception { +// assertTrue(ParserUtil.parseTags(Collections.emptyList()).isEmpty()); +// } +// +// @Test +// public void parseTags_collectionWithValidTags_returnsTagSet() throws Exception { +// Set actualTagSet = ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, VALID_TAG_2)); +// Set expectedTagSet = new HashSet(Arrays.asList(new Tag(VALID_TAG_1), new Tag(VALID_TAG_2))); +// +// assertEquals(expectedTagSet, actualTagSet); +// } +//} diff --git a/src/test/java/seedu/address/logic/parser/SortCommandParserTest.java b/src/test/java/seedu/address/logic/parser/SortCommandParserTest.java new file mode 100644 index 00000000000..8d293f6f76b --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/SortCommandParserTest.java @@ -0,0 +1,50 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.SortCommand; +import seedu.address.model.application.comparator.CompanyNameComparator; +import seedu.address.model.application.comparator.InterviewDateComparator; +import seedu.address.model.application.comparator.JobTitleComparator; +import seedu.address.model.application.comparator.StatusComparator; + +public class SortCommandParserTest { + + private SortCommandParser parser = new SortCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_validArgs_returnsSortCommand() { + // no leading and trailing whitespaces + SortCommand expectedSortCommand = + new SortCommand(new CompanyNameComparator()); + assertParseSuccess(parser, " n/", expectedSortCommand); + + // multiple whitespaces between keywords + assertParseSuccess(parser, " n/ \n \t ", expectedSortCommand); + + // with other prefixes + expectedSortCommand = + new SortCommand(new JobTitleComparator()); + assertParseSuccess(parser, " j/", expectedSortCommand); + + expectedSortCommand = + new SortCommand(new StatusComparator()); + assertParseSuccess(parser, " s/", expectedSortCommand); + + expectedSortCommand = + new SortCommand(new InterviewDateComparator()); + assertParseSuccess(parser, " d/", expectedSortCommand); + + } + +} diff --git a/src/test/java/seedu/address/logic/parser/UnarchiveCommandParserTest.java b/src/test/java/seedu/address/logic/parser/UnarchiveCommandParserTest.java new file mode 100644 index 00000000000..1e7d8cbd7e2 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/UnarchiveCommandParserTest.java @@ -0,0 +1,33 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_APPLICATION; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.UnarchiveCommand; + +/** + * As we are only doing white-box testing, our test cases do not cover path variations + * outside of the UnarchiveCommand code. For example, inputs "1" and "1 abc" take the + * same path through the UnarchiveCommand, and therefore we test only one of them. + * The path variation for those two cases occur inside the ParserUtil, and + * therefore should be covered by the ParserUtilTest. + */ +public class UnarchiveCommandParserTest { + + private UnarchiveCommandParser parser = new UnarchiveCommandParser(); + + @Test + public void parse_validArgs_returnsUnarchiveCommand() { + assertParseSuccess(parser, "1", new UnarchiveCommand(INDEX_FIRST_APPLICATION)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, UnarchiveCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/contact/AddContactCommandParserTest.java b/src/test/java/seedu/address/logic/parser/contact/AddContactCommandParserTest.java new file mode 100644 index 00000000000..49919a968d6 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/contact/AddContactCommandParserTest.java @@ -0,0 +1,82 @@ +package seedu.address.logic.parser.contact; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BANK_OF_AMERICA; +import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_META; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; +import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BANK_OF_AMERICA; +import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_META; +import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.address.testutil.TypicalContacts.BANK_OF_AMERICA_CONTACT; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_APPLICATION; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.contact.AddContactCommand; +import seedu.address.model.contact.Contact; +import seedu.address.model.contact.Phone; +import seedu.address.testutil.ContactBuilder; + +public class AddContactCommandParserTest { + private static final String MESSAGE_INVALID_FORMAT = + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddContactCommand.MESSAGE_USAGE); + private AddContactCommandParser parser = new AddContactCommandParser(); + + @Test + public void parse_missingParts_failure() { + // no index specified + assertParseFailure(parser, PHONE_DESC_BANK_OF_AMERICA, MESSAGE_INVALID_FORMAT); + + // no field specified + assertParseFailure(parser, "1", MESSAGE_INVALID_FORMAT); + + // no index and no field specified + assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidPreamble_failure() { + // negative index + assertParseFailure(parser, "-5" + PHONE_DESC_BANK_OF_AMERICA + EMAIL_DESC_BANK_OF_AMERICA, + MESSAGE_INVALID_FORMAT); + + // zero index + assertParseFailure(parser, "0" + PHONE_DESC_BANK_OF_AMERICA + EMAIL_DESC_BANK_OF_AMERICA, + MESSAGE_INVALID_FORMAT); + + // invalid arguments being parsed as preamble + assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT); + + // invalid prefix being parsed as preamble + assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidValue_failure() { + // multiple invalid values, but only the first invalid value is captured + assertParseFailure(parser, "1" + INVALID_PHONE_DESC + INVALID_EMAIL_DESC, + Phone.MESSAGE_CONSTRAINTS); + } + + @Test + public void parse_allFieldsSpecified_success() { + Index targetIndex = INDEX_SECOND_APPLICATION; + Contact expectedContact = new ContactBuilder(BANK_OF_AMERICA_CONTACT).build(); + + // whitespace only preamble + assertParseSuccess(parser, PREAMBLE_WHITESPACE + targetIndex.getOneBased() + PHONE_DESC_BANK_OF_AMERICA + + EMAIL_DESC_BANK_OF_AMERICA, new AddContactCommand(targetIndex, expectedContact)); + + // multiple phones - last phone accepted + assertParseSuccess(parser, targetIndex.getOneBased() + PHONE_DESC_META + PHONE_DESC_BANK_OF_AMERICA + + EMAIL_DESC_BANK_OF_AMERICA, new AddContactCommand(targetIndex, expectedContact)); + + // multiple emails - last email accepted + assertParseSuccess(parser, targetIndex.getOneBased() + PHONE_DESC_BANK_OF_AMERICA + EMAIL_DESC_META + + EMAIL_DESC_BANK_OF_AMERICA, new AddContactCommand(targetIndex, expectedContact)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/contact/ContactParserTest.java b/src/test/java/seedu/address/logic/parser/contact/ContactParserTest.java new file mode 100644 index 00000000000..81b5217d7bd --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/contact/ContactParserTest.java @@ -0,0 +1,64 @@ +package seedu.address.logic.parser.contact; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_APPLICATION; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.contact.AddContactCommand; +import seedu.address.logic.commands.contact.DeleteContactCommand; +import seedu.address.logic.commands.contact.EditContactCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.contact.Contact; +import seedu.address.testutil.ContactBuilder; +import seedu.address.testutil.ContactUtil; +import seedu.address.testutil.EditContactDescriptorBuilder; + +public class ContactParserTest { + + private final ContactParser parser = new ContactParser(); + + @Test + public void parseCommand_add_contact() throws Exception { + Index targetIndex = INDEX_FIRST_APPLICATION; + Contact contact = new ContactBuilder().build(); + AddContactCommand command = (AddContactCommand) parser.parseContactCommand( + AddContactCommand.COMMAND_WORD, + " " + targetIndex.getOneBased() + " " + + ContactUtil.getAddContactCommandsArguments(contact)); + assertEquals(new AddContactCommand(targetIndex, contact), command); + } + + @Test + public void parseCommand_delete_contact() throws Exception { + DeleteContactCommand command = (DeleteContactCommand) parser.parseContactCommand( + DeleteContactCommand.COMMAND_WORD, " " + INDEX_FIRST_APPLICATION.getOneBased()); + assertEquals(new DeleteContactCommand(INDEX_FIRST_APPLICATION), command); + } + + @Test + public void parseCommand_edit_contact() throws Exception { + Contact contact = new ContactBuilder().build(); + EditContactCommand.EditContactDescriptor descriptor = new EditContactDescriptorBuilder(contact).build(); + EditContactCommand command = (EditContactCommand) parser.parseContactCommand( + EditContactCommand.COMMAND_WORD, + " " + INDEX_FIRST_APPLICATION.getOneBased() + " " + + ContactUtil.getEditContactDescriptorDetails(descriptor)); + assertEquals(new EditContactCommand(INDEX_FIRST_APPLICATION, descriptor), command); + } + + @Test + public void parseCommand_unrecognisedInput_throwsParseException() { + assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, () -> + parser.parseContactCommand("", "")); + } + + @Test + public void parseCommand_unknownCommand_throwsParseException() { + assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, () -> + parser.parseContactCommand("unknownCommand", "unknownArguments")); + } +} diff --git a/src/test/java/seedu/address/logic/parser/contact/DeleteContactCommandParserTest.java b/src/test/java/seedu/address/logic/parser/contact/DeleteContactCommandParserTest.java new file mode 100644 index 00000000000..7f65636dd90 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/contact/DeleteContactCommandParserTest.java @@ -0,0 +1,33 @@ +package seedu.address.logic.parser.contact; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_APPLICATION; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.contact.DeleteContactCommand; + +/** + * As we are only doing white-box testing, our test cases do not cover path variations + * outside of the DeleteContactCommand code. For example, inputs "1" and "1 abc" take the + * same path through the DeleteContactCommand, and therefore we test only one of them. + * The path variation for those two cases occur inside the ParserUtil, and + * therefore should be covered by the ParserUtilTest. + */ +public class DeleteContactCommandParserTest { + + private DeleteContactCommandParser parser = new DeleteContactCommandParser(); + + @Test + public void parse_validArgs_returnsDeleteContactCommand() { + assertParseSuccess(parser, "1", new DeleteContactCommand(INDEX_FIRST_APPLICATION)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteContactCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/contact/EditContactCommandParserTest.java b/src/test/java/seedu/address/logic/parser/contact/EditContactCommandParserTest.java new file mode 100644 index 00000000000..c872b2a455b --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/contact/EditContactCommandParserTest.java @@ -0,0 +1,142 @@ +package seedu.address.logic.parser.contact; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BANK_OF_AMERICA; +import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_META; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; +import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BANK_OF_AMERICA; +import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_META; +import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BANK_OF_AMERICA; +import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_META; +import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BANK_OF_AMERICA; +import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_META; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_THIRD_APPLICATION; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.contact.EditContactCommand; +import seedu.address.model.contact.Phone; +import seedu.address.testutil.EditContactDescriptorBuilder; + +public class EditContactCommandParserTest { + + private static final String MESSAGE_INVALID_FORMAT = + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditContactCommand.MESSAGE_USAGE); + + private EditContactCommandParser parser = new EditContactCommandParser(); + + @Test + public void parse_missingParts_failure() { + // no index specified + assertParseFailure(parser, PHONE_DESC_BANK_OF_AMERICA, MESSAGE_INVALID_FORMAT); + + // no field specified + assertParseFailure(parser, "1", EditContactCommand.MESSAGE_NOT_EDITED); + + // no index and no field specified + assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidPreamble_failure() { + // negative index + assertParseFailure(parser, "-5" + PHONE_DESC_BANK_OF_AMERICA + EMAIL_DESC_BANK_OF_AMERICA, + MESSAGE_INVALID_FORMAT); + + // zero index + assertParseFailure(parser, "0" + PHONE_DESC_BANK_OF_AMERICA + EMAIL_DESC_BANK_OF_AMERICA, + MESSAGE_INVALID_FORMAT); + + // invalid arguments being parsed as preamble + assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT); + + // invalid prefix being parsed as preamble + assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidValue_failure() { + // multiple invalid values, but only the first invalid value is captured + assertParseFailure(parser, "1" + INVALID_PHONE_DESC + INVALID_EMAIL_DESC, + Phone.MESSAGE_CONSTRAINTS); + } + + @Test + public void parse_allFieldsSpecified_success() { + Index targetIndex = INDEX_SECOND_APPLICATION; + String userInput = targetIndex.getOneBased() + PHONE_DESC_BANK_OF_AMERICA + EMAIL_DESC_BANK_OF_AMERICA; + + EditContactCommand.EditContactDescriptor descriptor = new EditContactDescriptorBuilder() + .withPhone(VALID_PHONE_BANK_OF_AMERICA).withEmail(VALID_EMAIL_BANK_OF_AMERICA).build(); + EditContactCommand expectedCommand = new EditContactCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_someFieldsSpecified_success() { + Index targetIndex = INDEX_FIRST_APPLICATION; + String userInput = targetIndex.getOneBased() + PHONE_DESC_BANK_OF_AMERICA; + + EditContactCommand.EditContactDescriptor descriptor = new EditContactDescriptorBuilder() + .withPhone(VALID_PHONE_BANK_OF_AMERICA).build(); + EditContactCommand expectedCommand = new EditContactCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_oneFieldSpecified_success() { + // phone + Index targetIndex = INDEX_THIRD_APPLICATION; + String userInput = targetIndex.getOneBased() + PHONE_DESC_BANK_OF_AMERICA; + EditContactCommand.EditContactDescriptor descriptor = new EditContactDescriptorBuilder() + .withPhone(VALID_PHONE_BANK_OF_AMERICA).build(); + EditContactCommand expectedCommand = new EditContactCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // email + userInput = targetIndex.getOneBased() + EMAIL_DESC_BANK_OF_AMERICA; + descriptor = new EditContactDescriptorBuilder().withEmail(VALID_EMAIL_BANK_OF_AMERICA).build(); + expectedCommand = new EditContactCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_multipleRepeatedFields_acceptsLast() { + Index targetIndex = INDEX_FIRST_APPLICATION; + String userInput = targetIndex.getOneBased() + PHONE_DESC_BANK_OF_AMERICA + EMAIL_DESC_BANK_OF_AMERICA + + PHONE_DESC_META + EMAIL_DESC_META; + + EditContactCommand.EditContactDescriptor descriptor = new EditContactDescriptorBuilder() + .withPhone(VALID_PHONE_META).withEmail(VALID_EMAIL_META).build(); + EditContactCommand expectedCommand = new EditContactCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_invalidValueFollowedByValidValue_success() { + // no other valid values specified + Index targetIndex = INDEX_FIRST_APPLICATION; + String userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + PHONE_DESC_BANK_OF_AMERICA; + EditContactCommand.EditContactDescriptor descriptor = new EditContactDescriptorBuilder() + .withPhone(VALID_PHONE_BANK_OF_AMERICA).build(); + EditContactCommand expectedCommand = new EditContactCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // other valid values specified + userInput = targetIndex.getOneBased() + EMAIL_DESC_BANK_OF_AMERICA + INVALID_PHONE_DESC + + PHONE_DESC_BANK_OF_AMERICA; + descriptor = new EditContactDescriptorBuilder().withPhone(VALID_PHONE_BANK_OF_AMERICA) + .withEmail(VALID_EMAIL_BANK_OF_AMERICA).build(); + expectedCommand = new EditContactCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + } +} diff --git a/src/test/java/seedu/address/logic/parser/documents/AddDocumentsCommandParserTest.java b/src/test/java/seedu/address/logic/parser/documents/AddDocumentsCommandParserTest.java new file mode 100644 index 00000000000..38ebff0ad7f --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/documents/AddDocumentsCommandParserTest.java @@ -0,0 +1,82 @@ +package seedu.address.logic.parser.documents; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.commands.CommandTestUtil.COVER_LETTER_DESC_GOOGLE; +import static seedu.address.logic.commands.CommandTestUtil.COVER_LETTER_DESC_NETFLIX; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_COVER_LETTER_DESC; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_RESUME_DESC; +import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; +import static seedu.address.logic.commands.CommandTestUtil.RESUME_DESC_GOOGLE; +import static seedu.address.logic.commands.CommandTestUtil.RESUME_DESC_NETFLIX; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.address.testutil.TypicalDocuments.DOCUMENTS_GOOGLE; +import static seedu.address.testutil.TypicalIndexes.INDEX_FOURTH_APPLICATION; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.documents.AddDocumentsCommand; +import seedu.address.model.documents.Documents; +import seedu.address.model.documents.ResumeLink; +import seedu.address.testutil.DocumentsBuilder; + +public class AddDocumentsCommandParserTest { + private static final String MESSAGE_INVALID_FORMAT = + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddDocumentsCommand.MESSAGE_USAGE); + private AddDocumentsCommandParser parser = new AddDocumentsCommandParser(); + + @Test + public void parse_missingParts_failure() { + // no index specified + assertParseFailure(parser, RESUME_DESC_GOOGLE, MESSAGE_INVALID_FORMAT); + + // no field specified + assertParseFailure(parser, "1", MESSAGE_INVALID_FORMAT); + + // no index and no field specified + assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidPreamble_failure() { + // negative index + assertParseFailure(parser, "-5" + RESUME_DESC_GOOGLE + COVER_LETTER_DESC_GOOGLE, + MESSAGE_INVALID_FORMAT); + + // zero index + assertParseFailure(parser, "0" + RESUME_DESC_GOOGLE + COVER_LETTER_DESC_GOOGLE, + MESSAGE_INVALID_FORMAT); + + // invalid arguments being parsed as preamble + assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT); + + // invalid prefix being parsed as preamble + assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidValue_failure() { + // multiple invalid values, but only the first invalid value is captured + assertParseFailure(parser, "1" + INVALID_RESUME_DESC + INVALID_COVER_LETTER_DESC, + ResumeLink.MESSAGE_CONSTRAINTS); + } + + @Test + public void parse_allFieldsSpecified_success() { + Index targetIndex = INDEX_FOURTH_APPLICATION; + Documents expectedDocuments = new DocumentsBuilder(DOCUMENTS_GOOGLE).build(); + + // whitespace only preamble + assertParseSuccess(parser, PREAMBLE_WHITESPACE + targetIndex.getOneBased() + RESUME_DESC_GOOGLE + + COVER_LETTER_DESC_GOOGLE, new AddDocumentsCommand(targetIndex, expectedDocuments)); + + // multiple resume links - last resume link accepted + assertParseSuccess(parser, targetIndex.getOneBased() + RESUME_DESC_NETFLIX + RESUME_DESC_GOOGLE + + COVER_LETTER_DESC_GOOGLE, new AddDocumentsCommand(targetIndex, expectedDocuments)); + + // multiple cover letter links - last cover letter link accepted + assertParseSuccess(parser, targetIndex.getOneBased() + RESUME_DESC_GOOGLE + COVER_LETTER_DESC_NETFLIX + + COVER_LETTER_DESC_GOOGLE, new AddDocumentsCommand(targetIndex, expectedDocuments)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/documents/DeleteDocumentsCommandParserTest.java b/src/test/java/seedu/address/logic/parser/documents/DeleteDocumentsCommandParserTest.java new file mode 100644 index 00000000000..bb502fa6043 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/documents/DeleteDocumentsCommandParserTest.java @@ -0,0 +1,33 @@ +package seedu.address.logic.parser.documents; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_APPLICATION; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.documents.DeleteDocumentsCommand; + +/** + * As we are only doing white-box testing, our test cases do not cover path variations + * outside of the DeleteDocumentsCommand code. For example, inputs "1" and "1 abc" take the + * same path through the DeleteDocumentsCommand, and therefore we test only one of them. + * The path variation for those two cases occur inside the ParserUtil, and + * therefore should be covered by the ParserUtilTest. + */ +public class DeleteDocumentsCommandParserTest { + + private DeleteDocumentsCommandParser parser = new DeleteDocumentsCommandParser(); + + @Test + public void parse_validArgs_returnsDeleteDocumentCommand() { + assertParseSuccess(parser, "1", new DeleteDocumentsCommand(INDEX_FIRST_APPLICATION)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteDocumentsCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/documents/DocumentsParserTest.java b/src/test/java/seedu/address/logic/parser/documents/DocumentsParserTest.java new file mode 100644 index 00000000000..d305948209b --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/documents/DocumentsParserTest.java @@ -0,0 +1,66 @@ +package seedu.address.logic.parser.documents; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIFTH_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_APPLICATION; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.documents.AddDocumentsCommand; +import seedu.address.logic.commands.documents.DeleteDocumentsCommand; +import seedu.address.logic.commands.documents.EditDocumentsCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.documents.Documents; +import seedu.address.testutil.DocumentsBuilder; +import seedu.address.testutil.DocumentsUtil; +import seedu.address.testutil.EditDocumentsDescriptorBuilder; + +public class DocumentsParserTest { + + private final DocumentsParser parser = new DocumentsParser(); + + @Test + public void parseCommand_add_docs() throws Exception { + Index targetIndex = INDEX_FIRST_APPLICATION; + Documents documents = new DocumentsBuilder().build(); + AddDocumentsCommand command = (AddDocumentsCommand) parser.parseDocumentsCommand( + AddDocumentsCommand.COMMAND_WORD, + " " + targetIndex.getOneBased() + " " + + DocumentsUtil.getAddDocumentsCommandArguments(documents)); + assertEquals(new AddDocumentsCommand(targetIndex, documents), command); + } + + @Test + public void parseCommand_delete_docs() throws Exception { + DeleteDocumentsCommand command = (DeleteDocumentsCommand) parser.parseDocumentsCommand( + DeleteDocumentsCommand.COMMAND_WORD, " " + INDEX_FIFTH_APPLICATION.getOneBased()); + assertEquals(new DeleteDocumentsCommand(INDEX_FIFTH_APPLICATION), command); + } + + @Test + public void parseCommand_edit_docs() throws Exception { + Index targetIndex = INDEX_FIRST_APPLICATION; + Documents documents = new DocumentsBuilder().build(); + EditDocumentsCommand.EditDocumentsDescriptor descriptor = new EditDocumentsDescriptorBuilder(documents).build(); + EditDocumentsCommand command = (EditDocumentsCommand) parser.parseDocumentsCommand( + EditDocumentsCommand.COMMAND_WORD, + " " + INDEX_FIFTH_APPLICATION.getOneBased() + " " + + DocumentsUtil.getEditDocumentsDescriptorDetails(descriptor)); + assertEquals(new EditDocumentsCommand(INDEX_FIFTH_APPLICATION, descriptor), command); + } + + @Test + public void parseCommand_unrecognisedInput_throwsParseException() { + assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, () -> + parser.parseDocumentsCommand("", "")); + } + + @Test + public void parseCommand_unknownCommand_throwsParseException() { + assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, () -> + parser.parseDocumentsCommand("unknownCommand", "unknownArguments")); + } +} diff --git a/src/test/java/seedu/address/logic/parser/documents/EditDocumentsCommandParserTest.java b/src/test/java/seedu/address/logic/parser/documents/EditDocumentsCommandParserTest.java new file mode 100644 index 00000000000..49d0fd07b55 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/documents/EditDocumentsCommandParserTest.java @@ -0,0 +1,142 @@ +package seedu.address.logic.parser.documents; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.commands.CommandTestUtil.COVER_LETTER_DESC_GOOGLE; +import static seedu.address.logic.commands.CommandTestUtil.COVER_LETTER_DESC_NETFLIX; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_COVER_LETTER_DESC; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_RESUME_DESC; +import static seedu.address.logic.commands.CommandTestUtil.RESUME_DESC_GOOGLE; +import static seedu.address.logic.commands.CommandTestUtil.RESUME_DESC_NETFLIX; +import static seedu.address.logic.commands.CommandTestUtil.VALID_COVER_LETTER_LINK_GOOGLE; +import static seedu.address.logic.commands.CommandTestUtil.VALID_COVER_LETTER_LINK_NETFLIX; +import static seedu.address.logic.commands.CommandTestUtil.VALID_RESUME_LINK_GOOGLE; +import static seedu.address.logic.commands.CommandTestUtil.VALID_RESUME_LINK_NETFLIX; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIFTH_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_FOURTH_APPLICATION; +import static seedu.address.testutil.TypicalIndexes.INDEX_THIRD_APPLICATION; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.documents.EditDocumentsCommand; +import seedu.address.model.documents.ResumeLink; +import seedu.address.testutil.EditDocumentsDescriptorBuilder; + +public class EditDocumentsCommandParserTest { + + private static final String MESSAGE_INVALID_FORMAT = + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditDocumentsCommand.MESSAGE_USAGE); + + private EditDocumentsCommandParser parser = new EditDocumentsCommandParser(); + + @Test + public void parse_missingParts_failure() { + // no index specified + assertParseFailure(parser, RESUME_DESC_GOOGLE, MESSAGE_INVALID_FORMAT); + + // no field specified + assertParseFailure(parser, "1", EditDocumentsCommand.MESSAGE_NOT_EDITED); + + // no index and no field specified + assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidPreamble_failure() { + // negative index + assertParseFailure(parser, "-5" + RESUME_DESC_GOOGLE + COVER_LETTER_DESC_GOOGLE, + MESSAGE_INVALID_FORMAT); + + // zero index + assertParseFailure(parser, "0" + RESUME_DESC_GOOGLE + COVER_LETTER_DESC_GOOGLE, + MESSAGE_INVALID_FORMAT); + + // invalid arguments being parsed as preamble + assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT); + + // invalid prefix being parsed as preamble + assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidValue_failure() { + // multiple invalid values, but only the first invalid value is captured + assertParseFailure(parser, "1" + INVALID_RESUME_DESC + INVALID_COVER_LETTER_DESC, + ResumeLink.MESSAGE_CONSTRAINTS); + } + + @Test + public void parse_allFieldsSpecified_success() { + Index targetIndex = INDEX_FOURTH_APPLICATION; + String userInput = targetIndex.getOneBased() + RESUME_DESC_GOOGLE + COVER_LETTER_DESC_GOOGLE; + + EditDocumentsCommand.EditDocumentsDescriptor descriptor = new EditDocumentsDescriptorBuilder() + .withResumeLink(VALID_RESUME_LINK_GOOGLE).withCoverLetterLink(VALID_COVER_LETTER_LINK_GOOGLE).build(); + EditDocumentsCommand expectedCommand = new EditDocumentsCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_someFieldsSpecified_success() { + Index targetIndex = INDEX_FIFTH_APPLICATION; + String userInput = targetIndex.getOneBased() + RESUME_DESC_GOOGLE; + + EditDocumentsCommand.EditDocumentsDescriptor descriptor = new EditDocumentsDescriptorBuilder() + .withResumeLink(VALID_RESUME_LINK_GOOGLE).build(); + EditDocumentsCommand expectedCommand = new EditDocumentsCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_oneFieldSpecified_success() { + // resume link + Index targetIndex = INDEX_THIRD_APPLICATION; + String userInput = targetIndex.getOneBased() + RESUME_DESC_GOOGLE; + EditDocumentsCommand.EditDocumentsDescriptor descriptor = new EditDocumentsDescriptorBuilder() + .withResumeLink(VALID_RESUME_LINK_GOOGLE).build(); + EditDocumentsCommand expectedCommand = new EditDocumentsCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // cover letter link + userInput = targetIndex.getOneBased() + COVER_LETTER_DESC_GOOGLE; + descriptor = new EditDocumentsDescriptorBuilder().withCoverLetterLink(VALID_COVER_LETTER_LINK_GOOGLE).build(); + expectedCommand = new EditDocumentsCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_multipleRepeatedFields_acceptsLast() { + Index targetIndex = INDEX_FIFTH_APPLICATION; + String userInput = targetIndex.getOneBased() + RESUME_DESC_GOOGLE + COVER_LETTER_DESC_GOOGLE + + RESUME_DESC_NETFLIX + COVER_LETTER_DESC_NETFLIX; + + EditDocumentsCommand.EditDocumentsDescriptor descriptor = new EditDocumentsDescriptorBuilder() + .withResumeLink(VALID_RESUME_LINK_NETFLIX).withCoverLetterLink(VALID_COVER_LETTER_LINK_NETFLIX).build(); + EditDocumentsCommand expectedCommand = new EditDocumentsCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_invalidValueFollowedByValidValue_success() { + // no other valid values specified + Index targetIndex = INDEX_FIFTH_APPLICATION; + String userInput = targetIndex.getOneBased() + INVALID_RESUME_DESC + RESUME_DESC_GOOGLE; + EditDocumentsCommand.EditDocumentsDescriptor descriptor = new EditDocumentsDescriptorBuilder() + .withResumeLink(VALID_RESUME_LINK_GOOGLE).build(); + EditDocumentsCommand expectedCommand = new EditDocumentsCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // other valid values specified + userInput = targetIndex.getOneBased() + COVER_LETTER_DESC_GOOGLE + INVALID_RESUME_DESC + + RESUME_DESC_GOOGLE; + descriptor = new EditDocumentsDescriptorBuilder().withResumeLink(VALID_RESUME_LINK_GOOGLE) + .withCoverLetterLink(VALID_COVER_LETTER_LINK_GOOGLE).build(); + expectedCommand = new EditDocumentsCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + } +} diff --git a/src/test/java/seedu/address/logic/parser/task/FindTaskCommandParserTest.java b/src/test/java/seedu/address/logic/parser/task/FindTaskCommandParserTest.java new file mode 100644 index 00000000000..83b617e85e9 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/task/FindTaskCommandParserTest.java @@ -0,0 +1,36 @@ +package seedu.address.logic.parser.task; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.task.FindTaskCommand; +import seedu.address.model.task.ContentContainsKeywordsPredicate; +import seedu.address.model.task.TitleContainsKeywordsPredicate; + +/** + * Tests {@code FindTaskCommandParser} on its behaviours on taking valid and invalid arguments. + */ +public class FindTaskCommandParserTest { + + private FindTaskCommandParser parser = new FindTaskCommandParser(); + + @Test + public void parse_validArgs_returnsFindTaskCommand() { + String[] trimmed = ("key").split("\\s+"); + TitleContainsKeywordsPredicate tPred = new TitleContainsKeywordsPredicate(Arrays.asList(trimmed)); + ContentContainsKeywordsPredicate cPred = new ContentContainsKeywordsPredicate(Arrays.asList(trimmed)); + + assertParseSuccess(parser, "key", new FindTaskCommand(tPred, cPred)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindTaskCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/task/TaskParserTest.java b/src/test/java/seedu/address/logic/parser/task/TaskParserTest.java new file mode 100644 index 00000000000..a7b9a719eae --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/task/TaskParserTest.java @@ -0,0 +1,148 @@ +package seedu.address.logic.parser.task; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE_CONTENT; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_APPLICATION; + +import java.time.LocalDate; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.task.FindTaskCommand; +import seedu.address.logic.commands.task.ListTaskCommand; +import seedu.address.logic.commands.task.note.AddNoteCommand; +import seedu.address.logic.commands.task.note.ClearNoteCommand; +import seedu.address.logic.commands.task.note.DeleteNoteCommand; +import seedu.address.logic.commands.task.note.ListNoteCommand; +import seedu.address.logic.commands.task.todo.AddTodoCommand; +import seedu.address.logic.commands.task.todo.ClearTodoCommand; +import seedu.address.logic.commands.task.todo.DeleteTodoCommand; +import seedu.address.logic.commands.task.todo.EditDeadlineCommand; +import seedu.address.logic.commands.task.todo.EditNoteContentCommand; +import seedu.address.logic.commands.task.todo.ListTodoCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.task.ApplicationDeadline; +import seedu.address.model.task.ContentContainsKeywordsPredicate; +import seedu.address.model.task.InternshipTodo; +import seedu.address.model.task.Note; +import seedu.address.model.task.NoteContent; +import seedu.address.model.task.TitleContainsKeywordsPredicate; +import seedu.address.testutil.InternshipTodoBuilder; +import seedu.address.testutil.NoteBuilder; +import seedu.address.testutil.NoteUtil; +import seedu.address.testutil.TodoUtil; + +/** + * A test class designed to examine the sub-parser {@code TaskParser}. + */ +public class TaskParserTest { + + private final TaskParser parser = new TaskParser(); + + @Test + public void parseTaskCommand_addTodo() throws Exception { + InternshipTodo todo = new InternshipTodoBuilder().basicBuild(); + AddTodoCommand command = (AddTodoCommand) parser.parseTaskCommand(AddTodoCommand.COMMAND_WORD, + " " + TodoUtil.getTodoDetails(todo)); + assertEquals(new AddTodoCommand(todo), command); + } + + @Test + public void parseTaskCommand_addNote() throws Exception { + Note note = new NoteBuilder().basicBuild(); + AddNoteCommand command = (AddNoteCommand) parser.parseTaskCommand(AddNoteCommand.COMMAND_WORD, + " " + NoteUtil.getNoteDetails(note)); + assertEquals(new AddNoteCommand(note), command); + } + + @Test + public void parseTaskCommand_clearTodo() throws Exception { + assertTrue(parser.parseTaskCommand(ClearTodoCommand.COMMAND_WORD, "") instanceof ClearTodoCommand); + assertTrue(parser.parseTaskCommand(ClearTodoCommand.COMMAND_WORD, " 3") instanceof ClearTodoCommand); + } + + @Test + public void parseTaskCommand_clearNote() throws Exception { + assertTrue(parser.parseTaskCommand(ClearNoteCommand.COMMAND_WORD, "") instanceof ClearNoteCommand); + assertTrue(parser.parseTaskCommand(ClearNoteCommand.COMMAND_WORD, " 3") instanceof ClearNoteCommand); + } + + @Test + public void parseTaskCommand_deleteTodo() throws Exception { + DeleteTodoCommand command = (DeleteTodoCommand) parser.parseTaskCommand( + DeleteTodoCommand.COMMAND_WORD, " " + INDEX_FIRST_APPLICATION.getOneBased()); + assertEquals(new DeleteTodoCommand(INDEX_FIRST_APPLICATION), command); + } + + @Test + public void parseTaskCommand_deleteNote() throws Exception { + DeleteNoteCommand command = (DeleteNoteCommand) parser.parseTaskCommand( + DeleteNoteCommand.COMMAND_WORD, " " + INDEX_FIRST_APPLICATION.getOneBased()); + assertEquals(new DeleteNoteCommand(INDEX_FIRST_APPLICATION), command); + } + + @Test + public void parseTaskCommand_editDeadline() throws Exception { + InternshipTodo todo = new InternshipTodoBuilder().basicBuild(); + ApplicationDeadline deadline = new ApplicationDeadline(LocalDate.parse("2023-07-09")); + todo.setDeadline(deadline); + EditDeadlineCommand command = (EditDeadlineCommand) parser.parseTaskCommand(EditDeadlineCommand.COMMAND_WORD, + " " + INDEX_FIRST_APPLICATION.getOneBased() + " " + + PREFIX_DEADLINE + " 2023-07-09"); + assertEquals(new EditDeadlineCommand(INDEX_FIRST_APPLICATION, deadline), command); + } + + @Test + public void parseTaskCommand_editNoteContent() throws Exception { + InternshipTodo todo = new InternshipTodoBuilder().basicBuild(); + NoteContent note = new NoteContent("Test case"); + todo.setNote(note); + EditNoteContentCommand command = (EditNoteContentCommand) parser.parseTaskCommand( + EditNoteContentCommand.COMMAND_WORD, " " + INDEX_FIRST_APPLICATION.getOneBased() + " " + + PREFIX_NOTE_CONTENT + " Test case"); + assertEquals(new EditNoteContentCommand(INDEX_FIRST_APPLICATION, note), command); + } + + @Test + public void parseTaskCommand_findTask() throws Exception { + List keywords = Arrays.asList("foo", "bar", "baz"); + FindTaskCommand command = (FindTaskCommand) parser.parseTaskCommand( + FindTaskCommand.COMMAND_WORD , " " + keywords.stream().collect(Collectors.joining(" "))); + assertEquals(new FindTaskCommand(new TitleContainsKeywordsPredicate(keywords), + new ContentContainsKeywordsPredicate(keywords)), command); + } + + @Test + public void parseTaskCommand_listTask() throws Exception { + assertTrue(parser.parseTaskCommand(ListTaskCommand.COMMAND_WORD, "") instanceof ListTaskCommand); + assertTrue(parser.parseTaskCommand(ListTaskCommand.COMMAND_WORD, "2") instanceof ListTaskCommand); + } + + @Test + public void parseTaskCommand_listTodo() throws Exception { + assertTrue(parser.parseTaskCommand(ListTodoCommand.COMMAND_WORD, "") instanceof ListTodoCommand); + assertTrue(parser.parseTaskCommand(ListTodoCommand.COMMAND_WORD, "3") instanceof ListTodoCommand); + } + + @Test + public void parseTaskCommand_listNote() throws Exception { + assertTrue(parser.parseTaskCommand(ListNoteCommand.COMMAND_WORD, "") instanceof ListNoteCommand); + assertTrue(parser.parseTaskCommand(ListNoteCommand.COMMAND_WORD , " 3") instanceof ListNoteCommand); + } + + @Test + public void parseTaskCommand_unknownCommand_throwsParseException() { + assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, () -> + parser.parseTaskCommand("unknownCommand", "")); + assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, () -> + parser.parseTaskCommand("unknownCommand", " can have params")); + } +} + diff --git a/src/test/java/seedu/address/logic/parser/task/note/AddNoteCommandTest.java b/src/test/java/seedu/address/logic/parser/task/note/AddNoteCommandTest.java new file mode 100644 index 00000000000..9edb7143451 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/task/note/AddNoteCommandTest.java @@ -0,0 +1,22 @@ +package seedu.address.logic.parser.task.note; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.task.note.AddNoteCommand; + +/** + * Test class for {@code AddNoteCommandParser}. + */ +public class AddNoteCommandTest { + + private AddNoteCommandParser parser = new AddNoteCommandParser(); + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddNoteCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/task/note/DeleteNoteCommandTest.java b/src/test/java/seedu/address/logic/parser/task/note/DeleteNoteCommandTest.java new file mode 100644 index 00000000000..4e3a20b984e --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/task/note/DeleteNoteCommandTest.java @@ -0,0 +1,22 @@ +package seedu.address.logic.parser.task.note; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.task.note.DeleteNoteCommand; + +/** + * Test class for {@code DeleteNoteCommandParser}. + */ +public class DeleteNoteCommandTest { + + private DeleteNoteCommandParser parser = new DeleteNoteCommandParser(); + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteNoteCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/task/todo/AddTodoCommandParserTest.java b/src/test/java/seedu/address/logic/parser/task/todo/AddTodoCommandParserTest.java new file mode 100644 index 00000000000..a55fa5585d5 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/task/todo/AddTodoCommandParserTest.java @@ -0,0 +1,22 @@ +package seedu.address.logic.parser.task.todo; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.task.todo.AddTodoCommand; + +/** + * Test class for {@code AddTodoCommandParser}. + */ +public class AddTodoCommandParserTest { + + private AddTodoCommandParser parser = new AddTodoCommandParser(); + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTodoCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/task/todo/DeleteTodoCommandParserTest.java b/src/test/java/seedu/address/logic/parser/task/todo/DeleteTodoCommandParserTest.java new file mode 100644 index 00000000000..d0cdf40733f --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/task/todo/DeleteTodoCommandParserTest.java @@ -0,0 +1,22 @@ +package seedu.address.logic.parser.task.todo; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.task.todo.DeleteTodoCommand; + +/** + * Test class for {@code DeleteTodoCommandParser}. + */ +public class DeleteTodoCommandParserTest { + + private DeleteTodoCommandParser parser = new DeleteTodoCommandParser(); + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTodoCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/task/todo/EditContentCommandParserTest.java b/src/test/java/seedu/address/logic/parser/task/todo/EditContentCommandParserTest.java new file mode 100644 index 00000000000..74b1e0bf8b7 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/task/todo/EditContentCommandParserTest.java @@ -0,0 +1,22 @@ +package seedu.address.logic.parser.task.todo; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.task.todo.EditNoteContentCommand; + +/** + * Test class for {@code EditContentCommandParser}. + */ +public class EditContentCommandParserTest { + + private EditContentCommandParser parser = new EditContentCommandParser(); + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditNoteContentCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/task/todo/EditDeadlineParserTest.java b/src/test/java/seedu/address/logic/parser/task/todo/EditDeadlineParserTest.java new file mode 100644 index 00000000000..8a7fb2b1cc3 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/task/todo/EditDeadlineParserTest.java @@ -0,0 +1,22 @@ +package seedu.address.logic.parser.task.todo; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.task.todo.EditDeadlineCommand; + +/** + * Test class for {@code EditDeadlineCommandParser}. + */ +public class EditDeadlineParserTest { + + private EditDeadlineCommandParser parser = new EditDeadlineCommandParser(); + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditDeadlineCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/model/AddressBookTest.java b/src/test/java/seedu/address/model/AddressBookTest.java index 87782528ecd..d30e5905a50 100644 --- a/src/test/java/seedu/address/model/AddressBookTest.java +++ b/src/test/java/seedu/address/model/AddressBookTest.java @@ -3,11 +3,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalInternships.ALICE; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; import java.util.Arrays; import java.util.Collection; @@ -18,9 +16,10 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.testutil.PersonBuilder; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.ProgrammingLanguage; +import seedu.address.model.application.exceptions.DuplicateApplicationException; +import seedu.address.testutil.InternshipBuilder; public class AddressBookTest { @@ -28,7 +27,7 @@ public class AddressBookTest { @Test public void constructor() { - assertEquals(Collections.emptyList(), addressBook.getPersonList()); + assertEquals(Collections.emptyList(), addressBook.getInternshipList()); } @Test @@ -45,57 +44,59 @@ public void resetData_withValidReadOnlyAddressBook_replacesData() { @Test public void resetData_withDuplicatePersons_throwsDuplicatePersonException() { - // Two persons with the same identity fields - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) + // Two applications with the same identity fields + InternshipApplication editedAlice = new InternshipBuilder(ALICE) + .withCompanyName("Alice Wonder") .build(); - List newPersons = Arrays.asList(ALICE, editedAlice); - AddressBookStub newData = new AddressBookStub(newPersons); + List newApplications = Arrays.asList(ALICE, editedAlice); + AddressBookStub newData = new AddressBookStub(newApplications); - assertThrows(DuplicatePersonException.class, () -> addressBook.resetData(newData)); + assertThrows(DuplicateApplicationException.class, () -> addressBook.resetData(newData)); } @Test public void hasPerson_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> addressBook.hasPerson(null)); + assertThrows(NullPointerException.class, () -> addressBook.hasApplication(null)); } @Test public void hasPerson_personNotInAddressBook_returnsFalse() { - assertFalse(addressBook.hasPerson(ALICE)); + assertFalse(addressBook.hasApplication(ALICE)); } @Test public void hasPerson_personInAddressBook_returnsTrue() { - addressBook.addPerson(ALICE); - assertTrue(addressBook.hasPerson(ALICE)); + addressBook.addApplication(ALICE); + assertTrue(addressBook.hasApplication(ALICE)); } @Test public void hasPerson_personWithSameIdentityFieldsInAddressBook_returnsTrue() { - addressBook.addPerson(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) + addressBook.addInternshipApplication(ALICE); + InternshipApplication editedAlice = new InternshipBuilder(ALICE) + .withProgrammingLanguage(new ProgrammingLanguage("Java")) .build(); - assertTrue(addressBook.hasPerson(editedAlice)); + assertTrue(addressBook.hasApplication(editedAlice)); } @Test public void getPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> addressBook.getPersonList().remove(0)); + assertThrows(UnsupportedOperationException.class, () -> addressBook.getInternshipList().remove(0)); } /** * A stub ReadOnlyAddressBook whose persons list can violate interface constraints. */ private static class AddressBookStub implements ReadOnlyAddressBook { - private final ObservableList persons = FXCollections.observableArrayList(); + private final ObservableList applications = FXCollections.observableArrayList(); - AddressBookStub(Collection persons) { - this.persons.setAll(persons); + AddressBookStub(Collection persons) { + this.applications.setAll(persons); } @Override - public ObservableList getPersonList() { - return persons; + public ObservableList getInternshipList() { + return applications; } } diff --git a/src/test/java/seedu/address/model/ModelManagerTest.java b/src/test/java/seedu/address/model/ModelManagerTest.java index 2cf1418d116..cb28ed2d3fe 100644 --- a/src/test/java/seedu/address/model/ModelManagerTest.java +++ b/src/test/java/seedu/address/model/ModelManagerTest.java @@ -1,132 +1,132 @@ -package seedu.address.model; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BENSON; - -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.testutil.AddressBookBuilder; - -public class ModelManagerTest { - - private ModelManager modelManager = new ModelManager(); - - @Test - public void constructor() { - assertEquals(new UserPrefs(), modelManager.getUserPrefs()); - assertEquals(new GuiSettings(), modelManager.getGuiSettings()); - assertEquals(new AddressBook(), new AddressBook(modelManager.getAddressBook())); - } - - @Test - public void setUserPrefs_nullUserPrefs_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> modelManager.setUserPrefs(null)); - } - - @Test - public void setUserPrefs_validUserPrefs_copiesUserPrefs() { - UserPrefs userPrefs = new UserPrefs(); - userPrefs.setAddressBookFilePath(Paths.get("address/book/file/path")); - userPrefs.setGuiSettings(new GuiSettings(1, 2, 3, 4)); - modelManager.setUserPrefs(userPrefs); - assertEquals(userPrefs, modelManager.getUserPrefs()); - - // Modifying userPrefs should not modify modelManager's userPrefs - UserPrefs oldUserPrefs = new UserPrefs(userPrefs); - userPrefs.setAddressBookFilePath(Paths.get("new/address/book/file/path")); - assertEquals(oldUserPrefs, modelManager.getUserPrefs()); - } - - @Test - public void setGuiSettings_nullGuiSettings_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> modelManager.setGuiSettings(null)); - } - - @Test - public void setGuiSettings_validGuiSettings_setsGuiSettings() { - GuiSettings guiSettings = new GuiSettings(1, 2, 3, 4); - modelManager.setGuiSettings(guiSettings); - assertEquals(guiSettings, modelManager.getGuiSettings()); - } - - @Test - public void setAddressBookFilePath_nullPath_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> modelManager.setAddressBookFilePath(null)); - } - - @Test - public void setAddressBookFilePath_validPath_setsAddressBookFilePath() { - Path path = Paths.get("address/book/file/path"); - modelManager.setAddressBookFilePath(path); - assertEquals(path, modelManager.getAddressBookFilePath()); - } - - @Test - public void hasPerson_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> modelManager.hasPerson(null)); - } - - @Test - public void hasPerson_personNotInAddressBook_returnsFalse() { - assertFalse(modelManager.hasPerson(ALICE)); - } - - @Test - public void hasPerson_personInAddressBook_returnsTrue() { - modelManager.addPerson(ALICE); - assertTrue(modelManager.hasPerson(ALICE)); - } - - @Test - public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredPersonList().remove(0)); - } - - @Test - public void equals() { - AddressBook addressBook = new AddressBookBuilder().withPerson(ALICE).withPerson(BENSON).build(); - AddressBook differentAddressBook = new AddressBook(); - UserPrefs userPrefs = new UserPrefs(); - - // same values -> returns true - modelManager = new ModelManager(addressBook, userPrefs); - ModelManager modelManagerCopy = new ModelManager(addressBook, userPrefs); - assertTrue(modelManager.equals(modelManagerCopy)); - - // same object -> returns true - assertTrue(modelManager.equals(modelManager)); - - // null -> returns false - assertFalse(modelManager.equals(null)); - - // different types -> returns false - assertFalse(modelManager.equals(5)); - - // different addressBook -> returns false - assertFalse(modelManager.equals(new ModelManager(differentAddressBook, userPrefs))); - - // different filteredList -> returns false - String[] keywords = ALICE.getName().fullName.split("\\s+"); - modelManager.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(keywords))); - assertFalse(modelManager.equals(new ModelManager(addressBook, userPrefs))); - - // resets modelManager to initial state for upcoming tests - modelManager.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - - // different userPrefs -> returns false - UserPrefs differentUserPrefs = new UserPrefs(); - differentUserPrefs.setAddressBookFilePath(Paths.get("differentFilePath")); - assertFalse(modelManager.equals(new ModelManager(addressBook, differentUserPrefs))); - } -} +//package seedu.address.model; +// +//import static org.junit.jupiter.api.Assertions.assertEquals; +//import static org.junit.jupiter.api.Assertions.assertFalse; +//import static org.junit.jupiter.api.Assertions.assertTrue; +//import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; +//import static seedu.address.testutil.Assert.assertThrows; +//import static seedu.address.testutil.TypicalPersons.ALICE; +//import static seedu.address.testutil.TypicalPersons.BENSON; +// +//import java.nio.file.Path; +//import java.nio.file.Paths; +//import java.util.Arrays; +// +//import org.junit.jupiter.api.Test; +// +//import seedu.address.commons.core.GuiSettings; +//import seedu.address.model.person.NameContainsKeywordsPredicate; +//import seedu.address.testutil.AddressBookBuilder; +// +//public class ModelManagerTest { +// +// private ModelManager modelManager = new ModelManager(); +// +// @Test +// public void constructor() { +// assertEquals(new UserPrefs(), modelManager.getUserPrefs()); +// assertEquals(new GuiSettings(), modelManager.getGuiSettings()); +// assertEquals(new AddressBook(), new AddressBook(modelManager.getAddressBook())); +// } +// +// @Test +// public void setUserPrefs_nullUserPrefs_throwsNullPointerException() { +// assertThrows(NullPointerException.class, () -> modelManager.setUserPrefs(null)); +// } +// +// @Test +// public void setUserPrefs_validUserPrefs_copiesUserPrefs() { +// UserPrefs userPrefs = new UserPrefs(); +// userPrefs.setAddressBookFilePath(Paths.get("address/book/file/path")); +// userPrefs.setGuiSettings(new GuiSettings(1, 2, 3, 4)); +// modelManager.setUserPrefs(userPrefs); +// assertEquals(userPrefs, modelManager.getUserPrefs()); +// +// // Modifying userPrefs should not modify modelManager's userPrefs +// UserPrefs oldUserPrefs = new UserPrefs(userPrefs); +// userPrefs.setAddressBookFilePath(Paths.get("new/address/book/file/path")); +// assertEquals(oldUserPrefs, modelManager.getUserPrefs()); +// } +// +// @Test +// public void setGuiSettings_nullGuiSettings_throwsNullPointerException() { +// assertThrows(NullPointerException.class, () -> modelManager.setGuiSettings(null)); +// } +// +// @Test +// public void setGuiSettings_validGuiSettings_setsGuiSettings() { +// GuiSettings guiSettings = new GuiSettings(1, 2, 3, 4); +// modelManager.setGuiSettings(guiSettings); +// assertEquals(guiSettings, modelManager.getGuiSettings()); +// } +// +// @Test +// public void setAddressBookFilePath_nullPath_throwsNullPointerException() { +// assertThrows(NullPointerException.class, () -> modelManager.setAddressBookFilePath(null)); +// } +// +// @Test +// public void setAddressBookFilePath_validPath_setsAddressBookFilePath() { +// Path path = Paths.get("address/book/file/path"); +// modelManager.setAddressBookFilePath(path); +// assertEquals(path, modelManager.getNoteListFilePath()); +// } +// +// @Test +// public void hasPerson_nullPerson_throwsNullPointerException() { +// assertThrows(NullPointerException.class, () -> modelManager.hasPerson(null)); +// } +// +// @Test +// public void hasPerson_personNotInAddressBook_returnsFalse() { +// assertFalse(modelManager.hasPerson(ALICE)); +// } +// +// @Test +// public void hasPerson_personInAddressBook_returnsTrue() { +// modelManager.addPerson(ALICE); +// assertTrue(modelManager.hasPerson(ALICE)); +// } +// +// @Test +// public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() { +// assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredInternshipList().remove(0)); +// } +// +// @Test +// public void equals() { +// AddressBook addressBook = new AddressBookBuilder().withPerson(ALICE).withPerson(BENSON).build(); +// AddressBook differentAddressBook = new AddressBook(); +// UserPrefs userPrefs = new UserPrefs(); +// +// // same values -> returns true +// modelManager = new ModelManager(addressBook, userPrefs); +// ModelManager modelManagerCopy = new ModelManager(addressBook, userPrefs); +// assertTrue(modelManager.equals(modelManagerCopy)); +// +// // same object -> returns true +// assertTrue(modelManager.equals(modelManager)); +// +// // null -> returns false +// assertFalse(modelManager.equals(null)); +// +// // different types -> returns false +// assertFalse(modelManager.equals(5)); +// +// // different addressBook -> returns false +// assertFalse(modelManager.equals(new ModelManager(differentAddressBook, userPrefs))); +// +// // different filteredList -> returns false +// String[] keywords = ALICE.getName().fullName.split("\\s+"); +// modelManager.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(keywords))); +// assertFalse(modelManager.equals(new ModelManager(addressBook, userPrefs))); +// +// // resets modelManager to initial state for upcoming tests +// modelManager.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); +// +// // different userPrefs -> returns false +// UserPrefs differentUserPrefs = new UserPrefs(); +// differentUserPrefs.setAddressBookFilePath(Paths.get("differentFilePath")); +// assertFalse(modelManager.equals(new ModelManager(addressBook, differentUserPrefs))); +// } +//} diff --git a/src/test/java/seedu/address/model/NoteListTest.java b/src/test/java/seedu/address/model/NoteListTest.java new file mode 100644 index 00000000000..15dbea3c6c3 --- /dev/null +++ b/src/test/java/seedu/address/model/NoteListTest.java @@ -0,0 +1,37 @@ +package seedu.address.model; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.NoteBuilder; + +/** + * Contains tests for {@code NoteList}. + */ +public class NoteListTest { + private final NoteList noteList = new NoteList(); + + @Test + public void addNote_addSuccessful() { + noteList.addNote(new NoteBuilder().build()); + assertTrue(noteList.hasNote(new NoteBuilder().build())); + } + + @Test + public void setNote_setSuccessful() { + noteList.addNote(new NoteBuilder().build()); + noteList.setNote(new NoteBuilder().build(), new NoteBuilder().withNote("changed").build()); + assertFalse(noteList.hasNote(new NoteBuilder().build())); + assertTrue(noteList.hasNote(new NoteBuilder().withNote("changed").build())); + } + + @Test + public void equals_returnsTrue() { + noteList.addNote(new NoteBuilder().build()); + NoteList other = new NoteList(); + other.addNote(new NoteBuilder().build()); + assertTrue(noteList.equals(other)); + } +} diff --git a/src/test/java/seedu/address/model/TodoListTest.java b/src/test/java/seedu/address/model/TodoListTest.java new file mode 100644 index 00000000000..9fc044282f4 --- /dev/null +++ b/src/test/java/seedu/address/model/TodoListTest.java @@ -0,0 +1,22 @@ +package seedu.address.model; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.InternshipTodoBuilder; + +/** + * Contains tests for {@code TodoList}. + */ +public class TodoListTest { + + @Test + public void equals_returnsTrue() { + TodoList todoList = new TodoList(); + todoList.addTodo(new InternshipTodoBuilder().build()); + TodoList other = new TodoList(); + other.addTodo(new InternshipTodoBuilder().build()); + assertTrue(todoList.equals(other)); + } +} diff --git a/src/test/java/seedu/address/model/application/AfterDatePredicateTest.java b/src/test/java/seedu/address/model/application/AfterDatePredicateTest.java new file mode 100644 index 00000000000..164d7222383 --- /dev/null +++ b/src/test/java/seedu/address/model/application/AfterDatePredicateTest.java @@ -0,0 +1,71 @@ +package seedu.address.model.application; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.InternshipBuilder; + +public class AfterDatePredicateTest { + + @Test + public void equals() { + AfterDatePredicate firstPredicate = new AfterDatePredicate(new InterviewDate("2023-06-07 12:00 PM")); + AfterDatePredicate secondPredicate = new AfterDatePredicate(new InterviewDate("2023-06-07 01:00 PM")); + + // same object -> returns true + assertTrue(firstPredicate.equals(firstPredicate)); + + // same values -> returns true + AfterDatePredicate firstPredicateCopy = + new AfterDatePredicate(new InterviewDate("2023-06-07 12:00 PM")); + assertTrue(firstPredicate.equals(firstPredicateCopy)); + + // different types -> returns false + assertFalse(firstPredicate.equals(1)); + + // null -> returns false + assertFalse(firstPredicate.equals(null)); + + // different interview date -> returns false + assertFalse(firstPredicate.equals(secondPredicate)); + } + + @Test + public void test_interviewDateIsAfterPredicate_returnsTrue() { + // Interview date is few days after the predicate + AfterDatePredicate predicate = + new AfterDatePredicate(new InterviewDate("2023-04-16 01:00 PM")); + assertTrue(predicate.test(new InternshipBuilder() + .withInterviewDate(new InterviewDate("2023-04-18 01:00 PM")).build())); + + // Interview date is just one hour after the predicate + assertTrue(predicate.test(new InternshipBuilder() + .withInterviewDate(new InterviewDate("2023-04-16 02:00 PM")).build())); + + // Interview date is the same as predicate + assertTrue(predicate.test(new InternshipBuilder() + .withInterviewDate(new InterviewDate("2023-04-16 01:00 PM")).build())); + } + + @Test + public void test_interviewDateIsBeforePredicate_returnsFalse() { + // Interview date is few days before the predicate + AfterDatePredicate predicate = + new AfterDatePredicate(new InterviewDate("2023-04-01 01:00 PM")); + assertFalse(predicate.test(new InternshipBuilder() + .withInterviewDate(new InterviewDate("2023-03-30 08:00 PM")).build())); + + // Interview date is few hours before the predicate + assertFalse(predicate.test(new InternshipBuilder() + .withInterviewDate(new InterviewDate("2023-04-01 08:00 AM")).build())); + } + + @Test + public void test_interviewDateIsNull_returnsFalse() { + AfterDatePredicate predicate = + new AfterDatePredicate(new InterviewDate("2023-04-01 01:00 PM")); + assertFalse(predicate.test(new InternshipBuilder().withInterviewDate(null).build())); + } +} diff --git a/src/test/java/seedu/address/model/application/BeforeDatePredicateTest.java b/src/test/java/seedu/address/model/application/BeforeDatePredicateTest.java new file mode 100644 index 00000000000..dbbf351924a --- /dev/null +++ b/src/test/java/seedu/address/model/application/BeforeDatePredicateTest.java @@ -0,0 +1,70 @@ +package seedu.address.model.application; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.InternshipBuilder; + +public class BeforeDatePredicateTest { + @Test + public void equals() { + BeforeDatePredicate firstPredicate = new BeforeDatePredicate(new InterviewDate("2023-06-07 12:00 PM")); + BeforeDatePredicate secondPredicate = new BeforeDatePredicate(new InterviewDate("2023-06-07 01:00 PM")); + + // same object -> returns true + assertTrue(firstPredicate.equals(firstPredicate)); + + // same values -> returns true + BeforeDatePredicate firstPredicateCopy = + new BeforeDatePredicate(new InterviewDate("2023-06-07 12:00 PM")); + assertTrue(firstPredicate.equals(firstPredicateCopy)); + + // different types -> returns false + assertFalse(firstPredicate.equals(1)); + + // null -> returns false + assertFalse(firstPredicate.equals(null)); + + // different interview date -> returns false + assertFalse(firstPredicate.equals(secondPredicate)); + } + + @Test + public void test_interviewDateIsBeforePredicate_returnsTrue() { + // Interview date is few days before the predicate + BeforeDatePredicate predicate = + new BeforeDatePredicate(new InterviewDate("2023-04-16 01:00 PM")); + assertTrue(predicate.test(new InternshipBuilder() + .withInterviewDate(new InterviewDate("2023-04-13 01:00 PM")).build())); + + // Interview date is just one hour before the predicate + assertTrue(predicate.test(new InternshipBuilder() + .withInterviewDate(new InterviewDate("2023-04-16 12:00 PM")).build())); + + // Interview date is the same as predicate + assertTrue(predicate.test(new InternshipBuilder() + .withInterviewDate(new InterviewDate("2023-04-16 01:00 PM")).build())); + } + + @Test + public void test_interviewDateIsAfterPredicate_returnsFalse() { + // Interview date is few days after the predicate + BeforeDatePredicate predicate = + new BeforeDatePredicate(new InterviewDate("2023-04-01 01:00 PM")); + assertFalse(predicate.test(new InternshipBuilder() + .withInterviewDate(new InterviewDate("2023-04-15 08:00 PM")).build())); + + // Interview date is few hours after the predicate + assertFalse(predicate.test(new InternshipBuilder() + .withInterviewDate(new InterviewDate("2023-04-01 08:00 PM")).build())); + } + + @Test + public void test_interviewDateIsNull_returnsFalse() { + BeforeDatePredicate predicate = + new BeforeDatePredicate(new InterviewDate("2023-04-01 01:00 PM")); + assertFalse(predicate.test(new InternshipBuilder().withInterviewDate(null).build())); + } +} diff --git a/src/test/java/seedu/address/model/application/BetweenDatePredicateTest.java b/src/test/java/seedu/address/model/application/BetweenDatePredicateTest.java new file mode 100644 index 00000000000..bc2d8f65a18 --- /dev/null +++ b/src/test/java/seedu/address/model/application/BetweenDatePredicateTest.java @@ -0,0 +1,76 @@ +package seedu.address.model.application; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.InternshipBuilder; + +public class BetweenDatePredicateTest { + @Test + public void equals() { + BetweenDatePredicate firstPredicate = new BetweenDatePredicate( + new InterviewDate("2023-06-07 12:00 PM"), new InterviewDate("2023-06-08 12:00 PM")); + BetweenDatePredicate secondPredicate = new BetweenDatePredicate( + new InterviewDate("2023-04-07 01:00 PM"), new InterviewDate("2023-04-15 12:00 PM")); + + // same object -> returns true + assertTrue(firstPredicate.equals(firstPredicate)); + + // same values -> returns true + BetweenDatePredicate firstPredicateCopy = new BetweenDatePredicate( + new InterviewDate("2023-06-07 12:00 PM"), new InterviewDate("2023-06-08 12:00 PM")); + assertTrue(firstPredicate.equals(firstPredicateCopy)); + + // different types -> returns false + assertFalse(firstPredicate.equals(1)); + + // null -> returns false + assertFalse(firstPredicate.equals(null)); + + // different interview date -> returns false + assertFalse(firstPredicate.equals(secondPredicate)); + } + + @Test + public void test_interviewDateIsBetweenPredicate_returnsTrue() { + // Interview date is within the predicate + BetweenDatePredicate predicate = new BetweenDatePredicate( + new InterviewDate("2023-03-16 01:00 PM"), new InterviewDate("2023-03-27 12:00 PM")); + assertTrue(predicate.test(new InternshipBuilder() + .withInterviewDate(new InterviewDate("2023-03-16 09:00 PM")).build())); + + // Interview date is one hour before the end date time + assertTrue(predicate.test(new InternshipBuilder() + .withInterviewDate(new InterviewDate("2023-03-27 11:00 AM")).build())); + + // Interview date is the same as end date time + assertTrue(predicate.test(new InternshipBuilder() + .withInterviewDate(new InterviewDate("2023-03-27 12:00 PM")).build())); + } + + @Test + public void test_interviewDateIsNotBetweenPredicate_returnsFalse() { + // Interview date is few days before the predicate + BetweenDatePredicate predicate = new BetweenDatePredicate( + new InterviewDate("2023-03-16 01:00 PM"), new InterviewDate("2023-03-27 12:00 PM")); + assertFalse(predicate.test(new InternshipBuilder() + .withInterviewDate(new InterviewDate("2023-03-15 08:00 PM")).build())); + + // Interview date is few hours before the predicate + assertFalse(predicate.test(new InternshipBuilder() + .withInterviewDate(new InterviewDate("2023-03-16 08:00 AM")).build())); + + // Interview date is few hours after the predicate + assertFalse(predicate.test(new InternshipBuilder() + .withInterviewDate(new InterviewDate("2023-03-27 01:00 PM")).build())); + } + + @Test + public void test_interviewDateIsNull_returnsFalse() { + BetweenDatePredicate predicate = new BetweenDatePredicate( + new InterviewDate("2023-03-16 01:00 PM"), new InterviewDate("2023-03-27 12:00 PM")); + assertFalse(predicate.test(new InternshipBuilder().withInterviewDate(null).build())); + } +} diff --git a/src/test/java/seedu/address/model/application/InternshipApplicationTest.java b/src/test/java/seedu/address/model/application/InternshipApplicationTest.java new file mode 100644 index 00000000000..0d1e4ef28ea --- /dev/null +++ b/src/test/java/seedu/address/model/application/InternshipApplicationTest.java @@ -0,0 +1,200 @@ +package seedu.address.model.application; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_DEUTSCHE_BANK; +import static seedu.address.logic.commands.CommandTestUtil.VALID_JOB_TITLE_NETWORK_ENGINEER; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalInternships.LINKEDIN; +import static seedu.address.testutil.TypicalInternships.META; + +import java.util.HashSet; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.contact.Contact; +import seedu.address.model.documents.Documents; +import seedu.address.testutil.ContactBuilder; +import seedu.address.testutil.DocumentsBuilder; +import seedu.address.testutil.InternshipBuilder; + +public class InternshipApplicationTest { + @Test + public void asObservableList_modifyList_throwsUnsupportedOperationException() { + InternshipApplication internship = new InternshipBuilder().build(); + assertThrows(UnsupportedOperationException.class, () -> internship.getQualifications().remove(0)); + } + + @Test + public void isSameInternship() { + // same object -> returns true + assertTrue(LINKEDIN.isSameApplication(LINKEDIN)); + + // null -> returns false + assertFalse(LINKEDIN.isSameApplication(null)); + + // same company name and job title, some other attributes different -> returns true + InternshipApplication editedLinkedIn = new InternshipBuilder(LINKEDIN).withQualifications("Dancing").build(); + assertTrue(LINKEDIN.isSameApplication(editedLinkedIn)); + + // different name, all other attributes same -> returns false + editedLinkedIn = new InternshipBuilder(LINKEDIN).withCompanyName(VALID_COMPANY_NAME_DEUTSCHE_BANK).build(); + assertFalse(LINKEDIN.isSameApplication(editedLinkedIn)); + + // name differs in case, all other attributes same -> returns false + editedLinkedIn = new InternshipBuilder(LINKEDIN) + .withCompanyName(LINKEDIN.getCompanyName().toString().toLowerCase()).build(); + assertFalse(LINKEDIN.isSameApplication(editedLinkedIn)); + + // name has trailing spaces, all other attributes same -> returns false + String nameWithTrailingSpaces = LINKEDIN.getCompanyName().toString() + " "; + editedLinkedIn = new InternshipBuilder(LINKEDIN).withCompanyName(nameWithTrailingSpaces).build(); + assertFalse(LINKEDIN.isSameApplication(editedLinkedIn)); + } + + @Test + public void equals() { + // same values -> returns true + InternshipApplication linkedInCopy = new InternshipBuilder(LINKEDIN).build(); + assertTrue(LINKEDIN.equals(linkedInCopy)); + assertTrue(LINKEDIN.hashCode() == linkedInCopy.hashCode()); + assertTrue(LINKEDIN.compareTo(linkedInCopy) == 0); + + // same object -> returns true + assertTrue(LINKEDIN.equals(LINKEDIN)); + + // null -> returns false + assertFalse(LINKEDIN.equals(null)); + + // different type -> returns false + assertFalse(LINKEDIN.equals(5)); + + // different person -> returns false + assertFalse(LINKEDIN.equals(META)); + + // different name -> returns false + InternshipApplication editedLinkedIn = new InternshipBuilder(LINKEDIN) + .withCompanyName(VALID_COMPANY_NAME_DEUTSCHE_BANK).build(); + assertFalse(LINKEDIN.equals(editedLinkedIn)); + + // different job title -> returns false + editedLinkedIn = new InternshipBuilder(LINKEDIN).withJobTitle(VALID_JOB_TITLE_NETWORK_ENGINEER).build(); + assertFalse(LINKEDIN.equals(editedLinkedIn)); + } + + @Test + public void testInternshipApplicationConstructor() { + // Arrange + CompanyName companyName = new CompanyName("ABC Company"); + JobTitle jobTitle = new JobTitle("Software Engineer Intern"); + Set reviews = new HashSet<>(); + Set programmingLanguages = new HashSet<>(); + Set qualifications = new HashSet<>(); + Location location = new Location("New York"); + Salary salary = new Salary("5000 SGD"); + Set notes = new HashSet<>(); + Rating rating = new Rating("4.5"); + Set reflections = new HashSet<>(); + + // Act + InternshipApplication application = new InternshipApplication( + companyName, jobTitle, reviews, programmingLanguages, qualifications, + location, salary, notes, rating, reflections + ); + + // Assert + assertEquals(companyName, application.getCompanyName()); + assertEquals(jobTitle, application.getJobTitle()); + assertEquals(reviews, application.getReviews()); + assertEquals(programmingLanguages, application.getProgrammingLanguages()); + assertEquals(qualifications, application.getQualifications()); + assertEquals(location, application.getLocation()); + assertEquals(salary, application.getSalary()); + assertEquals(notes, application.getNotes()); + assertEquals(rating, application.getRating()); + assertEquals(reflections, application.getReflections()); + assertNull(application.getContact()); + assertEquals(InternshipStatus.PENDING, application.getStatus()); + assertFalse(application.isArchived()); + assertNull(application.getDocuments()); + assertNull(application.getInterviewDate()); + } + + @Test + public void testInternshipApplicationConstructorWithContact() { + // Arrange + CompanyName companyName = new CompanyName("ABC Company"); + JobTitle jobTitle = new JobTitle("Software Engineer Intern"); + Set reviews = new HashSet<>(); + Contact contact = new ContactBuilder().build(); + InternshipStatus status = InternshipStatus.ACCEPTED; + boolean isArchived = false; + Documents documents = new DocumentsBuilder().build(); + + // Act + InternshipApplication application = new InternshipApplication( + companyName, jobTitle, reviews, contact, status, isArchived, documents + ); + + // Assert + assertEquals(companyName, application.getCompanyName()); + assertEquals(jobTitle, application.getJobTitle()); + assertEquals(reviews, application.getReviews()); + assertEquals(contact, application.getContact()); + assertEquals(status, application.getStatus()); + assertNull(application.getInterviewDate()); + assertNull(application.getLocation()); + assertNull(application.getSalary()); + assertNull(application.getRating()); + assertEquals(documents, application.getDocuments()); + assertEquals(isArchived, application.isArchived()); + } + + @Test + public void testToString() { + // Create an instance of InternshipApplication with sample data + CompanyName companyName = new CompanyName("Acme Inc"); + JobTitle jobTitle = new JobTitle("Software Developer Intern"); + Set reviews = new HashSet<>(); + reviews.add(new Review("Great company to work for!")); + Set programmingLanguages = new HashSet<>(); + programmingLanguages.add(new ProgrammingLanguage("Java")); + Set qualifications = new HashSet<>(); + qualifications.add(new Qualification("Bachelor's degree in Computer Science")); + Location location = new Location("New York City"); + Salary salary = new Salary("25 SGD"); + Set notes = new HashSet<>(); + notes.add(new Note("Need to submit transcripts")); + Rating rating = new Rating("4 stars"); + Set reflections = new HashSet<>(); + reflections.add(new Reflection("Learned a lot about web development")); + + // Set contact and status fields + Contact contact = new ContactBuilder().build(); + InternshipStatus status = InternshipStatus.ACCEPTED; + boolean isArchived = false; + Documents documents = null; + + // Set interview date field + InterviewDate interviewDate = new InterviewDate("2023-02-27 08:00 AM"); + + InternshipApplication application = new InternshipApplication(companyName, jobTitle, reviews, + programmingLanguages, qualifications, location, salary, notes, rating, reflections, contact, status, + isArchived, interviewDate, documents); + + // Define expected output + String expected = "Acme Inc; Job Title: Software Developer Intern; Status: ACCEPTED; Archived: false; " + + "Review: Great company to work for!; Programming Language: Java; " + + "Qualification: Bachelor's degree in Computer Science; Location: New York City; Salary: 25 SGD; " + + "Note: Need to submit transcripts; Rating: 4 stars; Reflection: Learned a lot about web development; " + + "Company Phone: 55555555; Company Email: meta@example.com; Interview Date: 2023-02-27 08:00 AM"; + + // Verify that toString() method returns expected output + assertEquals(expected, application.toString()); + } + + +} diff --git a/src/test/java/seedu/address/model/application/LocationTest.java b/src/test/java/seedu/address/model/application/LocationTest.java new file mode 100644 index 00000000000..99bde874579 --- /dev/null +++ b/src/test/java/seedu/address/model/application/LocationTest.java @@ -0,0 +1,47 @@ +package seedu.address.model.application; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class LocationTest { + + @Test + public void equals() { + Location location1 = new Location("Singapore"); + Location location2 = new Location("Singapore"); + Location location3 = new Location("USA"); + + // reflexivity + assertTrue(location1.equals(location1)); + + // symmetry + assertTrue(location1.equals(location2)); + assertTrue(location2.equals(location1)); + + // transitivity + assertTrue(location1.equals(location2)); + assertFalse(location2.equals(location3)); + assertFalse(location1.equals(location3)); + + // non-nullity + assertFalse(location1.equals(null)); + + // different types + assertFalse(location1.equals("Singapore")); + } + + @Test + void testHashCode() { + Location location1 = new Location("Singapore"); + Location location2 = new Location("Singapore"); + Location location3 = new Location("USA"); + + assertEquals(location1.hashCode(), location2.hashCode()); + assertNotEquals(location1.hashCode(), location3.hashCode()); + } +} + diff --git a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/application/NameContainsKeywordsPredicateTest.java similarity index 53% rename from src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java rename to src/test/java/seedu/address/model/application/NameContainsKeywordsPredicateTest.java index f136664e017..9da61f1f067 100644 --- a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java +++ b/src/test/java/seedu/address/model/application/NameContainsKeywordsPredicateTest.java @@ -1,7 +1,8 @@ -package seedu.address.model.person; +package seedu.address.model.application; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.model.application.InternshipStatus.PENDING; import java.util.Arrays; import java.util.Collections; @@ -9,7 +10,10 @@ import org.junit.jupiter.api.Test; -import seedu.address.testutil.PersonBuilder; +import seedu.address.model.contact.Contact; +import seedu.address.model.contact.Email; +import seedu.address.model.contact.Phone; +import seedu.address.testutil.InternshipBuilder; public class NameContainsKeywordsPredicateTest { @@ -25,7 +29,8 @@ public void equals() { assertTrue(firstPredicate.equals(firstPredicate)); // same values -> returns true - NameContainsKeywordsPredicate firstPredicateCopy = new NameContainsKeywordsPredicate(firstPredicateKeywordList); + NameContainsKeywordsPredicate firstPredicateCopy = + new NameContainsKeywordsPredicate(firstPredicateKeywordList); assertTrue(firstPredicate.equals(firstPredicateCopy)); // different types -> returns false @@ -34,42 +39,45 @@ public void equals() { // null -> returns false assertFalse(firstPredicate.equals(null)); - // different person -> returns false + // different application -> returns false assertFalse(firstPredicate.equals(secondPredicate)); } @Test public void test_nameContainsKeywords_returnsTrue() { // One keyword - NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Collections.singletonList("Alice")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build())); + NameContainsKeywordsPredicate predicate = + new NameContainsKeywordsPredicate(Collections.singletonList("JP")); + assertTrue(predicate.test(new InternshipBuilder().withCompanyName("JP Morgan").build())); // Multiple keywords - predicate = new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build())); + predicate = new NameContainsKeywordsPredicate(Arrays.asList("JP", "Morgan")); + assertTrue(predicate.test(new InternshipBuilder().withCompanyName("JP Morgan").build())); // Only one matching keyword - predicate = new NameContainsKeywordsPredicate(Arrays.asList("Bob", "Carol")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Carol").build())); + predicate = new NameContainsKeywordsPredicate(Arrays.asList("Google", "Meta", "Morgan")); + assertTrue(predicate.test(new InternshipBuilder().withCompanyName("JP Morgan").build())); // Mixed-case keywords - predicate = new NameContainsKeywordsPredicate(Arrays.asList("aLIce", "bOB")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build())); + predicate = new NameContainsKeywordsPredicate(Arrays.asList("jP", "mOrGAN")); + assertTrue(predicate.test(new InternshipBuilder().withCompanyName("JP Morgan").build())); } @Test public void test_nameDoesNotContainKeywords_returnsFalse() { // Zero keywords NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Collections.emptyList()); - assertFalse(predicate.test(new PersonBuilder().withName("Alice").build())); + assertFalse(predicate.test(new InternshipBuilder().withCompanyName("JP Morgan").build())); // Non-matching keyword - predicate = new NameContainsKeywordsPredicate(Arrays.asList("Carol")); - assertFalse(predicate.test(new PersonBuilder().withName("Alice Bob").build())); - - // Keywords match phone, email and address, but does not match name - predicate = new NameContainsKeywordsPredicate(Arrays.asList("12345", "alice@email.com", "Main", "Street")); - assertFalse(predicate.test(new PersonBuilder().withName("Alice").withPhone("12345") - .withEmail("alice@email.com").withAddress("Main Street").build())); + predicate = new NameContainsKeywordsPredicate(Arrays.asList("Google")); + assertFalse(predicate.test(new InternshipBuilder().withCompanyName("JP Morgan").build())); + + // Keywords match status, email and phone, but do not match company name + predicate = new NameContainsKeywordsPredicate(Arrays.asList("RECEIVED", "google@email.com", "87654321")); + assertFalse(predicate.test(new InternshipBuilder() + .withCompanyName("Goo") + .withStatus(PENDING) + .withContact(new Contact(new Phone("87654321"), new Email("google@gmail.com"))).build())); } } diff --git a/src/test/java/seedu/address/model/application/NoteTest.java b/src/test/java/seedu/address/model/application/NoteTest.java new file mode 100644 index 00000000000..a267274c1b8 --- /dev/null +++ b/src/test/java/seedu/address/model/application/NoteTest.java @@ -0,0 +1,23 @@ +package seedu.address.model.application; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class NoteTest { + @Test + void testEquals() { + Note note1 = new Note("This is a note."); + Note note2 = new Note("This is a note."); + Note note3 = new Note("This is another note."); + String string = "This is a note."; + + // same note value, should be equal + assertTrue(note1.equals(note2)); + // different note value, should not be equal + assertFalse(note1.equals(note3)); + // different type, should not be equal + assertFalse(note1.equals(string)); + } +} diff --git a/src/test/java/seedu/address/model/application/ProgrammingLanguageTest.java b/src/test/java/seedu/address/model/application/ProgrammingLanguageTest.java new file mode 100644 index 00000000000..1bc1523f126 --- /dev/null +++ b/src/test/java/seedu/address/model/application/ProgrammingLanguageTest.java @@ -0,0 +1,41 @@ +package seedu.address.model.application; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class ProgrammingLanguageTest { + @Test + public void equals_sameObject_true() { + ProgrammingLanguage pl = new ProgrammingLanguage("Java"); + assertTrue(pl.equals(pl)); + } + + @Test + public void equals_sameValues_true() { + ProgrammingLanguage pl1 = new ProgrammingLanguage("Java"); + ProgrammingLanguage pl2 = new ProgrammingLanguage("Java"); + assertTrue(pl1.equals(pl2)); + } + + @Test + public void equals_differentValues_false() { + ProgrammingLanguage pl1 = new ProgrammingLanguage("Java"); + ProgrammingLanguage pl2 = new ProgrammingLanguage("Python"); + assertFalse(pl1.equals(pl2)); + } + + @Test + public void equals_null_false() { + ProgrammingLanguage pl = new ProgrammingLanguage("Java"); + assertFalse(pl.equals(null)); + } + + @Test + public void equals_differentClass_false() { + ProgrammingLanguage pl = new ProgrammingLanguage("Java"); + assertFalse(pl.equals("Java")); + } + +} diff --git a/src/test/java/seedu/address/model/application/QualificationTest.java b/src/test/java/seedu/address/model/application/QualificationTest.java new file mode 100644 index 00000000000..cea1829227a --- /dev/null +++ b/src/test/java/seedu/address/model/application/QualificationTest.java @@ -0,0 +1,33 @@ +package seedu.address.model.application; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class QualificationTest { + @Test + public void equals() { + Qualification qualification1 = new Qualification("Java"); + Qualification qualification2 = new Qualification("Java"); + Qualification qualification3 = new Qualification("C++"); + + // reflexivity + assertTrue(qualification1.equals(qualification1)); + + // symmetry + assertTrue(qualification1.equals(qualification2)); + assertTrue(qualification2.equals(qualification1)); + + // transitivity + assertTrue(qualification1.equals(qualification2)); + assertFalse(qualification2.equals(qualification3)); + assertFalse(qualification1.equals(qualification3)); + + // inequality + assertFalse(qualification1.equals(null)); + assertFalse(qualification1.equals("Java")); + assertFalse(qualification1.equals(qualification3)); + } +} + diff --git a/src/test/java/seedu/address/model/application/RatingTest.java b/src/test/java/seedu/address/model/application/RatingTest.java new file mode 100644 index 00000000000..44812ce7730 --- /dev/null +++ b/src/test/java/seedu/address/model/application/RatingTest.java @@ -0,0 +1,42 @@ +package seedu.address.model.application; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class RatingTest { + private static final Rating RATING_A = new Rating("3.5"); + private static final Rating RATING_B = new Rating("3.5"); + private static final Rating RATING_C = new Rating("4.0"); + + @Test + public void equals() { + // same object -> returns true + assertTrue(RATING_A.equals(RATING_A)); + + // same value -> returns true + assertTrue(RATING_A.equals(RATING_B)); + + // different types -> returns false + assertFalse(RATING_A.equals("3.5")); + + // null -> returns false + assertFalse(RATING_A.equals(null)); + + // different value -> returns false + assertFalse(RATING_A.equals(RATING_C)); + } + + @Test + void testHashCode() { + // same value -> returns same hashcode + assertEquals(RATING_A.hashCode(), RATING_B.hashCode()); + + // different value -> returns different hashcode + assertNotEquals(RATING_A.hashCode(), RATING_C.hashCode()); + } +} + diff --git a/src/test/java/seedu/address/model/application/ReflectionTest.java b/src/test/java/seedu/address/model/application/ReflectionTest.java new file mode 100644 index 00000000000..91aba9acb5a --- /dev/null +++ b/src/test/java/seedu/address/model/application/ReflectionTest.java @@ -0,0 +1,36 @@ +package seedu.address.model.application; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import org.junit.jupiter.api.Test; + +public class ReflectionTest { + @Test + public void equals() { + Reflection reflection1 = new Reflection("I learned a lot during my internship."); + Reflection reflection2 = new Reflection("I learned a lot during my internship."); + Reflection reflection3 = new Reflection("I had a great time during my internship."); + + // reflexive property + assertEquals(reflection1, reflection1); + + // symmetric property + assertEquals(reflection1, reflection2); + assertEquals(reflection2, reflection1); + + // transitive property + assertEquals(reflection1, reflection2); + assertNotEquals(reflection2, reflection3); + assertNotEquals(reflection1, reflection3); + + // null and different type + assertNotEquals(reflection1, null); + assertNotEquals(reflection1, "I learned a lot during my internship."); + + // comparing reflection with different value + Reflection differentReflection = new Reflection("I did not learn anything during my internship."); + assertNotEquals(reflection1, differentReflection); + } + +} diff --git a/src/test/java/seedu/address/model/application/ReviewTest.java b/src/test/java/seedu/address/model/application/ReviewTest.java new file mode 100644 index 00000000000..0c3f3628eec --- /dev/null +++ b/src/test/java/seedu/address/model/application/ReviewTest.java @@ -0,0 +1,34 @@ +package seedu.address.model.application; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class ReviewTest { + @Test + public void equals() { + Review review1 = new Review("This internship was amazing!"); + Review review2 = new Review("This internship was amazing!"); + Review review3 = new Review("This internship was terrible!"); + + // reflexivity + assertTrue(review1.equals(review1)); + + // symmetry + assertTrue(review1.equals(review2)); + assertTrue(review2.equals(review1)); + + // transitivity + assertTrue(review1.equals(review2)); + assertFalse(review2.equals(review3)); + assertFalse(review1.equals(review3)); + + // non-nullity + assertFalse(review1.equals(null)); + + // unequal objects + assertFalse(review1.equals(new Object())); + } + +} diff --git a/src/test/java/seedu/address/model/application/SalaryTest.java b/src/test/java/seedu/address/model/application/SalaryTest.java new file mode 100644 index 00000000000..478991dae7b --- /dev/null +++ b/src/test/java/seedu/address/model/application/SalaryTest.java @@ -0,0 +1,38 @@ +package seedu.address.model.application; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class SalaryTest { + + private Salary salary1; + private Salary salary2; + private Salary salary3; + + @BeforeEach + void setUp() { + salary1 = new Salary("1000 USD"); + salary2 = new Salary("2000 EUR"); + salary3 = new Salary("1000 USD"); + } + + @Test + void testEquals() { + assertEquals(salary1, salary1); // reflexivity + assertEquals(salary1, salary3); // symmetry + assertNotEquals(salary1, salary2); // inequality + assertNotEquals(salary1, null); // null comparison + assertNotEquals(salary1, "string"); // different class comparison + } + + @Test + void testHashCode() { + assertEquals(salary1.hashCode(), salary1.hashCode()); // consistency + assertEquals(salary1.hashCode(), salary3.hashCode()); // equals objects have same hash code + assertNotEquals(salary1.hashCode(), salary2.hashCode()); // different objects have different hash codes + } +} + diff --git a/src/test/java/seedu/address/model/application/StatusPredicateTest.java b/src/test/java/seedu/address/model/application/StatusPredicateTest.java new file mode 100644 index 00000000000..996f186d8cb --- /dev/null +++ b/src/test/java/seedu/address/model/application/StatusPredicateTest.java @@ -0,0 +1,49 @@ +package seedu.address.model.application; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.model.application.InternshipStatus.ACCEPTED; +import static seedu.address.model.application.InternshipStatus.PENDING; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.InternshipBuilder; + +public class StatusPredicateTest { + @Test + public void equals() { + StatusPredicate firstPredicate = new StatusPredicate(ACCEPTED); + StatusPredicate secondPredicate = new StatusPredicate(PENDING); + + // same object -> returns true + assertTrue(firstPredicate.equals(firstPredicate)); + + // same values -> returns true + StatusPredicate firstPredicateCopy = + new StatusPredicate(ACCEPTED); + assertTrue(firstPredicate.equals(firstPredicateCopy)); + + // different types -> returns false + assertFalse(firstPredicate.equals(1)); + + // null -> returns false + assertFalse(firstPredicate.equals(null)); + + // different status -> returns false + assertFalse(firstPredicate.equals(secondPredicate)); + } + + @Test + public void test_matchingStatus_returnsTrue() { + StatusPredicate predicate = + new StatusPredicate(PENDING); + assertTrue(predicate.test(new InternshipBuilder().withStatus(PENDING).build())); + } + + @Test + public void test_mismatchStatus_returnsFalse() { + StatusPredicate predicate = + new StatusPredicate(ACCEPTED); + assertFalse(predicate.test(new InternshipBuilder().withStatus(PENDING).build())); + } +} diff --git a/src/test/java/seedu/address/model/application/UniquePersonListTest.java b/src/test/java/seedu/address/model/application/UniquePersonListTest.java new file mode 100644 index 00000000000..29444000f6c --- /dev/null +++ b/src/test/java/seedu/address/model/application/UniquePersonListTest.java @@ -0,0 +1,171 @@ +//package seedu.address.model.person; +// +//import static org.junit.jupiter.api.Assertions.assertEquals; +//import static org.junit.jupiter.api.Assertions.assertFalse; +//import static org.junit.jupiter.api.Assertions.assertTrue; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; +//import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +//import static seedu.address.testutil.Assert.assertThrows; +//import static seedu.address.testutil.TypicalPersons.ALICE; +//import static seedu.address.testutil.TypicalPersons.BOB; +// +//import java.util.Arrays; +//import java.util.Collections; +//import java.util.List; +// +//import org.junit.jupiter.api.Test; +// +//import seedu.address.model.person.exceptions.DuplicateApplicationException; +//import seedu.address.model.person.exceptions.ApplicationNotFoundException; +//import seedu.address.testutil.PersonBuilder; +// +//public class UniquePersonListTest { +// +// private final UniquePersonList uniquePersonList = new UniquePersonList(); +// +// @Test +// public void contains_nullPerson_throwsNullPointerException() { +// assertThrows(NullPointerException.class, () -> uniquePersonList.contains(null)); +// } +// +// @Test +// public void contains_personNotInList_returnsFalse() { +// assertFalse(uniquePersonList.contains(ALICE)); +// } +// +// @Test +// public void contains_personInList_returnsTrue() { +// uniquePersonList.add(ALICE); +// assertTrue(uniquePersonList.contains(ALICE)); +// } +// +// @Test +// public void contains_personWithSameIdentityFieldsInList_returnsTrue() { +// uniquePersonList.add(ALICE); +// Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) +// .build(); +// assertTrue(uniquePersonList.contains(editedAlice)); +// } +// +// @Test +// public void add_nullPerson_throwsNullPointerException() { +// assertThrows(NullPointerException.class, () -> uniquePersonList.add(null)); +// } +// +// @Test +// public void add_duplicatePerson_throwsDuplicatePersonException() { +// uniquePersonList.add(ALICE); +// assertThrows(DuplicateApplicationException.class, () -> uniquePersonList.add(ALICE)); +// } +// +// @Test +// public void setPerson_nullTargetPerson_throwsNullPointerException() { +// assertThrows(NullPointerException.class, () -> uniquePersonList.setPerson(null, ALICE)); +// } +// +// @Test +// public void setPerson_nullEditedPerson_throwsNullPointerException() { +// assertThrows(NullPointerException.class, () -> uniquePersonList.setPerson(ALICE, null)); +// } +// +// @Test +// public void setPerson_targetPersonNotInList_throwsPersonNotFoundException() { +// assertThrows(ApplicationNotFoundException.class, () -> uniquePersonList.setPerson(ALICE, ALICE)); +// } +// +// @Test +// public void setPerson_editedPersonIsSamePerson_success() { +// uniquePersonList.add(ALICE); +// uniquePersonList.setPerson(ALICE, ALICE); +// UniquePersonList expectedUniquePersonList = new UniquePersonList(); +// expectedUniquePersonList.add(ALICE); +// assertEquals(expectedUniquePersonList, uniquePersonList); +// } +// +// @Test +// public void setPerson_editedPersonHasSameIdentity_success() { +// uniquePersonList.add(ALICE); +// Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) +// .build(); +// uniquePersonList.setPerson(ALICE, editedAlice); +// UniquePersonList expectedUniquePersonList = new UniquePersonList(); +// expectedUniquePersonList.add(editedAlice); +// assertEquals(expectedUniquePersonList, uniquePersonList); +// } +// +// @Test +// public void setPerson_editedPersonHasDifferentIdentity_success() { +// uniquePersonList.add(ALICE); +// uniquePersonList.setPerson(ALICE, BOB); +// UniquePersonList expectedUniquePersonList = new UniquePersonList(); +// expectedUniquePersonList.add(BOB); +// assertEquals(expectedUniquePersonList, uniquePersonList); +// } +// +// @Test +// public void setPerson_editedPersonHasNonUniqueIdentity_throwsDuplicatePersonException() { +// uniquePersonList.add(ALICE); +// uniquePersonList.add(BOB); +// assertThrows(DuplicateApplicationException.class, () -> uniquePersonList.setPerson(ALICE, BOB)); +// } +// +// @Test +// public void remove_nullPerson_throwsNullPointerException() { +// assertThrows(NullPointerException.class, () -> uniquePersonList.remove(null)); +// } +// +// @Test +// public void remove_personDoesNotExist_throwsPersonNotFoundException() { +// assertThrows(ApplicationNotFoundException.class, () -> uniquePersonList.remove(ALICE)); +// } +// +// @Test +// public void remove_existingPerson_removesPerson() { +// uniquePersonList.add(ALICE); +// uniquePersonList.remove(ALICE); +// UniquePersonList expectedUniquePersonList = new UniquePersonList(); +// assertEquals(expectedUniquePersonList, uniquePersonList); +// } +// +// @Test +// public void setPersons_nullUniquePersonList_throwsNullPointerException() { +// assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((UniquePersonList) null)); +// } +// +// @Test +// public void setPersons_uniquePersonList_replacesOwnListWithProvidedUniquePersonList() { +// uniquePersonList.add(ALICE); +// UniquePersonList expectedUniquePersonList = new UniquePersonList(); +// expectedUniquePersonList.add(BOB); +// uniquePersonList.setPersons(expectedUniquePersonList); +// assertEquals(expectedUniquePersonList, uniquePersonList); +// } +// +// @Test +// public void setPersons_nullList_throwsNullPointerException() { +// assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((List) null)); +// } +// +// @Test +// public void setPersons_list_replacesOwnListWithProvidedList() { +// uniquePersonList.add(ALICE); +// List personList = Collections.singletonList(BOB); +// uniquePersonList.setPersons(personList); +// UniquePersonList expectedUniquePersonList = new UniquePersonList(); +// expectedUniquePersonList.add(BOB); +// assertEquals(expectedUniquePersonList, uniquePersonList); +// } +// +// @Test +// public void setPersons_listWithDuplicatePersons_throwsDuplicatePersonException() { +// List listWithDuplicatePersons = Arrays.asList(ALICE, ALICE); +// assertThrows(DuplicateApplicationException.class, () -> uniquePersonList +// .setPersons(listWithDuplicatePersons)); +// } +// +// @Test +// public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() { +// assertThrows(UnsupportedOperationException.class, () +// -> uniquePersonList.asUnmodifiableObservableList().remove(0)); +// } +//} diff --git a/src/test/java/seedu/address/model/application/comparator/CompanyNameComparatorTest.java b/src/test/java/seedu/address/model/application/comparator/CompanyNameComparatorTest.java new file mode 100644 index 00000000000..d40860ca310 --- /dev/null +++ b/src/test/java/seedu/address/model/application/comparator/CompanyNameComparatorTest.java @@ -0,0 +1,35 @@ +package seedu.address.model.application.comparator; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.InternshipBuilder; + +public class CompanyNameComparatorTest { + private CompanyNameComparator cmp = new CompanyNameComparator(); + + @Test + public void compare_twoInternshipApplications_returnsNegativeInt() { + assertTrue(cmp.compare( + new InternshipBuilder().withCompanyName("Google").build(), + new InternshipBuilder().withCompanyName("Tesla").build()) + < 0); + } + + @Test + public void compare_twoInternshipApplications_returnsPositiveInt() { + assertTrue(cmp.compare( + new InternshipBuilder().withCompanyName("Google").build(), + new InternshipBuilder().withCompanyName("Alice").build()) + > 0); + } + + @Test + public void compare_twoInternshipApplications_returnsZero() { + assertTrue(cmp.compare( + new InternshipBuilder().withCompanyName("Google").build(), + new InternshipBuilder().withCompanyName("Google").build()) + == 0); + } +} diff --git a/src/test/java/seedu/address/model/application/comparator/InterviewDateComparatorTest.java b/src/test/java/seedu/address/model/application/comparator/InterviewDateComparatorTest.java new file mode 100644 index 00000000000..89c2c81ad19 --- /dev/null +++ b/src/test/java/seedu/address/model/application/comparator/InterviewDateComparatorTest.java @@ -0,0 +1,36 @@ +package seedu.address.model.application.comparator; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.application.InterviewDate; +import seedu.address.testutil.InternshipBuilder; + +public class InterviewDateComparatorTest { + private InterviewDateComparator cmp = new InterviewDateComparator(); + + @Test + public void compare_twoInternshipApplications_returnsNegativeInt() { + assertTrue(cmp.compare( + new InternshipBuilder().withInterviewDate(new InterviewDate("2023-04-01 01:00 PM")).build(), + new InternshipBuilder().withInterviewDate(new InterviewDate("2023-04-02 01:00 PM")).build()) + < 0); + } + + @Test + public void compare_twoInternshipApplications_returnsPositiveInt() { + assertTrue(cmp.compare( + new InternshipBuilder().withInterviewDate(new InterviewDate("2023-04-02 02:00 PM")).build(), + new InternshipBuilder().withInterviewDate(new InterviewDate("2023-04-02 01:00 PM")).build()) + > 0); + } + + @Test + public void compare_twoInternshipApplications_returnsZero() { + assertTrue(cmp.compare( + new InternshipBuilder().withInterviewDate(new InterviewDate("2023-04-01 01:00 PM")).build(), + new InternshipBuilder().withInterviewDate(new InterviewDate("2023-04-01 01:00 PM")).build()) + == 0); + } +} diff --git a/src/test/java/seedu/address/model/application/comparator/JobTitleComparatorTest.java b/src/test/java/seedu/address/model/application/comparator/JobTitleComparatorTest.java new file mode 100644 index 00000000000..f139dd7fe0b --- /dev/null +++ b/src/test/java/seedu/address/model/application/comparator/JobTitleComparatorTest.java @@ -0,0 +1,48 @@ +package seedu.address.model.application.comparator; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.InternshipBuilder; + +public class JobTitleComparatorTest { + private JobTitleComparator cmp = new JobTitleComparator(); + + @Test + public void compare_bothStartWithUpperCase_returnsNegativeInt() { + assertTrue(cmp.compare( + new InternshipBuilder().withJobTitle("Software Engineer").build(), + new InternshipBuilder().withJobTitle("Test Engineer").build()) + < 0); + } + + @Test + public void compare_mixedCases_returnsNegativeInt() { + assertTrue(cmp.compare( + new InternshipBuilder().withJobTitle("Software Engineer").build(), + new InternshipBuilder().withJobTitle("test Engineer").build()) + < 0); + + assertTrue(cmp.compare( + new InternshipBuilder().withJobTitle("software Engineer").build(), + new InternshipBuilder().withJobTitle("Test Engineer").build()) + < 0); + } + + @Test + public void compare_twoInternshipApplications_returnsPositiveInt() { + assertTrue(cmp.compare( + new InternshipBuilder().withJobTitle("Tesla Software Engineer").build(), + new InternshipBuilder().withJobTitle("Software Engineer").build()) + > 0); + } + + @Test + public void compare_twoInternshipApplications_returnsZero() { + assertTrue(cmp.compare( + new InternshipBuilder().withJobTitle("Tesla Software Engineer").build(), + new InternshipBuilder().withJobTitle("Tesla Software Engineer").build()) + == 0); + } +} diff --git a/src/test/java/seedu/address/model/application/comparator/StatusComparatorTest.java b/src/test/java/seedu/address/model/application/comparator/StatusComparatorTest.java new file mode 100644 index 00000000000..07d61ec590b --- /dev/null +++ b/src/test/java/seedu/address/model/application/comparator/StatusComparatorTest.java @@ -0,0 +1,40 @@ +package seedu.address.model.application.comparator; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.model.application.InternshipStatus.ACCEPTED; +import static seedu.address.model.application.InternshipStatus.DECLINED; +import static seedu.address.model.application.InternshipStatus.PENDING; +import static seedu.address.model.application.InternshipStatus.RECEIVED; +import static seedu.address.model.application.InternshipStatus.REJECTED; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.InternshipBuilder; + +public class StatusComparatorTest { + private StatusComparator cmp = new StatusComparator(); + + @Test + public void compare_bothStartWithUpperCase_returnsNegativeInt() { + assertTrue(cmp.compare( + new InternshipBuilder().withStatus(RECEIVED).build(), + new InternshipBuilder().withStatus(REJECTED).build()) + < 0); + } + + @Test + public void compare_twoInternshipApplications_returnsPositiveInt() { + assertTrue(cmp.compare( + new InternshipBuilder().withStatus(DECLINED).build(), + new InternshipBuilder().withStatus(ACCEPTED).build()) + > 0); + } + + @Test + public void compare_twoInternshipApplications_returnsZero() { + assertTrue(cmp.compare( + new InternshipBuilder().withStatus(PENDING).build(), + new InternshipBuilder().withStatus(PENDING).build()) + == 0); + } +} diff --git a/src/test/java/seedu/address/model/contact/ContactTest.java b/src/test/java/seedu/address/model/contact/ContactTest.java new file mode 100644 index 00000000000..e4dd29a8980 --- /dev/null +++ b/src/test/java/seedu/address/model/contact/ContactTest.java @@ -0,0 +1,42 @@ +package seedu.address.model.contact; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_META; +import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_META; +import static seedu.address.testutil.TypicalContacts.BANK_OF_AMERICA_CONTACT; +import static seedu.address.testutil.TypicalContacts.META_CONTACT; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.ContactBuilder; + +public class ContactTest { + + @Test + public void equals() { + // same values -> returns true + Contact companyACopy = new ContactBuilder(BANK_OF_AMERICA_CONTACT).build(); + assertEquals(BANK_OF_AMERICA_CONTACT, companyACopy); + + // same object -> returns true + assertEquals(BANK_OF_AMERICA_CONTACT, BANK_OF_AMERICA_CONTACT); + + // null -> returns false + assertNotEquals(null, BANK_OF_AMERICA_CONTACT); + + // different type -> returns false + assertNotEquals(5, BANK_OF_AMERICA_CONTACT); + + // different contact -> returns false + assertNotEquals(BANK_OF_AMERICA_CONTACT, META_CONTACT); + + // different phone -> returns false + Contact editedContact = new ContactBuilder(BANK_OF_AMERICA_CONTACT).withPhone(VALID_PHONE_META).build(); + assertNotEquals(BANK_OF_AMERICA_CONTACT, editedContact); + + // different email -> returns false + editedContact = new ContactBuilder(BANK_OF_AMERICA_CONTACT).withEmail(VALID_EMAIL_META).build(); + assertNotEquals(BANK_OF_AMERICA_CONTACT, editedContact); + } +} diff --git a/src/test/java/seedu/address/model/person/EmailTest.java b/src/test/java/seedu/address/model/contact/EmailTest.java similarity index 93% rename from src/test/java/seedu/address/model/person/EmailTest.java rename to src/test/java/seedu/address/model/contact/EmailTest.java index bbcc6c8c98e..78dc0666248 100644 --- a/src/test/java/seedu/address/model/person/EmailTest.java +++ b/src/test/java/seedu/address/model/contact/EmailTest.java @@ -1,4 +1,4 @@ -package seedu.address.model.person; +package seedu.address.model.contact; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -57,9 +57,6 @@ public void isValidEmail() { assertTrue(Email.isValidEmail("PeterJack.1190@example.com")); // period in local part assertTrue(Email.isValidEmail("PeterJack+1190@example.com")); // '+' symbol in local part assertTrue(Email.isValidEmail("PeterJack-1190@example.com")); // hyphen in local part - assertTrue(Email.isValidEmail("a@bc")); // minimal - assertTrue(Email.isValidEmail("test@localhost")); // alphabets only - assertTrue(Email.isValidEmail("123@145")); // numeric local part and domain name assertTrue(Email.isValidEmail("a1+be.d@example1.com")); // mixture of alphanumeric and special characters assertTrue(Email.isValidEmail("peter_jack@very-very-very-long-example.com")); // long domain name assertTrue(Email.isValidEmail("if.you.dream.it_you.can.do.it@example.com")); // long local part diff --git a/src/test/java/seedu/address/model/person/PhoneTest.java b/src/test/java/seedu/address/model/contact/PhoneTest.java similarity index 97% rename from src/test/java/seedu/address/model/person/PhoneTest.java rename to src/test/java/seedu/address/model/contact/PhoneTest.java index 8dd52766a5f..daae8739149 100644 --- a/src/test/java/seedu/address/model/person/PhoneTest.java +++ b/src/test/java/seedu/address/model/contact/PhoneTest.java @@ -1,4 +1,4 @@ -package seedu.address.model.person; +package seedu.address.model.contact; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/src/test/java/seedu/address/model/documents/CoverLetterLinkTest.java b/src/test/java/seedu/address/model/documents/CoverLetterLinkTest.java new file mode 100644 index 00000000000..1bf8768c610 --- /dev/null +++ b/src/test/java/seedu/address/model/documents/CoverLetterLinkTest.java @@ -0,0 +1,78 @@ +package seedu.address.model.documents; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class CoverLetterLinkTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new CoverLetterLink(null)); + } + + @Test + public void constructor_invalidCoverLetterLink_throwsIllegalArgumentException() { + String invalidCoverLetterLink = ""; + assertThrows(IllegalArgumentException.class, () -> new CoverLetterLink(invalidCoverLetterLink)); + } + + @Test + public void isValidCoverLetterLink() { + // null cover letter link + assertThrows(NullPointerException.class, () -> CoverLetterLink.isValidCoverLetterLink(null)); + + // blank cover letter link + assertFalse(CoverLetterLink.isValidCoverLetterLink("")); // empty string + assertFalse(CoverLetterLink.isValidCoverLetterLink(" ")); // spaces only + + // missing cover letter link + assertFalse(CoverLetterLink.isValidCoverLetterLink("example.com/coverletter")); // missing protocol + assertFalse(CoverLetterLink.isValidCoverLetterLink("/coverletter")); // missing protocol and path + assertFalse(CoverLetterLink.isValidCoverLetterLink("ftp://example")); // wrong protocol + + // invalid parts + assertFalse(CoverLetterLink.isValidCoverLetterLink("https://-/coverletter")); // invalid domain name + // underscore in domain name + assertFalse(CoverLetterLink.isValidCoverLetterLink("https://exam_ple.com/coverletter")); + // spaces in domain name + assertFalse(CoverLetterLink.isValidCoverLetterLink("https://exam ple.com/coverletter")); + // triple '/' after protocol + assertFalse(CoverLetterLink.isValidCoverLetterLink("https:///example.com/coverletter")); + // domain name starts with a period + assertFalse(CoverLetterLink.isValidCoverLetterLink("https://.example.com/coverletter")); + // domain name ends with a period + assertFalse(CoverLetterLink.isValidCoverLetterLink("https://example.com./coverletter")); + // domain name starts with a hyphen + assertFalse(CoverLetterLink.isValidCoverLetterLink("https://-example.com/coverletter")); + // domain name ends with a hyphen + assertFalse(CoverLetterLink.isValidCoverLetterLink("https://example.com-/coverletter")); + // top level domain has less than two chars + assertFalse(CoverLetterLink.isValidCoverLetterLink("https://example.c/coverletter")); + + // valid cover letter link + // underscore in path + assertTrue(CoverLetterLink.isValidCoverLetterLink("https://example.com/coverletter_1")); + // period in path + assertTrue(CoverLetterLink.isValidCoverLetterLink("https://example.com/coverletter.1")); + // '+' symbol in path + assertTrue(CoverLetterLink.isValidCoverLetterLink("https://example.com/coverletter+1")); + // hyphen in path + assertTrue(CoverLetterLink.isValidCoverLetterLink("https://example.com/coverletter-1")); + // hyphen in domain name + assertTrue(CoverLetterLink.isValidCoverLetterLink("https://example-1.com/coverletter-1")); + assertTrue(CoverLetterLink.isValidCoverLetterLink("http://example.com/coverletter")); // alphabets only + assertTrue(CoverLetterLink.isValidCoverLetterLink("http://12.34.56.78/9")); // IP address and path + // mixture of alphanumeric and special characters + assertTrue(CoverLetterLink.isValidCoverLetterLink("http://example1.com/a1+be.")); + // long domain name + assertTrue(CoverLetterLink + .isValidCoverLetterLink("https://very-very-very-long-example.com/coverletter_new")); + // long path + assertTrue(CoverLetterLink.isValidCoverLetterLink("https://example.com/if.you.dream.it_you.can.do.it")); + // more than one period in domain + assertTrue(CoverLetterLink.isValidCoverLetterLink("https://u.nus.edu/coverletter")); + } +} diff --git a/src/test/java/seedu/address/model/documents/DocumentsTest.java b/src/test/java/seedu/address/model/documents/DocumentsTest.java new file mode 100644 index 00000000000..aa69d4625cf --- /dev/null +++ b/src/test/java/seedu/address/model/documents/DocumentsTest.java @@ -0,0 +1,44 @@ +package seedu.address.model.documents; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static seedu.address.logic.commands.CommandTestUtil.VALID_COVER_LETTER_LINK_NETFLIX; +import static seedu.address.logic.commands.CommandTestUtil.VALID_RESUME_LINK_NETFLIX; +import static seedu.address.testutil.TypicalDocuments.DOCUMENTS_GOOGLE; +import static seedu.address.testutil.TypicalDocuments.DOCUMENTS_NETFLIX; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.DocumentsBuilder; + +public class DocumentsTest { + + @Test + public void equals() { + // same values -> returns true + Documents googleCopy = new DocumentsBuilder(DOCUMENTS_GOOGLE).build(); + assertEquals(DOCUMENTS_GOOGLE, googleCopy); + + // same object -> returns true + assertEquals(DOCUMENTS_GOOGLE, DOCUMENTS_GOOGLE); + + // null -> returns false + assertNotEquals(null, DOCUMENTS_GOOGLE); + + // different type -> returns false + assertNotEquals(5, DOCUMENTS_GOOGLE); + + // different documents -> returns false + assertNotEquals(DOCUMENTS_GOOGLE, DOCUMENTS_NETFLIX); + + // different resume link -> returns false + Documents editedDocuments = new DocumentsBuilder(DOCUMENTS_GOOGLE) + .withResumeLink(VALID_RESUME_LINK_NETFLIX).build(); + assertNotEquals(DOCUMENTS_GOOGLE, editedDocuments); + + // different cover letter link -> returns false + editedDocuments = new DocumentsBuilder(DOCUMENTS_GOOGLE) + .withCoverLetterLink(VALID_COVER_LETTER_LINK_NETFLIX).build(); + assertNotEquals(DOCUMENTS_GOOGLE, editedDocuments); + } +} diff --git a/src/test/java/seedu/address/model/documents/ResumeLinkTest.java b/src/test/java/seedu/address/model/documents/ResumeLinkTest.java new file mode 100644 index 00000000000..3795e7cdf2b --- /dev/null +++ b/src/test/java/seedu/address/model/documents/ResumeLinkTest.java @@ -0,0 +1,65 @@ +package seedu.address.model.documents; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class ResumeLinkTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new ResumeLink(null)); + } + + @Test + public void constructor_invalidResumeLink_throwsIllegalArgumentException() { + String invalidResumeLink = ""; + assertThrows(IllegalArgumentException.class, () -> new ResumeLink(invalidResumeLink)); + } + + @Test + public void isValidResumeLink() { + // null resume link + assertThrows(NullPointerException.class, () -> ResumeLink.isValidResumeLink(null)); + + // blank resume link + assertFalse(ResumeLink.isValidResumeLink("")); // empty string + assertFalse(ResumeLink.isValidResumeLink(" ")); // spaces only + + // missing resume link + assertFalse(ResumeLink.isValidResumeLink("example.com/resume")); // missing protocol + assertFalse(ResumeLink.isValidResumeLink("/resume")); // missing protocol and path + assertFalse(ResumeLink.isValidResumeLink("ftp://example")); // wrong protocol + + // invalid parts + assertFalse(ResumeLink.isValidResumeLink("https://-/resume")); // invalid domain name + assertFalse(ResumeLink.isValidResumeLink("https://exam_ple.com/resume")); // underscore in domain name + assertFalse(ResumeLink.isValidResumeLink("https://exam ple.com/resume")); // spaces in domain name + assertFalse(ResumeLink.isValidResumeLink("https:///example.com/resume")); // triple '/' after protocol + // domain name starts with a period + assertFalse(ResumeLink.isValidResumeLink("https://.example.com/resume")); + assertFalse(ResumeLink.isValidResumeLink("https://example.com./resume")); // domain name ends with a period + // domain name starts with a hyphen + assertFalse(ResumeLink.isValidResumeLink("https://-example.com/resume")); + assertFalse(ResumeLink.isValidResumeLink("https://example.com-/resume")); // domain name ends with a hyphen + // top level domain has less than two chars + assertFalse(ResumeLink.isValidResumeLink("https://example.c/resume")); + + // valid resume link + assertTrue(ResumeLink.isValidResumeLink("https://example.com/resume_1")); // underscore in path + assertTrue(ResumeLink.isValidResumeLink("https://example.com/resume.1")); // period in path + assertTrue(ResumeLink.isValidResumeLink("https://example.com/resume+1")); // '+' symbol in path + assertTrue(ResumeLink.isValidResumeLink("https://example.com/resume-1")); // hyphen in path + assertTrue(ResumeLink.isValidResumeLink("https://example-1.com/resume-1")); // hyphen in domain name + assertTrue(ResumeLink.isValidResumeLink("http://example.com/resume")); // alphabets only + assertTrue(ResumeLink.isValidResumeLink("http://12.34.56.78/9")); // IP address and path + // mixture of alphanumeric and special characters + assertTrue(ResumeLink.isValidResumeLink("http://example1.com/a1+be.")); + // long domain name + assertTrue(ResumeLink.isValidResumeLink("https://very-very-very-long-example.com/resume_new")); + assertTrue(ResumeLink.isValidResumeLink("https://example.com/if.you.dream.it_you.can.do.it")); // long path + assertTrue(ResumeLink.isValidResumeLink("https://u.nus.edu/resume")); // more than one period in domain + } +} diff --git a/src/test/java/seedu/address/model/person/AddressTest.java b/src/test/java/seedu/address/model/person/AddressTest.java deleted file mode 100644 index dcd3be87b3a..00000000000 --- a/src/test/java/seedu/address/model/person/AddressTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class AddressTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Address(null)); - } - - @Test - public void constructor_invalidAddress_throwsIllegalArgumentException() { - String invalidAddress = ""; - assertThrows(IllegalArgumentException.class, () -> new Address(invalidAddress)); - } - - @Test - public void isValidAddress() { - // null address - assertThrows(NullPointerException.class, () -> Address.isValidAddress(null)); - - // invalid addresses - assertFalse(Address.isValidAddress("")); // empty string - assertFalse(Address.isValidAddress(" ")); // spaces only - - // valid addresses - assertTrue(Address.isValidAddress("Blk 456, Den Road, #01-355")); - assertTrue(Address.isValidAddress("-")); // one character - assertTrue(Address.isValidAddress("Leng Inc; 1234 Market St; San Francisco CA 2349879; USA")); // long address - } -} diff --git a/src/test/java/seedu/address/model/person/NameTest.java b/src/test/java/seedu/address/model/person/NameTest.java deleted file mode 100644 index c9801392874..00000000000 --- a/src/test/java/seedu/address/model/person/NameTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class NameTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Name(null)); - } - - @Test - public void constructor_invalidName_throwsIllegalArgumentException() { - String invalidName = ""; - assertThrows(IllegalArgumentException.class, () -> new Name(invalidName)); - } - - @Test - public void isValidName() { - // null name - assertThrows(NullPointerException.class, () -> Name.isValidName(null)); - - // invalid name - assertFalse(Name.isValidName("")); // empty string - assertFalse(Name.isValidName(" ")); // spaces only - assertFalse(Name.isValidName("^")); // only non-alphanumeric characters - assertFalse(Name.isValidName("peter*")); // contains non-alphanumeric characters - - // valid name - assertTrue(Name.isValidName("peter jack")); // alphabets only - assertTrue(Name.isValidName("12345")); // numbers only - assertTrue(Name.isValidName("peter the 2nd")); // alphanumeric characters - assertTrue(Name.isValidName("Capital Tan")); // with capital letters - assertTrue(Name.isValidName("David Roger Jackson Ray Jr 2nd")); // long names - } -} diff --git a/src/test/java/seedu/address/model/person/PersonTest.java b/src/test/java/seedu/address/model/person/PersonTest.java deleted file mode 100644 index b29c097cfd4..00000000000 --- a/src/test/java/seedu/address/model/person/PersonTest.java +++ /dev/null @@ -1,91 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BOB; - -import org.junit.jupiter.api.Test; - -import seedu.address.testutil.PersonBuilder; - -public class PersonTest { - - @Test - public void asObservableList_modifyList_throwsUnsupportedOperationException() { - Person person = new PersonBuilder().build(); - assertThrows(UnsupportedOperationException.class, () -> person.getTags().remove(0)); - } - - @Test - public void isSamePerson() { - // same object -> returns true - assertTrue(ALICE.isSamePerson(ALICE)); - - // null -> returns false - assertFalse(ALICE.isSamePerson(null)); - - // same name, all other attributes different -> returns true - Person editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB) - .withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND).build(); - assertTrue(ALICE.isSamePerson(editedAlice)); - - // different name, all other attributes same -> returns false - editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build(); - assertFalse(ALICE.isSamePerson(editedAlice)); - - // name differs in case, all other attributes same -> returns false - Person editedBob = new PersonBuilder(BOB).withName(VALID_NAME_BOB.toLowerCase()).build(); - assertFalse(BOB.isSamePerson(editedBob)); - - // name has trailing spaces, all other attributes same -> returns false - String nameWithTrailingSpaces = VALID_NAME_BOB + " "; - editedBob = new PersonBuilder(BOB).withName(nameWithTrailingSpaces).build(); - assertFalse(BOB.isSamePerson(editedBob)); - } - - @Test - public void equals() { - // same values -> returns true - Person aliceCopy = new PersonBuilder(ALICE).build(); - assertTrue(ALICE.equals(aliceCopy)); - - // same object -> returns true - assertTrue(ALICE.equals(ALICE)); - - // null -> returns false - assertFalse(ALICE.equals(null)); - - // different type -> returns false - assertFalse(ALICE.equals(5)); - - // different person -> returns false - assertFalse(ALICE.equals(BOB)); - - // different name -> returns false - Person editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different phone -> returns false - editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different email -> returns false - editedAlice = new PersonBuilder(ALICE).withEmail(VALID_EMAIL_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different address -> returns false - editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different tags -> returns false - editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND).build(); - assertFalse(ALICE.equals(editedAlice)); - } -} diff --git a/src/test/java/seedu/address/model/person/UniquePersonListTest.java b/src/test/java/seedu/address/model/person/UniquePersonListTest.java deleted file mode 100644 index 1cc5fe9e0fe..00000000000 --- a/src/test/java/seedu/address/model/person/UniquePersonListTest.java +++ /dev/null @@ -1,170 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BOB; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; -import seedu.address.testutil.PersonBuilder; - -public class UniquePersonListTest { - - private final UniquePersonList uniquePersonList = new UniquePersonList(); - - @Test - public void contains_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.contains(null)); - } - - @Test - public void contains_personNotInList_returnsFalse() { - assertFalse(uniquePersonList.contains(ALICE)); - } - - @Test - public void contains_personInList_returnsTrue() { - uniquePersonList.add(ALICE); - assertTrue(uniquePersonList.contains(ALICE)); - } - - @Test - public void contains_personWithSameIdentityFieldsInList_returnsTrue() { - uniquePersonList.add(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - assertTrue(uniquePersonList.contains(editedAlice)); - } - - @Test - public void add_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.add(null)); - } - - @Test - public void add_duplicatePerson_throwsDuplicatePersonException() { - uniquePersonList.add(ALICE); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.add(ALICE)); - } - - @Test - public void setPerson_nullTargetPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPerson(null, ALICE)); - } - - @Test - public void setPerson_nullEditedPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPerson(ALICE, null)); - } - - @Test - public void setPerson_targetPersonNotInList_throwsPersonNotFoundException() { - assertThrows(PersonNotFoundException.class, () -> uniquePersonList.setPerson(ALICE, ALICE)); - } - - @Test - public void setPerson_editedPersonIsSamePerson_success() { - uniquePersonList.add(ALICE); - uniquePersonList.setPerson(ALICE, ALICE); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(ALICE); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPerson_editedPersonHasSameIdentity_success() { - uniquePersonList.add(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - uniquePersonList.setPerson(ALICE, editedAlice); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(editedAlice); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPerson_editedPersonHasDifferentIdentity_success() { - uniquePersonList.add(ALICE); - uniquePersonList.setPerson(ALICE, BOB); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(BOB); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPerson_editedPersonHasNonUniqueIdentity_throwsDuplicatePersonException() { - uniquePersonList.add(ALICE); - uniquePersonList.add(BOB); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.setPerson(ALICE, BOB)); - } - - @Test - public void remove_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.remove(null)); - } - - @Test - public void remove_personDoesNotExist_throwsPersonNotFoundException() { - assertThrows(PersonNotFoundException.class, () -> uniquePersonList.remove(ALICE)); - } - - @Test - public void remove_existingPerson_removesPerson() { - uniquePersonList.add(ALICE); - uniquePersonList.remove(ALICE); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPersons_nullUniquePersonList_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((UniquePersonList) null)); - } - - @Test - public void setPersons_uniquePersonList_replacesOwnListWithProvidedUniquePersonList() { - uniquePersonList.add(ALICE); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(BOB); - uniquePersonList.setPersons(expectedUniquePersonList); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPersons_nullList_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((List) null)); - } - - @Test - public void setPersons_list_replacesOwnListWithProvidedList() { - uniquePersonList.add(ALICE); - List personList = Collections.singletonList(BOB); - uniquePersonList.setPersons(personList); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(BOB); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPersons_listWithDuplicatePersons_throwsDuplicatePersonException() { - List listWithDuplicatePersons = Arrays.asList(ALICE, ALICE); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.setPersons(listWithDuplicatePersons)); - } - - @Test - public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () - -> uniquePersonList.asUnmodifiableObservableList().remove(0)); - } -} diff --git a/src/test/java/seedu/address/model/tag/TagTest.java b/src/test/java/seedu/address/model/tag/TagTest.java index 64d07d79ee2..1675fdc05a8 100644 --- a/src/test/java/seedu/address/model/tag/TagTest.java +++ b/src/test/java/seedu/address/model/tag/TagTest.java @@ -1,26 +1,26 @@ -package seedu.address.model.tag; - -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class TagTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Tag(null)); - } - - @Test - public void constructor_invalidTagName_throwsIllegalArgumentException() { - String invalidTagName = ""; - assertThrows(IllegalArgumentException.class, () -> new Tag(invalidTagName)); - } - - @Test - public void isValidTagName() { - // null tag name - assertThrows(NullPointerException.class, () -> Tag.isValidTagName(null)); - } - -} +//package seedu.address.model.tag; +// +//import static seedu.address.testutil.Assert.assertThrows; +// +//import org.junit.jupiter.api.Test; +// +//public class TagTest { +// +// @Test +// public void constructor_null_throwsNullPointerException() { +// assertThrows(NullPointerException.class, () -> new Tag(null)); +// } +// +// @Test +// public void constructor_invalidTagName_throwsIllegalArgumentException() { +// String invalidTagName = ""; +// assertThrows(IllegalArgumentException.class, () -> new Tag(invalidTagName)); +// } +// +// @Test +// public void isValidTagName() { +// // null tag name +// assertThrows(NullPointerException.class, () -> Tag.isValidTagName(null)); +// } +// +//} diff --git a/src/test/java/seedu/address/model/tag/TaskTypeTest.java b/src/test/java/seedu/address/model/tag/TaskTypeTest.java new file mode 100644 index 00000000000..306f4fe7d75 --- /dev/null +++ b/src/test/java/seedu/address/model/tag/TaskTypeTest.java @@ -0,0 +1,32 @@ +package seedu.address.model.tag; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +/** + * A test class to test the input validity in the enum class {@code TaskType}. + */ +public class TaskTypeTest { + + @Test + public void validTodoType_evaluateToTrue() { + assertTrue(TaskType.isValidTodo("TODO")); + } + + @Test + public void invalidTodoType_evaluateToFalse() { + assertFalse(TaskType.isValidTodo("any")); + } + + @Test + public void validNoteType_evaluateToTrue() { + assertTrue(TaskType.isValidNote("NOTE")); + } + + @Test + public void invalidNoteType_evaluateToFalse() { + assertFalse(TaskType.isValidNote("keep blank")); + } +} diff --git a/src/test/java/seedu/address/model/task/ApplicationDeadlineTest.java b/src/test/java/seedu/address/model/task/ApplicationDeadlineTest.java new file mode 100644 index 00000000000..dec20e5eaa8 --- /dev/null +++ b/src/test/java/seedu/address/model/task/ApplicationDeadlineTest.java @@ -0,0 +1,44 @@ +package seedu.address.model.task; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.LocalDate; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.InternshipTodoBuilder; + +/** + * Contains tests for {@code ApplicationDeadline}. + */ +public class ApplicationDeadlineTest { + private final InternshipTodo todoTester = new InternshipTodoBuilder().build(); + private final ApplicationDeadline ad = todoTester.getDeadline(); + + @Test + public void isValidDate_returnsTrue() { + assertTrue(ApplicationDeadline.isValidDate( + LocalDate.of(2100, 3, 4))); + } + + @Test + public void getDeadline_getSuccessful() { + assertEquals(ad.getDeadline(), ad.applicationDeadline); + } + + @Test + public void hashCode_sameHashTrue() { + assertEquals(ad.hashCode(), ad.applicationDeadline.hashCode()); + } + + @Test + public void equals() { + ApplicationDeadline notAd = new ApplicationDeadline(LocalDate.of(2024, 6, 4)); + // same object -> returns true + assertTrue(ad.equals(ad)); + + assertFalse(notAd.equals(ad)); + } +} diff --git a/src/test/java/seedu/address/model/task/ContentContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/task/ContentContainsKeywordsPredicateTest.java new file mode 100644 index 00000000000..e4275bdc08d --- /dev/null +++ b/src/test/java/seedu/address/model/task/ContentContainsKeywordsPredicateTest.java @@ -0,0 +1,25 @@ +package seedu.address.model.task; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +public class ContentContainsKeywordsPredicateTest { + + @Test + public void equals() { + List l1 = new ArrayList<>(); + l1.add("1"); + List l2 = new ArrayList<>(); + l1.add("2"); + ContentContainsKeywordsPredicate k1 = new ContentContainsKeywordsPredicate(l1); + ContentContainsKeywordsPredicate k2 = new ContentContainsKeywordsPredicate(l2); + + assertTrue(k1.equals(k1)); + assertFalse(k1.equals(k2)); + } +} diff --git a/src/test/java/seedu/address/model/task/InternshipTodoTest.java b/src/test/java/seedu/address/model/task/InternshipTodoTest.java new file mode 100644 index 00000000000..1b37b6f5f05 --- /dev/null +++ b/src/test/java/seedu/address/model/task/InternshipTodoTest.java @@ -0,0 +1,56 @@ +package seedu.address.model.task; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Objects; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.InternshipTodoBuilder; +import seedu.address.testutil.NoteBuilder; + +/** + * Contains tests for {@code InternshipTodo}. + */ +public class InternshipTodoTest { + private final InternshipTodo todoTester = new InternshipTodoBuilder().build(); + + @Test + public void newTodo_nullNote_createSuccessful() { + InternshipTodo basicTodoTester = new InternshipTodoBuilder().basicBuild(); + InternshipTodo todoBuild = new InternshipTodo(basicTodoTester.getInternshipTitle(), + basicTodoTester.getJobTitle(), basicTodoTester.getDeadline(), basicTodoTester.getDate(), + basicTodoTester.getType()); + assertEquals(basicTodoTester, todoBuild); + } + + @Test + public void getJsonDeadline_getSuccessful() { + assertEquals(todoTester.getJsonDeadline(), "2025-03-04"); + } + + @Test + public void getJsonDate_getSuccessful() { + assertEquals(todoTester.getJsonDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd").format(LocalDate.now())); + } + + @Test + public void getDateString_getSuccessful() { + assertEquals(todoTester.getDateString(), + DateTimeFormatter.ofPattern("dd MMM yyyy, EEEE").format(LocalDate.now())); + } + + @Test + public void getTodoHash_getSuccessful() { + assertEquals(todoTester.hashCode(), Objects.hash(todoTester.getInternshipTitle(), todoTester.getJobTitle(), + todoTester.getDeadline(), todoTester.getNote(), todoTester.getDate())); + } + + @Test + public void equals_testNote_alertDifferentClass() { + assertFalse(todoTester.equals(new NoteBuilder().build())); + } +} diff --git a/src/test/java/seedu/address/model/task/NoteTest.java b/src/test/java/seedu/address/model/task/NoteTest.java new file mode 100644 index 00000000000..2ff6ece30a2 --- /dev/null +++ b/src/test/java/seedu/address/model/task/NoteTest.java @@ -0,0 +1,48 @@ +package seedu.address.model.task; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Objects; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.InternshipTodoBuilder; +import seedu.address.testutil.NoteBuilder; + +/** + * Contains tests for {@code Note}. + */ +public class NoteTest { + + private final Note noteTester = new NoteBuilder().build(); + + @Test + public void getJsonDate_getSuccessful() { + assertEquals(noteTester.getJsonDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd").format(LocalDate.now())); + } + + @Test + public void getDateString_getSuccessful() { + assertEquals(noteTester.getDateString(), + DateTimeFormatter.ofPattern("dd MMM yyyy, EEEE").format(LocalDate.now())); + } + + @Test + public void setNote_setSuccessful() { + noteTester.setNote(new NoteContent("This is only a small test!")); + assertEquals(noteTester.getNote().content, "This is only a small test!"); + } + + @Test + public void hashCode_sameHashGenerated() { + assertEquals(noteTester.hashCode(), Objects.hash(noteTester.getNote(), noteTester.getDate())); + } + + @Test + public void equals_alertDifferentClass() { + assertNotEquals(noteTester, new InternshipTodoBuilder().build()); + } +} diff --git a/src/test/java/seedu/address/model/task/TitleContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/task/TitleContainsKeywordsPredicateTest.java new file mode 100644 index 00000000000..b94e26ad5a1 --- /dev/null +++ b/src/test/java/seedu/address/model/task/TitleContainsKeywordsPredicateTest.java @@ -0,0 +1,25 @@ +package seedu.address.model.task; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +public class TitleContainsKeywordsPredicateTest { + + @Test + public void equals() { + List l1 = new ArrayList<>(); + l1.add("1"); + List l2 = new ArrayList<>(); + l1.add("2"); + TitleContainsKeywordsPredicate k1 = new TitleContainsKeywordsPredicate(l1); + TitleContainsKeywordsPredicate k2 = new TitleContainsKeywordsPredicate(l2); + + assertTrue(k1.equals(k1)); + assertFalse(k1.equals(k2)); + } +} diff --git a/src/test/java/seedu/address/model/task/UniqueNoteListTest.java b/src/test/java/seedu/address/model/task/UniqueNoteListTest.java new file mode 100644 index 00000000000..cbaf59cc48f --- /dev/null +++ b/src/test/java/seedu/address/model/task/UniqueNoteListTest.java @@ -0,0 +1,82 @@ +package seedu.address.model.task; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.task.exceptions.DuplicateNoteException; +import seedu.address.model.task.exceptions.NoteNotFoundException; +import seedu.address.testutil.NoteBuilder; + +/** + * Contains tests for {@code UniqueNoteList}. + */ +public class UniqueNoteListTest { + + private final UniqueNoteList uniqueNoteList = new UniqueNoteList(); + + @Test + public void addNote_duplicateNotes_alertDuplicates() { + uniqueNoteList.addNote(new NoteBuilder().build()); + assertThrows(DuplicateNoteException.class, () -> { + uniqueNoteList.addNote(new NoteBuilder().build()); + }); + } + + @Test + public void setNotes_newNotes_setSuccessful() { + uniqueNoteList.addNote(new NoteBuilder().withNote("test 1").build()); + uniqueNoteList.setNotes(new NoteBuilder().withNote("test 1").build(), + new NoteBuilder().withNote("test 2").build()); + assertTrue(uniqueNoteList.containsNote(new NoteBuilder().withNote("test 2").build())); + assertFalse(uniqueNoteList.containsNote(new NoteBuilder().withNote("test 1").build())); + } + + @Test + public void setNotes_duplicateNotes_alertDuplicates() { + uniqueNoteList.addNote(new NoteBuilder().withNote("test 1").build()); + uniqueNoteList.addNote(new NoteBuilder().withNote("test 2").build()); + assertThrows(DuplicateNoteException.class, () -> { + uniqueNoteList.setNotes(new NoteBuilder().withNote("test 2").build(), + new NoteBuilder().withNote("test 1").build()); + }); + } + + @Test + public void setNotes_noteNotExists_alertNotFound() { + uniqueNoteList.addNote(new NoteBuilder().withNote("test 1").build()); + assertThrows(NoteNotFoundException.class, () -> { + uniqueNoteList.setNotes(new NoteBuilder().withNote("test 2").build(), + new NoteBuilder().withNote("test 1").build()); + }); + } + + @Test + public void setNotes_noteList_setSuccessful() { + uniqueNoteList.addNote(new NoteBuilder().withNote("test 1").build()); + UniqueNoteList replacement = new UniqueNoteList(); + replacement.addNote(new NoteBuilder().withNote("repl 1").build()); + replacement.addNote(new NoteBuilder().withNote("repl 2").build()); + uniqueNoteList.setNotes(replacement); + assertEquals(uniqueNoteList, replacement); + } + + @Test + public void remove_notes_alertNotFound() { + uniqueNoteList.addNote(new NoteBuilder().withNote("test 1").build()); + assertThrows(NoteNotFoundException.class, () -> { + uniqueNoteList.remove(new NoteBuilder().withNote("test 2").build()); + }); + } + + @Test + public void equals() { + UniqueNoteList replacement = new UniqueNoteList(); + replacement.addNote(new NoteBuilder().withNote("Different").build()); + assertTrue(replacement.equals(replacement)); + assertFalse(replacement.equals(uniqueNoteList)); + } +} diff --git a/src/test/java/seedu/address/model/task/UniqueTodoListTest.java b/src/test/java/seedu/address/model/task/UniqueTodoListTest.java new file mode 100644 index 00000000000..4ce04bbfd1f --- /dev/null +++ b/src/test/java/seedu/address/model/task/UniqueTodoListTest.java @@ -0,0 +1,74 @@ +package seedu.address.model.task; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.task.exceptions.DuplicateTodoException; +import seedu.address.model.task.exceptions.TodoNotFoundException; +import seedu.address.testutil.InternshipTodoBuilder; + +/** + * Contains tests for {@code UniqueTodoList}. + */ +public class UniqueTodoListTest { + + private final UniqueTodoList uniqueTodoList = new UniqueTodoList(); + + @Test + public void addTodo_duplicateTodos_alertDuplicates() { + uniqueTodoList.addTodo(new InternshipTodoBuilder().build()); + assertThrows(DuplicateTodoException.class, () -> { + uniqueTodoList.addTodo(new InternshipTodoBuilder().build()); + }); + } + + @Test + public void setTodo_duplicateTodos_alertDuplicates() { + uniqueTodoList.addTodo(new InternshipTodoBuilder().withJobTitle("test 1").build()); + uniqueTodoList.addTodo(new InternshipTodoBuilder().withJobTitle("test 2").build()); + assertThrows(DuplicateTodoException.class, () -> { + uniqueTodoList.setTodo(new InternshipTodoBuilder().withJobTitle("test 2").build(), + new InternshipTodoBuilder().withJobTitle("test 1").build()); + }); + } + + @Test + public void setTodo_todoExists_alertNotFound() { + uniqueTodoList.addTodo(new InternshipTodoBuilder().withJobTitle("test 1").build()); + assertThrows(TodoNotFoundException.class, () -> { + uniqueTodoList.setTodo(new InternshipTodoBuilder().withJobTitle("test 2").build(), + new InternshipTodoBuilder().withJobTitle("test 1").build()); + }); + } + + @Test + public void setTodo_todoList_setSuccessful() { + uniqueTodoList.addTodo(new InternshipTodoBuilder().withJobTitle("test 1").build()); + UniqueTodoList replacement = new UniqueTodoList(); + replacement.addTodo(new InternshipTodoBuilder().withJobTitle("repl 1").build()); + replacement.addTodo(new InternshipTodoBuilder().withJobTitle("repl 2").build()); + uniqueTodoList.setTodo(replacement); + assertEquals(uniqueTodoList, replacement); + } + + @Test + public void remove_todoNotExists_alertNotFound() { + uniqueTodoList.addTodo(new InternshipTodoBuilder().withJobTitle("test 1").build()); + assertThrows(TodoNotFoundException.class, () -> { + uniqueTodoList.remove(new InternshipTodoBuilder().withJobTitle("test 2").build()); + }); + } + + @Test + public void equals() { + UniqueTodoList replacement = new UniqueTodoList(); + replacement.addTodo(new InternshipTodoBuilder().withJobTitle("Different").build()); + + assertTrue(replacement.equals(replacement)); + assertFalse(replacement.equals(uniqueTodoList)); + } +} diff --git a/src/test/java/seedu/address/storage/JsonAdaptedInternshipApplicationTest.java b/src/test/java/seedu/address/storage/JsonAdaptedInternshipApplicationTest.java new file mode 100644 index 00000000000..04f68c8bf2a --- /dev/null +++ b/src/test/java/seedu/address/storage/JsonAdaptedInternshipApplicationTest.java @@ -0,0 +1,129 @@ +package seedu.address.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.address.storage.JsonAdaptedInternshipApplication.MISSING_FIELD_MESSAGE_FORMAT; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalInternships.META; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.contact.Contact; +import seedu.address.model.contact.Email; +import seedu.address.model.contact.Phone; + +public class JsonAdaptedInternshipApplicationTest { + + private static final String INVALID_COMPANY_NAME = "R@chel"; + private static final String INVALID_JOB_TITLE = "soft^^engineer"; + private static final String INVALID_PHONE = " "; + private static final String INVALID_EMAIL = "example.com"; + private static final List INVALID_CONTACT = Arrays.asList(INVALID_PHONE); + + private static final String VALID_COMPANY_NAME = META.getCompanyName().toString(); + private static final String VALID_JOB_TITLE = META.getJobTitle().toString(); + private static final String VALID_PHONE = META.getContact().getPhone().toString(); + private static final String VALID_EMAIL = META.getContact().getEmail().toString(); + private static final List VALID_CONTACT = Arrays.asList(VALID_PHONE, VALID_EMAIL); + + private static final List INVALID_CONTACT_PHONE = Arrays.asList(INVALID_PHONE, VALID_EMAIL); + private static final List INVALID_CONTACT_EMAIL = Arrays.asList(VALID_PHONE, INVALID_EMAIL); + + private static final List PLACEHOLDER_EMPTY_LIST = Collections.emptyList(); + private static final String PLACEHOLDER_EMPTY_STRING = ""; + + @Test + public void toModelType_validInternshipApplicationDetails_returnsInternshipApplication() throws Exception { + JsonAdaptedInternshipApplication application = new JsonAdaptedInternshipApplication(META); + assertEquals(META, application.toModelType()); + } + + @Test + public void toModelType_invalidCompanyName_throwsIllegalValueException() { + JsonAdaptedInternshipApplication internshipApplication = + new JsonAdaptedInternshipApplication(INVALID_COMPANY_NAME, + VALID_JOB_TITLE, PLACEHOLDER_EMPTY_LIST, PLACEHOLDER_EMPTY_LIST, PLACEHOLDER_EMPTY_LIST, + PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_LIST, + PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_LIST, "PENDING", false, null, + PLACEHOLDER_EMPTY_LIST, PLACEHOLDER_EMPTY_LIST); + String expectedMessage = CompanyName.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, internshipApplication::toModelType); + } + + @Test + public void toModelType_nullCompanyName_throwsIllegalValueException() { + JsonAdaptedInternshipApplication application = new JsonAdaptedInternshipApplication(null, + VALID_JOB_TITLE, PLACEHOLDER_EMPTY_LIST, PLACEHOLDER_EMPTY_LIST, PLACEHOLDER_EMPTY_LIST, + PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_LIST, + PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_LIST, "PENDING", false, null, + PLACEHOLDER_EMPTY_LIST, PLACEHOLDER_EMPTY_LIST); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, CompanyName.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, application::toModelType); + } + + @Test + public void toModelType_invalidStatus_throwsIllegalValueException() { + JsonAdaptedInternshipApplication application = + new JsonAdaptedInternshipApplication(VALID_COMPANY_NAME, + VALID_JOB_TITLE, PLACEHOLDER_EMPTY_LIST, PLACEHOLDER_EMPTY_LIST, PLACEHOLDER_EMPTY_LIST, + PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_LIST, + PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_LIST, "INVALIDStatus", false, null, + VALID_CONTACT, PLACEHOLDER_EMPTY_LIST); + String expectedMessage = InternshipStatus.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, application::toModelType); + } + + @Test + public void toModelType_nullStatus_throwsIllegalValueException() { + JsonAdaptedInternshipApplication application = + new JsonAdaptedInternshipApplication(VALID_COMPANY_NAME, + VALID_JOB_TITLE, PLACEHOLDER_EMPTY_LIST, PLACEHOLDER_EMPTY_LIST, PLACEHOLDER_EMPTY_LIST, + PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_LIST, + PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_LIST, null, false, null, + VALID_CONTACT, PLACEHOLDER_EMPTY_LIST); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, InternshipStatus.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, application::toModelType); + } + + @Test + public void toModelType_invalidContact_throwsIllegalValueException() { + JsonAdaptedInternshipApplication application = + new JsonAdaptedInternshipApplication(VALID_COMPANY_NAME, + VALID_JOB_TITLE, PLACEHOLDER_EMPTY_LIST, PLACEHOLDER_EMPTY_LIST, PLACEHOLDER_EMPTY_LIST, + PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_LIST, + PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_LIST, "PENDING", false, null, + INVALID_CONTACT, PLACEHOLDER_EMPTY_LIST); + String expectedMessage = Contact.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, application::toModelType); + } + + @Test + public void toModelType_invalidContactPhone_throwsIllegalValueException() { + JsonAdaptedInternshipApplication application = + new JsonAdaptedInternshipApplication(VALID_COMPANY_NAME, + VALID_JOB_TITLE, PLACEHOLDER_EMPTY_LIST, PLACEHOLDER_EMPTY_LIST, PLACEHOLDER_EMPTY_LIST, + PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_LIST, + PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_LIST, "PENDING", false, null, + INVALID_CONTACT_PHONE, PLACEHOLDER_EMPTY_LIST); + String expectedMessage = Phone.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, application::toModelType); + } + + @Test + public void toModelType_invalidContactEmail_throwsIllegalValueException() { + JsonAdaptedInternshipApplication application = + new JsonAdaptedInternshipApplication(VALID_COMPANY_NAME, + VALID_JOB_TITLE, PLACEHOLDER_EMPTY_LIST, PLACEHOLDER_EMPTY_LIST, PLACEHOLDER_EMPTY_LIST, + PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_LIST, + PLACEHOLDER_EMPTY_STRING, PLACEHOLDER_EMPTY_LIST, "PENDING", false, null, + INVALID_CONTACT_EMAIL, PLACEHOLDER_EMPTY_LIST); + String expectedMessage = Email.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, application::toModelType); + } +} diff --git a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java b/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java deleted file mode 100644 index 83b11331cdb..00000000000 --- a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package seedu.address.storage; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.storage.JsonAdaptedPerson.MISSING_FIELD_MESSAGE_FORMAT; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.BENSON; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; - -public class JsonAdaptedPersonTest { - private static final String INVALID_NAME = "R@chel"; - private static final String INVALID_PHONE = "+651234"; - private static final String INVALID_ADDRESS = " "; - private static final String INVALID_EMAIL = "example.com"; - private static final String INVALID_TAG = "#friend"; - - private static final String VALID_NAME = BENSON.getName().toString(); - private static final String VALID_PHONE = BENSON.getPhone().toString(); - private static final String VALID_EMAIL = BENSON.getEmail().toString(); - private static final String VALID_ADDRESS = BENSON.getAddress().toString(); - private static final List VALID_TAGS = BENSON.getTags().stream() - .map(JsonAdaptedTag::new) - .collect(Collectors.toList()); - - @Test - public void toModelType_validPersonDetails_returnsPerson() throws Exception { - JsonAdaptedPerson person = new JsonAdaptedPerson(BENSON); - assertEquals(BENSON, person.toModelType()); - } - - @Test - public void toModelType_invalidName_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(INVALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = Name.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullName_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(null, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidPhone_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, INVALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = Phone.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullPhone_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, null, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidEmail_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, INVALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = Email.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullEmail_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, null, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidAddress_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, INVALID_ADDRESS, VALID_TAGS); - String expectedMessage = Address.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullAddress_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, null, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidTags_throwsIllegalValueException() { - List invalidTags = new ArrayList<>(VALID_TAGS); - invalidTags.add(new JsonAdaptedTag(INVALID_TAG)); - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, invalidTags); - assertThrows(IllegalValueException.class, person::toModelType); - } - -} diff --git a/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java b/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java index ac3c3af9566..f2d34480965 100644 --- a/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java +++ b/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java @@ -3,10 +3,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.HOON; -import static seedu.address.testutil.TypicalPersons.IDA; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalInternships.LINKEDIN; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; import java.io.IOException; import java.nio.file.Path; @@ -57,7 +55,8 @@ public void readAddressBook_invalidPersonAddressBook_throwDataConversionExceptio @Test public void readAddressBook_invalidAndValidPersonAddressBook_throwDataConversionException() { - assertThrows(DataConversionException.class, () -> readAddressBook("invalidAndValidPersonAddressBook.json")); + assertThrows(DataConversionException.class, () -> + readAddressBook("invalidAndValidPersonAddressBook.json")); } @Test @@ -72,14 +71,14 @@ public void readAndSaveAddressBook_allInOrder_success() throws Exception { assertEquals(original, new AddressBook(readBack)); // Modify data, overwrite exiting file, and read back - original.addPerson(HOON); - original.removePerson(ALICE); + original.addApplication(LINKEDIN); + original.removeApplication(LINKEDIN); jsonAddressBookStorage.saveAddressBook(original, filePath); readBack = jsonAddressBookStorage.readAddressBook(filePath).get(); assertEquals(original, new AddressBook(readBack)); // Save and read without specifying file path - original.addPerson(IDA); + original.addApplication(LINKEDIN); jsonAddressBookStorage.saveAddressBook(original); // file path not specified readBack = jsonAddressBookStorage.readAddressBook().get(); // file path not specified assertEquals(original, new AddressBook(readBack)); diff --git a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java b/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java deleted file mode 100644 index 188c9058d20..00000000000 --- a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package seedu.address.storage; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.testutil.Assert.assertThrows; - -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.AddressBook; -import seedu.address.testutil.TypicalPersons; - -public class JsonSerializableAddressBookTest { - - private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonSerializableAddressBookTest"); - private static final Path TYPICAL_PERSONS_FILE = TEST_DATA_FOLDER.resolve("typicalPersonsAddressBook.json"); - private static final Path INVALID_PERSON_FILE = TEST_DATA_FOLDER.resolve("invalidPersonAddressBook.json"); - private static final Path DUPLICATE_PERSON_FILE = TEST_DATA_FOLDER.resolve("duplicatePersonAddressBook.json"); - - @Test - public void toModelType_typicalPersonsFile_success() throws Exception { - JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(TYPICAL_PERSONS_FILE, - JsonSerializableAddressBook.class).get(); - AddressBook addressBookFromFile = dataFromFile.toModelType(); - AddressBook typicalPersonsAddressBook = TypicalPersons.getTypicalAddressBook(); - assertEquals(addressBookFromFile, typicalPersonsAddressBook); - } - - @Test - public void toModelType_invalidPersonFile_throwsIllegalValueException() throws Exception { - JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(INVALID_PERSON_FILE, - JsonSerializableAddressBook.class).get(); - assertThrows(IllegalValueException.class, dataFromFile::toModelType); - } - - @Test - public void toModelType_duplicatePersons_throwsIllegalValueException() throws Exception { - JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(DUPLICATE_PERSON_FILE, - JsonSerializableAddressBook.class).get(); - assertThrows(IllegalValueException.class, JsonSerializableAddressBook.MESSAGE_DUPLICATE_PERSON, - dataFromFile::toModelType); - } - -} diff --git a/src/test/java/seedu/address/storage/StorageManagerTest.java b/src/test/java/seedu/address/storage/StorageManagerTest.java index 99a16548970..8d118092802 100644 --- a/src/test/java/seedu/address/storage/StorageManagerTest.java +++ b/src/test/java/seedu/address/storage/StorageManagerTest.java @@ -2,7 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalInternships.getTypicalAddressBook; import java.nio.file.Path; @@ -14,6 +14,8 @@ import seedu.address.model.AddressBook; import seedu.address.model.ReadOnlyAddressBook; import seedu.address.model.UserPrefs; +import seedu.address.storage.task.note.JsonNoteListStorage; +import seedu.address.storage.task.todo.JsonTodoListStorage; public class StorageManagerTest { @@ -26,7 +28,9 @@ public class StorageManagerTest { public void setUp() { JsonAddressBookStorage addressBookStorage = new JsonAddressBookStorage(getTempFilePath("ab")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(getTempFilePath("prefs")); - storageManager = new StorageManager(addressBookStorage, userPrefsStorage); + JsonTodoListStorage todoListStorage = new JsonTodoListStorage(getTempFilePath("prefsTodo")); + JsonNoteListStorage noteListStorage = new JsonNoteListStorage(getTempFilePath("prefsNote")); + storageManager = new StorageManager(addressBookStorage, userPrefsStorage, todoListStorage, noteListStorage); } private Path getTempFilePath(String fileName) { @@ -36,7 +40,7 @@ private Path getTempFilePath(String fileName) { @Test public void prefsReadSave() throws Exception { /* - * Note: This is an integration test that verifies the StorageManager is properly wired to the + * NoteList: This is an integration test that verifies the StorageManager is properly wired to the * {@link JsonUserPrefsStorage} class. * More extensive testing of UserPref saving/reading is done in {@link JsonUserPrefsStorageTest} class. */ @@ -50,7 +54,7 @@ public void prefsReadSave() throws Exception { @Test public void addressBookReadSave() throws Exception { /* - * Note: This is an integration test that verifies the StorageManager is properly wired to the + * NoteList: This is an integration test that verifies the StorageManager is properly wired to the * {@link JsonAddressBookStorage} class. * More extensive testing of UserPref saving/reading is done in {@link JsonAddressBookStorageTest} class. */ @@ -61,8 +65,8 @@ public void addressBookReadSave() throws Exception { } @Test - public void getAddressBookFilePath() { - assertNotNull(storageManager.getAddressBookFilePath()); + public void getNoteListFilePath() { + assertNotNull(storageManager.getNoteListFilePath()); } } diff --git a/src/test/java/seedu/address/testutil/AddressBookBuilder.java b/src/test/java/seedu/address/testutil/AddressBookBuilder.java index d53799fd110..e54cd4c6a5a 100644 --- a/src/test/java/seedu/address/testutil/AddressBookBuilder.java +++ b/src/test/java/seedu/address/testutil/AddressBookBuilder.java @@ -1,7 +1,7 @@ package seedu.address.testutil; import seedu.address.model.AddressBook; -import seedu.address.model.person.Person; +import seedu.address.model.application.InternshipApplication; /** * A utility class to help with building Addressbook objects. @@ -21,10 +21,10 @@ public AddressBookBuilder(AddressBook addressBook) { } /** - * Adds a new {@code Person} to the {@code AddressBook} that we are building. + * Adds a new {@code InternshipApplication} to the {@code AddressBook} that we are building. */ - public AddressBookBuilder withPerson(Person person) { - addressBook.addPerson(person); + public AddressBookBuilder withApplication(InternshipApplication internshipApplication) { + addressBook.addApplication(internshipApplication); return this; } diff --git a/src/test/java/seedu/address/testutil/Assert.java b/src/test/java/seedu/address/testutil/Assert.java index 9863093bd6e..2a8851f0d94 100644 --- a/src/test/java/seedu/address/testutil/Assert.java +++ b/src/test/java/seedu/address/testutil/Assert.java @@ -10,7 +10,8 @@ public class Assert { /** * Asserts that the {@code executable} throws the {@code expectedType} Exception. - * This is a wrapper method that invokes {@link Assertions#assertThrows(Class, Executable)}, to maintain consistency + * This is a wrapper method that invokes {@link Assertions#assertThrows(Class, Executable)}, + * to maintain consistency * with our custom {@link #assertThrows(Class, String, Executable)} method. * To standardize API calls in this project, users should use this method instead of * {@link Assertions#assertThrows(Class, Executable)}. diff --git a/src/test/java/seedu/address/testutil/ContactBuilder.java b/src/test/java/seedu/address/testutil/ContactBuilder.java new file mode 100644 index 00000000000..a6205d639ae --- /dev/null +++ b/src/test/java/seedu/address/testutil/ContactBuilder.java @@ -0,0 +1,54 @@ +package seedu.address.testutil; + +import seedu.address.model.contact.Contact; +import seedu.address.model.contact.Email; +import seedu.address.model.contact.Phone; + +/** + * A utility class to help with building Contact objects. + */ +public class ContactBuilder { + + public static final String DEFAULT_PHONE = "55555555"; + public static final String DEFAULT_EMAIL = "meta@example.com"; + + private Phone phone; + private Email email; + private Contact contact; + + /** + * Creates a {@code ContactBuilder} with the default details. + */ + public ContactBuilder() { + phone = new Phone(DEFAULT_PHONE); + email = new Email(DEFAULT_EMAIL); + } + + /** + * Initializes the ContactBuilder with the data of {@code contactToCopy}. + */ + public ContactBuilder(Contact contactToCopy) { + phone = contactToCopy.getPhone(); + email = contactToCopy.getEmail(); + } + + /** + * Sets the {@code Phone} of the {@code Contact} that we are building. + */ + public ContactBuilder withPhone(String phone) { + this.phone = new seedu.address.model.contact.Phone(phone); + return this; + } + + /** + * Sets the {@code Email} of the {@code Contact} that we are building. + */ + public ContactBuilder withEmail(String email) { + this.email = new seedu.address.model.contact.Email(email); + return this; + } + + public Contact build() { + return new Contact(phone, email); + } +} diff --git a/src/test/java/seedu/address/testutil/ContactUtil.java b/src/test/java/seedu/address/testutil/ContactUtil.java new file mode 100644 index 00000000000..3a0dd09bf78 --- /dev/null +++ b/src/test/java/seedu/address/testutil/ContactUtil.java @@ -0,0 +1,40 @@ +package seedu.address.testutil; + +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; + +import seedu.address.logic.commands.contact.EditContactCommand; +import seedu.address.model.contact.Contact; + +/** + * A utility class for Contact. + */ +public class ContactUtil { + + /** + * Returns an add_contact command string for adding the {@code contact}. + */ + public static String getAddContactCommandsArguments(Contact contact) { + return getContactDetails(contact); + } + + /** + * Returns the part of command string for the given {@code contact}'s details. + */ + public static String getContactDetails(Contact contact) { + return PREFIX_PHONE + contact.getPhone().value + " " + + PREFIX_EMAIL + contact.getEmail().value; + } + + /** + * Returns the part of command string for the given {@code EditContactDescriptor}'s details. + */ + public static String getEditContactDescriptorDetails(EditContactCommand.EditContactDescriptor descriptor) { + StringBuilder sb = new StringBuilder(); + descriptor.getPhone().ifPresent(phone -> + sb.append(PREFIX_PHONE).append(phone.value).append(" ")); + descriptor.getEmail().ifPresent(email -> + sb.append(PREFIX_EMAIL).append(email.value)); + return sb.toString(); + } +} diff --git a/src/test/java/seedu/address/testutil/DocumentsBuilder.java b/src/test/java/seedu/address/testutil/DocumentsBuilder.java new file mode 100644 index 00000000000..7f33c3155e8 --- /dev/null +++ b/src/test/java/seedu/address/testutil/DocumentsBuilder.java @@ -0,0 +1,53 @@ +package seedu.address.testutil; + +import seedu.address.model.documents.CoverLetterLink; +import seedu.address.model.documents.Documents; +import seedu.address.model.documents.ResumeLink; + +/** + * A utility class to help with building Documents objects. + */ +public class DocumentsBuilder { + + public static final String DEFAULT_RESUME_LINK = "https://example.com/resume"; + public static final String DEFAULT_COVER_LETTER_LINK = "https://example.com/coverletter"; + private ResumeLink resumeLink; + private CoverLetterLink coverLetterLink; + private Documents documents; + + /** + * Creates a {@code DocumentsBuilder} with the default details. + */ + public DocumentsBuilder() { + resumeLink = new ResumeLink(DEFAULT_RESUME_LINK); + coverLetterLink = new CoverLetterLink(DEFAULT_COVER_LETTER_LINK); + } + + /** + * Initializes the DocumentsBuilder with the data of {@code documentsToCopy}. + */ + public DocumentsBuilder(Documents documentsToCopy) { + resumeLink = documentsToCopy.getResumeLink(); + coverLetterLink = documentsToCopy.getCoverLetterLink(); + } + + /** + * Sets the {@code ResumeLink} of the {@code Documents} that we are building. + */ + public DocumentsBuilder withResumeLink(String resumeLink) { + this.resumeLink = new ResumeLink(resumeLink); + return this; + } + + /** + * Sets the {@code CoverLetterLink} of the {@code Documents} that we are building. + */ + public DocumentsBuilder withCoverLetterLink(String coverLetterLink) { + this.coverLetterLink = new CoverLetterLink(coverLetterLink); + return this; + } + + public Documents build() { + return new Documents(resumeLink, coverLetterLink); + } +} diff --git a/src/test/java/seedu/address/testutil/DocumentsUtil.java b/src/test/java/seedu/address/testutil/DocumentsUtil.java new file mode 100644 index 00000000000..32d37c0e92b --- /dev/null +++ b/src/test/java/seedu/address/testutil/DocumentsUtil.java @@ -0,0 +1,40 @@ +package seedu.address.testutil; + +import static seedu.address.logic.parser.CliSyntax.PREFIX_COVER_LETTER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RESUME; + +import seedu.address.logic.commands.documents.EditDocumentsCommand; +import seedu.address.model.documents.Documents; + +/** + * A utility class for Documents. + */ +public class DocumentsUtil { + + /** + * Returns an add_docs command string for adding the {@code documents}. + */ + public static String getAddDocumentsCommandArguments(Documents documents) { + return getDocumentsDetails(documents); + } + + /** + * Returns the part of command string for the given {@code documents}'s details. + */ + public static String getDocumentsDetails(Documents documents) { + return PREFIX_RESUME + documents.getResumeLink().value + " " + + PREFIX_COVER_LETTER + documents.getCoverLetterLink().value; + } + + /** + * Returns the part of command string for the given {@code EditDocumentsDescriptor}'s details. + */ + public static String getEditDocumentsDescriptorDetails(EditDocumentsCommand.EditDocumentsDescriptor descriptor) { + StringBuilder sb = new StringBuilder(); + descriptor.getResumeLink().ifPresent(resumeLink -> + sb.append(PREFIX_RESUME).append(resumeLink.value).append(" ")); + descriptor.getCoverLetterLink().ifPresent(coverLetterLink -> + sb.append(PREFIX_COVER_LETTER).append(coverLetterLink.value)); + return sb.toString(); + } +} diff --git a/src/test/java/seedu/address/testutil/EditContactDescriptorBuilder.java b/src/test/java/seedu/address/testutil/EditContactDescriptorBuilder.java new file mode 100644 index 00000000000..ac8857f868b --- /dev/null +++ b/src/test/java/seedu/address/testutil/EditContactDescriptorBuilder.java @@ -0,0 +1,50 @@ +package seedu.address.testutil; + +import seedu.address.logic.commands.contact.EditContactCommand; +import seedu.address.model.contact.Contact; +import seedu.address.model.contact.Email; +import seedu.address.model.contact.Phone; + +/** + * A utility class to help with building EditContactDescriptor objects. + */ +public class EditContactDescriptorBuilder { + private EditContactCommand.EditContactDescriptor descriptor; + + public EditContactDescriptorBuilder() { + descriptor = new EditContactCommand.EditContactDescriptor(); + } + + public EditContactDescriptorBuilder(EditContactCommand.EditContactDescriptor descriptor) { + this.descriptor = new EditContactCommand.EditContactDescriptor(descriptor); + } + + /** + * Returns an {@code EditContactDescriptor} with fields containing {@code contact}'s details + */ + public EditContactDescriptorBuilder(Contact contact) { + descriptor = new EditContactCommand.EditContactDescriptor(); + descriptor.setPhone(contact.getPhone()); + descriptor.setEmail(contact.getEmail()); + } + + /** + * Sets the {@code Phone} of the {@code EditContactDescriptor} that we are building. + */ + public EditContactDescriptorBuilder withPhone(String phone) { + descriptor.setPhone(new Phone(phone)); + return this; + } + + /** + * Sets the {@code Email} of the {@code EditContactDescriptor} that we are building. + */ + public EditContactDescriptorBuilder withEmail(String email) { + descriptor.setEmail(new Email(email)); + return this; + } + + public EditContactCommand.EditContactDescriptor build() { + return descriptor; + } +} diff --git a/src/test/java/seedu/address/testutil/EditDocumentsDescriptorBuilder.java b/src/test/java/seedu/address/testutil/EditDocumentsDescriptorBuilder.java new file mode 100644 index 00000000000..6374d4898c8 --- /dev/null +++ b/src/test/java/seedu/address/testutil/EditDocumentsDescriptorBuilder.java @@ -0,0 +1,50 @@ +package seedu.address.testutil; + +import seedu.address.logic.commands.documents.EditDocumentsCommand; +import seedu.address.model.documents.CoverLetterLink; +import seedu.address.model.documents.Documents; +import seedu.address.model.documents.ResumeLink; + +/** + * A utility class to help with building EditDocumentsDescriptor objects. + */ +public class EditDocumentsDescriptorBuilder { + private EditDocumentsCommand.EditDocumentsDescriptor descriptor; + + public EditDocumentsDescriptorBuilder() { + descriptor = new EditDocumentsCommand.EditDocumentsDescriptor(); + } + + public EditDocumentsDescriptorBuilder(EditDocumentsCommand.EditDocumentsDescriptor descriptor) { + this.descriptor = new EditDocumentsCommand.EditDocumentsDescriptor(descriptor); + } + + /** + * Returns an {@code EditDocumentsDescriptor} with fields containing {@code documents}'s details + */ + public EditDocumentsDescriptorBuilder(Documents documents) { + descriptor = new EditDocumentsCommand.EditDocumentsDescriptor(); + descriptor.setResumeLink(documents.getResumeLink()); + descriptor.setCoverLetterLink(documents.getCoverLetterLink()); + } + + /** + * Sets the {@code ResumeLink} of the {@code EditDocumentsDescriptor} that we are building. + */ + public EditDocumentsDescriptorBuilder withResumeLink(String resumeLink) { + descriptor.setResumeLink(new ResumeLink(resumeLink)); + return this; + } + + /** + * Sets the {@code CoverLetterLink} of the {@code EditDocumentsDescriptor} that we are building. + */ + public EditDocumentsDescriptorBuilder withCoverLetterLink(String coverLetterLink) { + descriptor.setCoverLetterLink(new CoverLetterLink(coverLetterLink)); + return this; + } + + public EditDocumentsCommand.EditDocumentsDescriptor build() { + return descriptor; + } +} diff --git a/src/test/java/seedu/address/testutil/EditInternshipDescriptorBuilder.java b/src/test/java/seedu/address/testutil/EditInternshipDescriptorBuilder.java new file mode 100644 index 00000000000..acac65d0570 --- /dev/null +++ b/src/test/java/seedu/address/testutil/EditInternshipDescriptorBuilder.java @@ -0,0 +1,150 @@ +package seedu.address.testutil; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import seedu.address.logic.commands.EditCommand.EditInternshipDescriptor; +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.JobTitle; +import seedu.address.model.application.Location; +import seedu.address.model.application.Note; +import seedu.address.model.application.ProgrammingLanguage; +import seedu.address.model.application.Qualification; +import seedu.address.model.application.Rating; +import seedu.address.model.application.Reflection; +import seedu.address.model.application.Review; +import seedu.address.model.application.Salary; + +/** + * A utility class to help with building EditPersonDescriptor objects. + */ +public class EditInternshipDescriptorBuilder { + + private EditInternshipDescriptor descriptor; + + public EditInternshipDescriptorBuilder() { + descriptor = new EditInternshipDescriptor(); + } + + public EditInternshipDescriptorBuilder(EditInternshipDescriptor descriptor) { + this.descriptor = new EditInternshipDescriptor(descriptor); + } + + /** + * Returns an {@code EditInternshipDescriptor} with fields containing {@code internship}'s details + */ + public EditInternshipDescriptorBuilder(InternshipApplication internship) { + descriptor = new EditInternshipDescriptor(); + descriptor.setCompanyName(internship.getCompanyName()); + descriptor.setJobTitle(internship.getJobTitle()); + descriptor.setLocation(internship.getLocation()); + descriptor.setSalary(internship.getSalary()); + descriptor.setRating(internship.getRating()); + descriptor.setQualifications(internship.getQualifications()); + descriptor.setProgrammingLanguages(internship.getProgrammingLanguages()); + descriptor.setReviews(internship.getReviews()); + descriptor.setNotes(internship.getNotes()); + descriptor.setReflections(internship.getReflections()); + } + + /** + * Sets the {@code CompanyName} of the {@code EditInternshipDescriptor} that we are building. + */ + public EditInternshipDescriptorBuilder withCompanyName(String name) { + descriptor.setCompanyName(new CompanyName(name)); + return this; + } + + /** + * Sets the {@code JobTitle} of the {@code EditJobTitleDescriptor} that we are building. + */ + public EditInternshipDescriptorBuilder withJobTitle(String jobTitle) { + descriptor.setJobTitle(new JobTitle(jobTitle)); + return this; + } + + /** + * Sets the {@code Location} of the {@code EditInternshipDescriptor} that we are building. + */ + public EditInternshipDescriptorBuilder withLocation(String location) { + descriptor.setLocation(new Location(location)); + return this; + } + + /** + * Sets the {@code Salary} of the {@code EditInternshipDescriptor} that we are building. + */ + public EditInternshipDescriptorBuilder withSalary(String salary) { + descriptor.setSalary(new Salary(salary)); + return this; + } + + /** + * Sets the {@code Rating} of the {@code EditInternshipDescriptor} that we are building. + */ + public EditInternshipDescriptorBuilder withRating(String rating) { + descriptor.setRating(new Rating(rating)); + return this; + } + + /** + * Parses the {@code qualifications} into a {@code Set} and set it to the + * {@code EditInternshipDescriptor} that we are building. + */ + public EditInternshipDescriptorBuilder withQualifications(String... qualifications) { + Set qualificationSet = Stream.of(qualifications).map(Qualification::new) + .collect(Collectors.toSet()); + descriptor.setQualifications(qualificationSet); + return this; + } + + /** + * Parses the {@code programming languages} into a {@code Set} and set it to the + * {@code EditInternshipDescriptor} that we are building. + */ + public EditInternshipDescriptorBuilder withProgrammingLanguages(String... programmingLanguages) { + Set programmingLanguageSet = Stream.of(programmingLanguages).map(ProgrammingLanguage::new) + .collect(Collectors.toSet()); + descriptor.setProgrammingLanguages(programmingLanguageSet); + return this; + } + + /** + * Parses the {@code reviews} into a {@code Set} and set it to the + * {@code EditInternshipDescriptor} that we are building. + */ + public EditInternshipDescriptorBuilder withReviews(String... reviews) { + Set reviewSet = Stream.of(reviews).map(Review::new) + .collect(Collectors.toSet()); + descriptor.setReviews(reviewSet); + return this; + } + + /** + * Parses the {@code notes} into a {@code Set} and set it to the + * {@code EditInternshipDescriptor} that we are building. + */ + public EditInternshipDescriptorBuilder withNotes(String... notes) { + Set noteSet = Stream.of(notes).map(Note::new) + .collect(Collectors.toSet()); + descriptor.setNotes(noteSet); + return this; + } + + /** + * Parses the {@code reflections} into a {@code Set} and set it to the + * {@code EditInternshipDescriptor} that we are building. + */ + public EditInternshipDescriptorBuilder withReflections(String... reflections) { + Set reflectionSet = Stream.of(reflections).map(Reflection::new) + .collect(Collectors.toSet()); + descriptor.setReflections(reflectionSet); + return this; + } + + public EditInternshipDescriptor build() { + return descriptor; + } +} diff --git a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java b/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java deleted file mode 100644 index 4584bd5044e..00000000000 --- a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java +++ /dev/null @@ -1,87 +0,0 @@ -package seedu.address.testutil; - -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * A utility class to help with building EditPersonDescriptor objects. - */ -public class EditPersonDescriptorBuilder { - - private EditPersonDescriptor descriptor; - - public EditPersonDescriptorBuilder() { - descriptor = new EditPersonDescriptor(); - } - - public EditPersonDescriptorBuilder(EditPersonDescriptor descriptor) { - this.descriptor = new EditPersonDescriptor(descriptor); - } - - /** - * Returns an {@code EditPersonDescriptor} with fields containing {@code person}'s details - */ - public EditPersonDescriptorBuilder(Person person) { - descriptor = new EditPersonDescriptor(); - descriptor.setName(person.getName()); - descriptor.setPhone(person.getPhone()); - descriptor.setEmail(person.getEmail()); - descriptor.setAddress(person.getAddress()); - descriptor.setTags(person.getTags()); - } - - /** - * Sets the {@code Name} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withName(String name) { - descriptor.setName(new Name(name)); - return this; - } - - /** - * Sets the {@code Phone} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withPhone(String phone) { - descriptor.setPhone(new Phone(phone)); - return this; - } - - /** - * Sets the {@code Email} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withEmail(String email) { - descriptor.setEmail(new Email(email)); - return this; - } - - /** - * Sets the {@code Address} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withAddress(String address) { - descriptor.setAddress(new Address(address)); - return this; - } - - /** - * Parses the {@code tags} into a {@code Set} and set it to the {@code EditPersonDescriptor} - * that we are building. - */ - public EditPersonDescriptorBuilder withTags(String... tags) { - Set tagSet = Stream.of(tags).map(Tag::new).collect(Collectors.toSet()); - descriptor.setTags(tagSet); - return this; - } - - public EditPersonDescriptor build() { - return descriptor; - } -} diff --git a/src/test/java/seedu/address/testutil/InternshipBuilder.java b/src/test/java/seedu/address/testutil/InternshipBuilder.java new file mode 100644 index 00000000000..176c8ac0015 --- /dev/null +++ b/src/test/java/seedu/address/testutil/InternshipBuilder.java @@ -0,0 +1,161 @@ +package seedu.address.testutil; + +import java.util.HashSet; +import java.util.Set; + +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.application.JobTitle; +import seedu.address.model.application.Location; +import seedu.address.model.application.Note; +import seedu.address.model.application.ProgrammingLanguage; +import seedu.address.model.application.Qualification; +import seedu.address.model.application.Rating; +import seedu.address.model.application.Reflection; +import seedu.address.model.application.Review; +import seedu.address.model.application.Salary; +import seedu.address.model.contact.Contact; +import seedu.address.model.documents.Documents; +import seedu.address.model.util.SampleDataUtil; + +/** + * A utility class to help with building InternshipApplication objects. + */ +public class InternshipBuilder { + public static final String DEFAULT_COMPANY_NAME = "Company A"; + public static final String DEFAULT_JOB_TITLE = "Position A"; + public static final String DEFAULT_PHONE = "55555555"; + public static final String DEFAULT_EMAIL = "meta@example.com"; + public static final String DEFAULT_INTERVIEW_DATE = "2023-04-01 08:00 PM"; + + // Identity fields + private CompanyName companyName; + private JobTitle jobTitle; + private Set reviews; + private Set programmingLanguages = new HashSet<>(); + private Set qualifications = new HashSet<>(); + private Location location; + private Salary salary; + private Set notes = new HashSet<>(); + private Rating rating; + private Set reflections = new HashSet<>(); + + // Interview fields + private InterviewDate interviewDate; + private InternshipStatus status; + private boolean isArchived; + + // Data fields + private Contact contact; + private Documents documents; + + /** + * Creates an {@code InternshipApplicationBuilder} with the default details. + */ + public InternshipBuilder() { + companyName = new CompanyName(DEFAULT_COMPANY_NAME); + jobTitle = new JobTitle(DEFAULT_JOB_TITLE); + reviews = new HashSet<>(); + status = InternshipStatus.PENDING; + isArchived = false; + interviewDate = null; + + } + + /** + * Initializes the InternshipApplicationBuilder with the data of {@code internshipToCopy}. + */ + public InternshipBuilder(InternshipApplication internshipToCopy) { + companyName = internshipToCopy.getCompanyName(); + jobTitle = internshipToCopy.getJobTitle(); + reviews = new HashSet<>(internshipToCopy.getReviews()); + documents = internshipToCopy.getDocuments(); + contact = internshipToCopy.getContact(); + status = internshipToCopy.getStatus(); + isArchived = internshipToCopy.isArchived(); + interviewDate = internshipToCopy.getInterviewDate(); + } + + /** + * Sets the {@code CompanyName} of the {@code InternshipApplication} that we are building. + */ + public InternshipBuilder withCompanyName(String companyName) { + this.companyName = new CompanyName(companyName); + return this; + } + + /** + * Sets the {@code JobTitle} of the {@code InternshipApplication} that we are building. + */ + public InternshipBuilder withJobTitle(String jobTitle) { + this.jobTitle = new JobTitle(jobTitle); + return this; + } + + /** + * Parses the {@code qualifications} into a {@code Set} and set it to the + * {@code InternshipApplication} that we are building. + */ + public InternshipBuilder withQualifications(String ... qualifications) { + this.qualifications = SampleDataUtil.getQualificationSet(qualifications); + return this; + } + + /** + * Sets the {@code Documents} of the {@code InternshipApplication} that we are building. + */ + public InternshipBuilder withDocuments(Documents documents) { + this.documents = documents; + return this; + } + + /** + * Sets the {@code Contact} of the {@code InternshipApplication} that we are building. + */ + public InternshipBuilder withContact(Contact contact) { + this.contact = contact; + return this; + } + + /** + * Sets the {@code Status} of the {@code InternshipApplication} that we are building. + */ + public InternshipBuilder withStatus(InternshipStatus status) { + this.status = status; + return this; + } + + /** + * Sets the boolean {@code isArchived} of the {@code InternshipApplication} that we are building. + */ + public InternshipBuilder withIsArchived(boolean isArchived) { + this.isArchived = isArchived; + return this; + } + + /** + * Sets the {@code InterviewDate} of the {@code InternshipApplication} that we are building. + */ + public InternshipBuilder withInterviewDate(InterviewDate interviewDate) { + this.interviewDate = interviewDate; + return this; + } + + /** + * Sets the {@code ProgrammingLanguage} of the {@code InternshipApplication} that we are building. + */ + public InternshipBuilder withProgrammingLanguage(ProgrammingLanguage language) { + this.programmingLanguages.add(language); + return this; + } + + /** + * Returns the {@code InternshipApplication} with configured attributes. + */ + public InternshipApplication build() { + return new InternshipApplication(companyName, jobTitle, reviews, programmingLanguages, qualifications, location, + salary, notes, rating, reflections, contact, status, isArchived, interviewDate, documents); + } +} diff --git a/src/test/java/seedu/address/testutil/InternshipTodoBuilder.java b/src/test/java/seedu/address/testutil/InternshipTodoBuilder.java new file mode 100644 index 00000000000..bd8c52d1cc8 --- /dev/null +++ b/src/test/java/seedu/address/testutil/InternshipTodoBuilder.java @@ -0,0 +1,102 @@ +package seedu.address.testutil; + +import java.time.LocalDate; + +import seedu.address.model.application.CompanyName; +import seedu.address.model.application.JobTitle; +import seedu.address.model.tag.TaskType; +import seedu.address.model.task.ApplicationDeadline; +import seedu.address.model.task.InternshipTodo; +import seedu.address.model.task.NoteContent; + +/** + * A utility class to help with building {@code InternshipTodo} objects. + */ +public class InternshipTodoBuilder { + + private static final CompanyName DEFAULT_TITLE = new CompanyName("Aloha Nice"); + private static final JobTitle DEFAULT_JOB = new JobTitle("Software Engineer"); + private static final LocalDate CURRENT_DATE = LocalDate.now(); + private static final TaskType TASK_TYPE = TaskType.TODO; + private static final ApplicationDeadline DEFAULT_DEADLINE = new ApplicationDeadline(LocalDate.parse("2025-03-04")); + private static final NoteContent DEFAULT_NOTE = new NoteContent("No content"); + + private CompanyName title; + private JobTitle jobTitle; + private LocalDate date; + private TaskType type; + private ApplicationDeadline deadline; + private NoteContent note; + + /** + * Creates a {@code InternshipTodoBuilder} with the default details. + */ + public InternshipTodoBuilder() { + title = DEFAULT_TITLE; + jobTitle = DEFAULT_JOB; + date = CURRENT_DATE; + type = TASK_TYPE; + deadline = DEFAULT_DEADLINE; + note = DEFAULT_NOTE; + } + + /** + * Initializes the {@code InternshipTodoBuilder} with the data of {@code todoToCopy}. + */ + public InternshipTodoBuilder(InternshipTodo todoToCopy) { + title = todoToCopy.getInternshipTitle(); + jobTitle = todoToCopy.getJobTitle(); + date = todoToCopy.getDate(); + type = todoToCopy.getType(); + deadline = todoToCopy.getDeadline(); + note = todoToCopy.getNote(); + } + + /** + * Sets the {@code Title} of the {@code InternshipTodo} that we are building. + */ + public InternshipTodoBuilder withTitle(String title) { + this.title = new CompanyName(title); + return this; + } + + /** + * Sets the {@code JobTitle} of the {@code InternshipTodo} that we are building. + */ + public InternshipTodoBuilder withJobTitle(String job) { + this.jobTitle = new JobTitle(job); + return this; + } + + /** + * Sets the {@code LocalDate} of the {@code InternshipTodo} that we are building. + */ + public InternshipTodoBuilder withDate(String date) { + this.date = LocalDate.parse(date); + return this; + } + + /** + * Sets the {@code ApplicationDeadline} of the {@code InternshipTodo} that we are building. + */ + public InternshipTodoBuilder withDeadline(String deadline) { + this.deadline = new ApplicationDeadline(LocalDate.parse(deadline)); + return this; + } + + /** + * Sets the {@code NoteContent} of the {@code InternshipTodo} that we are building. + */ + public InternshipTodoBuilder withNote(String note) { + this.note = new NoteContent(note); + return this; + } + + public InternshipTodo basicBuild() { + return new InternshipTodo(DEFAULT_TITLE, DEFAULT_JOB, DEFAULT_DEADLINE); + } + + public InternshipTodo build() { + return new InternshipTodo(title, jobTitle, deadline, note, date, type); + } +} diff --git a/src/test/java/seedu/address/testutil/InterviewDateBuilder.java b/src/test/java/seedu/address/testutil/InterviewDateBuilder.java new file mode 100644 index 00000000000..74d3b823f17 --- /dev/null +++ b/src/test/java/seedu/address/testutil/InterviewDateBuilder.java @@ -0,0 +1,23 @@ +package seedu.address.testutil; + +import seedu.address.model.application.InterviewDate; + +/** + * A utility class to help with building InterviewDate objects. + */ +public class InterviewDateBuilder { + public static final String DEFAULT_DATE = "2099-12-01 11:00 AM"; + + private InterviewDate interviewDate; + + /** + * Creates a {@code InterviewDateBuilder} with the default details. + */ + public InterviewDateBuilder() { + interviewDate = new InterviewDate(DEFAULT_DATE); + } + + public InterviewDate build() { + return new InterviewDate(DEFAULT_DATE); + } +} diff --git a/src/test/java/seedu/address/testutil/NoteBuilder.java b/src/test/java/seedu/address/testutil/NoteBuilder.java new file mode 100644 index 00000000000..6a244a3fe6a --- /dev/null +++ b/src/test/java/seedu/address/testutil/NoteBuilder.java @@ -0,0 +1,63 @@ +package seedu.address.testutil; + +import java.time.LocalDate; + +import seedu.address.model.tag.TaskType; +import seedu.address.model.task.Note; +import seedu.address.model.task.NoteContent; + +/** + * A utility class to help with building {@code Note} objects. + */ +public class NoteBuilder { + + private static final LocalDate CURRENT_DATE = LocalDate.now(); + private static final TaskType TASK_TYPE = TaskType.NOTE; + private static final NoteContent DEFAULT_NOTE = new NoteContent("This is a note!"); + + private LocalDate date; + private TaskType type; + private NoteContent note; + + /** + * Creates a {@code NoteBuilder} with the default details. + */ + public NoteBuilder() { + date = CURRENT_DATE; + type = TASK_TYPE; + note = DEFAULT_NOTE; + } + + /** + * Initializes the {@code NoteBuilder} with the data of {@code noteToCopy}. + */ + public NoteBuilder(Note noteToCopy) { + date = noteToCopy.getDate(); + type = noteToCopy.getType(); + note = noteToCopy.getNote(); + } + + /** + * Sets the {@code LocalDate} of the {@code Note} that we are building. + */ + public NoteBuilder withDate(String date) { + this.date = LocalDate.parse(date); + return this; + } + + /** + * Sets the {@code NoteContent} of the {@code Note} that we are building. + */ + public NoteBuilder withNote(String note) { + this.note = new NoteContent(note); + return this; + } + + public Note basicBuild() { + return new Note(DEFAULT_NOTE, CURRENT_DATE, TASK_TYPE); + } + + public Note build() { + return new Note(note, date, type); + } +} diff --git a/src/test/java/seedu/address/testutil/NoteUtil.java b/src/test/java/seedu/address/testutil/NoteUtil.java new file mode 100644 index 00000000000..5a0fe82dde0 --- /dev/null +++ b/src/test/java/seedu/address/testutil/NoteUtil.java @@ -0,0 +1,20 @@ +package seedu.address.testutil; + +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE_CONTENT; + +import seedu.address.model.task.Note; + + + +/** + * A utility class for Note. + */ +public class NoteUtil { + + /** + * Returns the part of command string for the given {@code note}'s details. + */ + public static String getNoteDetails(Note note) { + return PREFIX_NOTE_CONTENT + note.getNote().content; + } +} diff --git a/src/test/java/seedu/address/testutil/PersonBuilder.java b/src/test/java/seedu/address/testutil/PersonBuilder.java deleted file mode 100644 index 6be381d39ba..00000000000 --- a/src/test/java/seedu/address/testutil/PersonBuilder.java +++ /dev/null @@ -1,96 +0,0 @@ -package seedu.address.testutil; - -import java.util.HashSet; -import java.util.Set; - -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -import seedu.address.model.util.SampleDataUtil; - -/** - * A utility class to help with building Person objects. - */ -public class PersonBuilder { - - public static final String DEFAULT_NAME = "Amy Bee"; - public static final String DEFAULT_PHONE = "85355255"; - public static final String DEFAULT_EMAIL = "amy@gmail.com"; - public static final String DEFAULT_ADDRESS = "123, Jurong West Ave 6, #08-111"; - - private Name name; - private Phone phone; - private Email email; - private Address address; - private Set tags; - - /** - * Creates a {@code PersonBuilder} with the default details. - */ - public PersonBuilder() { - name = new Name(DEFAULT_NAME); - phone = new Phone(DEFAULT_PHONE); - email = new Email(DEFAULT_EMAIL); - address = new Address(DEFAULT_ADDRESS); - tags = new HashSet<>(); - } - - /** - * Initializes the PersonBuilder with the data of {@code personToCopy}. - */ - public PersonBuilder(Person personToCopy) { - name = personToCopy.getName(); - phone = personToCopy.getPhone(); - email = personToCopy.getEmail(); - address = personToCopy.getAddress(); - tags = new HashSet<>(personToCopy.getTags()); - } - - /** - * Sets the {@code Name} of the {@code Person} that we are building. - */ - public PersonBuilder withName(String name) { - this.name = new Name(name); - return this; - } - - /** - * Parses the {@code tags} into a {@code Set} and set it to the {@code Person} that we are building. - */ - public PersonBuilder withTags(String ... tags) { - this.tags = SampleDataUtil.getTagSet(tags); - return this; - } - - /** - * Sets the {@code Address} of the {@code Person} that we are building. - */ - public PersonBuilder withAddress(String address) { - this.address = new Address(address); - return this; - } - - /** - * Sets the {@code Phone} of the {@code Person} that we are building. - */ - public PersonBuilder withPhone(String phone) { - this.phone = new Phone(phone); - return this; - } - - /** - * Sets the {@code Email} of the {@code Person} that we are building. - */ - public PersonBuilder withEmail(String email) { - this.email = new Email(email); - return this; - } - - public Person build() { - return new Person(name, phone, email, address, tags); - } - -} diff --git a/src/test/java/seedu/address/testutil/PersonUtil.java b/src/test/java/seedu/address/testutil/PersonUtil.java deleted file mode 100644 index 90849945183..00000000000 --- a/src/test/java/seedu/address/testutil/PersonUtil.java +++ /dev/null @@ -1,62 +0,0 @@ -package seedu.address.testutil; - -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; - -import java.util.Set; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.person.Person; -import seedu.address.model.tag.Tag; - -/** - * A utility class for Person. - */ -public class PersonUtil { - - /** - * Returns an add command string for adding the {@code person}. - */ - public static String getAddCommand(Person person) { - return AddCommand.COMMAND_WORD + " " + getPersonDetails(person); - } - - /** - * Returns the part of command string for the given {@code person}'s details. - */ - public static String getPersonDetails(Person person) { - StringBuilder sb = new StringBuilder(); - sb.append(PREFIX_NAME + person.getName().fullName + " "); - sb.append(PREFIX_PHONE + person.getPhone().value + " "); - sb.append(PREFIX_EMAIL + person.getEmail().value + " "); - sb.append(PREFIX_ADDRESS + person.getAddress().value + " "); - person.getTags().stream().forEach( - s -> sb.append(PREFIX_TAG + s.tagName + " ") - ); - return sb.toString(); - } - - /** - * Returns the part of command string for the given {@code EditPersonDescriptor}'s details. - */ - public static String getEditPersonDescriptorDetails(EditPersonDescriptor descriptor) { - StringBuilder sb = new StringBuilder(); - descriptor.getName().ifPresent(name -> sb.append(PREFIX_NAME).append(name.fullName).append(" ")); - descriptor.getPhone().ifPresent(phone -> sb.append(PREFIX_PHONE).append(phone.value).append(" ")); - descriptor.getEmail().ifPresent(email -> sb.append(PREFIX_EMAIL).append(email.value).append(" ")); - descriptor.getAddress().ifPresent(address -> sb.append(PREFIX_ADDRESS).append(address.value).append(" ")); - if (descriptor.getTags().isPresent()) { - Set tags = descriptor.getTags().get(); - if (tags.isEmpty()) { - sb.append(PREFIX_TAG); - } else { - tags.forEach(s -> sb.append(PREFIX_TAG).append(s.tagName).append(" ")); - } - } - return sb.toString(); - } -} diff --git a/src/test/java/seedu/address/testutil/TestUtil.java b/src/test/java/seedu/address/testutil/TestUtil.java index 896d103eb0b..bd0fbd4df8a 100644 --- a/src/test/java/seedu/address/testutil/TestUtil.java +++ b/src/test/java/seedu/address/testutil/TestUtil.java @@ -7,7 +7,7 @@ import seedu.address.commons.core.index.Index; import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.address.model.application.InternshipApplication; /** * A utility class for test cases. @@ -36,20 +36,20 @@ public static Path getFilePathInSandboxFolder(String fileName) { * Returns the middle index of the person in the {@code model}'s person list. */ public static Index getMidIndex(Model model) { - return Index.fromOneBased(model.getFilteredPersonList().size() / 2); + return Index.fromOneBased(model.getSortedFilteredInternshipList().size() / 2); } /** * Returns the last index of the person in the {@code model}'s person list. */ public static Index getLastIndex(Model model) { - return Index.fromOneBased(model.getFilteredPersonList().size()); + return Index.fromOneBased(model.getSortedFilteredInternshipList().size()); } /** * Returns the person in the {@code model}'s person list at {@code index}. */ - public static Person getPerson(Model model, Index index) { - return model.getFilteredPersonList().get(index.getZeroBased()); + public static InternshipApplication getPerson(Model model, Index index) { + return model.getSortedFilteredInternshipList().get(index.getZeroBased()); } } diff --git a/src/test/java/seedu/address/testutil/TodoUtil.java b/src/test/java/seedu/address/testutil/TodoUtil.java new file mode 100644 index 00000000000..4fc60413ad7 --- /dev/null +++ b/src/test/java/seedu/address/testutil/TodoUtil.java @@ -0,0 +1,25 @@ +package seedu.address.testutil; + +import static seedu.address.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_JOB_TITLE; + +import java.time.format.DateTimeFormatter; + +import seedu.address.model.task.InternshipTodo; + +/** + * A utility class for Todo. + */ +public class TodoUtil { + + /** + * Returns the part of command string for the given {@code todo}'s details. + */ + public static String getTodoDetails(InternshipTodo todo) { + String deadline = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(todo.getDeadline().applicationDeadline); + return PREFIX_COMPANY_NAME + todo.getInternshipTitle().fullName + " " + + PREFIX_JOB_TITLE + todo.getJobTitle().fullName + " " + + PREFIX_DEADLINE + deadline; + } +} diff --git a/src/test/java/seedu/address/testutil/TypicalContacts.java b/src/test/java/seedu/address/testutil/TypicalContacts.java new file mode 100644 index 00000000000..a81a6ebc219 --- /dev/null +++ b/src/test/java/seedu/address/testutil/TypicalContacts.java @@ -0,0 +1,21 @@ +package seedu.address.testutil; + +import seedu.address.logic.commands.CommandTestUtil; +import seedu.address.model.contact.Contact; + +/** + * A utility class containing a list of {@code Contact} objects to be used in tests. + */ +public class TypicalContacts { + + public static final Contact BANK_OF_AMERICA_CONTACT = new ContactBuilder() + .withPhone(CommandTestUtil.VALID_PHONE_BANK_OF_AMERICA) + .withEmail(CommandTestUtil.VALID_EMAIL_BANK_OF_AMERICA) + .build(); + + public static final Contact META_CONTACT = new ContactBuilder() + .withPhone(CommandTestUtil.VALID_PHONE_META) + .withEmail(CommandTestUtil.VALID_EMAIL_META) + .build(); + private TypicalContacts() {} // prevents instantiation +} diff --git a/src/test/java/seedu/address/testutil/TypicalDocuments.java b/src/test/java/seedu/address/testutil/TypicalDocuments.java new file mode 100644 index 00000000000..dd63872dfac --- /dev/null +++ b/src/test/java/seedu/address/testutil/TypicalDocuments.java @@ -0,0 +1,21 @@ +package seedu.address.testutil; + +import seedu.address.logic.commands.CommandTestUtil; +import seedu.address.model.documents.Documents; + +/** + * A utility class containing a list of {@code Documents} objects to be used in tests. + */ +public class TypicalDocuments { + + public static final Documents DOCUMENTS_GOOGLE = new DocumentsBuilder() + .withResumeLink(CommandTestUtil.VALID_RESUME_LINK_GOOGLE) + .withCoverLetterLink(CommandTestUtil.VALID_COVER_LETTER_LINK_GOOGLE) + .build(); + + public static final Documents DOCUMENTS_NETFLIX = new DocumentsBuilder() + .withResumeLink(CommandTestUtil.VALID_RESUME_LINK_NETFLIX) + .withCoverLetterLink(CommandTestUtil.VALID_COVER_LETTER_LINK_NETFLIX) + .build(); + private TypicalDocuments() {} // prevents instantiation +} diff --git a/src/test/java/seedu/address/testutil/TypicalIndexes.java b/src/test/java/seedu/address/testutil/TypicalIndexes.java index 1e613937657..e7011abdbc1 100644 --- a/src/test/java/seedu/address/testutil/TypicalIndexes.java +++ b/src/test/java/seedu/address/testutil/TypicalIndexes.java @@ -6,7 +6,11 @@ * A utility class containing a list of {@code Index} objects to be used in tests. */ public class TypicalIndexes { - public static final Index INDEX_FIRST_PERSON = Index.fromOneBased(1); - public static final Index INDEX_SECOND_PERSON = Index.fromOneBased(2); - public static final Index INDEX_THIRD_PERSON = Index.fromOneBased(3); + public static final Index INDEX_FIRST_APPLICATION = Index.fromOneBased(1); + public static final Index INDEX_SECOND_APPLICATION = Index.fromOneBased(2); + public static final Index INDEX_THIRD_APPLICATION = Index.fromOneBased(3); + public static final Index INDEX_FOURTH_APPLICATION = Index.fromOneBased(4); + public static final Index INDEX_FIFTH_APPLICATION = Index.fromOneBased(5); + public static final Index INDEX_SIXTH_APPLICATION = Index.fromOneBased(6); + public static final Index INDEX_SEVENTH_APPLICATION = Index.fromOneBased(7); } diff --git a/src/test/java/seedu/address/testutil/TypicalInternships.java b/src/test/java/seedu/address/testutil/TypicalInternships.java new file mode 100644 index 00000000000..d0d49bcd972 --- /dev/null +++ b/src/test/java/seedu/address/testutil/TypicalInternships.java @@ -0,0 +1,114 @@ +package seedu.address.testutil; + +import static seedu.address.model.application.InternshipStatus.ACCEPTED; +import static seedu.address.model.application.InternshipStatus.DECLINED; +import static seedu.address.model.application.InternshipStatus.PENDING; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import seedu.address.model.AddressBook; +import seedu.address.model.application.InternshipApplication; +import seedu.address.model.application.InternshipStatus; +import seedu.address.model.application.InterviewDate; +import seedu.address.model.contact.Contact; +import seedu.address.model.contact.Email; +import seedu.address.model.contact.Phone; +import seedu.address.model.documents.CoverLetterLink; +import seedu.address.model.documents.Documents; +import seedu.address.model.documents.ResumeLink; + +/** + * A utility class containing a list of {@code InternshipApplication} objects to be used in tests. + */ +public class TypicalInternships { + public static final InternshipApplication META = new InternshipBuilder() + .withCompanyName("Meta") + .withJobTitle("Software Tester") + .withContact(new Contact(new Phone("55555555"), new Email("meta@example.com"))) + .withInterviewDate(new InterviewDate("2023-04-01 08:00 PM")) + .withStatus(ACCEPTED) + .build(); + public static final InternshipApplication BANK_OF_AMERICA = new InternshipBuilder() + .withCompanyName("Bank of America") + .withJobTitle("Software Engineer") + .withInterviewDate(new InterviewDate("2023-04-09 12:00 PM")) + .withStatus(DECLINED) + .build(); + public static final InternshipApplication ALICE = new InternshipBuilder() + .withCompanyName("Alice Wonder") + .withJobTitle("Software Engineer") + .withInterviewDate(new InterviewDate("2023-03-27 08:00 AM")) + .withStatus(DECLINED) + .build(); + public static final InternshipApplication BENSON = new InternshipBuilder() + .withCompanyName("Benson Meier") + .withJobTitle("Software Engineer") + .withInterviewDate(new InterviewDate("2023-02-27 08:00 AM")) + .withStatus(PENDING) + .build(); + public static final InternshipApplication CARL = new InternshipBuilder().withCompanyName("Carl Kurz") + .withJobTitle("Web Developer").withStatus(PENDING).build(); + public static final InternshipApplication GOOGLE = new InternshipBuilder() + .withCompanyName("Google").withJobTitle("Product Manager").build(); + public static final InternshipApplication NETFLIX = new InternshipBuilder() + .withCompanyName("Netflix") + .withJobTitle("Network Engineer") + .withDocuments(new Documents(new ResumeLink("https://drive.example.com/resume_netflix"), + new CoverLetterLink("https://drive.example.com/coverletter_netflix"))).build(); + + public static final InternshipApplication MICROSOFT = new InternshipBuilder() + .withCompanyName("Microsoft") + .withJobTitle("Operating System Developer") + .withIsArchived(true).build(); + + public static final InternshipApplication MICRON = new InternshipBuilder() + .withCompanyName("Micron") + .withJobTitle("Embedded Systems Engineer") + .withIsArchived(false).build(); + + public static final InternshipApplication DANIEL = new InternshipBuilder().withCompanyName("Daniel Meier") + .withJobTitle("Software Engineer").build(); + public static final InternshipApplication ELLE = new InternshipBuilder().withCompanyName("Elle Meyer") + .withJobTitle("Web Developer").withStatus(PENDING).build(); + public static final InternshipApplication FIONA = new InternshipBuilder().withCompanyName("Fiona Kunz") + .withJobTitle("Software Engineer").build(); + public static final InternshipApplication GEORGE = new InternshipBuilder().withCompanyName("George Best") + .withJobTitle("Software Engineer").withStatus(PENDING).build(); + public static final InternshipApplication ORACLE = new InternshipBuilder() + .withCompanyName("Oracle") + .withJobTitle("Data Engineer") + .withDocuments(new Documents(new ResumeLink("https://drive.example.com/resume_oracle"), + new CoverLetterLink("https://drive.example.com/coverletter_oracle"))).build(); + public static final InternshipApplication HARRY = new InternshipBuilder().withCompanyName("Harry Better") + .withJobTitle("Web Developer").withStatus(PENDING).build(); + public static final InternshipApplication IAN = new InternshipBuilder().withCompanyName("Ian Hande") + .withJobTitle("Web Developer").withStatus(InternshipStatus.REJECTED).build(); + public static final InternshipApplication JAMES = new InternshipBuilder().withCompanyName("Fiona K") + .withJobTitle("Software Engineer").build(); + public static final InternshipApplication AMAZON = new InternshipBuilder().withCompanyName("Amazon") + .withJobTitle("Cloud Engineer") + .withContact(new Contact(new Phone("66666666"), new Email("example@amazon.com"))).build(); + public static final InternshipApplication LINKEDIN = new InternshipBuilder().withCompanyName("LINKEDIN") + .withJobTitle("Cloud Engineer").withQualifications("ioi medalist").build(); + + private TypicalInternships() {} // prevents instantiation + + /** + * Returns an {@code AddressBook} with all the typical internships. + */ + public static AddressBook getTypicalAddressBook() { + AddressBook ab = new AddressBook(); + for (InternshipApplication internship : getTypicalInternships()) { + ab.addApplication(internship); + } + return ab; + } + + public static List getTypicalInternships() { + return new ArrayList<>(Arrays.asList(META, BANK_OF_AMERICA, ALICE, GOOGLE, NETFLIX, BENSON, CARL, MICROSOFT, + MICRON, DANIEL, ELLE, FIONA, GEORGE, HARRY, IAN, JAMES, ORACLE, AMAZON)); + } + +} diff --git a/src/test/java/seedu/address/testutil/TypicalNotes.java b/src/test/java/seedu/address/testutil/TypicalNotes.java new file mode 100644 index 00000000000..d98b4417fe9 --- /dev/null +++ b/src/test/java/seedu/address/testutil/TypicalNotes.java @@ -0,0 +1,35 @@ +package seedu.address.testutil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import seedu.address.model.NoteList; +import seedu.address.model.task.Note; + +/** + * A utility class containing a list of {@code Note} objects to be used in tests. + */ +public class TypicalNotes { + public static final Note NOTE_1 = new NoteBuilder().withNote("Today").build(); + public static final Note NOTE_2 = new NoteBuilder().withNote("is").build(); + public static final Note NOTE_3 = new NoteBuilder().withNote("a").build(); + public static final Note NOTE_4 = new NoteBuilder().withNote("nice").build(); + public static final Note NOTE_5 = new NoteBuilder().withNote("day").build(); + public static final Note NOTE_6 = new NoteBuilder().withNote("?").build(); + + /** + * Returns {@code NoteList} with all the typical notes. + */ + public static NoteList getTypicalNoteList() { + NoteList nl = new NoteList(); + for (Note note : getNotes()) { + nl.addNote(note); + } + return nl; + } + + public static List getNotes() { + return new ArrayList<>(Arrays.asList(NOTE_1, NOTE_2, NOTE_3, NOTE_4, NOTE_5, NOTE_6)); + } +} diff --git a/src/test/java/seedu/address/testutil/TypicalPersons.java b/src/test/java/seedu/address/testutil/TypicalPersons.java deleted file mode 100644 index fec76fb7129..00000000000 --- a/src/test/java/seedu/address/testutil/TypicalPersons.java +++ /dev/null @@ -1,76 +0,0 @@ -package seedu.address.testutil; - -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import seedu.address.model.AddressBook; -import seedu.address.model.person.Person; - -/** - * A utility class containing a list of {@code Person} objects to be used in tests. - */ -public class TypicalPersons { - - public static final Person ALICE = new PersonBuilder().withName("Alice Pauline") - .withAddress("123, Jurong West Ave 6, #08-111").withEmail("alice@example.com") - .withPhone("94351253") - .withTags("friends").build(); - public static final Person BENSON = new PersonBuilder().withName("Benson Meier") - .withAddress("311, Clementi Ave 2, #02-25") - .withEmail("johnd@example.com").withPhone("98765432") - .withTags("owesMoney", "friends").build(); - public static final Person CARL = new PersonBuilder().withName("Carl Kurz").withPhone("95352563") - .withEmail("heinz@example.com").withAddress("wall street").build(); - public static final Person DANIEL = new PersonBuilder().withName("Daniel Meier").withPhone("87652533") - .withEmail("cornelia@example.com").withAddress("10th street").withTags("friends").build(); - public static final Person ELLE = new PersonBuilder().withName("Elle Meyer").withPhone("9482224") - .withEmail("werner@example.com").withAddress("michegan ave").build(); - public static final Person FIONA = new PersonBuilder().withName("Fiona Kunz").withPhone("9482427") - .withEmail("lydia@example.com").withAddress("little tokyo").build(); - public static final Person GEORGE = new PersonBuilder().withName("George Best").withPhone("9482442") - .withEmail("anna@example.com").withAddress("4th street").build(); - - // Manually added - public static final Person HOON = new PersonBuilder().withName("Hoon Meier").withPhone("8482424") - .withEmail("stefan@example.com").withAddress("little india").build(); - public static final Person IDA = new PersonBuilder().withName("Ida Mueller").withPhone("8482131") - .withEmail("hans@example.com").withAddress("chicago ave").build(); - - // Manually added - Person's details found in {@code CommandTestUtil} - public static final Person AMY = new PersonBuilder().withName(VALID_NAME_AMY).withPhone(VALID_PHONE_AMY) - .withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY).withTags(VALID_TAG_FRIEND).build(); - public static final Person BOB = new PersonBuilder().withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB) - .withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND) - .build(); - - public static final String KEYWORD_MATCHING_MEIER = "Meier"; // A keyword that matches MEIER - - private TypicalPersons() {} // prevents instantiation - - /** - * Returns an {@code AddressBook} with all the typical persons. - */ - public static AddressBook getTypicalAddressBook() { - AddressBook ab = new AddressBook(); - for (Person person : getTypicalPersons()) { - ab.addPerson(person); - } - return ab; - } - - public static List getTypicalPersons() { - return new ArrayList<>(Arrays.asList(ALICE, BENSON, CARL, DANIEL, ELLE, FIONA, GEORGE)); - } -} diff --git a/src/test/java/seedu/address/testutil/TypicalTodos.java b/src/test/java/seedu/address/testutil/TypicalTodos.java new file mode 100644 index 00000000000..18a7566793f --- /dev/null +++ b/src/test/java/seedu/address/testutil/TypicalTodos.java @@ -0,0 +1,39 @@ +package seedu.address.testutil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import seedu.address.model.TodoList; +import seedu.address.model.task.InternshipTodo; + +/** + * A utility class containing a list of {@code InternshipTodo} objects to be used in tests. + */ +public class TypicalTodos { + public static final InternshipTodo META_T = new InternshipTodoBuilder().withTitle("Meta") + .withJobTitle("Software Tester").withDeadline("2024-05-06").build(); + public static final InternshipTodo BANK_OF_AMERICA_T = new InternshipTodoBuilder().withTitle("Bank of America") + .withJobTitle("Software Engineer").withDeadline("2023-05-06").build(); + public static final InternshipTodo GOOGLE_T = new InternshipTodoBuilder().withTitle("Google") + .withJobTitle("Product Manager").withDeadline("2023-08-15").withNote("aim this").build(); + public static final InternshipTodo NETFLIX_T = new InternshipTodoBuilder().withTitle("Netflix") + .withJobTitle("Network Engineer").withDeadline("2024-01-31").build(); + public static final InternshipTodo ORACLE_T = new InternshipTodoBuilder().withTitle("Oracle") + .withJobTitle("Data Engineer").withDeadline("2023-12-06").withNote("3 tech interviews").build(); + + /** + * Returns a {@code TodoList} with all the typical todos. + */ + public static TodoList getTypicalTodoList() { + TodoList tl = new TodoList(); + for (InternshipTodo todo : getTodos()) { + tl.addTodo(todo); + } + return tl; + } + + public static List getTodos() { + return new ArrayList<>(Arrays.asList(META_T, BANK_OF_AMERICA_T, GOOGLE_T, NETFLIX_T, ORACLE_T)); + } +} diff --git a/src/test/java/seedu/address/ui/TestFxmlObject.java b/src/test/java/seedu/address/ui/TestFxmlObject.java index 5ecd82656f2..3fee9513c79 100644 --- a/src/test/java/seedu/address/ui/TestFxmlObject.java +++ b/src/test/java/seedu/address/ui/TestFxmlObject.java @@ -27,8 +27,8 @@ public void setText(String text) { @Override public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof TestFxmlObject // instanceof handles nulls + return other == this + || (other instanceof TestFxmlObject && text.equals(((TestFxmlObject) other).getText())); } diff --git a/src/test/java/seedu/address/ui/UiPartTest.java b/src/test/java/seedu/address/ui/UiPartTest.java index 33d82d911b8..7f26af4f3b3 100644 --- a/src/test/java/seedu/address/ui/UiPartTest.java +++ b/src/test/java/seedu/address/ui/UiPartTest.java @@ -54,7 +54,18 @@ public void constructor_validFileUrl_loadsFile() { public void constructor_validFileWithFxRootUrl_loadsFile() { URL validFileUrl = getTestFileUrl(VALID_FILE_WITH_FX_ROOT_PATH); TestFxmlObject root = new TestFxmlObject(); - assertEquals(VALID_FILE_ROOT, new TestUiPart(validFileUrl, root).getRoot()); + assertEquals(VALID_FILE_ROOT, new TestUiPart<>(validFileUrl, root).getRoot()); + } + + @Test + public void constructor_validFileName_loadsFile() { + assertEquals(VALID_FILE_ROOT, new TestUiPart<>(VALID_FILE_PATH).getRoot()); + } + + @Test + public void constructor_validFileNameWithFxRootUrl_loadsFile() { + TestFxmlObject root = new TestFxmlObject(); + assertEquals(VALID_FILE_ROOT, new TestUiPart<>(VALID_FILE_WITH_FX_ROOT_PATH, root).getRoot()); } @Test @@ -84,7 +95,7 @@ private URL getTestFileUrl(String testFilePath) { /** * UiPart used for testing. - * It should only be used with invalid FXML files or the valid file located at {@link VALID_FILE_PATH}. + * It should only be used with invalid FXML files or the valid file located at {@code VALID_FILE_PATH}. */ private static class TestUiPart extends UiPart {