diff --git a/.gitignore b/.gitignore index 71c9194e8bd..884a338610a 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,6 @@ src/test/data/sandbox/ # MacOS custom attributes files created by Finder .DS_Store docs/_site/ + +# /bin folder +/bin/ diff --git a/README.md b/README.md index 13f5c77403f..30ae062ae1b 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,70 @@ -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) - -![Ui](docs/images/Ui.png) - -* This is **a sample project for Software Engineering (SE) students**.
- Example usages: - * as a starting point of a course project (as opposed to writing everything from scratch) - * as a case study -* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details. - * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big. - * It comes with a **reasonable level of user and developer documentation**. -* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...). -* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**. -* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info. +[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/AY2223S2-CS2103T-T14-3/tp/actions) +[![codecov](https://codecov.io/gh/AY2223S2-CS2103T-T14-3/tp/branch/master/graph/badge.svg)](https://codecov.io/gh/AY2223S2-CS2103T-T14-3/tp)
+ +![Logo](docs/images/internbuddy-logo.png) +## About InternBuddy +InternBuddy is a desktop application for Computing undergraduates to manage their internship applications. +It is optimized for typing where it allows you to complete internship management tasks much more efficiently +via the keyboard as compared to using traditional Graphical User Interface (GUI) applications. +If you are a fast typist who is seeking a one-stop platform to systematically organise your internship +applications, then InternBuddy is the perfect buddy to accompany you during your internship hunt. + +InternBuddy runs using Java 11, and is available on the Windows, macOS and Linux operating systems. + + +## Core Features +1. List all internships +2. Add an internship +3. Edit an internship +4. View an internship +5. Copy an internship to clipboard +6. Find internships +7. Get upcoming events and deadlines +8. Delete internships by indices +9. Delete internships by fields +10. Clear all internships +11. Get help +12. Exit +13. Navigating through past commands + +In InternBuddy, internship data is auto-saved whenever changes are made. + +## Getting Started +1. Ensure you have Java 11 or above installed in your computer. Otherwise, you can download it + [here](https://www.oracle.com/java/technologies/downloads/#java11). +2. Download the latest release of InternBuddy [here](https://github.com/AY2223S2-CS2103T-T14-3/tp/releases). +3. Copy the file `internbuddy.jar` to the folder that you want to use as the home folder for InternBuddy. +4. Double-click on the file internbuddy.jar to launch InternBuddy. A GUI similar to the diagram below should appear in + a few seconds. Note how the app contains some sample data. + + ![Ui](docs/images/Ui.png) + +5. You can interact with InternBuddy by typing into the box with the text `Enter command here...`, then pressing `Enter` +to execute your command. For example, typing `help` and pressing Enter will open the help window. +6. Enjoy tracking your internships with InternBuddy! + + +## Additional Information + +- If you are a user who is interested in learning more about what InternBuddy offers, do visit the +[InternBuddy User Guide](https://ay2223s2-cs2103t-t14-3.github.io/tp/UserGuide.html). + +- Alternatively, if you are a developer who is curious about the implementation of InternBuddy, check +out the [InternBuddy Developer Guide](https://ay2223s2-cs2103t-t14-3.github.io/tp/DeveloperGuide.html). + + +## Acknowledgements + +* InternBuddy is written in **Java 11**. +* It is adapted from the [AddressBook Level 3](https://github.com/se-edu/addressbook-level3) project created by + the [SE-EDU initiative](https://se-education.org). +* Libraries and frameworks used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), + [JUnit5](https://github.com/junit-team/junit5) and [TestFX](https://github.com/TestFX/TestFX). +* GUI testing is implemented with references from [AddressBook Level 4](https://github.com/se-edu/addressbook-level4) + and [Please Hire Us](https://github.com/AY2223S1-CS2103T-W17-4/tp). We utilised code from these projects to set + up GUI testing and added our own test cases to test the UI components that we created. +* The feature of Navigating Through Past Commands is primarily adapted from [HackNet](https://github.com/AY2122S2-CS2103T-W13-3/tp), + but we added code modifications and test cases. +* The sections on explaining the formatting standards and GUI interface in the User and Developer Guides are + inspired by [Please Hire Us](https://github.com/AY2223S1-CS2103T-W17-4/tp). + diff --git a/build.gradle b/build.gradle index 108397716bd..d77a1f038e2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,5 @@ +import org.gradle.api.tasks.testing.logging.TestLogEvent + plugins { id 'java' id 'checkstyle' @@ -6,7 +8,8 @@ plugins { id 'jacoco' } -mainClassName = 'seedu.address.Main' + +mainClassName = 'seedu.internship.Main' sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 @@ -20,6 +23,10 @@ checkstyle { toolVersion = '10.2' } +run { + enableAssertions = true +} + test { useJUnitPlatform() finalizedBy jacocoTestReport @@ -43,6 +50,7 @@ task coverage(type: JacocoReport) { dependencies { String jUnitVersion = '5.4.0' String javaFxVersion = '11' + String testFxVersion = '4.0.15-alpha' implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'win' implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'mac' @@ -60,13 +68,81 @@ dependencies { implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.7.0' implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.7.4' - testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: jUnitVersion + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: jUnitVersion testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: jUnitVersion + + //@@author eugenetangkj-reused + //Reused from //Reused from https://github.com/se-edu/addressbook-level4/blob/master/build.gradle where it taught + //me how to include the dependencies for TestFx + testImplementation group: 'org.testfx', name: 'testfx-core', version: testFxVersion + testRuntimeOnly group: 'org.testfx', name: 'openjfx-monocle', version: 'jdk-12.0.1+2' } shadowJar { - archiveFileName = 'addressbook.jar' + archiveFileName = 'internbuddy.jar' +} + +//@@author eugenetangkj-reused +//Reused from //Reused from https://github.com/se-edu/addressbook-level4/blob/master/build.gradle + +task(guiTests) +task(nonGuiTests) + +// Run `test` task if `guiTests` or `nonGuiTests` is specified +guiTests.dependsOn test +nonGuiTests.dependsOn test + +task(allTests) + +// `allTests` implies both `guiTests` and `nonGuiTests` +allTests.dependsOn guiTests +allTests.dependsOn nonGuiTests + +test { + systemProperty 'testfx.setup.timeout', '60000' + + testLogging { + events TestLogEvent.FAILED, TestLogEvent.SKIPPED + + // Prints the currently running test's name in the CI's build log, + // so that we can check if tests are being silently skipped or + // stalling the build. + if (System.env.'CI') { + events << TestLogEvent.STARTED + } + } + + jacoco { + destinationFile = new File("${buildDir}/jacoco/test.exec") + } + + doFirst { + boolean runGuiTests = gradle.taskGraph.hasTask(guiTests) + boolean runNonGuiTests = gradle.taskGraph.hasTask(nonGuiTests) + + if (!runGuiTests && !runNonGuiTests) { + runGuiTests = true + runNonGuiTests = true + } + + if (runNonGuiTests) { + test.include 'seedu/internship/**' + } + + if (runGuiTests) { + test.include 'systemtests/**' + test.include 'seedu/internship/ui/**' + } + + if (!runGuiTests) { + test.exclude 'seedu/internship/ui/**' + } + } +} + +tasks.withType(JavaExec) { + enableAssertions = true } defaultTasks 'clean', 'test' diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..bf4c712f597 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -2,58 +2,59 @@ layout: page title: About Us --- - -We are a team based in the [School of Computing, National University of Singapore](http://www.comp.nus.edu.sg). +# About InternBuddy +We are team InternBuddy, and we are a team based in the [School of Computing, National University of Singapore](http://www.comp.nus.edu.sg). You can reach us at the email `seer[at]comp.nus.edu.sg` -## Project team +## Project Team -### John Doe +### Christopher Tan Rui Yang - + -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/potty10)] +[[portfolio](team/potty10.md)] -* Role: Project Advisor +* Role: Team Lead +* Responsibilities: Testing -### Jane Doe +### Eugene Tang KangJie - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/eugenetangkj)] +[[portfolio](team/eugenetangkj.md)] -* Role: Team Lead -* Responsibilities: UI +* Role: Developer +* Responsibilities: UI, Documentation -### Johnny Doe +### Koh Kai Xun - + -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] +[[github](http://github.com/kohkaixun)] +[[portfolio](team/kohkaixun.md)] * Role: Developer -* Responsibilities: Data +* Responsibilities: Code Quality -### Jean Doe +### Lim Hai Leong Shawn - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/seadragon2000341)] +[[portfolio](team/seadragon2000341.md)] * Role: Developer -* Responsibilities: Dev Ops + Threading +* Responsibilities: Scheduling and Tracking, Deliverables and Deadlines -### James Doe +### Ou Chuhao - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/DerrickSaltFish)] +[[portfolio](team/derricksaltfish.md)] * Role: Developer -* Responsibilities: UI +* Responsibilities: Integration diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 46eae8ee565..f556bceb232 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -2,41 +2,140 @@ layout: page title: Developer Guide --- +## **Table of Contents** * Table of Contents {:toc} --------------------------------------------------------------------------------------------------------------------- +
-## **Acknowledgements** +## **Introducing InternBuddy** -* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +InternBuddy is a desktop application for [Computing undergraduates](#glossary) to manage their internship applications. +It is optimized for typing where it allows you to complete internship management tasks much more efficiently +via the keyboard as compared to using traditional [Graphical User Interface](#glossary) (GUI) applications. +If you are a fast typist who is seeking a one-stop platform to systematically organise your internship applications, +then InternBuddy is the perfect buddy to accompany you during your internship hunt. --------------------------------------------------------------------------------------------------------------------- -## **Setting up, getting started** +InternBuddy runs using Java 11, and is available on the Windows, macOS and Linux operating systems. -Refer to the guide [_Setting up and getting started_](SettingUp.md). +
+

+ +

--------------------------------------------------------------------------------------------------------------------- +
-## **Design** +[//]: # (@@author eugenetangkj - reused with modifications) +[//]: # (Adapted from https://ay2223s1-cs2103t-w17-4.github.io/tp/UserGuide.html#navigating-the-user-guide) + + +## **About the Developer Guide** + +### Objectives of the Developer Guide +This developer guide aims to provide developers with insights into the implementation of InternBuddy and to explain the +design considerations behind its features. It utilises Unified Modeling Language (UML) diagrams created using +[PlantUML](https://plantuml.com/) for a visual explanation of the implementation. + +Apart from shedding light on InternBuddy's internal details, this developer guide also provides +information on how to conduct feature testing and showcases the user study component that we went through +in the initial development phase for requirements gathering. + +Hopefully, this developer guide will enable you to easily set up the InternBuddy project and extend its +functionality if you are interested in doing so. + +
+### Using the Developer Guide +This developer guide uses a set of formatting standards and syntax to better communicate +information. + +**Information Box**
-: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. +:information_source: **Info:** Provides useful information that supplements the main text +
+ +**Tip Box** +
+ +:bulb: **Tip:** Suggestions on how to enhance your experience +
+ +**Warning Box** +
+ +:warning: **Warning:** Warns of a dangerous action that you should be aware of and to consider +carefully before committing +
+ +**Syntax Highlighting** + +[Commands](#glossary), [fields](#glossary), file paths and class names are highlighted. + +`command`, `FIELD`, `filepath.json`, `ClassName` + + +**Keyboard Actions** + +Keyboard keys are indicated using rounded buttons. + + + +
+ +## **Setting Up and Getting Started** + +Refer to the guide [_Setting up and getting started_](SettingUp.md) for instructions on how to +set up the InternBuddy project in your personal computer. After launching InternBuddy, you would see a GUI. +Figure 1 illustrates the main parts +of InternBuddy's GUI and Table 1 explains their functions. We will be referencing +these different parts throughout this developer guide. + +[//]: # (@@author eugenetangkj - reused with modifications) +[//]: # (Adapted from https://ay2223s1-cs2103t-w17-4.github.io/tp/UserGuide.html#navigating-the-user-guide) + +

+ +

+ +

Figure 1: InternBuddy's GUI

+ + +| Part | Usage | +|----------------|----------------------------------------------------------------------------------------------| +| Command Box | You can type in your commands here to interact with InternBuddy. | +| Result Display | This is where the results of your command will be displayed. | +| List Panel | Displays a list of internship entries. | +| View Panel | Displays either the welcome message or detailed information of a specified internship entry. | +| Status Bar | States where your InternBuddy data file is located on your computer. | + +

Table 1: Explanation of the different parts of InternBuddy's GUI

+ + +
+ +## **Design** + +
+ +:bulb: **Tip:** The `.puml` files used to create diagrams in this developer guide can be found in the [diagrams](https://github.com/AY2223S2-CS2103T-T14-3/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 these diagrams.
### Architecture +The ***Architecture Diagram*** shown in Figure 2 explains the high-level design of the App. +

+ +

- +

Figure 2: InternBuddy's architecture diagram

-The ***Architecture Diagram*** given above explains the high-level design of the App. -Given below is a quick overview of main components and how they interact with each other. +Given below is a quick overview of the main components and how they interact with each other. **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-T14-3/tp/blob/master/src/main/java/seedu/internship/Main.java) and [`MainApp`](https://github.com/AY2223S2-CS2103T-T14-3/tp/blob/master/src/main/java/seedu/internship/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. @@ -44,7 +143,7 @@ Given below is a quick overview of main components and how they interact with ea The rest of the App consists of four components. -* [**`UI`**](#ui-component): The UI of the App. +* [**`UI`**](#ui-component): The UI (User Interface) of the App. * [**`Logic`**](#logic-component): The command executor. * [**`Model`**](#model-component): Holds the data of the App in memory. * [**`Storage`**](#storage-component): Reads data from, and writes data to, the hard disk. @@ -52,196 +151,573 @@ The rest of the App consists of four components. **How the architecture components interact with each other** -The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete 1`. +Figure 3 is a *Sequence Diagram* that shows how the components interact with each other for the scenario where the user +issues the command `delete-index 1`. + - +

+ +

-Each of the four main components (also shown in the diagram above), +

Figure 3: Sequence diagram that shows interactions between components

+
+ +
+ +Each of the four main components (also shown in the Figure 3), * defines its *API* in an `interface` with the same name as the Component. * implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API `interface` mentioned in the previous point. -For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below. +For example, the `Logic` component defines its API in the `Logic.java` interface and implements its +functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components +interact with a given component through its interface rather than the concrete class +(reason: to prevent outside component's being coupled to the implementation of a component), +as illustrated in the (partial) class diagram shown in Figure 4. + + +

+ +

+ +

Figure 4: Partial class diagram for the logic, model and storage components

+
- The sections below give more details of each component. -### UI component +
+ +### UI Component -The **API** of this component is specified in [`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java) +The **API** of this component is specified in +[`Ui.java`](https://github.com/AY2223S2-CS2103T-T14-3/tp/blob/master/src/main/java/seedu/internship/ui/Ui.java). +The class diagram for the UI component is shown in Figure 5. -![Structure of the UI Component](images/UiClassDiagram.png) +

+ +

-The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. +

Figure 5: Class diagram for the UI component

+
-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 consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, +`InternshipListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, +inherit from the abstract `UiPart` class which captures the commonalities between classes that +represent parts of the visible GUI. + +The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching +`.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the +[`MainWindow`](https://github.com/AY2223S2-CS2103T-T14-3/tp/blob/master/src/main/java/seedu/internship/ui/MainWindow.java) +is specified in [`MainWindow.fxml`](https://github.com/AY2223S2-CS2103T-T14-3/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 `Internship` object residing in the `Model`. + +
-### Logic component +### 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-T14-3/tp/blob/master/src/main/java/seedu/internship/logic/Logic.java) + +Figure 6 illustrates a (partial) class diagram of the `Logic` component: + +

+ +

+ +

Figure 6: Partial class diagram for the logic component

+
-Here's a (partial) class diagram of the `Logic` component: - How the `Logic` component works: -1. When `Logic` is called upon to execute a command, it uses the `AddressBookParser` class to parse the user command. -1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddCommand`) which is executed by the `LogicManager`. -1. The command can communicate with the `Model` when it is executed (e.g. to add a person). -1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. +1. When `Logic` is called upon to execute a command, it uses the `InternBuddyParser` class to parse the user command. +2. This results in a `Command` object (more precisely, an object of one of its subclasses such as `AddCommand`) which is executed by the `LogicManager`. +3. The command can communicate with the `Model` when it is executed (e.g. to add an internship). +4. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. + + +Figure 7 below illustrates the interactions within the `Logic` component for the `execute("delete-index 1 2")` API call. + +![Interactions Inside the Logic Component for the `delete-index 1 2` Command](images/DeleteIndexSequenceDiagram.png) + + +

Figure 7: Sequence diagram for the delete-index command

+
-The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("delete 1")` API call. -![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png) -
:information_source: **Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. + + +
:information_source: **Info:** The lifeline for +`DeleteIndexCommandParser` and `DeleteIndexCommand` should end at the destroy marker (X) but due to a +limitation of PlantUML, the lifeline reaches the end of diagram.
-Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command: - + +Figure 8 shows the other classes in `Logic` (omitted from Figure 6) that are used for parsing a user command: + +

+ +

+ +

Figure 8: Class diagram for parser classes in the logic component

+
How the parsing works: -* When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `AddressBookParser` returns back as a `Command` object. -* All `XYZCommandParser` classes (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing. +* When called upon to parse a user command, the `InternBuddyParser` 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 +`InternBuddyParser` returns back as a `Command` object. +* All `XYZCommandParser` classes (e.g., `AddCommandParser`, `FindCommandParser`, ...) inherit from +the `Parser` interface so that they can be treated similarly where possible e.g, during testing. + +
+ +### Model Component +**API** : [`Model.java`](https://github.com/AY2223S2-CS2103T-T14-3/tp/blob/master/src/main/java/seedu/internship/model/Model.java) -### Model component -**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java) +Figure 9 is a class diagram for the `Model` component. - +

+ +

+

Figure 9: Class diagram for the model component

+
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 internsip data i.e., all `Internship` objects (which are contained in a `UniqueInternshipList` object). +* stores the currently 'selected' `Internship` objects (e.g., results of a search query) as a separate _filtered_ list +which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' +(e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.). * stores a `UserPref` object that represents the user’s preferences. This is exposed to the outside as a `ReadOnlyUserPref` objects. * does not depend on any of the other three components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components) -
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Person` references. This allows `AddressBook` to only require one `Tag` object per unique tag, instead of each `Person` needing their own `Tag` objects.
+
- +
:information_source: **Info:** An alternative (arguably, a more OOP) +model is shown in Figure 10. It has a `Tag` list in the `InternBuddy`, which `Internship` references. This allows `InternBuddy` +to only require one `Tag` object per unique tag, instead of each `Internship` needing their own `Tag` objects.
+

+ +

-### Storage component +

Figure 10: Alternative model that is more OOP

+
-**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java) - +
+ +### Storage Component + +**API** : [`Storage.java`](https://github.com/AY2223S2-CS2103T-T14-3/tp/blob/master/src/main/java/seedu/internship/storage/Storage.java) + + +Figure 11 is a class diagram for the `Storage` component. +

+ +

+ +

Figure 11: Class diagram for the storage component

+
+ The `Storage` component, -* can save both address book data and user preference data in json format, and read them back into corresponding objects. -* inherits from both `AddressBookStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed). +* can save both internship data and user preference data in json format, and read them back into corresponding objects. +* inherits from both `InternBuddyStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed). * depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`) -### Common classes +### Common Classes -Classes used by multiple components are in the `seedu.addressbook.commons` package. +Classes used by multiple components are in the [`seedu.internship.commons`](https://github.com/AY2223S2-CS2103T-T14-3/tp/tree/master/src/main/java/seedu/internship/commons) package. --------------------------------------------------------------------------------------------------------------------- +
## **Implementation** This section describes some noteworthy details on how certain features are implemented. -### \[Proposed\] Undo/redo feature +
:information_source: **Info:** Due to a limitation of PlantUML, the lifeline +for objects in sequence diagrams would always reach the end of the diagrams. However, it is worthy to note that objects +with destroy markers (X) would have been destroyed at the markers. -#### Proposed Implementation +
-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: +[//]: # (@@author eugenetangkj) +### Add an Internship - `add` -* `VersionedAddressBook#commit()` — Saves the current address book state in its history. -* `VersionedAddressBook#undo()` — Restores the previous address book state from its history. -* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history. +#### Implementation -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. +Figure 12 shows how the `add` command works. -Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. +![AddSequenceDiagram](images/AddSequenceDiagram.png) -Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state. +

Figure 12: Sequence diagram for the add command

+
-![UndoRedoState0](images/UndoRedoState0.png) -Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state. -![UndoRedoState1](images/UndoRedoState1.png) +The following gives a more detailed explanation of the `add` command. -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`. -![UndoRedoState2](images/UndoRedoState2.png) +1. When the user enters an `add` command, the `AddCommandParser` parses the user's input. +2. It checks for the following: + - `n/` followed by the company's name [Compulsory] + - `r/` followed by the role applied [Compulsory] + - `s/` followed by the status of the internship application [Compulsory] + - `d/` followed by the date associated with the entry [Compulsory] + - `c/` followed by the comment for the entry [Optional] + - `t/` followed by tags for the entry [Optional] +3. If any of the compulsory fields is missing or any of the fields entered by the user + does not meet the field requirements, a `ParseException` will be thrown. +4. An `Internship` will be created from the parsed user's input if Step 3 passes. +5. A check is done to see if the `Model` component, which stores all the `Internship` entries, + contains the `Internship` created in Step 4. +6. If a duplicate `Internship` is found, a `CommandException` will be thrown. +7. Else if there is no duplicate `Internship`, the `Internship` created will be added into + the `Model` component. +8. The currently selected `Internship` in the `Model` component will be updated to become + this new `Internship` such that the [View Panel](#setting-up-and-getting-started) displays the information for + this new `Internship`. -
: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`. +
-
+#### Design Considerations -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. +- Whether to make all fields in the `add` command compulsory -![UndoRedoState3](images/UndoRedoState3.png) +1. **Alternative 1 (chosen): Make only essential fields compulsory** + * Pros: More user-centric as not all users want to enter the optional information, + which is not exactly critical in tracking internships. + * Cons: More work needs to be done in code implementation. For example, the absence of optional + fields should not cause a `ParseException`, and there is a need to include a + default value of `NA` for input without any `Comment`. -
: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. +2. **Alternative 2: Make all fields compulsory** + * Pros: Easier to implement as there is no need to differentiate between compulsory + and optional fields during command parsing, and it is easier to compare between + different `Internship` since we just require an exact match of all fields. + * Cons: Less user-centric where users who do not want to include `Comment` and `Tag` + are forced to input something for the `Add` command to work. -
+
-The following sequence diagram shows how the undo operation works: +- Whether to update the [View Panel](#setting-up-and-getting-started) according to the `add` command -![UndoSequenceDiagram](images/UndoSequenceDiagram.png) +1. **Alternative 1 (chosen): Update the [View Panel](#setting-up-and-getting-started) whenever a new** `Internship` **is added** + * Pros: Better visual indication that the `add` command has been successfully executed. + If the user has a lot of `Internship` entries, when a new `Internship` is added, + the new entry will be placed at the bottom of the [List Panel](#setting-up-and-getting-started), + which is not visible if the user's scroll position is at the top of the + [List Panel](#setting-up-and-getting-started). Therefore, updating + the [View Panel](#setting-up-and-getting-started) enhances visual indication to the user + that the `Internship` has been successfully added. + * Cons: An additional line of code is required in the `execute` method of `AddCommand` + to update the selected `Internship` in the `Model` component in order to update + the [View Panel](#setting-up-and-getting-started). -
: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. +2. **Alternative 2: Do not update the [View Panel](#setting-up-and-getting-started) when a new** `Internship` **is added** + * Pros: No additional code is required in the `execute` method of `AddCommand`. + * Cons: When the user has a lot of `Internship` entries, the added entry in the + [List Panel](#setting-up-and-getting-started) may not be visible since it is added to the bottom. + Without scrolling, users have to rely on the [Result Display](#setting-up-and-getting-started) to + determine if the `AddCommand` is successful. -
-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. -
: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. +
-
+[//]: # (@@author seadragon2000341) +### Edit an Internship - `edit` + +#### Implementation + + +Figure 13 shows how the `edit` command works. + +![EditSequenceDiagram](images/EditSequenceDiagram.png) +

Figure 13: Sequence diagram for the edit command

+
+ + +The following gives a more detailed explanation of the `edit` command. + +1. When the user enters an `edit` command, the `EditCommandParser` parses the user's input. +2. If the internship index specified is invalid, a `ParseException` will be thrown and the specified `Internship` will not be edited. +3. If the company name, role, status, date, tag or comment fields are missing (at least one must be present) or invalid, a `ParseException` will be thrown and the specified `Internship` will not be edited. +4. After the successful parsing of user input into `EditCommandParser`, the `EditCommand` object is created with a new updated `Internship` object (to maintain immutability). +5. Following which, `EditCommand#execute(Model model)` method is called which eventually calls the `Model#setInternship(Internship toEdit, Internship edited)` method, replacing the old `Internship` object with the newly updated one. + +
+ +#### Design considerations + +- How `edit` executes + +1. **Alternative 1 (chosen): Edit command will create a new** `Internship` **to replace the existing** `Internship` **object.** + * Pros: + * Maintains immutability of `Internship` class. + * Cons: + * May be less efficient than Alternative 2. + +2. **Alternative 2: Edit command will directly edit the** `Internship` **by modifying its attributes**. + * Pros: + * Will use less memory (no new `Internship` object will be created). + * Saves time since there is no need to create the new object. + * Cons: + * Reduces the defensiveness of the code and the class. + +
+ +[//]: # (@@author eugenetangkj) +### View an Internship - `view` + +#### Implementation +Figure 14 shows how the `view` command works. + +![ViewSequenceDiagram](images/ViewSequenceDiagram.png) + +

Figure 14: Sequence diagram for the view command

+
+ +The following gives a more detailed explanation of the `view` command. + + +1. When the user enters a `view` command, the `ViewCommandParser` parses the user's input. +2. It checks for the following: + - The `INDEX` entered by the user must be able to be converted into a numeric index. +3. If the user entered a value of `INDEX` that cannot be converted, a `ParseException` will + be thrown. +4. An `Index` will be created from the user's input if Step 2 passes. +5. A check is done to see if the `Index` created in Step 4 is a valid index given the number + of `Internship` entries in the filtered `Internship` list of the `Model` component. +6. If the `Index` is invalid, a `CommandException` will be thrown. +7. Else if the `Index` is valid, the `Internship` which belongs to that `Index` will be + retrieved by accessing the filtered `Internship` list. +8. The currently selected `Internship` in the `Model` component will be updated to become + the `Internship` obtained from Step 7 such that the [View Panel](#setting-up-and-getting-started) displays the + information for this selected `Internship`. + + +#### Design Considerations + +- Whether to separate the checking of valid user input into 2 classes + +1. **Alternative 1: Allow** `ViewCommand` **to handle the checking of whether user input can be + parsed into an index, and whether it is a valid index** + * Pros: No need for a separate `ViewCommandParser` class and any problems with checking of + user input can be isolated to the `ViewCommand` class. + * Cons: Breaks the abstraction where parsing of user input should be done by a `Parser` + class instead of a `Command` class. + +2. **Alternative 2 (chosen): Allow** `ViewCommandParser` **to handle checking of whether user input + can be parsed into an index, and `ViewCommand` to handle checking of whether it + is a valid index** + * Pros: Maintains the abstraction between the `Parser` and `Command` classes. Also, it + makes it more maintainable for future extensions in the event that further checks + to the user input are required. + * Cons: Have to maintain code in 2 separate classes and in the event that there + is an issue in processing user input for the `ViewCommand`, there is a need to + identify and isolate which of the 2 checks does the problem originate from. + +
+ +[//]: # (@@author DerrickSaltFish) +### Copy an Internship to Clipboard - `copy` +#### Implementation +Figure 15 shows how the `copy` command works. + +![CopySequenceDiagram](images/CopySequenceDiagram.png) + +

Figure 15: Sequence diagram for the copy command

+
+ + +The following gives a more detailed explanation of the `copy` command. + +1. When the user enters a `copy` command, the `CopyCommandParser` parses the user's input. +2. It checks for the following: + - The `INDEX` entered by the user must be able to be converted into a numeric index. +3. If the user entered a value of `INDEX` that cannot be converted, a `ParseException` will + be thrown. +4. An `Index` will be created from the user's input if Step 2 passes. +5. A check is done to see if the `Index` created in Step 4 is a valid index given the number + of `Internship` entries in the filtered `Internship` list of the `Model` component. +6. If the `Index` is invalid, a `CommandException` will be thrown. +7. Else if the `Index` is valid, the `Internship` which belongs to that `Index` will be + retrieved by accessing the filtered `Internship` list. +8. Following which, `CopyCommand#execute(Model model)` method is called which eventually calls the `Model#copyInternship(Internship target)` method. This copies the `toString()` representation of the `Internship` object onto the clipboard. + +#### Design Considerations + +- How to run the clipboard operation + +1. **Alternative 1: Run the clipboard code directly.** + * Will work if the testing framework is already running on the event dispatch thread (such as JUnit Swing or FEST-Swing). However, this is + not the case for this project. + +2. **Alternative 2: (chosen): Use the** `SwingUtilities.invokeLater()` **method to wrap the clipboard code in a Runnable object** + * Ensures that the clipboard operation is safe to run from a test or a non-GUI context. + + +
+ +[//]: # (@@author kohkaixun) +### Find Internships - `find` +#### Implementation + +Figure 16 shows how the `find` command works. + +![FindSequenceDiagram](images/FindSequenceDiagram.png) + +

Figure 16: Sequence diagram for the find command

+
+ + + + +The following gives a more detailed explanation of the `find` command. + +1. If the name, role, status, date and tag fields are all missing or one of their values are invalid, a `ParseException` will be thrown and the `FindCommand` will not be executed. +2. After the successful parsing of user input into `FindCommandParser`, an `InternshipContainsKeywordPredicate` object, containing the lists of keywords specified in the user input, is created, which in turn is used to create a `FindCommand` object. +3. Following which, the `FindCommand#execute(Model model)` method is called which eventually calls the `updateFilteredInternshipList(Predicate predicate)` method with the `InternshipContainsKeywordPredicate` object, previously created by `FindCommandParser`, as its argument and updates the `FilteredList` stored inside the `Model`. + +
+ +#### Design Considerations + +- How `find` command uses user input -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. +1. **Alternative 1 (chosen): Find an internship by exact match of user input and the** `Internship` **object's corresponding attributes.** + * Pros: + * Instructions on how to use the find command will be clear and easily communicated to user. + * Cons: + * Restrictive for the user (e.g. An internship with the company name `Google Ltd` will not turn up for the command `find n/Google`). -![UndoRedoState4](images/UndoRedoState4.png) +2. **Alternative 2: Find an internship by match of user input and the substrings in the** `Internship` **object's attributes**. + * Pros: + * Command is much more flexible (e.g. An internship with the company name `Google` will turn up for the find command `find n/goo`). + * Cons: + * May be confusing for the user (e.g. The user assumes that the `find` command works for matching substrings of individual words. He inputs `find n/goo inc` for an `Internship` with company name `Google Incorporated`). -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. +3. **Alternative 3: Find an internship by match of any word of user input and any word of the** `Internship` **object's corresponding attributes.** + * Pros: + * Command is much more flexible. + * Cons: + * Command becomes too flexible (e.g. A `find` command like `find n/google ltd` will return `Internship` objects that have either the word `google` or `ltd` in their company names. Internships with company names such as `Apple ltd` and `Meta Ltd` will be returned. -![UndoRedoState5](images/UndoRedoState5.png) -The following activity diagram summarizes what happens when a user executes a new command: - +
-#### Design considerations: +[//]: # (@@author seadragon2000341) +### Get Upcoming Events and Deadlines - `upcoming` -**Aspect: How undo & redo executes:** +#### Implementation +Figure 17 shows how the `upcoming` command works. -* **Alternative 1 (current choice):** Saves the entire address book. - * Pros: Easy to implement. - * Cons: May have performance issues in terms of memory usage. +![UpcomingSequenceDiagram](images/UpcomingSequenceDiagram.png) +

Figure 17: Sequence diagram for the upcoming command

+
-* **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 following gives a more detailed explanation of the `upcoming` command. +1. When the user enters an `upcoming` command, an `UpcomingCommand` object is created. +2. Following which, `FilteredList` stored inside the model will be updated by checking each internship entry against a predicate. +3. The predicate checks whether both of the following conditions are met: +- The `STATUS` of the internship must be one of the following: + - `NEW` + - `ASSESSMENT` + - `INTERVIEW` + - `OFFERED` +- The `DATE` must be within the upcoming week. -_{more aspects and alternatives to be added}_ +
-### \[Proposed\] Data archiving +#### Design Considerations -_{Explain here how the data archiving feature will be implemented}_ +- Whether to include all possible statuses of an internship +1. **Alternative 1 (chosen): The predicate for the** `upcoming` **command should be limited to internships that have the status** `NEW/ASSESSMENT/INTERVIEW/OFFERED`. + * Pros: Makes more practical sense as these statuses have dates that are tied to an event or deadline: + * `NEW` - Deadline of Application + * `ASSESSMENT` - Date of Assessment + * `INTERVIEW` - Date of Interview + * `Offered` - Deadline of Offer Acceptance + * Cons: If the instructions for using the command are not clearly explained in the user guide, users may have difficulty understanding the output that is generated. + +2. **Alternative 2: Internships with any status would be accepted, even with statuses that are not tied to an upcoming event or deadline.** + * Pros: May be more intuitive for users to understand. + * Cons: This may cause users to forget the intended use case of the command, leading to confusion or misuse. --------------------------------------------------------------------------------------------------------------------- +
-## **Documentation, logging, testing, configuration, dev-ops** +[//]: # (@@author potty10) +### Delete Internship Entries by Fields - `delete-field` +#### Implementation +Figure 18 shows how the `delete-field` command works. + +![DeleteFieldSequenceDiagram](images/DeleteFieldSequenceDiagram.png) +

Figure 18: Sequence diagram for the delete-field command

+
+ +The following gives a more detailed explanation of the `delete-field` command. + +1. When the user enters a `delete-field` command, the `DeleteFieldCommandParser` parses the user's input. It attempts to parse the arguments as a set of prefixed +fields (`[n/COMPANY_NAME] [r/ROLE] [s/STATUS] [d/DATE] [t/TAG]`). +2. A new `InternshipContainsKeywordPredicate` object is created. A `DeleteFieldCommand` object is then created. +3. When the `DeleteFieldCommand` object executes, a list of `Internship` objects is obtained with `model.getFilteredInternshipList()`. +4. For each `Internship` object in the list that matches with **at least one** value for + **every** field type that is specified, it will be deleted using `model.deleteInternship(internshipToDelete)` + +
+ +#### Design Considerations + +- **Whether to use an AND relationship or an OR relationship between different fields** + * For example, should `delete-field n\Google r\Software Engineer` delete internships that have company name of Google AND role as Software Engineer, or delete internships that have company name of Google OR role of Software Engineer? + +1. **Alternative 1 (chosen): Use an AND relationship** + * Pros: More user-centric as users will be able to have more fine-grained control over what internships they want to delete. For example, they may want to delete all internship entries related to a certain company AND role. + * Cons: If users want to delete internships based on an OR relationships, they need to call `delete-field` multiple times. + +2. **Alternative 2: Use an OR relationship** + * Pros: Much easier for the user to reason about which internships will be deleted. + * Cons: Less fine-grained control over `delete`. For example, there is no way to delete internships that have company name of Google AND role of Software Engineer + +
+ +- **Whether to add this enhancement to the** `clear` **or** `delete` **command, or to create a new command entirely** + +1. **Alternative 1 (chosen):** Split the `delete` command into `delete-index` and `delete-field`. `delete-index` will delete only indices, while `delete-field` will only delete by fields. + * Pros: Both commands now share the same prefix `delete`, so it is easier to remember. + * Cons: Delete now has 2 formats, and this may be a source of confusion. +2. **Alternative 2: Enhance the** `delete` **command only. For example,** `delete 1 2 n/Google` **will delete any of the first 2 entries if they have a company name of** `Google`. + * Pros: Combining all delete features into one command makes it easier to remember. + * Cons: + * Very difficult to justify usage. It is unlikely for a user to require such absolute fine-grained control. A more likely use case is to mass delete internships that are no longer required. + * Difficult to define a suitable interpretation of the fields. For example, in the command `delete 1 2 n/Google`, + the command should delete internships with (Index 1 OR 2) AND have a company name `Google`. Maintaining both AND and OR relationships can be confusing for the user. + +3. **Alternative 3: Enhance the** `clear` **command**. + * Pros: The current `clear` command takes in no arguments, so it is much easier to implement. + * Cons: May be confusing to the user, since there will be no clear distinction between `delete` and `clear`. + + + + + +## **Documentation, Logging, Testing, Configuration, Dev-ops** * [Documentation guide](Documentation.md) * [Testing guide](Testing.md) @@ -249,129 +725,1022 @@ _{Explain here how the data archiving feature will be implemented}_ * [Configuration guide](Configuration.md) * [DevOps guide](DevOps.md) --------------------------------------------------------------------------------------------------------------------- -## **Appendix: Requirements** +
+ +## **Appendix A: Requirements** -### Product scope +### Product Scope -**Target user profile**: +**Target user profile:** +[Computing Undergraduates](#glossary) -* 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 +**Characteristics of user profile:** +* Has a need to manage many internship applications -**Value proposition**: manage contacts faster than a typical mouse/GUI driven app +Internships form an integral part of the undergraduate curriculum for Computing Undergraduates. +In a technical field like Computing, it is especially important for undergraduates to practice what they have learnt in classrooms. +However, given the tight competition in the market, many undergraduates source for numerous internship opportunities +before being accepted for one. Therefore, many Computing undergraduates face the need to track many applications +where each could be in a different phase of the application process. +* Prefers typing to mouse interactions, with good typing speed + +Computing undergraduates have great exposure to computer usage where coding assignments and projects in school require +extensive typing. This justifies a sufficiently good level of proficiency with regard to typing. In fact, with the +existence of keyboard shortcuts, many programmers prefer typing over using the mouse because of the efficiency gains. + + +* Reasonably comfortable in using [Command Line Interface](#glossary) (CLI) apps + +CLI provides a simple way to interact with computers to run programs and manage files. +Computing undergraduates are taught how to use the CLI in their curriculums, and are often required to use it +to run system tasks that cannot be done over the GUI. Hence, this would imply a reasonable level of comfort in using +the CLI interface. + + +* Prefers desktop applications over other types + +**Value proposition**: + +InternBuddy aims to provide a 1-stop platform for a computing undergraduate to view and +manage his internship applications. Consolidating internship information, the application provides organisational +capabilities for easy tracking and follow-ups while eliminating the need to handle data across multiple platforms. + + + +### User Stories +Table 2 documents the user stories for InternBuddy. -### User stories Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` -| Priority | As a …​ | I want to …​ | So that I can…​ | -| -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- | -| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App | -| `* * *` | user | add a new person | | -| `* * *` | user | delete a person | remove entries that I no longer need | -| `* * *` | user | find a person by name | locate details of persons without having to go through the entire list | -| `* *` | user | hide private contact details | minimize chance of someone else seeing them by accident | -| `*` | user with many persons in the address book | sort persons by name | locate a person easily | -*{More to be added}* +| Priority | As a …​ | I want to …​ | So that I can…​ | +|----------|------------------------------------------------------------------------------------------|------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------| +| `* * *` | Computing undergraduate with many internship applications | list out all the entries | browse through my list of applications. | +| `* * *` | Computing undergraduate applying for many internships | add a new entry | manage new applications using InternBuddy. | +| `* * *` | Computing undergraduate managing many concurrent internship applications | add a status to each entry | track the status of each application. | +| `* * *` | Computing undergraduate who is planning to track internship applications in the long run | delete entries | remove outdated or irrelevant entries from InternBuddy. | +| `* * *` | Computing undergraduate who is planning to track internship applications in the long run | store data | resume from where I left off in my previous run of InternBuddy. | +| `* * *` | Computing undergraduate who is a new user of InternBuddy | view the list of supported commands | refer to it when I am unsure about the usage of InternBuddy. | +| `* *` | meticulous Computing undergraduate | be notified that InternBuddy is exiting | be rest assured that InternBuddy has successfully terminated when I exit it. | +| `* *` | careless Computing undergraduate | modify the details of an entry | correct my typos without having to create a new entry from scratch. | +| `* *` | careless Computing undergraduate | be prompted with a confirmation message before I delete an entry | avoid accidental deletes. | +| `* *` | forgetful Computing undergraduate | rely on auto-saving of data | avoid the problem of forgetting to save my entries when I make changes to them. | +| `* *` | Computing undergraduate applying for technical roles | tag each entry with its associated [tech stack](#glossary) | identify the technical requirements associated with each application. | +| `* *` | Computing undergraduate applying for technical roles | filter internship entries by tags | narrow down the search to internship applications with the tech stack that I would like to work on. | +| `* *` | Computing undergraduate managing many concurrent internship applications | filter internship entries by date | identify the upcoming tasks or deadlines. | +| `* *` | Computing undergraduate with many internship applications | search an entry by name | easily and swiftly locate the desired entry. | +| `* *` | Computing undergraduate who is not extremely proficient with the command line interface | have intuitive and simple-to-pick-up commands | use InternBuddy without much technical difficulties. | +| `* *` | detail-oriented Computing undergraduate | add custom remarks to each entry | have the flexibility of documenting miscellaneous but relevant information. | +| `* *` | Computing undergraduate managing many concurrent internship applications | obtain reminders | avoid forgetting about upcoming tasks or deadlines. | +| `* *` | Computing undergraduate using multiple devices | export my internship data into a file | view the same data when I am not using the device with InternBuddy installed. | +| `* *` | Computing undergraduate who wants to share my internship details with others | copy the details of an internship | send the details to other people. | +| `*` | Computing undergraduate who is slow in learning | go through a step-by-step in-app tutorial | learn how to use InternBuddy in a guided and self-paced environment. | +| `*` | analytical Computing undergraduate | have a summary overview of all the entries | analyse the composition of the entries, such as what percentage of applications were successful. | +| `*` | Computing undergraduate who is new to internships | receive tips | use the tips to improve my experience during the internship application process. | +| `*` | Computing undergraduate who is planning to track internship applications in the long run | archive old entries | delete them from InternBuddy while maintaining a backup copy of the outdated data. | +| `*` | Computing undergraduate who is experienced in using InternBuddy | have shortcuts to existing commands | carry out tasks in InternBuddy even more efficiently than previously. | + +

Table 2: List of user stories

+ + + +### Use Cases -### Use cases +(For all use cases below, the **System** is `InternBuddy` and the **Actor** is the `user`, unless specified otherwise) -(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise) +**Use case: List all internships** -**Use case: Delete a person** +**Main Success Scenario** -**MSS** +1. User requests for the list of all internship applications in InternBuddy. +2. InternBuddy displays a list of all the internship entries. -1. User requests to list persons -2. AddressBook shows a list of persons -3. User requests to delete a specific person in the list -4. AddressBook deletes the person + Use case ends. + +
+ +**Use case: Add an internship** + +**Main Success Scenario** + +1. User enters internship entry. +2. InternBuddy adds an internship entry, updates the View Panel, and displays a success message. Use case ends. **Extensions** -* 2a. The list is empty. +* 1a. InternBuddy detects that one or more compulsory fields are missing. + * 1a1. InternBuddy prompts the user for an `add` command of the correct format. - Use case ends. + Use case resumes from Step 1. -* 3a. The given index is invalid. +* 1b. InternBuddy detects one or more fields are invalid. + * 1b1. InternBuddy prompts the user for an `add` command of the correct format. - * 3a1. AddressBook shows an error message. + Use case resumes from Step 1. - Use case resumes at step 2. +
-*{More to be added}* +**Use Case: Edit an internship** -### Non-Functional Requirements +**Main Success Story** + +1. User edits an internship entry. +2. InternBuddy updates that particular internship entry, updates the View Panel, and displays a success message. + + Use case ends. + +**Extensions** + +* 1a. InternBuddy detects that no fields are provided. + * 1a1. InternBuddy prompts the user for an `edit` command of the correct format. + + Use case resumes from Step 1. + +* 1b. InternBuddy detects one or more fields are invalid. + * 1b1. InternBuddy prompts the user for an `edit` command of the correct format. + + Use case resumes from Step 1. + +
+ +**Use Case: View an internship** + +**Main Success Scenario** + +1. User views an internship entry. +2. InternBuddy updates the View Panel with the details of the internship that is being viewed, + and displays a success message. + + Use case ends. + +**Extensions** + +* 1a. InternBuddy detects that the required internship index is missing. + * 1a1. InternBuddy prompts the user for an `view` command of the correct format. + + Use case resumes from Step 1. + +* 1b. InternBuddy detects that the internship index entered is invalid. + * 1b1. InternBuddy prompts the user for a `view` command of the correct format. + + Use case resumes from Step 1. + +* 1c. InternBuddy detects that the index is out of range. + * 1c1. InternBuddy informs the user that the index is out of range. + + Use case resumes from Step 1. + +
+ +**Use Case: Copy an internship to clipboard** + +**Main Success Scenario** + +1. User copies an internship entry to clipboard. +2. InternBuddy displays a success message. + + Use case ends. + +**Extensions** + +* 1a. InternBuddy detects that the required internship index is missing. + * 1a1. InternBuddy prompts the user for a `copy` command of the correct format. + + Use case resumes from Step 1. + +* 1b. InternBuddy detects that the internship index entered is invalid. + * 1b1. InternBuddy prompts the user for a `view` command of the correct format. + + Use case resumes from Step 1. + +* 1c. InternBuddy detects that the index is out of range. + * 1c1. InternBuddy informs the user that the index is out of range. + + Use case resumes from Step 1. + + +
+ + +**Use Case: Find internships** + +**Main Success Scenario** + +1. User finds internship entries based on the fields given. +2. InternBuddy lists the internships that match the given fields and displays a success message indicating + how many internships were found. + + Use case ends. + +**Extensions** + +* 1a. InternBuddy detects that no fields are provided. + * 1a1. InternBuddy prompts the user for a `find` command of the correct format. + + Use case resumes from Step 1. + +* 1b. InternBuddy detects that one or more fields given are invalid. + * 1b1. InternBuddy prompts the user for a `find` command of the correct format. + + Use case resumes from Step 1. + + +
+ +**Use Case: Get internships with upcoming events or deadlines** + +**Main Success Scenario** + +1. User requests for internship entries with upcoming events or deadlines. +2. InternBuddy displays a list of internship entries with events or deadlines within the week. + + Use case ends. + +
+ +**Use Case: Delete internships by indices** + +**Main Success Scenario** + +1. User deletes internship entries based on specified indices. +2. InternBuddy displays a success message that indicates how many internships were deleted. + + Use case ends. + +**Extensions** + +* 1a. InternBuddy detects that no index is given. + * 1a1. InternBuddy prompts the user for a `delete-index` command of the correct format. + + Use case resumes from Step 1. + +* 1b. InternBuddy detects that one or more fields given are invalid. + * 1b1. InternBuddy prompts the user for a `delete-index` command of the correct format. + + Use case resumes from Step 1. + +* 1c. InternBuddy detects that one or more of the given indices are out of range. + * 1c1. InternBuddy informs the user that the index is out of range. + + Use case resumes from Step 1. + +* 2a. InternBuddy detects that the internship whose details are currently displayed in the + [View Panel](#setting-up-and-getting-started) has been deleted by this `delete-index` command. + * 2a1. InternBuddy resets the [View Panel](#setting-up-and-getting-started) to display the + welcome message. + + Use case resumes from Step 2. + +
+ +
+ +**Use Case: Delete internships by fields** + +**Main Success Scenario** + +1. User deletes internship entries based on specified fields. +2. InternBuddy displays a success message that indicates how many internships were deleted. + + Use case ends. + +**Extensions** + +* 1a. InternBuddy detects that no field is given. + * 1a1. InternBuddy prompts the user for a `delete-field` command of the correct format. + + Use case resumes from Step 1. + +* 1b. InternBuddy detects that one or more fields given are invalid. + * 1b1. InternBuddy prompts the user for a `delete-field` command of the correct format. -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. + Use case resumes from Step 1. -*{More to be added}* +* 2a. InternBuddy detects that the internship whose details are currently displayed in the + [View Panel](#setting-up-and-getting-started) has been deleted by this `delete-field` command. + * 2a1. InternBuddy resets the [View Panel](#setting-up-and-getting-started) to display the + welcome message. -### Glossary + Use case resumes from Step 2. -* **Mainstream OS**: Windows, Linux, Unix, OS-X -* **Private contact detail**: A contact detail that is not meant to be shared with others +
--------------------------------------------------------------------------------------------------------------------- -## **Appendix: Instructions for manual testing** +**Use Case: Clear all internships** -Given below are instructions to test the app manually. +**Main Success Scenario** -
:information_source: **Note:** These instructions only provide a starting point for testers to work on; -testers are expected to do more *exploratory* testing. +1. User requests to clear all internship entries stored in InternBuddy. +2. InternBuddy deletes all internship entries. It resets the [View Panel](#setting-up-and-getting-started) to display the welcome + message and shows a success message. + + Use case ends. + +
+ +
+ +**Use Case: Get Help** + +**Main Success Scenario** + +1. User requests for help in using InternBuddy. +2. InternBuddy opens up a new window that displays the list of commands supported by InternBuddy and provides + the link to InternBuddy's user guide. + + Use case ends. + +
+ + +**Use case: Exit InternBuddy** + +**Main Success Scenario** + +1. User requests to exit InternBuddy. +2. InternBuddy closes. + + Use case ends. + + +### Non-Functional Requirements + +1. InternBuddy should work on any [mainstream operating systems](#glossary) as long as it has Java `11` or above installed. +2. InternBuddy should be able to hold up to 500 internship entries without a noticeable sluggishness in performance for typical usage. +3. InternBuddy should be able to respond to user input within 6 seconds. +4. A Computing undergraduate 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. +5. Computing undergraduates who have never used command line applications to track internships before should be able to easily pick up InternBuddy. +6. InternBuddy is not required to handle concurrent users. +7. InternBuddy is not required to make data available online. + + +
+ +## **Appendix B: Instructions for Manual Testing** + +Given below are instructions and test cases to test InternBuddy manually. + +
:information_source: **Note:** These instructions only provide a starting point for testers to work on. +Testers are expected to do more *exploratory* testing. Also, each test case is independent of the other test cases.
-### Launch and shutdown +### Launch and Shutdown + +1. **Initial launch** + + 1. Download the [InternBuddy jar file](https://github.com/AY2223S2-CS2103T-T14-3/tp/releases) and copy into an empty folder. + + 2. Double-click the jar file. + + **Expected**: Shows the GUI with a set of sample internships. The window size may not be optimal. + + +2. **Saving window preferences** + + 1. Resize the window to an optimum 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 are retained. + +### List all Internships + +1. `list` + + **Expected**: All internship entries are listed out and displayed in the List Panel. + + +2. `list hello` + + **Expected**: All internship entries are listed out and displayed in the List Panel. + + +3. `list edit 1 n/Apples` + + **Expected**: All internship entries are listed out and displayed in the List Panel. + +### Add an Internship + +1. `add n/Visa r/Software Engineer s/New d/2023-03-01 c/Considering to apply t/Payment` + + **Expected**: A new internship entry is successfully added. The new internship entry will have company name + `Visa`, role `Software Engineer`, status `New`, deadline of application `2023-03-01`, comment `Considering to apply`, + and tag `Payment`. The View Panel displays the information for this new internship entry, and a success + message is displayed in the Result Display. + +2. `add n/Mastercard r/Software Engineer s/New d/2023-03-01` + + **Expected**: A new internship entry is successfully added. The new internship entry will have company name + `Mastercard`, role `Software Engineer`, status `New`, deadline of application `2023-03-01`. The View Panel displays the information for this new internship entry, where the comment is shown as `NA`. A success message + is displayed in the Result Display. + +3. `add n/Visa s/New d/2023-03-01` + + **Expected**: No new internship is added. An error message is displayed in the Result Display. + This is because the compulsory field for role is missing. + +4. `add n/Visa r/Software Engineer s/Applying d/2023-03-01` + + **Expected**: No new internship is added. An error message is displayed in the Result Display. + This is because `Applying` is not a valid value for the `STATUS` field. + +5. `add n/Visa r/Software Engineer s/Applied d/1st March 2023` + + **Expected**: No new internship is added. An error message is displayed in the Result Display. + This is because the field for `DATE` must be in the format of `YYYY-MM-DD`. + +6. `add n/Visa r/Software Engineer s/Applied d/2023-02-32` + + **Expected**: No new internship is added. An error message is displayed in the Result Display. + This is because `2023-02-32` is not a valid date (i.e. March does not have 32 days). + +7. `add n/Visa r/Software Engineer s/Applied d/2023-02-15 c/` + + **Expected**: No new internship is added. An error message is displayed in the Result Display. + This is because the `COMMENT` field cannot be left blank. + +
+ +### Edit an Internship +Assumptions: The sample data provided by InternBuddy is used, where there is a total of 7 internship entries. + + +1. `edit 2 n/Amazon Technologies` + + **Expected**: The company name of the second internship entry is updated to `Amazon Technologies`. + The View Panel displays the updated details of the second internship entry. + +2. `edit 2 n/Amazon Technologies s/Applied` + + **Expected**: The company name and status of the second internship entry are updated to + `Amazon Technologies` and `Applied` respectively. The View Panel displays the updated details + of the second internship entry. + +3. `edit 2 t/front-end` + + **Expected**: All previous tags for the second internship entry are removed, and a new tag + `front-end` is added. The View Panel displays the updated details of the second internship + entry. + +4. `edit 2 c/` + + **Expected**: The comment of the second internship entry is updated to `NA`. The View Panel + displays the updated details of the second internship entry. + +5. Successful editing through the filtered internship list + 1. `find n/Apple n/Google` + 2. `edit 2 n/Google Technologies` + + **Expected**: The company name of the internship entry whose original company name is `Google` is updated + to become `Google Technologies`. The View Panel displays the updated details for this internship entry. + +6. Unsuccessful editing through the filtered internship list + 1. `find n/Apple n/Google` + 2. `edit 3 n/Google Technologies` + + **Expected**: An error message is displayed in the Result Display. This is because in the filtered + internship list, there are only 2 internship entries, implying that`3` is not a valid value for the + `INDEX` field. + +7. `edit 2 n/Amazon Technologies s/Applying` + + **Expected**: No internship is edited. An error message is displayed in the Result Display. + This is because `Applying` is an invalid status. + +8. `edit` + + **Expected**: An error message is displayed in the Result Display. This is because a minimum of 1 + optional field must be specified. + +9. `edit -2 n/Amazon Technologies` + + **Expected**: An error message is displayed in the Result Display. This is because the `INDEX` + field must be a positive integer greater than or equal to 1. + +10. `edit 12 n/Amazon Technologies` + + **Expected**: An error message is displayed in the Result Display. This is because there are only + 7 internship entries in the sample data. Index 12 is out of range. + +
+ +### View an Internship +Assumptions: The sample data provided by InternBuddy is used, where there is a total of 7 internship entries. +1. `view 2` + + **Expected**: The View Panel displays the details for the second internship entry. + +2. Successful viewing through the filtered internship list + + 1. `find n/Apple n/Google` + 2. `view 2` + + **Expected**: The View Panel displays the details for the second internship entry in the + filtered internship list. In this case, it displays the details for the entry whose company + name is `Google`. + +3. Unsuccessful viewing through the filtered internship list + 1. `find n/Apple n/Google` + 2. `view 3` + + **Expected**: An error message is displayed in the Result Display. This is because in the filtered + internship list, there are only 2 internship entries, implying that `3` is not a valid value for the + `INDEX` field. + +4. `view -1` + + **Expected**: An error message is displayed in the Result Display. This is because the `INDEX` + field must be a positive integer greater than or equal to 1. + +5. `view 1 2` + + **Expected**: An error message is displayed in the Result Display. This is because the `view` + command does not support viewing of more than 1 internship entry simultaneously. + +6. `view 12` + + **Expected**: An error message is displayed in the Result Display. This is because there are only + 7 internship entries in the sample data. Index 12 is out of range. + +7. `view` + + **Expected**: An error message is displayed in the Result Display. This is because the compulsory + `INDEX` field is missing. + +
+ +### Copy an Internship to Clipboard +Assumptions: The sample data provided by InternBuddy is used, where there is a total of 7 internship entries. +1. `copy 2` + + **Expected**: The details of the second internship entry are copied to the clipboard. + +2. Successful copying through the filtered internship list + + 1. `find n/Apple n/Google` + 2. `copy 2` + + **Expected**: The details of the second internship shown in the List Panel will be copied to the + clipboard. In this case, it copies the details for the entry whose company + name is `Google`. + +3. Unsuccessful copying through the filtered internship list + 1. `find n/Apple n/Google` + 2. `copy 3` + + **Expected**: An error message is displayed in the Result Display. This is because in the filtered + internship list, there are only 2 internship entries, implying that `3` is not a valid value for the + `INDEX` field. + +4. `copy -1` + + **Expected**: An error message is displayed in the Result Display. This is because the `INDEX` + field must be a positive integer greater than or equal to 1. + +5. `copy 1 2` + + **Expected**: An error message is displayed in the Result Display. This is because the `copy` + command does not support copying of more than 1 internship entry simultaneously. + +6. `copy 12` + + **Expected**: An error message is displayed in the Result Display. This is because there are only + 7 internship entries in the sample data. Index 12 is out of range. + +7. `copy` + + **Expected**: An error message is displayed in the Result Display. This is because the compulsory + `INDEX` field is missing. + +
+ +### Find Internships +Assumptions: The sample data provided by InternBuddy is used, where there is a total of 7 internship entries. +1. `find n/Amazon` + + **Expected**: The List Panel shows the internship entry whose company name matches with + `Amazon`. A success message is displayed in the Result Display. + +2. `find n/Amazon n/Google` + + **Expected**: The List Panel shows the internship entries whose company name matches with + `Amazon` or `Google`. A success message is displayed in the Result Display. + +3. Finding through the filtered internship list + 1. `find n/Apple n/Google` + 2. `find n/Amazon` + + **Expected**: The List Panel shows the internship entry whose company name matches with + `Amazon`. This means that for the `find` command, the search is always done in the **unfiltered list** + even if the List Panel was showing a filtered list. + +4. `find` + + **Expected**: An error message is displayed in the Result Display. This is because a minimum of 1 + optional field must be specified. + +5. `find s/Applied s/Interviewing` + + **Expected**: An error message is displayed in the Result Display. This is because `Interviewing` + is not a valid value for the `STATUS` field. -1. Initial launch +6. `find n/Google n/Samsung s/Applied s/Assessment` - 1. Download the jar file and copy into an empty folder + **Expected**: Only the internship entry with company name `Google` and status `Assessment` is + filtered out. This is because all other internship entries do not have a matching value with both + `CompanyName` and `Status`. - 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. +7. `find s/Assessment s/Interview t/Golang` -1. Saving window preferences + **Expected**: Only the internship entry with status `Assessment` and tag `Golang` is filtered out. + This is because all other internship entries do not have a matching value with both `Status` and + `Tag`. - 1. Resize the window to an optimum size. Move the window to a different location. Close the window. +
- 1. Re-launch the app by double-clicking the jar file.
- Expected: The most recent window size and location is retained. +### Get Upcoming Events and Deadlines -1. _{ more test cases …​ }_ +1. `upcoming` -### Deleting a person + **Expected**: All internship entries with events or deadlines in the upcoming week are + listed out and displayed in the List Panel. -1. Deleting a person while all persons are being shown - 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. +2. `upcoming hello` - 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. + **Expected**: All internship entries with events or deadlines in the upcoming week are + listed out and displayed in the List Panel. - 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. - 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
- Expected: Similar to previous. +3. `upcoming edit 1 n/Apples` -1. _{ more test cases …​ }_ + **Expected**: All internship entries with events or deadlines in the upcoming week are + listed out and displayed in the List Panel. -### Saving data -1. Dealing with missing/corrupted data files +### Delete Internships by Indices +Prerequisites: List all internships using the `list` command. Multiple internships are present in the list. - 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ +1. `delete-index 1` + + **Expected**: The first internship entry in the List Panel is deleted. If the View Panel was displaying + the details of the deleted internship entry, it defaults to displaying the welcome message. + +2. `delete-index 1 2` + + **Expected**: The first and second internship entry in the List Panel are deleted. If the View + Panel was displaying the details of either of the deleted internship entries, it defaults to displaying + the welcome message. + +3. `delete-index 3 1 3 3 1` + + **Expected**: The first and third internship entries in the List Panel are deleted. If the View Panel was + displaying the details of either of the deleted internship entries, it defaults to displaying the + welcome message. + +4. `delete-index` + + **Expected**: An error message is displayed in the Result Display. This is because a minimum of 1 + index must be specified. + + +### Delete Internships by Fields +Assumptions: The sample data provided by InternBuddy is used, where there is a total of 7 internship entries. +1. `delete-field n/Amazon` + + **Expected**: The internship entry whose company name matches with `Amazon` is deleted. + A success message is displayed in the Result Display. + +2. `delete-field n/Amazon n/Google` + + **Expected**: The internship entries whose company name is either `Amazon` or `Google` are deleted. + A success message is displayed in the Result Display. + +3. Deleting through the filtered internship list + 1. `find n/Apple n/Google` + 2. `delete-field n/Amazon` + + **Expected**: No internship is deleted. This is because in the List Panel, there is no internship + with company name `Amazon`. This shows that `delete-field` searches through the **filtered list**. + +4. `delete-field` + + **Expected**: An error message is displayed in the Result Display. This is because a minimum of 1 + optional field must be specified. + +5. `delete-field s/Applied s/Interviewing` + + **Expected**: An error message is displayed in the Result Display. This is because `Interviewing` + is not a valid value for the `STATUS` field. + +6. `delete-field n/Google n/Samsung s/Assessment s/Applied` + + **Expected**: Only the internship with company name `Google` and status `Assessment` is deleted, + because all the other internships do not have a matching field for both `CompanyName` and `Status`. + +7. `delete-field s/Assessment s/Interview t/Android` + + **Expected**: Only the internship with status `Interview` and tag `Android` is deleted, because + all the other internships do not have a matching field for both `Status` and `Tag`. + +
+ +### Clear all Internships + +1. `clear` + + **Expected**: All internship entries are deleted. The View Panel displays the welcome message. + + +2. `clear hello` + + **Expected**: All internship entries are deleted. The View Panel displays the welcome message. + + +3. `clear edit 1 n/Apples` + + **Expected**: All internship entries are deleted. The View Panel displays the welcome message. + + +### Get Help + +1. `help` + + **Expected**: The help window opens. + + +2. `help hello` + + **Expected**: The help window opens. + + +3. `help edit 1 n/Apples` + + **Expected**: The help window opens. + +
+ +### Exit InternBuddy + +1. `exit` + + **Expected**: InternBuddy closes. + + +2. `exit hello` + + **Expected**: InternBuddy closes. + + +3. `exit edit 1 n/Apples` + + **Expected**: InternBuddy closes. + + +### Save Data + +1. Missing Data File + + Prerequisite: There is no file called `internbuddy.json` in the `data` subfolder of the folder + where InternBuddy is located. + + 1. If you have an existing `internbuddy.json` file, delete the file. + 2. Double-click InternBuddy's jar file. + + **Expected**: InternBuddy launches with the sample internship data shown in the List Panel. There + is a total of 7 internship entries. + +2. Corrupted Data File + + Prerequisite: There is a file called `internbuddy.json` in the `data` subfolder where InternBuddy is located. + + 1. Ensure that InternBuddy is not running. If it is running, close it. + 2. Open the file `internbuddy.json`. + 3. In `internbuddy.json`, locate any line that contains the word `CompanyName`. + 4. Highlight the line located and delete the entire line. + 5. Save the `internbuddy.json` file and close it. + 6. Launch InternBuddy. + + **Expected**: No entry is shown in the List Panel. InternBuddy starts afresh with 0 internship entry. + +
+ +## **Appendix C: Planned Enhancements** +While we strive to make InternBuddy a perfect product for you, there are nevertheless areas of improvement. +Hence, we are constantly striving to make InternBuddy better. This appendix documents some of the areas +that our team is currently working on, and we would release them in the future once their functionalities +have been finalised. + +### Make Prefixes Case-Insensitive +#### Problem +Prefixes are case-sensitive, hence command arguments such as `T/` will be interpreted as plain text. In InternBuddy, command prefixes are lowercase. For example, `add n/Visa r/Software Engineer s/New d/2023-03-01 t/javascript T/react` will add an internship entry with company name +`Visa`, role `Software Engineer`, status `New`, deadline of application `2023-03-01`, +and tag `javascript T/react` (refer to Figure 19). +Therefore, it is possible to add substrings such as `T/`, `C/` or `R/` to any of the fields, even though the user could have intended to enter `t/`, `c/` or `r/`. + +

+ +

+ +

Figure 19: Adding the tag 'javascript T/react'

+ +
+ +We originally intended for this design to allow maximum flexibility for the user. If the user had entered something wrongly, it is possible to just use the command `edit`. However, this design could have an unintentional side-effect. + +To illustrate this side effect, we use an example of the `find` command. `find t/javascript t/react` tries to find entries with either the tag `javascript` or `react` (refer to Figure 20). Tag matching is case-insensitive, so it also tries to find `Javascript` or `React` (refer to Figure 21). Similarly, `delete-field t/javascript t/react` tries to delete entries with either the tag `javascript` or `react` or `Javascript` or `React`. + +

+ +

+ +

Figure 20: Find the tags "javascript" and "react"

+ +

+ +

+ +

Figure 21: Result of the find command in figure 20

+ +There could be another conflicting meaning of the command. In this case, instead of trying to find entries with either the tag `javascript` or `react`, the user could have intended to find the tag `javascript T/react`. This could lead to some confusion. + +#### Proposed Design Tweak +Make prefixes for all commands with prefixes (`add`, `edit`, `find`, `delete-field`) case-insensitive. For example, `add n/Visa r/Software Engineer s/New d/2023-03-01 t/javascript t/react` should have the same result as `add n/Visa r/Software Engineer s/New d/2023-03-01 t/javascript T/react`, where `t/` and `T/` both refer to the tag field. The new internship entry will have company name `Visa`, role `Software Engineer`, status `New`, deadline of application `2023-03-01`, and the tags `javascript` and `react`. The View Panel displays the information for this new internship entry, and a success +message is displayed in the Result Display. Do refer to Figures 22 and 23 for an illustrated example. + + +

+ +

+ +

Figure 22: Adding a new entry with case-insensitive prefix

+ + +

+ +

+ + +

Figure 23: Result of the add command in figure 22

+ + +A possible implementation is to change the `findPrefixPosition()` method in `seedu.internship.logic.parser.ArgumentTokenizer` as shown in Figure 24. Instead of finding the first exact match of the prefix, the method tries to find the first case-insensitive match of the prefix. + + +

+ +

+ + + +

Figure 24: The parser checks for both lowercase prefix and uppercase prefix

+ +This would address the above problem. For example, `find t/javascript t/react` should have the same result as `find T/javascript T/react`. This would remove the confusion as substrings such as `T/` cannot be entered in any of the fields. + +
+ +
+ +### Inaccurate Error Message for Integer Overflow +#### Problem +There are currently 4 types of commands that require internship indices: `edit`, `view` `copy` and `delete-index`. +However, the current implementation of the parsing of these indices does not take into account integer overflows, which is when the integer is too large or too small for InternBuddy to compute. + +When trying to parse these integers, it recognises that they are either too large or too small and treats them as not an integer. +As a result, InternBuddy is unable to differentiate between internship index inputs that are not integers and are integer overflows. + +Thus, an invalid command format error message is displayed when these integer overflows are inputted (refer to Figure 25), instead of an index out of range error message when the integer is too large or an invalid index error message when the integer is too small. + +

+ +

+ +

Figure 25: Invalid command format error message is returned even though internship index is an integer

+ +
+ +#### Proposed Design Tweak +We plan to display the invalid index and out of range index error messages even when there is a negative and positive integer overflow respectively. + +* When InternBuddy recognises an input as not an integer, we can check if the input begins with the negative sign and is followed by only digits or contains only digits (refer to Figure 26). + * If the latter is true, there is a negative integer overflow and InternBuddy can be configured to display an invalid index error message (refer to Figure 27). + * If the former is true, there is a positive integer overflow and InternBuddy can be configured to display an index out of range error message (refer to Figure 28). + +

+ +

+ +

Figure 26: Checking of input added when InternBuddy recognises that it isn't an integer

+ +
+ +

+ +

+ +

Figure 27: Invalid index error message displayed when there is negative integer overflow

+ +
+ +

+ +

+ +

Figure 28: Out of range index error message displayed when there is positive integer overflow

+ +
+ +
+ +## **Appendix D: Effort** +InternBuddy is a brownfield project adapted from +[AddressBook Level 3](https://github.com/se-edu/addressbook-level3). Our team morphed it from a contact +management application to an internship tracker that is designed for Computing undergraduates. Development +took place over 4 sprints which spanned a period of 2-3 months. + +### Difficulty Level +For most of our team members, InternBuddy is the first brownfield project that we have undertaken. Dealing +with a large code base is no simple feat. Therefore, our team went with a more conservative approach of +focusing our efforts on refactoring and enhancing existing features to suit our product. This enabled us +to create a functional product at the end of our sprints, with a lower likelihood of encountering bugs. + +### Challenges +There are several challenges that we faced during the development phase. +1. **Understanding and Refactoring the Code Base** + + * [AddressBook Level 3](https://github.com/se-edu/addressbook-level3) is a project with around 6k lines of +code. Much effort was required in understanding how the different components (e.g. `Model` and `Logic`) +interact with one another. As our team decided to morph the project, we had to refactor a significant +amount of code. + * For example, `Person` had to be refactored into `Internship`, and classes like `Phone` +and `Email` were no longer relevant, and we had to transform them into classes like `Role` and `Status`. + +2. **Matching of Multiple Fields** + * The original `find` command in [AddressBook Level 3](https://github.com/se-edu/addressbook-level3) + does matching by only one field, `Name`. + * We wanted to extend the functionality of the `find` command to enable matching by multiple fields. + * There were many possible options on how to implement it, and we struggled to find an implementation + that is both simple-to-understand and intuitive to use. + * We eventually decided on using exact matching and a mix of both OR and AND relationships, as explained + in the implementation for the [`find`](#find-internships---find) command. +3. **Responsive User Interface** + * It was challenging to set up the [View Panel](#setting-up-and-getting-started) to make it respond + to both the `view` command and the user interactions with the [List Panel](#setting-up-and-getting-started), + as programming the GUI is something new to many of us. + * Furthermore, we had to ensure that the text wraps smoothly when the interface is resized. In other + words, the wrapping width had to scale with screen size. It took us time to learn how to do it in code, + and how to adjust our JavaFX components accordingly. + +### Achievements +Our team is proud to have successfully morphed the project from a contact management application +to an internship tracker. InternBuddy offers a total of 13 different features. +- 3 features that are unmodified from [AddressBook Level 3](https://github.com/se-edu/addressbook-level3) (`list`, `clear`, `exit`). +- 6 features that are modified from [AddressBook Level 3](https://github.com/se-edu/addressbook-level3) (`add`, `edit`, `find`, `delete-index`, `delete-field`, `help`). +- 1 feature that is adapted from [HackNet](https://github.com/AY2122S2-CS2103T-W13-3/tp) (Navigating through Past Commands). +- 3 new features implemented by our team (`view`, `copy`, `upcoming`) + +In the area of testing, our team has managed to increase code coverage to [79.55%](https://app.codecov.io/gh/AY2223S2-CS2103T-T14-3/tp). +This is made possible with GUI testing, where we received help from [AddressBook Level 4](https://github.com/se-edu/addressbook-level4) +and [Please Hire Us](https://github.com/AY2223S1-CS2103T-W17-4/tp). + +
+ +## **Glossary** + +Table 3 provides the glossary for the terms used in this developer guide. + +| Term | Definition | +|--------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Command | An instruction for InternBuddy to perform an action. | +| Command Line Interface (CLI) | A CLI is the text-based interface that you can use to provide instructions to your computer. Examples of instructions include opening files and running programs. | +| Computing undergraduate | A university undergraduate pursuing a Computing degree. | +| Graphical User Interface (GUI) | A GUI is the visual interface that you see when an application launches, allowing you to interact with it by clicking on its various buttons and components. | +| Mainstream Operating Systems | Include Windows, macOS, Linux and Unix. | +| Field | A part of the command where you have to supply a value for the command to be valid. | +| Prefix | A short form for the name of a field. It indicates which field does a value belongs to. For example, in `n/Apple`, the value `Apple` is supplied to the field `COMPANY_NAME` since the `n/` prefix is used. | +| Tech Stack | A set of technologies that an individual or company uses to create and/or maintain a software system or product. | + +

Table 3: Glossary for Developer Guide

+ +
+ +## **Acknowledgements** -1. _{ more test cases …​ }_ +* InternBuddy is written in **Java 11**. +* It is adapted from the [AddressBook Level 3](https://github.com/se-edu/addressbook-level3) project created by + the [SE-EDU initiative](https://se-education.org). +* Libraries and frameworks used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), + [JUnit5](https://github.com/junit-team/junit5) and [TestFX](https://github.com/TestFX/TestFX). +* GUI testing was implemented with references from [AddressBook Level 4](https://github.com/se-edu/addressbook-level4) + and [Please Hire Us](https://github.com/AY2223S1-CS2103T-W17-4/tp). We utilised code from these projects to set + up GUI testing, then added our own test cases to test the UI components that we created ourselves. +* The feature of Navigating Through Past Commands was primarily adapted from [HackNet](https://github.com/AY2122S2-CS2103T-W13-3/tp), + but we added code modifications and test coverage. +* The sections on explaining the formatting standards and GUI interface in the User and Developer Guides are + inspired by [Please Hire Us](https://github.com/AY2223S1-CS2103T-W17-4/tp). diff --git a/docs/SettingUp.md b/docs/SettingUp.md index 275445bd551..a016d944437 100644 --- a/docs/SettingUp.md +++ b/docs/SettingUp.md @@ -23,7 +23,7 @@ If you plan to use Intellij IDEA (highly recommended): 1. **Import the project as a Gradle project**: Follow the guide [_[se-edu/guides] IDEA: Importing a Gradle project_](https://se-education.org/guides/tutorials/intellijImportGradleProject.html) to import the project into IDEA.
:exclamation: Note: Importing a Gradle project is slightly different from importing a normal Java project. 1. **Verify the setup**: - 1. Run the `seedu.address.Main` and try a few commands. + 1. Run the `seedu.internship.Main` and try a few commands. 1. [Run the tests](Testing.md) to ensure they all pass. -------------------------------------------------------------------------------------------------------------------- diff --git a/docs/Testing.md b/docs/Testing.md index 8a99e82438a..a20d89462ab 100644 --- a/docs/Testing.md +++ b/docs/Testing.md @@ -29,8 +29,8 @@ There are two ways to run tests. This project has three types of tests: 1. *Unit tests* targeting the lowest level methods/classes.
- e.g. `seedu.address.commons.StringUtilTest` + e.g. `seedu.internship.commons.StringUtilTest` 1. *Integration tests* that are checking the integration of multiple code units (those code units are assumed to be working).
- e.g. `seedu.address.storage.StorageManagerTest` + e.g. `seedu.internship.storage.StorageManagerTest` 1. Hybrids of unit and integration tests. These test are checking multiple code units as well as how the are connected together.
- e.g. `seedu.address.logic.LogicManagerTest` + e.g. `seedu.internship.logic.LogicManagerTest` diff --git a/docs/UserGuide.md b/docs/UserGuide.md index e7df68b01ea..004636c53ee 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -2,192 +2,932 @@ 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** * Table of Contents {:toc} --------------------------------------------------------------------------------------------------------------------- +
+ + +## **Introducing InternBuddy** + +InternBuddy is a desktop application for university undergraduates to manage their internship applications. +It is optimized for typing where it allows you to complete internship management tasks much more efficiently via +the keyboard as compared to using traditional [Graphical User Interface](#glossary) (GUI) applications. If you are a fast typist +who is seeking a one-stop platform to systematically organise your internship applications, +then InternBuddy is the perfect buddy to accompany you during your internship hunt. + +InternBuddy runs using Java 11 and is available on the Windows, macOS, and Linux operating systems. + +
+

+ +

+ +
+ +## **About the User Guide** + +### Objectives of the User Guide +This user guide aims to provide comprehensive instructions for learning how to use InternBuddy, +including details on the installation process and features. For more advanced users, +this guide will also help customize your experience. + +[//]: # (@@author eugenetangkj - reused with modifications) +[//]: # (Adapted from https://ay2223s1-cs2103t-w17-4.github.io/tp/UserGuide.html#navigating-the-user-guide) + +### Using the User Guide +This uses guide uses a set of formatting standards and visuals to better communicate information. + +**Information Box** +
+ +:information_source: **Info:** Provides useful information that supplements the main text +
+ +**Tip Box** +
+ +:bulb: **Tip:** Suggestions on how to enhance your experience +
+ +**Warning Box** +
+ +:warning: **Warning:** Warns of a dangerous action that you should be aware of and to consider +carefully before committing +
+ +**Syntax Highlighting** + +[Commands](#glossary), [fields](#glossary), file paths and class names are highlighted. -## Quick start +`command`, `FIELD`, `filepath.json`, `ClassName` -1. Ensure you have Java `11` or above installed in your Computer. -1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases). +**Keyboard Actions** -1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook. +Keyboard keys are indicated using rounded buttons. -1. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar addressbook.jar` command to run the application.
- A GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
- ![Ui](images/Ui.png) + -1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
- Some example commands you can try: +
- * `list` : Lists all contacts. - * `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. +## **Quick Start** - * `delete 3` : Deletes the 3rd contact shown in the current list. +1. Ensure you have Java `11` or above installed in your computer. +

+
+ :information_source: **Info:** If you are unsure of whether you have Java 11 installed, or need help installing + it, you can refer to Appendix A. +
- * `clear` : Deletes all contacts. +2. Download the latest `internbuddy.jar` from [here](https://github.com/AY2223S2-CS2103T-T14-3/tp/releases). + +3. Copy the file `internbuddy.jar` to the folder you want to use as the _home folder_ for InternBuddy. +

+
+ :information_source: **Info:** The home folder is the folder that you will navigate to when you want to launch + InternBuddy. It will contain a subfolder where your InternBuddy data will be stored. +
+
+ +4. Double-click on the file `internbuddy.jar` to launch InternBuddy. A GUI similar to Figure 1 should + appear in a few seconds. Note how the app contains some sample data.
+

+ +

+ +

Figure 1: InternBuddy's GUI

+
+ +5. You can interact with InternBuddy by typing into the box with the text `Enter command here...`, then pressing + to execute your command. For example, typing `help` and pressing will open + the help window. + + +6. Here are some other example commands you can try: + + - `list`: Lists all internships stored in InternBuddy + - `add n/Food Panda r/Web Developer s/Applied d/2023-04-01`: Adds a new internship into InternBuddy. + - `delete-index 3` : Deletes the 3rd internship of the current list displayed in InternBuddy. + - `exit` : Exits InternBuddy. + + +Do refer to [Features](#features) below for a comprehensive list of supported features and their associated details. + +
+ +[//]: # (@@author eugenetangkj - reused with modifications) +[//]: # (Adapted from https://ay2223s1-cs2103t-w17-4.github.io/tp/UserGuide.html#navigating-the-user-guide) + +## **Exploring the Graphical User Interface** +Figure 2 provides a visual representation of the different parts of InternBuddy's GUI, while +Table 1 explains what each part is used for. + +

+ +

+ +

Figure 2: Different parts of InternBuddy's GUI

+
+ +| Part | Usage | +|----------------|----------------------------------------------------------------------------------------------| +| Command Box | You can type in your commands here to interact with InternBuddy. | +| Result Display | This is where the results of your command will be displayed. | +| List Panel | Displays a list of internship entries. | +| View Panel | Displays either the welcome message or detailed information of a specified internship entry. | +| Status Bar | States where your InternBuddy data file is located on your computer. | + +

Table 1: Explanation of the different parts of InternBuddy's GUI

+ + +
+ +:bulb: **Tip:** The GUI is resizeable. You can resize it according to your preferences. + +
+ +
+ +:bulb: **Tip:** You can left-click on any of the entries displayed in the List Panel to view more information +about an entry. Your selected entry will be highlighted blue. You can also use +and to change your selected entry. + +
- * `exit` : Exits the app. +
-1. Refer to the [Features](#features) below for details of each command. +:information_source: **Info:** Commands in InternBuddy will not change your selected entry in the +List Panel. To change your selected entry, you will have to manually change it either via +left-clicking and/or using and +
--------------------------------------------------------------------------------------------------------------------- +
-## Features -
+## **Command Information** -**:information_source: Notes about the command format:**
+[//]: # (@@author potty10) +### 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`. +* Words in `UPPER_CASE` are the fields that are to be supplied by you.
+ e.g. If the command format is `add n/COMPANY_NAME`, you may input the command as `add n/Apple` where you supply the + value `Apple` to the field `COMPANY_NAME`. * 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. If the command format is `edit INDEX [n/COMPANY_NAME] [c/COMMENT]`, you may input the command as `edit 2 n/Apple` where + you omit the value for the field `COMMENT`. * 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. `[t/TAG]…​` can be used as ` ` (i.e. 0 times), `t/Java`, `t/Java t/Python` etc. + +* Fields can be in any order.
+ e.g. If the command format is `n/COMPANY_NAME r/ROLE`, both `n/Apple r/Software Engineer` and `r/Software Engineer n/Apple` + are acceptable. + +* If a field is expected only once in the command, but you specified it multiple times, only the last occurrence of + the field will be taken.
+ e.g. If the command format is `r/ROLE`, typing in `r/Front-end Developer r/Back-end Developer` will cause your + input to be interpreted as `r/Back-end Developer`. + +* Extraneous values for commands that do not take in fields (such as `help`, `list`, and `exit`) will be + ignored.
+ e.g. If the command format is `help`, typing in `help 123` will cause your input to be interpreted as `help`. + +* Command names are case-sensitive. For example, `help` will work, but `HELP` or `Help` will not. + +
+ +[//]: # (@@author eugenetangkj) +### Prefixes and Constraints for Fields +In InternBuddy's commands, we refer to a range of fields that you can replace with values to input information that +is customised to your internship applications. + +There are 3 important things that you should note: +1. Most fields have associated [**prefixes**](#glossary). + * Prefixes are short character combinations that you can use to identify the field to which a value belongs. + They are a convenient shorthand that allows you to refer a field without having to type out its + entire name, saving precious time. + * For example, in `add n/Apple`, the value `Apple` is associated with the + field `COMPANY_NAME` since the `n/` prefix is used. + * Prefixes are **case-sensitive**. `n/` will work but `N/` will not. + +2. When entering a command, do remember to use **spaces** to separate different fields of information. If you enter multiple fields without using space(s) to separate them, InternBuddy will interpret them as a single field. + * For example, `edit 1 n/Visa r/Software Engineer` associates `Visa` with the field `COMPANY_NAME` and `Software Engineer` with the field `ROLE`. + * Conversely, `edit 1 n/Visar/Software Engineer` associates `Visar/Software Engineer` with the field `COMPANY_NAME`. + +3. There are **constraints** that you must adhere to when replacing fields with values. + * Constraints differ based on the fields. + * If you do not adhere to these constraints and enter invalid values, an error message will be + displayed in the [Result Display](#exploring-the-graphical-user-interface) when you + press . The message will alert you to the invalid input and provide information + on how to correct the command. + +Table 2 provides a summary of the fields with their descriptions, prefixes and constraints. + +| Field | Description | Prefix | Constraints | +|----------------|--------------------------------------------------------------------------------------------------------------------|--------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `COMPANY_NAME` | The name of the company | `n/` | Cannot be blank and must be at most 50 characters. | +| `ROLE` | The role that you applied for | `r/` | Cannot be blank and must be at most 50 characters. | +| `STATUS` | The status of the internship application | `s/` | Must be one of the following: `New`, `Applied`, `Assessment`, `Interview`, `Offered`, `Accepted`, `Rejected`. Note that this is **not** case-sensitive. | +| `DATE` | The date associated with the internship application | `d/` | Must be a valid date in the format of `YYYY-MM-DD`. | +| `COMMENT` | A comment that you can make on an internship application | `c/` | Cannot be blank (except when used in the `edit` command). | +| `TAG` | A label that you can give to an internship application | `t/` | Cannot be blank (except when used in the `edit` command) and must be at most 30 characters. | +| `INDEX` | The index number of the internship entry as displayed in the [List Panel](#exploring-the-graphical-user-interface) | - | A positive integer that is smaller than or equal to the largest index number shown in the [List Panel](#exploring-the-graphical-user-interface). Note that 0 is not a positive integer. | + +

Table 2: Fields with their descriptions, prefixes and constraints

+ +### Details on `STATUS` and `DATE` +The field `STATUS` represents the current status of an internship application. It can only take on one +of the following values: `New`, `Applied`, `Assessment`, `Interview`, `Offered`, `Accepted` and `Rejected`. +Note that this is **not case-sensitive**. Table 3 explains the meaning of each status. + + +| `STATUS` | Description | +|--------------|-----------------------------------------------------------------------------------------------------------------------------------| +| `New` | You have recently saw this internship opportunity and would like to record it in InternBuddy. Also, you have yet to apply for it. | +| `Applied` | You have applied for this internship opportunity and you are currently waiting for the company's response. | +| `Assessment` | You are currently in the technical assessment stage of the application process. | +| `Interview` | You are currently in the behavioral interview stage of the application process. | +| `Offered` | You have been offered the internship opportunity. | +| `Accepted` | You have accepted the internship opportunity. | +| `Rejected` | You have either been rejected by the company, or that you have rejected the internship offer. | + +

Table 3: Description of statuses

+ +Depending on the status of the internship application, the `DATE` field will be interpreted differently. Table 4 +documents the meaning of `DATE` with respect to each `STATUS` value. + +| `STATUS` | Interpretation of `DATE` | +|--------------|------------------------------| +| `New` | Deadline of Application | +| `Applied` | Date Applied | +| `Assessment` | Date of Technical Assessment | +| `Interview` | Date of Behavioral Interview | +| `Offered` | Deadline of Offer Acceptance | +| `Accepted` | Date of Acceptance | +| `Rejected` | Date of Rejection | + +

Table 4: Description of dates

+ +[//]: # (@@author seadragon2000341) +### Duplicate Internships +InternBuddy does not allow for the storage of duplicate internships. Should you enter a command that attempts to store +a duplicate internship, InternBuddy will remind you that the internship already exists. + +2 internships are considered to be duplicates if they have matching `COMPANY_NAME`, `STATUS`, `ROLE` **and** +`DATE`. The comparison is **case-insensitive**. + +In Table 5, internships A and B are considered as duplicate internships, because they have the same +`COMPANY_NAME`, `ROLE`, `STATUS` and `DATE`. Note how the capitalisation differences in `COMPANY_NAME` +and `ROLE` do not affect the comparison. -* 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. -* 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. +| `Field` | Internship A | Internship B | +|----------------|-------------------|-------------------| +| `COMPANY_NAME` | Google | google | +| `ROLE` | Frontend Engineer | frontend engineer | +| `STATUS` | New | New | +| `DATE` | 2023-02-02 | 2023-02-02 | +| `COMMENT` | NA | NA | +| `TAGS` | C++ | Java | -* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
- e.g. if the command specifies `help 123`, it will be interpreted as `help`. +

Table 5: Duplicate internships

+ +Meanwhile, in Table 6, internships C and D are not considered as duplicate +internships, because they have different values for `COMPANY_NAME`. + + +| `Field` | Internship C | Internship D | +|----------------|-------------------|-------------------| +| `COMPANY_NAME` | Apple | Apple Inc | +| `ROLE` | Frontend Engineer | frontend engineer | +| `STATUS` | New | New | +| `DATE` | 2023-02-02 | 2023-02-02 | +| `COMMENT` | NA | NA | +| `TAGS` | Java | Java | + +

Table 6: Non-duplicate internships

+ +
+ +[//]: # (@@author potty10) +### Duplicate Tags +If you attempt to store duplicate tags within the same internship entry, InternBuddy would only store one of them. A duplicate tag refers to a tag that is identical to another tag in every way, including the spelling and capitalisation (tags are **case-sensitive**). In other words, a duplicate tag is a tag that is an exact copy of another tag. + +
+ +## **Features** +InternBuddy offers a variety of features that can empower you to systematically track your internships. + +[//]: # (@@author eugenetangkj) +### Listing all Internships : `list` +Shows the list of all internship entries that you have stored in InternBuddy. + +
+ +:information_source: **Info:** `list` will always reset the +[View Panel](#exploring-the-graphical-user-interface) to display the welcome message.
-### Viewing help : `help` -Shows a message explaning how to access the help page. +Format: `list` -![help message](images/helpMessage.png) +[//]: # (@@author eugenetangkj) +### Adding an Internship : `add` -Format: `help` +Do you have a new internship to track? Add it to InternBuddy using the `add` command. +Format: `add n/COMPANY_NAME r/ROLE s/STATUS d/DATE [c/COMMENT] [t/TAG]...` -### Adding a person: `add` +* The optional `COMMENT` field has a default value of `NA`. This means that if you do not specify any value for it, + the comment for the newly added internship will be `NA`. +* The optional `TAG` field will be empty by default. This means that if you do not specify any value for it, there + will be no tags associated with the newly added internship. -Adds a person to the address book. +
-Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` +:information_source: **Info:** You will not be able to add [duplicate internships](#duplicate-internships) into InternBuddy (an error message will be shown). If you attempt to add [duplicate tags](#duplicate-tags) into the same internship entry, InternBuddy will only store one of the tags. -
:bulb: **Tip:** -A person can have any number of tags (including 0)
Examples: -* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` -* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal` +* `add n/Food Panda r/Web Developer s/New d/2023-02-01 c/I love Food Panda! t/React t/Front-end` Adds a new internship entry + with company name `Food Panda`, role `Web Developer`, status `New`, deadline of application `2023-02-01`, + comment `I love Food Panda!` and tags `React` and `Front-End`. This example is illustrated in Figure 3. +* `add n/Deliveroo r/Software Engineer s/Assessment d/2023-02-01` Adds a new internship entry with + company name `Deliveroo`, role `Software Engineer`, status `Assessment` and date of technical assessment + `2023-02-01`. +* `add n/Food Panda s/New d/2023-02-01` Displays an error because the `ROLE` field is missing. +* `add n/Apple r/App Developer s/New d/2023-02-01 t/Java t/Java` Adds a new internship entry + with company name `Apple`, role `App Developer`, status `New`, deadline of application `2023-02-01`, + and tag `Java` (only one tag is stored since the two tags provided are duplicates). -### Listing all persons : `list` -Shows a list of all persons in the address book. +![Add Command](images/ug-add-example.png) +

Figure 3: Example of the add command in action

-Format: `list` +
+ +[//]: # (@@author seadragon2000341) +### Editing an Internship : `edit` + +Made a mistake, or wish to update your internship entry? The `edit` command allows you to make modifications. -### Editing a person : `edit` +Format: `edit INDEX [n/COMPANY_NAME] [r/ROLE] [s/STATUS] [d/DATE] [c/COMMENT] [t/TAG]...` -Edits an existing person in the address book. +* Edits the internship whose index number is `INDEX`. +* You have to provide at least one of the optional fields. +* You can reset the comment of an internship to the default value of `NA` by typing `c/` without specifying any comments + after it. +* You can remove all of an internship’s tags by typing `t/` without specifying any other tags after it. -Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` +
-* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …​ -* 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. +:warning: **Warning:** When editing tags, the existing tags of the internship will be removed. For example, if the +internship with index 1 currently has the tags `iOS` and `Swift`, editing the internship via `edit 1 t/macOS` will lead +to the internship only having the tag `macOS`. To have all 3 tags, you need to type `edit 1 t/iOS t/Swift t/macOS`. + +
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. -### Locating persons by name: `find` +Suppose you have at least 2 internships displayed in the [List Panel](#exploring-the-graphical-user-interface). + +* `edit 2 s/Assessment r/Software Developer` Sets the status and role of the second internship entry as `Assessment` and + `Software Developer` respectively. This example is illustrated in Figure 4. +* `edit 2` Displays an error because the command does not satisfy the criteria of having at least one optional field. +* `edit 2 t/Java t/Go` Sets the tags of the second internship entry as Java and Go (existing tags will be removed). +* `edit 2 c/` Sets the comment of the second internship entry to be `NA`. +* `edit 2 t/` Removes all the tags of the second internship entry. -Finds persons whose names contain any of the given keywords. +![Edit Command](images/ug-edit-example.png) +

Figure 4: Example of the edit command in action

-Format: `find KEYWORD [MORE_KEYWORDS]` +
-* 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` +
+ +[//]: # (@@author eugenetangkj) +### Viewing an Internship : `view` +Want to view the details of a specific internship entry? You can do so using the `view` command. + +Format: `view INDEX` +* Views the details of the internship entry with index number `INDEX` as indicated in + the [List Panel](#exploring-the-graphical-user-interface). +* The details will be shown in the [View Panel](#exploring-the-graphical-user-interface). +* Apart from the internship details, a custom tips box is also included in the + [View Panel](#exploring-the-graphical-user-interface). The tips provide you with suggestions on how to improve your internship experience, + where their content change according to the status of the internship entry. Examples: -* `find John` returns `john` and `John Doe` -* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png) +* `view 3` Assuming that you have at least 3 internships displayed in the +[List Panel](#exploring-the-graphical-user-interface), this displays the details of the third internship in the +[View Panel](#exploring-the-graphical-user-interface). +* `view -1` Displays an error because `INDEX` must be a positive integer. +* `view 8` Assuming that you have 7 internships displayed in the + [List Panel](#exploring-the-graphical-user-interface), this displays an error because `INDEX` cannot be greater + than the maximum index shown in the [List Panel](#exploring-the-graphical-user-interface), which is 7 in this case. + -### Deleting a person : `delete` +
-Deletes the specified person from the address book. +[//]: # (@@author DerrickSaltFish) +### Copying an Internship to Clipboard : `copy` +Need to quickly export the details of an internship? Use `copy` to copy the details of an internship to +your clipboard. -Format: `delete INDEX` +Format: `copy INDEX` -* Deletes the person at the specified `INDEX`. -* The index refers to the index number shown in the displayed person list. -* The index **must be a positive integer** 1, 2, 3, …​ +* Copies the details of the internship entry with index number `INDEX` as indicated in + the [List Panel](#exploring-the-graphical-user-interface). +* The copied text will be in the format of + `Company Name: COMPANY_NAME; Role: ROLE; Status: STATUS; Date: DATE; Comment: [COMMENT]; Tags: [TAG]`. 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. +* Suppose the first internship displayed in the [List Panel](#exploring-the-graphical-user-interface) + has company name `Amazon`, role `Cloud Architect`, status `New`, date `2023-03-28`, comment `I love Amazon!`, + and tags `AWS` and `Cloud Services`. Then, `copy 1` copies `Company Name: Amazon; Role: Cloud Architect; Status: New; Date: 2023-03-28; Comment: [I love Amazon!]; Tags: [Cloud Services][AWS]` to your clipboard. + +* `copy -1` Displays an error because `INDEX` must be a positive integer. +* `copy 8` Assuming that you have 7 internships displayed in the + [List Panel](#exploring-the-graphical-user-interface), this displays an error because `INDEX` cannot be greater + than the maximum index shown in the [List Panel](#exploring-the-graphical-user-interface), which is 7 in this case. + +
+ +[//]: # (@@author kohkaixun) +### Finding Internships : `find` +Want to locate selected internship entries? Use the `find` command to filter through +your entries and narrow down your search. + + +Format: `find [n/COMPANY_NAME]... [r/ROLE]... [s/STATUS]... [d/DATE]... [t/TAG]...` + +* You have to provide at least one of the optional fields. +* The `find` command is **case-insensitive**, and it returns **exact matches only**. For example, + `find n/Google Ltd` will not return an entry with company name `Google` because `Google` + does not exactly match with `Google Ltd`. On the other hand, `find t/Java` will return + an entry with tag `java` because the search is case-insensitive. + +There are 2 possible ways for you to use the `find` command. + +**Method 1: Use a single field type** + +e.g. `find s/Applied`, `find s/Applied s/New`, `find n/Google n/Apple n/Meta` + +* The `find` command returns all internship entries that match with **any** of the values that you provide. + +More examples: +* `find s/Applied s/New` returns all internship entries that have a status of **either** + `Applied` **or** `New`. + + + +**Method 2: Use 2 or more different field types** -### Clearing all entries : `clear` +e.g. `find n/Google n/Apple s/New`, `find n/Google n/Apple s/Applied s/New`, `find n/Google r/Engineer t/Python t/Java` -Clears all entries from the address book. +* The `find` command returns all internship entries that match with **at least one** value for + **every** field type that is specified. + +Detailed example: +* Figure 5 below shows InternBuddy starting with 5 internship entries. After entering `find n/Google n/Apple t/python t/java`, + the internships in green boxes were returned while those in red boxes were not. + + +

+ +

+ +

Figure 5: Example of the find command in action

+ +
+ +
+ +* Table 7 explains the reasoning behind Figure 5. + + +| Index of Internship | Returned? | Explanation | +|---------------------|-----------|-----------------------------------------------------------------------------------| +| 1 | Yes | It contains at least one company name `Google` and both tags `python` and `java`. | +| 2 | Yes | It contains at least one company name `Google` and at least one tag `python`. | +| 3 | Yes | It contains at least one company name `Apple` and at least one tag `java`. | +| 4 | No | It lacks both of the searched tags, `python` and `java`. | +| 5 | No | It lacks both of the searched company names, `Google` and `Apple`. | + +

Table 7: Explanation of Figure 5

+ + + + + +More examples: +* `find n/Google n/Apple s/Interview` returns internship entries that have a status `Interview` **and** have + a company name of `Google` **or** `Apple`. +* `find n/Google n/Apple s/Applied s/Interview` returns internship entries that have company names + of `Google` **or** `Apple` **and** roles of `Applied` **or** `Interview`. + + + +
+ +:information_source: **Info:** The `find` command always searches through all of your internship entries +that are stored in InternBuddy. This means that it will also search through entries that are not currently displayed +in the [List Panel](#exploring-the-graphical-user-interface). +
+ +
+ +[//]: # (@@author seadragon2000341) +### Getting Upcoming Events and Deadlines : `upcoming` +Want to view your upcoming events and deadlines? You can do so using the `upcoming` command. + +Format: `upcoming` +* The `upcoming` command provides the list of internships that have events (interviews/assessments) or deadlines (application deadline/offer acceptance deadline) within the upcoming week. +* In other words, it gives you the list of internships that have a `STATUS` of `New/Offered/Assessment/Interview` and a `DATE` that falls within the upcoming week. +* Upcoming week is defined as the current day and the 6 days that follow it. + + + +Examples: +* `upcoming` If today's date is 5 January 2023, it will list all internships that have a `STATUS` of `New/Offered/Assessment/Interview` and a `DATE` that is from 5 January 2023 to 11 January 2023 inclusive. + + +[//]: # (@@author potty10) +### Deleting Internships by Indices : `delete-index` +Need to keep your screen neat and tidy? `delete-index` can help you achieve this by deleting multiple internships using their indices. + +Format: `delete-index INDEX [INDEX]...` + +* Deletes the internship whose index number is `INDEX`. +* If multiple `INDEX` are provided, multiple internships can be deleted. +* At least 1 `INDEX` must be provided. +* `INDEX` does not need to be unique. If 2 or more of `INDEX` have the same value, only the first one will be taken. + + +Examples: +* If you run `delete-index 1` after `find`, it will delete the first entry as displayed by `find` in the [List Panel](#exploring-the-graphical-user-interface). +* If you run `delete-index 1` after `list`, it will delete the first entry as displayed by `list` in the [List Panel](#exploring-the-graphical-user-interface). +* `delete-index 1 3` Deletes the first and third + internships in the [List Panel](#exploring-the-graphical-user-interface). +* `delete-index 3 1 3 3 1` Deletes the first and third + internships in the [List Panel](#exploring-the-graphical-user-interface). +* `delete-index` Displays an error because at least one `INDEX` must be specified. + +[//]: # (@@author potty10) +### Deleting Internships by Fields : `delete-field` +Wish that you can delete internships using fields instead of indices? You can +certainly do so using `delete-field`. + +Format: `delete-field [n/COMPANY_NAME]... [r/ROLE]... [s/STATUS]... [d/DATE]... [t/TAG]...` + +* You have to provide at least one of the optional fields. +* The `delete-field` command is **case-insensitive**. For example, `delete-field n/Google` deletes all internships with company names `google`, `Google` or `gOOgle`. + Also, `delete-field t/java` deletes all internships with tags `Java`, `java` or `JaVa`. +* `delete-field` deletes entries with **exact matches only**. For example, `delete-field n/Google Ltd` will not delete an entry with company name `Google` because + `Google` does not exactly match with `Google Ltd`. + +There are 2 possible ways for you to use the `delete-field` command. + +**Method 1: Use a single field type** + +e.g. `delete-field s/Applied`, `delete-field s/Applied s/New`, +`delete-field n/Google n/Apple n/Meta` + +* The `delete-field` command deletes all internship entries that match with **any** of the values that you provide. + +More Examples: +* `delete-field s/Applied s/New` deletes all internship entries that have a status of **either** + `Applied` **or** `New`. + + +**Method 2: Use 2 or more different field types** + +e.g. `delete-field n/Google n/Apple s/New`, `delete-field n/Google n/Apple s/Applied s/New`, +`delete-field n/Google r/Engineer t/Python t/Java` + +* The `delete-field` command deletes all internship entries that matches with **at least one** value for + **every** field type that is specified. + +More Examples: +* `delete-field n/Google n/Apple s/Interview` Deletes internship entries that have a status `Interview` **and** have + a company name of `Google` **or** `Apple`. +* `delete-field n/Google n/Apple s/New s/Interview` Deletes internship entries that have company names + of `Google` **or** `Apple` **and** roles of `New` **or** `Interview`. + + +
+ +:information_source: **Info:** The `delete-field` command only searches through the internship entries +that are displayed in the [List Panel](#exploring-the-graphical-user-interface). This means that it +will never delete entries that are not currently displayed in the +[List Panel](#exploring-the-graphical-user-interface). +
+ + +
+ +:warning: **Warning:** If you use an unknown prefix or a prefix not specific to this command, it will be interpreted as +part of your input value. For example, `delete-field r/Engineer c/Good company` will try to delete internships with a role +of `Engineer c/Good company`. +
+ +[//]: # (@@author potty10) +### Clearing all Internships : `clear` +The `clear` command permanently deletes all entries from InternBuddy. Format: `clear` -### Exiting the program : `exit` +
-Exits the program. +:warning: **Warning:** It will be good to think twice before running this command because +once you run this command, all your internship data will be deleted. In the event that you +accidentally ran `clear`, you can refer to [Appendix C](#appendix-c-populating-internbuddy-with-sample-data) +to repopulate InternBuddy with sample data. + +
+ +
+ +[//]: # (@@author eugenetangkj) +### Getting Help : `help` +Forgot the commands for InternBuddy? Fret not! You can easily view the list of supported commands and their formats +using the `help` command. + +Format: `help` +* Opens a new window which displays the list of supported commands in InternBuddy, and provides a link to InternBuddy's + user guide where you can view more detailed information about each command. Figure 6 shows how the Help Window looks like. +* You can click on the button to copy the link to your clipboard. + +

+ +

+ +

Figure 6: Help Window

+ +
+ +
+ +:information_source: **Info:** Clicking on the hyperlink in the help window may not work on some Linux +devices as the functionality is dependent on Gnome libraries. If you are using a Linux device and +the hyperlink does not work, you can click on the button instead. +
+ +
+ +### Exiting InternBuddy : `exit` + +Done with tracking your internships for the day? Exit InternBuddy using the `exit` command. Format: `exit` -### Saving the data -AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. +[//]: # (@@author kohkaixun) +### Navigating through Past Commands -### Editing the data file +Want to reuse a command you entered just now but too lazy to type it all out again? InternBuddy has got your back! +After clicking on the [Command Box](#exploring-the-graphical-user-interface), pressing and will fill the [Command Box](#exploring-the-graphical-user-interface) with commands that you have recently entered. +This allows you to effortlessly access and use past commands without having to go through the tedious process of typing them all over again. -AddressBook data are saved as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file. +
-
:exclamation: **Caution:** -If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run. +:information_source: **Info:** InternBuddy only tracks your past commands from the current run of +InternBuddy. This means that if you restart InternBuddy, you can no longer navigate to the commands that +you entered during the previous run.
-### Archiving data files `[coming in v2.0]` +
-_Details coming soon ..._ +:information_source: **Info:** Once you have navigated to the first command that you have ever entered into +InternBuddy, pressing will not lead to any further change. Similarly, once you have +navigated to the current state of waiting for new input, pressing will not +change anything. +
--------------------------------------------------------------------------------------------------------------------- +
-## FAQ +:bulb: **Tip:** InternBuddy keeps track of all past commands entered, regardless of whether they were +valid or not. Hence, if you accidentally entered an invalid command, you can easily navigate to it and +make amendments accordingly without having to type out the entire command again. +
+ +Example: +- Figure 7 illustrates how you can navigate through past commands where the inputs + `list`, `clear` and `upcoming` are entered in this particular order. The and + keys are used to navigate between the different inputs. + + +

+ +

+ +

Figure 7: Navigating between commands

+ +
+ +[//]: # (@@author DerrickSaltFish) +### Saving your Internship Data + +Your internship data for InternBuddy are saved automatically after any command that changes the data. The data are saved +in a file `internbuddy.json` which is located in a subfolder `data` in the [home folder](#quick-start) +where you placed `internbuddy.json`. There is no need to save manually. + +
+ +:information_source: **Info:** The file location of `internbuddy.json` is stated in the +Status Bar of the GUI. +
-**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. --------------------------------------------------------------------------------------------------------------------- +### Loading the Data -## Command summary +InternBuddy data is loaded from `internbuddy.json` automatically at the beginning of each run. There is no need to load +manually. +* If `internbuddy.json` is missing, InternBuddy will start with a new data file containing the sample internship + entries. +* If the content in `internbuddy.json` was altered and as a result has an invalid format, InternBuddy will start with an + empty data file. + +
+ +
+ +:warning: **Warning:** Starting with an empty data file means that all internship entries previously stored in +InternBuddy will no longer be present. This is equivalent to a data wipeout. Therefore, we advise against tampering +with the content in `internbuddy.json` unless you are confident in doing so. If you are interested, you can refer to + [Appendix B](#appendix-b-customising-the-data-file) for instructions on how to do so. + +
+ + +## **FAQ** + +**Q**: How do I transfer my data to another Computer?
+**A**: Install InternBuddy in the other computer and overwrite the file `internbuddy.json` that it creates with the +file `internbuddy.json` that is stored on your existing computer. + +**Q**: Does InternBuddy support undoing of commands? For example, can I undo a `delete-index` action?
+**A**: Unfortunately, the current version of InternBuddy does not support the `undo` command. However, it is a feature +that we are exploring and hope to implement in the future! + +
+ +[//]: # (@@author potty10) +## **Command Summary** +Table 8 provides an overview of the commands supported in InternBuddy. + +| Action | Format, Examples | +|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------| +| List all Internships | `list` | +| Add an Internship | `add n/COMPANY_NAME r/ROLE s/STATUS d/DATE [c/COMMENT] [t/TAG]...`
e.g. `add n/Apple r/Software Engineer s/New d/2023-03-01` | +| Edit an Internship | `edit INDEX [n/COMPANY_NAME] [r/ROLE] [s/STATUS] [d/DATE] [c/COMMENT] [t/TAG]...`
e.g.`edit 2 s/Assessment r/Software Developer` | +| View an Internship | `view INDEX`
e.g. `view 1` | +| Copy an Internship to Clipboard | `copy INDEX`
e.g. `copy 1` | +| Find Internships | `find [n/COMPANY_NAME]... [r/ROLE]... [s/STATUS]... [d/DATE]... [t/TAG]...`
e.g. `find n/Apple n/Google` | +| Get Upcoming Events/Deadlines | `upcoming` | +| Delete Internships by Indices | `delete-index INDEX [INDEX]...`
e.g. `delete-index 1 3` | +| Delete Internships by Fields | `delete-field [n/COMPANY_NAME]... [r/ROLE]... [s/STATUS]... [d/DATE]... [t/TAG]...`
e.g. `delete-field n/Apple n/Google s/New s/Applied` | +| Clear all Internships | `clear` | +| Get Help | `help` | +| Exit InternBuddy | `exit` | + + +

Table 8: Commands in InternBuddy

+ + +## **Appendix A: Installing Java 11** +Follow the following steps to set up Java 11 on your computer. +1. Open up a terminal on your computer. + - If you are using Windows, click on the `Windows` icon at the bottom left of your computer. Then, type in `terminal` in + the search bar and double-click the application called `Terminal`. + - If you are using macOS, click on the `Spotlight` search icon at the top right of your computer. Then, type in `terminal` + in the search bar and double-click the application called `Terminal`. + - If you are using Linux, press + + to launch the terminal. +2. In the terminal, type in `java -version` and press . The terminal will display the version of + Java that you have installed on your computer. +3. If you do not have any versions of Java installed, or you have a version older than Java 11, download [Java 11](https://www.oracle.com/java/technologies/downloads/#java11) here. + You may then return to Step 1 to check whether you have the correct version of Java installed. + + +
+ +
+ +## **Appendix B: Customising the Data File** +If you are an advanced user of InternBuddy, you can directly edit the contents of your data through the `internbuddy.json` +file without using the GUI. The `internbuddy.json` file is found in the `data` subfolder of your InternBuddy +[home folder](#quick-start). + +
+:warning: **Warning:** If you are new to InternBuddy or are not confident in reading JSON files, we will advise you against +directly editing the `internbuddy.json` file. This is because if you accidentally make a mistake that leads to the JSON +file not having the correct format, InternBuddy would restart with the sample data file, wiping out any data that you had +previously. + +
-Action | Format, Examples ---------|------------------ -**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​`
e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague` -**Clear** | `clear` -**Delete** | `delete INDEX`
e.g., `delete 3` -**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…​`
e.g.,`edit 2 n/James Lee e/jameslee@example.com` -**Find** | `find KEYWORD [MORE_KEYWORDS]`
e.g., `find James Jake` -**List** | `list` -**Help** | `help` +The following steps outline how you can properly edit the `internbuddy.json` file: +1. Open the `internbuddy.json` file by double-clicking on it. If you are prompted to select an application to open + the file, you can choose any text editor such as `Notepad`. +2. Once opened, you will see the JSON data file in a format as shown in Figure 8. Referring to Figure 8, + each box contains the data for one specific internship entry. +3. Within each box, you can see that there are pairings where each pair is made up of a `FIELD` and `VALUE`. +

+ +

+

Figure 8: Sample JSON data file

+ +
+ +4. To manually change the value of a field, simply replace the text for `VALUE`. Figure 9 illustrates an example + where we change the value of `STATUS` from `assessment` to `interview` for the internship entry with company name + `Google`. Once your changes have been made, you can save the file by pressing + . +

+
+ :warning: **Warning:** Make sure that you follow the constraints + when substituting in your own values. If the constraints are not satisfied, InternBuddy will not be able to + read your data in the `internbuddy.json` file and will restart with all your internship data cleared. + +
+
+ + ![Editing the JSON File](images/ug-appendix-b-json-change.png) +

Figure 9: Editing the JSON file

+ +
+5. Launch InternBuddy and you will see that your data have been updated accordingly. + + +
+ +## **Appendix C: Populating InternBuddy with Sample Data** +Follow the following steps to populate InternBuddy with sample data. +1. Visit this [link](https://github.com/AY2223S2-CS2103T-T14-3/tp/blob/master/internbuddy.json). +2. Left-click the button labelled `Raw`. Figure 10 shows where the `Raw` button is. + + ![InternBuddy Sample Data](images/github-raw.png) +

Figure 10: Raw button on the GitHub interface

+
+ +
+ +3. Your screen will look like Figure 11. Right click, then click on `Save As`. +

+ +

+ +

Figure 11: InternBuddy Sample Data

+
+4. You will be prompted to choose a folder to save the file in. Choose the `data` subfolder that is found in the [home folder](#quick-start) that + you have chosen for InternBuddy. +5. Click `Save`. +

+
+ :warning: **Warning:** If you already have an internbuddy.json file in the subfolder, you will be prompted to + confirm whether you want to overwrite it when attempting to populate InternBuddy with sample data. + It is important to only choose to overwrite the existing file if you are absolutely certain that you + do not need your old InternBuddy data anymore. If you are unsure, you can always make a backup copy + of the file before overwriting. +
+6. You are done! InternBuddy will be populated with the sample data the next time you launch it. + +
+ + +## **Glossary** +Table 9 provides a glossary for the technical terms used in this user guide. + +| Term | Definition | +|--------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Command | An instruction for InternBuddy to perform an action. | +| Command Line Interface (CLI) | A CLI is the text-based interface that you can use to provide instructions to your computer. Examples of instructions include opening files and running programs. | +| Graphical User Interface (GUI) | A GUI is the visual interface that you see when an application launches, allowing you to interact with it by clicking on its various buttons and components. | +| Mainstream Operating Systems | Include Windows, macOS, Linux and Unix. | +| Field | A part of the command where you have to supply a value for the command to be valid. | +| Prefix | A short form for the name of a field. It indicates which field does a value belongs to. For example, in `n/Apple`, the value `Apple` is supplied to the field `COMPANY_NAME` since the `n/` prefix is used. | + +

Table 9: Glossary for InternBuddy's User Guide

+ +
+ +## **Acknowledgements** + +* InternBuddy is written in **Java 11**. +* It is adapted from the [AddressBook Level 3](https://github.com/se-edu/addressbook-level3) project created by + the [SE-EDU initiative](https://se-education.org). +* Libraries and frameworks used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), + [JUnit5](https://github.com/junit-team/junit5) and [TestFX](https://github.com/TestFX/TestFX). +* GUI testing is implemented with references from [AddressBook Level 4](https://github.com/se-edu/addressbook-level4) + and [Please Hire Us](https://github.com/AY2223S1-CS2103T-W17-4/tp). We utilised code from these projects to set + up GUI testing and added our own test cases to test the UI components that we created. +* The feature of Navigating Through Past Commands is primarily adapted from [HackNet](https://github.com/AY2122S2-CS2103T-W13-3/tp), + but we added code modifications and test cases. +* The sections on explaining the formatting standards and GUI interface in the User and Developer Guides are + inspired by [Please Hire Us](https://github.com/AY2223S1-CS2103T-W17-4/tp). diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..e9c15ecc606 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: "AB-3" +title: "InternBuddy" theme: minima header_pages: @@ -8,7 +8,7 @@ header_pages: markdown: kramdown -repository: "se-edu/addressbook-level3" +repository: "AY2223S2-CS2103T-T14-3/tp" github_icon: "images/github-icon.png" plugins: diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss index 0d3f6e80ced..f5be31f58ae 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: "InternBuddy"; font-size: 32px; } } diff --git a/docs/diagrams/AddSequenceDiagram.puml b/docs/diagrams/AddSequenceDiagram.puml new file mode 100644 index 00000000000..fbed503353b --- /dev/null +++ b/docs/diagrams/AddSequenceDiagram.puml @@ -0,0 +1,78 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternBuddyParser" as InternBuddyParser LOGIC_COLOR +participant ":AddCommandParser" as AddCommandParser LOGIC_COLOR +participant "a:AddCommand" as AddCommand LOGIC_COLOR +participant "cr:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR + +end box +[-> LogicManager : execute("add n/Google r/Engineer s/Applied d/2023-02-01") +activate LogicManager + +LogicManager -> InternBuddyParser : parseCommand("add n/Google r/Engineer s/Applied d/2023-02-01") +activate InternBuddyParser + +create AddCommandParser +InternBuddyParser -> AddCommandParser +activate AddCommandParser + +AddCommandParser --> InternBuddyParser +deactivate AddCommandParser + + + +InternBuddyParser -> AddCommandParser : parse("n/Google r/Engineer s/Applied d/2023-02-01") +activate AddCommandParser + +create AddCommand +AddCommandParser -> AddCommand : AddCommand(toAdd) +activate AddCommand + +AddCommand --> AddCommandParser +deactivate AddCommand + +AddCommandParser --> InternBuddyParser : a +deactivate AddCommandParser + + +InternBuddyParser --> LogicManager : a +destroy AddCommandParser +deactivate InternBuddyParser + +LogicManager -> AddCommand : execute() +activate AddCommand + +AddCommand -> Model : addInternship(toAdd) +activate Model + +Model --> AddCommand +deactivate Model + +AddCommand -> Model : updateSelectedInternship(toAdd) +activate Model + +Model --> AddCommand +deactivate Model + +create CommandResult +AddCommand -> CommandResult +activate CommandResult +CommandResult --> AddCommand +deactivate CommandResult + + +AddCommand --> LogicManager : cr +deactivate AddCommand +AddCommand -[hidden]-> LogicManager : result +destroy AddCommand + +[<--LogicManager : cr +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml index ef81d18c337..64fc110e5e3 100644 --- a/docs/diagrams/ArchitectureSequenceDiagram.puml +++ b/docs/diagrams/ArchitectureSequenceDiagram.puml @@ -7,19 +7,19 @@ Participant ":Logic" as logic LOGIC_COLOR Participant ":Model" as model MODEL_COLOR Participant ":Storage" as storage STORAGE_COLOR -user -[USER_COLOR]> ui : "delete 1" +user -[USER_COLOR]> ui : "delete-index 1" activate ui UI_COLOR -ui -[UI_COLOR]> logic : execute("delete 1") +ui -[UI_COLOR]> logic : execute("delete-index 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 deactivate model -logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook) +logic -[LOGIC_COLOR]> storage : saveInternBuddy(internBuddy) activate storage STORAGE_COLOR storage -[STORAGE_COLOR]> storage : Save to file diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml index 598474a5c82..85784a164f2 100644 --- a/docs/diagrams/BetterModelClassDiagram.puml +++ b/docs/diagrams/BetterModelClassDiagram.puml @@ -4,18 +4,19 @@ skinparam arrowThickness 1.1 skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR -AddressBook *-right-> "1" UniquePersonList -AddressBook *-right-> "1" UniqueTagList -UniqueTagList -[hidden]down- UniquePersonList -UniqueTagList -[hidden]down- UniquePersonList +InternBuddy *-right-> "1" UniqueInternshipList +InternBuddy *-right-> "1" UniqueTagList +UniqueTagList -[hidden]down- UniqueInternshipList +UniqueTagList -[hidden]down- UniqueInternshipList UniqueTagList -right-> "*" Tag -UniquePersonList -right-> Person +UniqueInternshipList -right-> Internship -Person -up-> "*" Tag +Internship -up-> "*" Tag -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address +Internship *--> CompanyName +Internship *--> Role +Internship *--> Status +Internship *--> Date +Internship *--> Comment @enduml diff --git a/docs/diagrams/ClearSequenceDiagram.puml b/docs/diagrams/ClearSequenceDiagram.puml new file mode 100644 index 00000000000..5c725025c5d --- /dev/null +++ b/docs/diagrams/ClearSequenceDiagram.puml @@ -0,0 +1,81 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternBuddyParser" as InternBuddyParser LOGIC_COLOR +participant ":ClearCommandParser" as ClearCommandParser LOGIC_COLOR +participant "p:InternshipContainsKeywordPredicate" as InternshipContainsKeywordPredicate LOGIC_COLOR +participant "c:ClearCommand" as ClearCommand LOGIC_COLOR +participant "cr:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("clear n/google") +activate LogicManager + +LogicManager -> InternBuddyParser : parseCommand("clear n/google") +activate InternBuddyParser + +create ClearCommandParser +InternBuddyParser -> ClearCommandParser +activate ClearCommandParser + +ClearCommandParser --> InternBuddyParser +deactivate ClearCommandParser + +InternBuddyParser -> ClearCommandParser : parse("n/google") +activate ClearCommandParser + +create InternshipContainsKeywordPredicate +ClearCommandParser -> InternshipContainsKeywordPredicate : InternshipContainsKeywordPredicate() +activate InternshipContainsKeywordPredicate + +InternshipContainsKeywordPredicate --> ClearCommandParser : p +deactivate InternshipContainsKeywordPredicate + +create ClearCommand +ClearCommandParser -> ClearCommand : ClearCommand(p) +activate ClearCommand + +ClearCommand --> ClearCommandParser : c +deactivate ClearCommand + +ClearCommandParser --> InternBuddyParser : c +deactivate ClearCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +ClearCommandParser -[hidden]-> InternBuddyParser +destroy ClearCommandParser + +InternBuddyParser --> LogicManager : c +deactivate InternBuddyParser + +LogicManager -> ClearCommand : execute() +activate ClearCommand + +ClearCommand -> Model : deleteInternshipByPredicate(p) +activate Model + +Model --> ClearCommand +deactivate Model + +create CommandResult +ClearCommand -> CommandResult +activate CommandResult + +CommandResult --> ClearCommand : cr +deactivate CommandResult + +ClearCommand --> LogicManager : cr +deactivate ClearCommand +ClearCommand -[hidden]-> LogicManager +destroy ClearCommand + +destroy InternshipContainsKeywordPredicate + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/CommitActivityDiagram.puml b/docs/diagrams/CommitActivityDiagram.puml index 6a6b23a006f..e71780ccd56 100644 --- a/docs/diagrams/CommitActivityDiagram.puml +++ b/docs/diagrams/CommitActivityDiagram.puml @@ -5,10 +5,10 @@ start 'Since the beta syntax does not support placing the condition outside the 'diamond we place it as the true branch instead. -if () then ([command commits AddressBook]) +if () then ([command commits InternBuddy]) :Purge redundant states; - :Save AddressBook to - addressBookStateList; + :Save InternBuddy to + internBuddyStateList; else ([else]) endif stop diff --git a/docs/diagrams/CopyActivityDiagram.puml b/docs/diagrams/CopyActivityDiagram.puml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/docs/diagrams/CopySequenceDiagram.puml b/docs/diagrams/CopySequenceDiagram.puml new file mode 100644 index 00000000000..a4836f574fd --- /dev/null +++ b/docs/diagrams/CopySequenceDiagram.puml @@ -0,0 +1,75 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternBuddyParser" as InternBuddyParser LOGIC_COLOR +participant ":CopyCommandParser" as CopyCommandParser LOGIC_COLOR +participant "c:CopyCommand" as CopyCommand LOGIC_COLOR +participant "<>\nParserUtil" as ParserUtil LOGIC_COLOR +participant "cr:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute("copy 1") +activate LogicManager + +LogicManager -> InternBuddyParser : parseCommand("copy 1") +activate InternBuddyParser + +create CopyCommandParser +InternBuddyParser -> CopyCommandParser +activate CopyCommandParser + +CopyCommandParser --> InternBuddyParser +deactivate CopyCommandParser + +InternBuddyParser -> CopyCommandParser : parse("1") +activate CopyCommandParser + +CopyCommandParser -> ParserUtil : parseIndex("1") +activate ParserUtil + +ParserUtil --> CopyCommandParser : indexObject +deactivate ParserUtil + +create CopyCommand +CopyCommandParser -> CopyCommand : CopyCommand(indexObject) +activate CopyCommand + +CopyCommand --> CopyCommandParser +deactivate CopyCommand + +CopyCommandParser --> InternBuddyParser : c +deactivate CopyCommandParser + +InternBuddyParser --> LogicManager : c +destroy CopyCommandParser +deactivate InternBuddyParser + +LogicManager -> CopyCommand : execute() +activate CopyCommand + +CopyCommand -> Model : copyInternship(internshipToCopy) +activate Model + +Model --> CopyCommand +deactivate Model + +create CommandResult +CopyCommand -> CommandResult +activate CommandResult + +CommandResult --> CopyCommand +deactivate CommandResult + +CopyCommand --> LogicManager : cr +deactivate CopyCommand + +[<--LogicManager : cr +destroy CopyCommand +deactivate LogicManager + +@enduml diff --git a/docs/diagrams/DeleteFieldSequenceDiagram.puml b/docs/diagrams/DeleteFieldSequenceDiagram.puml new file mode 100644 index 00000000000..a076ffe5d99 --- /dev/null +++ b/docs/diagrams/DeleteFieldSequenceDiagram.puml @@ -0,0 +1,100 @@ +'(@@author potty10) +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternBuddyParser" as InternBuddyParser LOGIC_COLOR +participant ":DeleteFieldCommandParser" as DeleteFieldCommandParser LOGIC_COLOR +participant "p:InternshipContainsKeywordPredicate\n" as InternshipContainsKeywordPredicate LOGIC_COLOR +participant "c:DeleteFieldCommand" as DeleteFieldCommand LOGIC_COLOR +participant "cr:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("delete-field n/google") +activate LogicManager + +LogicManager -> InternBuddyParser : parseCommand("delete-field n/google") +activate InternBuddyParser + +create DeleteFieldCommandParser +InternBuddyParser -> DeleteFieldCommandParser +activate DeleteFieldCommandParser + +DeleteFieldCommandParser --> InternBuddyParser +deactivate DeleteFieldCommandParser + +InternBuddyParser -> DeleteFieldCommandParser : parse("n/google") +activate DeleteFieldCommandParser + +create InternshipContainsKeywordPredicate +DeleteFieldCommandParser -> InternshipContainsKeywordPredicate : InternshipContainsKeywordPredicate(nameList,\n roleList, statusList, dateList, tagList) +activate InternshipContainsKeywordPredicate + +InternshipContainsKeywordPredicate --> DeleteFieldCommandParser +deactivate InternshipContainsKeywordPredicate + +create DeleteFieldCommand +DeleteFieldCommandParser -> DeleteFieldCommand : DeleteFieldCommand(p) +activate DeleteFieldCommand + +DeleteFieldCommand --> DeleteFieldCommandParser +deactivate DeleteFieldCommand + +DeleteFieldCommandParser --> InternBuddyParser : c +deactivate DeleteFieldCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +DeleteFieldCommandParser -[hidden]-> InternBuddyParser +destroy DeleteFieldCommandParser + +InternBuddyParser --> LogicManager : c +deactivate InternBuddyParser + +LogicManager -> DeleteFieldCommand : execute() +activate DeleteFieldCommand + +DeleteFieldCommand -> Model : getFilteredInternshipList() +activate Model + +Model --> DeleteFieldCommand +deactivate Model + + + +loop internship in internshipsToDelete + DeleteFieldCommand -> Model: deleteInternship(internship) + activate Model + Model --> DeleteFieldCommand + deactivate Model + +opt internship is selected + DeleteFieldCommand -> Model: updateSelectedInternship(null) + activate Model + Model --> DeleteFieldCommand + deactivate Model +end +end + +deactivate Model + +create CommandResult +DeleteFieldCommand -> CommandResult +activate CommandResult + +CommandResult --> DeleteFieldCommand : cr +deactivate CommandResult + +DeleteFieldCommand --> LogicManager : cr +deactivate DeleteFieldCommand +DeleteFieldCommand -[hidden]-> LogicManager +destroy DeleteFieldCommand +destroy InternshipContainsKeywordPredicate + + +[<--LogicManager : cr +deactivate LogicManager +@enduml diff --git a/docs/diagrams/DeleteIndexSequenceDiagram.puml b/docs/diagrams/DeleteIndexSequenceDiagram.puml new file mode 100644 index 00000000000..bba36929714 --- /dev/null +++ b/docs/diagrams/DeleteIndexSequenceDiagram.puml @@ -0,0 +1,85 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternBuddyParser" as InternBuddyParser LOGIC_COLOR +participant ":DeleteIndexCommandParser" as DeleteIndexCommandParser LOGIC_COLOR +participant "c:DeleteIndexCommand" as DeleteIndexCommand LOGIC_COLOR +participant "cr:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("delete-index 1 2") +activate LogicManager + +LogicManager -> InternBuddyParser : parseCommand("delete-index 1 2") +activate InternBuddyParser + +create DeleteIndexCommandParser +InternBuddyParser -> DeleteIndexCommandParser +activate DeleteIndexCommandParser + +DeleteIndexCommandParser --> InternBuddyParser +deactivate DeleteIndexCommandParser + +InternBuddyParser -> DeleteIndexCommandParser : parse("1 2") +activate DeleteIndexCommandParser + +create DeleteIndexCommand +DeleteIndexCommandParser -> DeleteIndexCommand : DeleteIndexCommand(indexes) +activate DeleteIndexCommand + +DeleteIndexCommand --> DeleteIndexCommandParser +deactivate DeleteIndexCommand + +DeleteIndexCommandParser --> InternBuddyParser : c +deactivate DeleteIndexCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +DeleteIndexCommandParser -[hidden]-> InternBuddyParser +destroy DeleteIndexCommandParser + +InternBuddyParser --> LogicManager : c +deactivate InternBuddyParser + +LogicManager -> DeleteIndexCommand : execute() +activate DeleteIndexCommand + +DeleteIndexCommand -> Model : getFilteredInternshipList() +activate Model + +Model --> DeleteIndexCommand +deactivate Model + +loop internship in internshipsToDelete + DeleteIndexCommand -> Model: deleteInternship(internship) + activate Model + Model --> DeleteIndexCommand + deactivate Model +opt internship is selected + DeleteIndexCommand -> Model: updateSelectedInternship(null) + activate Model + Model --> DeleteIndexCommand + deactivate Model +end +end + +create CommandResult +DeleteIndexCommand -> CommandResult +activate CommandResult + +CommandResult --> DeleteIndexCommand : cr +deactivate CommandResult + +DeleteIndexCommand --> LogicManager : cr +deactivate DeleteIndexCommand +DeleteIndexCommand -[hidden]-> LogicManager +destroy DeleteIndexCommand + + +[<--LogicManager : cr +deactivate LogicManager +@enduml diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml index 1dc2311b245..528c1f10efd 100644 --- a/docs/diagrams/DeleteSequenceDiagram.puml +++ b/docs/diagrams/DeleteSequenceDiagram.puml @@ -3,66 +3,83 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":InternBuddyParser" as InternBuddyParser LOGIC_COLOR participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR -participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR -participant ":CommandResult" as CommandResult LOGIC_COLOR +participant "p:InternshipContainsKeywordPredicate" as InternshipContainsKeywordPredicate LOGIC_COLOR +participant "c:DeleteCommand" as DeleteCommand LOGIC_COLOR +participant "cr:CommandResult" as CommandResult LOGIC_COLOR end box box Model MODEL_COLOR_T1 participant ":Model" as Model MODEL_COLOR end box -[-> LogicManager : execute("delete 1") +[-> LogicManager : execute("delete n/google") activate LogicManager -LogicManager -> AddressBookParser : parseCommand("delete 1") -activate AddressBookParser +LogicManager -> InternBuddyParser : parseCommand("delete n/google") +activate InternBuddyParser create DeleteCommandParser -AddressBookParser -> DeleteCommandParser +InternBuddyParser -> DeleteCommandParser activate DeleteCommandParser -DeleteCommandParser --> AddressBookParser +DeleteCommandParser --> InternBuddyParser deactivate DeleteCommandParser -AddressBookParser -> DeleteCommandParser : parse("1") +InternBuddyParser -> DeleteCommandParser : parse("n/google") activate DeleteCommandParser +create InternshipContainsKeywordPredicate +DeleteCommandParser -> InternshipContainsKeywordPredicate : InternshipContainsKeywordPredicate() +activate InternshipContainsKeywordPredicate + +InternshipContainsKeywordPredicate --> DeleteCommandParser : p +deactivate InternshipContainsKeywordPredicate + create DeleteCommand -DeleteCommandParser -> DeleteCommand +DeleteCommandParser -> DeleteCommand : DeleteCommand(p) activate DeleteCommand -DeleteCommand --> DeleteCommandParser : d +DeleteCommand --> DeleteCommandParser : c deactivate DeleteCommand -DeleteCommandParser --> AddressBookParser : d +DeleteCommandParser --> InternBuddyParser : c deactivate DeleteCommandParser 'Hidden arrow to position the destroy marker below the end of the activation bar. -DeleteCommandParser -[hidden]-> AddressBookParser +DeleteCommandParser -[hidden]-> InternBuddyParser destroy DeleteCommandParser -AddressBookParser --> LogicManager : d -deactivate AddressBookParser +InternBuddyParser --> LogicManager : c +deactivate InternBuddyParser LogicManager -> DeleteCommand : execute() activate DeleteCommand -DeleteCommand -> Model : deletePerson(1) +DeleteCommand -> Model : getFilteredInternshipList() activate Model Model --> DeleteCommand + +loop internship in internshipsToDelete + DeleteCommand -> Model: deleteInternship(internship) +end + deactivate Model create CommandResult DeleteCommand -> CommandResult activate CommandResult -CommandResult --> DeleteCommand +CommandResult --> DeleteCommand : cr deactivate CommandResult -DeleteCommand --> LogicManager : result +DeleteCommand --> LogicManager : cr deactivate DeleteCommand +DeleteCommand -[hidden]-> LogicManager +destroy DeleteCommand + +destroy InternshipContainsKeywordPredicate [<--LogicManager deactivate LogicManager diff --git a/docs/diagrams/EditActivityDiagram.puml b/docs/diagrams/EditActivityDiagram.puml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/docs/diagrams/EditSequenceDiagram.puml b/docs/diagrams/EditSequenceDiagram.puml new file mode 100644 index 00000000000..e2184153a55 --- /dev/null +++ b/docs/diagrams/EditSequenceDiagram.puml @@ -0,0 +1,91 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternBuddyParser" as InternBuddyParser LOGIC_COLOR +participant ":EditCommandParser" as EditCommandParser LOGIC_COLOR +participant "e:EditCommand" as EditCommand LOGIC_COLOR +participant "cr:CommandResult" as CommandResult LOGIC_COLOR +participant ":EditInternshipDescriptor" as Descriptor LOGIC_COLOR +participant "<> \n EditCommand" as ClassEditCommand LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("edit 2 n/Google") +activate LogicManager + +LogicManager -> InternBuddyParser : parseCommand("edit 2 n/Google") +activate InternBuddyParser + +create EditCommandParser +InternBuddyParser -> EditCommandParser +activate EditCommandParser + +EditCommandParser --> InternBuddyParser +deactivate EditCommandParser + +InternBuddyParser -> EditCommandParser : parse("2 n/Google") +activate EditCommandParser + +create EditCommand +EditCommandParser -> EditCommand +activate EditCommand +create Descriptor +EditCommand -> Descriptor +activate Descriptor +Descriptor --> EditCommand : +deactivate Descriptor +EditCommand --> EditCommandParser : e +deactivate EditCommand + +EditCommandParser --> InternBuddyParser : e +deactivate EditCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +EditCommandParser -[hidden]-> InternBuddyParser +destroy EditCommandParser + +InternBuddyParser --> LogicManager : e +deactivate InternBuddyParser + +LogicManager -> EditCommand : execute() +activate EditCommand +EditCommand -> ClassEditCommand : createEditedInternship(toEdit, editInternshipDescriptor) +activate ClassEditCommand +ClassEditCommand -->EditCommand :edited +deactivate ClassEditCommand +'EditCommand -> Descriptor +'activate Descriptor +'Descriptor --> EditCommand : edited +'deactivate Descriptor +'Descriptor -[hidden]-> EditCommand +'destroy Descriptor +EditCommand -> Model : setInternship(toEdit, edited) +activate Model + +Model --> EditCommand +deactivate Model +EditCommand -> Model : updateSelectedInternship(edited) +activate Model + +Model --> EditCommand +deactivate Model + +create CommandResult +EditCommand -> CommandResult +activate CommandResult + +CommandResult --> EditCommand +deactivate CommandResult + +EditCommand --> LogicManager : cr +deactivate EditCommand + +[<--LogicManager : cr +destroy EditCommand +destroy Descriptor +deactivate LogicManager +@enduml diff --git a/docs/diagrams/FindSequenceDiagram.puml b/docs/diagrams/FindSequenceDiagram.puml new file mode 100644 index 00000000000..f117de3256f --- /dev/null +++ b/docs/diagrams/FindSequenceDiagram.puml @@ -0,0 +1,82 @@ +'(@@author kohkaixun) +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternBuddyParser" as InternBuddyParser LOGIC_COLOR +participant ":FindCommandParser" as FindCommandParser LOGIC_COLOR +participant "p:InternshipContainsKeywordPredicate" as InternshipContainsKeywordPredicate 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 n/Google") +activate LogicManager + +LogicManager -> InternBuddyParser : parseCommand("find n/Google") +activate InternBuddyParser + +create FindCommandParser +InternBuddyParser -> FindCommandParser +activate FindCommandParser + +FindCommandParser --> InternBuddyParser +deactivate FindCommandParser + +InternBuddyParser -> FindCommandParser : parse("n/ Google") +activate FindCommandParser + +create InternshipContainsKeywordPredicate +FindCommandParser -> InternshipContainsKeywordPredicate +activate InternshipContainsKeywordPredicate + +InternshipContainsKeywordPredicate --> FindCommandParser : p +deactivate InternshipContainsKeywordPredicate + +create FindCommand +FindCommandParser -> FindCommand : FindCommand(p) +activate FindCommand + +FindCommand --> FindCommandParser : f +deactivate FindCommand + +FindCommandParser --> InternBuddyParser : f +deactivate FindCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +FindCommandParser -[hidden]-> InternBuddyParser +destroy FindCommandParser + +InternBuddyParser --> LogicManager : f +deactivate InternBuddyParser + +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 +'Hidden arrow to position the destroy marker below the end of the activation bar. +FindCommand -[hidden]-> LogicManager +destroy FindCommand +destroy InternshipContainsKeywordPredicate + +[<--LogicManager : result +deactivate LogicManager +@enduml diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml index d4193173e18..1f196f63cd2 100644 --- a/docs/diagrams/LogicClassDiagram.puml +++ b/docs/diagrams/LogicClassDiagram.puml @@ -6,13 +6,13 @@ skinparam classBackgroundColor LOGIC_COLOR package Logic { -Class AddressBookParser +Class InternBuddyParser Class XYZCommand Class CommandResult Class "{abstract}\nCommand" as Command -Class "<>\nLogic" as Logic +Class "<>\nLogic" as LogicInterface Class LogicManager } @@ -24,11 +24,11 @@ package Storage{ } Class HiddenOutside #FFFFFF -HiddenOutside ..> Logic +HiddenOutside ..> LogicInterface -LogicManager .right.|> Logic -LogicManager -right->"1" AddressBookParser -AddressBookParser ..> XYZCommand : creates > +LogicManager .right.|> LogicInterface +LogicManager -right->"1" InternBuddyParser +InternBuddyParser ..> XYZCommand : creates > XYZCommand -up-|> Command LogicManager .left.> Command : executes > @@ -40,7 +40,7 @@ Command .[hidden]up.> Storage Command .right.> Model note right of XYZCommand: XYZCommand = AddCommand, \nFindCommand, etc -Logic ..> CommandResult +LogicInterface ..> CommandResult LogicManager .down.> CommandResult Command .up.> CommandResult : produces > @enduml diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 4439108973a..4b47be8810f 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -5,46 +5,48 @@ skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR Package Model <>{ -Class "<>\nReadOnlyAddressBook" as ReadOnlyAddressBook +Class "<>\nReadOnlyInternBuddy" as ReadOnlyInternBuddy Class "<>\nReadOnlyUserPrefs" as ReadOnlyUserPrefs -Class "<>\nModel" as Model -Class AddressBook +Class "<>\nModel" as ModelInterface +Class InternBuddy Class ModelManager Class UserPrefs -Class UniquePersonList -Class Person -Class Address -Class Email -Class Name -Class Phone +Class UniqueInternshipList +Class Internship +Class Status +Class Date +Class CompanyName +Class Role +class Comment Class Tag } Class HiddenOutside #FFFFFF -HiddenOutside ..> Model +HiddenOutside ..> ModelInterface -AddressBook .up.|> ReadOnlyAddressBook +InternBuddy .up.|> ReadOnlyInternBuddy -ModelManager .up.|> Model -Model .right.> ReadOnlyUserPrefs -Model .left.> ReadOnlyAddressBook -ModelManager -left-> "1" AddressBook +ModelManager .up.|> ModelInterface +ModelInterface .right.> ReadOnlyUserPrefs +ModelInterface .left.> ReadOnlyInternBuddy +ModelManager -left-> "1" InternBuddy ModelManager -right-> "1" UserPrefs UserPrefs .up.|> ReadOnlyUserPrefs -AddressBook *--> "1" UniquePersonList -UniquePersonList --> "~* all" Person -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address -Person *--> "*" Tag +InternBuddy *--> "1" UniqueInternshipList +UniqueInternshipList --> "~* all" Internship +Internship *--> CompanyName +Internship *--> Role +Internship *--> Date +Internship *--> Status +Internship *--> Comment +Internship *--> "*" Tag -Name -[hidden]right-> Phone -Phone -[hidden]right-> Address -Address -[hidden]right-> Email +CompanyName -[hidden]right-> Role +Role -[hidden]right-> Status +Status -[hidden]right-> Date -ModelManager -->"~* filtered" Person +ModelManager -->"~* filtered" Internship @enduml diff --git a/docs/diagrams/ParserClasses.puml b/docs/diagrams/ParserClasses.puml index 0c7424de6e0..f9137798737 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 InternBuddyParser Class XYZCommandParser Class CliSyntax Class ParserUtil @@ -19,12 +19,12 @@ Class Prefix } Class HiddenOutside #FFFFFF -HiddenOutside ..> AddressBookParser +HiddenOutside ..> InternBuddyParser -AddressBookParser .down.> XYZCommandParser: creates > +InternBuddyParser .down.> XYZCommandParser: creates > XYZCommandParser ..> XYZCommand : creates > -AddressBookParser ..> Command : returns > +InternBuddyParser ..> Command : returns > XYZCommandParser .up.|> Parser XYZCommandParser ..> ArgumentMultimap XYZCommandParser ..> ArgumentTokenizer diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml index 760305e0e58..12c83691b7f 100644 --- a/docs/diagrams/StorageClassDiagram.puml +++ b/docs/diagrams/StorageClassDiagram.puml @@ -11,33 +11,33 @@ Class "<>\nUserPrefsStorage" as UserPrefsStorage Class JsonUserPrefsStorage } -Class "<>\nStorage" as Storage +Class "<>\nStorage" as StorageInterface Class StorageManager -package "AddressBook Storage" #F4F6F6{ -Class "<>\nAddressBookStorage" as AddressBookStorage -Class JsonAddressBookStorage -Class JsonSerializableAddressBook -Class JsonAdaptedPerson +package "InternBuddy Storage" #F4F6F6{ +Class "<>\nInternBuddyStorage" as InternBuddyStorage +Class JsonInternBuddyStorage +Class JsonSerializableInternBuddy +Class JsonAdaptedInternship Class JsonAdaptedTag } } Class HiddenOutside #FFFFFF -HiddenOutside ..> Storage +HiddenOutside ..> StorageInterface -StorageManager .up.|> Storage +StorageManager .up.|> StorageInterface StorageManager -up-> "1" UserPrefsStorage -StorageManager -up-> "1" AddressBookStorage +StorageManager -up-> "1" InternBuddyStorage -Storage -left-|> UserPrefsStorage -Storage -right-|> AddressBookStorage +StorageInterface -left-|> UserPrefsStorage +StorageInterface -right-|> InternBuddyStorage JsonUserPrefsStorage .up.|> UserPrefsStorage -JsonAddressBookStorage .up.|> AddressBookStorage -JsonAddressBookStorage ..> JsonSerializableAddressBook -JsonSerializableAddressBook --> "*" JsonAdaptedPerson -JsonAdaptedPerson --> "*" JsonAdaptedTag +JsonInternBuddyStorage .up.|> InternBuddyStorage +JsonInternBuddyStorage ..> JsonSerializableInternBuddy +JsonSerializableInternBuddy --> "*" JsonAdaptedInternship +JsonAdaptedInternship --> "*" JsonAdaptedTag @enduml diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml index 95473d5aa19..6e63338b1b0 100644 --- a/docs/diagrams/UiClassDiagram.puml +++ b/docs/diagrams/UiClassDiagram.puml @@ -11,10 +11,11 @@ Class UiManager Class MainWindow Class HelpWindow Class ResultDisplay -Class PersonListPanel -Class PersonCard +Class InternshipListPanel +Class InternshipCard Class StatusBarFooter Class CommandBox +Class InternshipDetailsCard } package Model <> { @@ -32,26 +33,30 @@ UiManager .left.|> Ui UiManager -down-> "1" MainWindow MainWindow *-down-> "1" CommandBox MainWindow *-down-> "1" ResultDisplay -MainWindow *-down-> "1" PersonListPanel +MainWindow *-down-> "1" InternshipListPanel MainWindow *-down-> "1" StatusBarFooter MainWindow --> "0..1" HelpWindow +MainWindow *-down-> "0..1" InternshipDetailsCard -PersonListPanel -down-> "*" PersonCard +InternshipListPanel -down-> "*" InternshipCard MainWindow -left-|> UiPart ResultDisplay --|> UiPart CommandBox --|> UiPart -PersonListPanel --|> UiPart -PersonCard --|> UiPart +InternshipListPanel --|> UiPart +InternshipCard --|> UiPart +InternshipDetailsCard --|> UiPart StatusBarFooter --|> UiPart HelpWindow --|> UiPart -PersonCard ..> Model +InternshipDetailsCard ...> Model +InternshipCard ...> Model + UiManager -right-> Logic MainWindow -left-> Logic -PersonListPanel -[hidden]left- HelpWindow +InternshipListPanel -[hidden]left- HelpWindow HelpWindow -[hidden]left- CommandBox CommandBox -[hidden]left- ResultDisplay ResultDisplay -[hidden]left- StatusBarFooter diff --git a/docs/diagrams/UndoRedoState0.puml b/docs/diagrams/UndoRedoState0.puml index 96e30744d24..b761e07e08c 100644 --- a/docs/diagrams/UndoRedoState0.puml +++ b/docs/diagrams/UndoRedoState0.puml @@ -6,15 +6,15 @@ skinparam ClassBorderColor #000000 title Initial state package States { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "ib0:InternBuddy" + class State2 as "ib1:InternBuddy" + class State3 as "ib2:InternBuddy" } State1 -[hidden]right-> State2 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/UndoRedoState1.puml b/docs/diagrams/UndoRedoState1.puml index 01fcb9b2b96..aac6165d778 100644 --- a/docs/diagrams/UndoRedoState1.puml +++ b/docs/diagrams/UndoRedoState1.puml @@ -6,9 +6,9 @@ skinparam ClassBorderColor #000000 title After command "delete 5" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "ib0:InternBuddy" + class State2 as "ib1:InternBuddy" + class State3 as "ib2:InternBuddy" } State1 -[hidden]right-> State2 @@ -16,7 +16,7 @@ State2 -[hidden]right-> State3 hide State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State2 @end diff --git a/docs/diagrams/UndoRedoState2.puml b/docs/diagrams/UndoRedoState2.puml index bccc230a5d1..8708d39a475 100644 --- a/docs/diagrams/UndoRedoState2.puml +++ b/docs/diagrams/UndoRedoState2.puml @@ -3,18 +3,18 @@ skinparam ClassFontColor #000000 skinparam ClassBorderColor #000000 -title After command "add n/David" +title After command "add n/Tesla" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "ib0:InternBuddy" + class State2 as "ib1:InternBuddy" + class State3 as "ib2:InternBuddy" } 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/UndoRedoState3.puml b/docs/diagrams/UndoRedoState3.puml index ea29c9483e4..f75f199d98f 100644 --- a/docs/diagrams/UndoRedoState3.puml +++ b/docs/diagrams/UndoRedoState3.puml @@ -6,15 +6,15 @@ skinparam ClassBorderColor #000000 title After command "undo" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "ib0:InternBuddy" + class State2 as "ib1:InternBuddy" + class State3 as "ib2:InternBuddy" } State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State2 @end diff --git a/docs/diagrams/UndoRedoState4.puml b/docs/diagrams/UndoRedoState4.puml index 1b784cece80..73ff37ca27a 100644 --- a/docs/diagrams/UndoRedoState4.puml +++ b/docs/diagrams/UndoRedoState4.puml @@ -6,15 +6,15 @@ skinparam ClassBorderColor #000000 title After command "list" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "ib0:InternBuddy" + class State2 as "ib1:InternBuddy" + class State3 as "ib2:InternBuddy" } State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State2 @end diff --git a/docs/diagrams/UndoRedoState5.puml b/docs/diagrams/UndoRedoState5.puml index 88927be32bc..a1b95f0898c 100644 --- a/docs/diagrams/UndoRedoState5.puml +++ b/docs/diagrams/UndoRedoState5.puml @@ -6,16 +6,16 @@ skinparam ClassBorderColor #000000 title After command "clear" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab3:AddressBook__" + class State1 as "ib0:InternBuddy" + class State2 as "ib1:InternBuddy" + class State3 as "ib3:InternBuddy" } State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State3 -note right on link: State ab2 deleted. +note right on link: State ib2 deleted. @end diff --git a/docs/diagrams/UndoSequenceDiagram.puml b/docs/diagrams/UndoSequenceDiagram.puml index 410aab4e412..9fbc8cce56e 100644 --- a/docs/diagrams/UndoSequenceDiagram.puml +++ b/docs/diagrams/UndoSequenceDiagram.puml @@ -3,42 +3,42 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":InternBuddyParser" as InternBuddyParser 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 +participant ":VersionedInternBuddy" as VersionedInternBuddy MODEL_COLOR end box [-> LogicManager : execute(undo) activate LogicManager -LogicManager -> AddressBookParser : parseCommand(undo) -activate AddressBookParser +LogicManager -> InternBuddyParser : parseCommand(undo) +activate InternBuddyParser create UndoCommand -AddressBookParser -> UndoCommand +InternBuddyParser -> UndoCommand activate UndoCommand -UndoCommand --> AddressBookParser +UndoCommand --> InternBuddyParser deactivate UndoCommand -AddressBookParser --> LogicManager : u -deactivate AddressBookParser +InternBuddyParser --> LogicManager : u +deactivate InternBuddyParser LogicManager -> UndoCommand : execute() activate UndoCommand -UndoCommand -> Model : undoAddressBook() +UndoCommand -> Model : undoInternBuddy() activate Model -Model -> VersionedAddressBook : undo() -activate VersionedAddressBook +Model -> VersionedInternBuddy : undo() +activate VersionedInternBuddy -VersionedAddressBook -> VersionedAddressBook :resetData(ReadOnlyAddressBook) -VersionedAddressBook --> Model : -deactivate VersionedAddressBook +VersionedInternBuddy -> VersionedInternBuddy :resetData(ReadOnlyInternBuddy) +VersionedInternBuddy --> Model : +deactivate VersionedInternBuddy Model --> UndoCommand deactivate Model diff --git a/docs/diagrams/UpcomingSequenceDiagram.puml b/docs/diagrams/UpcomingSequenceDiagram.puml new file mode 100644 index 00000000000..cde144a6979 --- /dev/null +++ b/docs/diagrams/UpcomingSequenceDiagram.puml @@ -0,0 +1,54 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternBuddyParser" as InternBuddyParser LOGIC_COLOR +participant "u:UpcomingCommand" as UpcomingCommand LOGIC_COLOR +participant "cr:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute("upcoming") +activate LogicManager + +LogicManager -> InternBuddyParser : parseCommand("upcoming") +activate InternBuddyParser + +create UpcomingCommand +InternBuddyParser -> UpcomingCommand +activate UpcomingCommand + +UpcomingCommand -->InternBuddyParser : u +deactivate UpcomingCommand + +InternBuddyParser --> LogicManager : u + +deactivate InternBuddyParser + +LogicManager -> UpcomingCommand : execute() +activate UpcomingCommand + +UpcomingCommand -> Model : updateFilteredInternshipList(predicate) +activate Model + +Model --> UpcomingCommand +deactivate Model + +create CommandResult +UpcomingCommand -> CommandResult +activate CommandResult + +CommandResult --> UpcomingCommand +deactivate CommandResult + +UpcomingCommand --> LogicManager : cr +deactivate UpcomingCommand + +[<--LogicManager : cr +destroy UpcomingCommand +deactivate LogicManager + +@enduml diff --git a/docs/diagrams/ViewActivityDiagram.puml b/docs/diagrams/ViewActivityDiagram.puml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/docs/diagrams/ViewSequenceDiagram.puml b/docs/diagrams/ViewSequenceDiagram.puml new file mode 100644 index 00000000000..f0105836b1c --- /dev/null +++ b/docs/diagrams/ViewSequenceDiagram.puml @@ -0,0 +1,81 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":InternBuddyParser" as InternBuddyParser LOGIC_COLOR +participant ":ViewCommandParser" as ViewCommandParser LOGIC_COLOR +participant "v:ViewCommand" as ViewCommand LOGIC_COLOR +participant "<>\nParserUtil" as ParserUtil LOGIC_COLOR +participant "cr:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute("view 1") +activate LogicManager + +LogicManager -> InternBuddyParser : parseCommand("view 1") +activate InternBuddyParser + +create ViewCommandParser +InternBuddyParser -> ViewCommandParser +activate ViewCommandParser + +ViewCommandParser --> InternBuddyParser +deactivate ViewCommandParser + +InternBuddyParser -> ViewCommandParser : parse("1") +activate ViewCommandParser + +ViewCommandParser -> ParserUtil : parseIndex("1") +activate ParserUtil + +ParserUtil --> ViewCommandParser : indexObject +deactivate ParserUtil + +create ViewCommand +ViewCommandParser -> ViewCommand : ViewCommand(indexObject) +activate ViewCommand + +ViewCommand --> ViewCommandParser +deactivate ViewCommand + +ViewCommandParser --> InternBuddyParser : v +deactivate ViewCommandParser + +InternBuddyParser --> LogicManager : v +destroy ViewCommandParser +deactivate InternBuddyParser + +LogicManager -> ViewCommand : execute() +activate ViewCommand + +ViewCommand -> Model : getFilteredInternshipList() +activate Model + +Model --> ViewCommand : filteredInternshipList +deactivate Model + +ViewCommand -> Model : updateSelectedInternship(retrievedInternship) +activate Model + +Model --> ViewCommand +deactivate Model + +create CommandResult +ViewCommand -> CommandResult +activate CommandResult + +CommandResult --> ViewCommand +deactivate CommandResult + +ViewCommand --> LogicManager : cr +deactivate ViewCommand + +[<--LogicManager : cr +destroy ViewCommand +deactivate LogicManager + +@enduml diff --git a/docs/images/AddSequenceDiagram.png b/docs/images/AddSequenceDiagram.png new file mode 100644 index 00000000000..a631e385440 Binary files /dev/null and b/docs/images/AddSequenceDiagram.png differ diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png index 2f1346869d0..02a29078513 100644 Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png index 94440f0ac4a..7ad7ea85fc5 100644 Binary files a/docs/images/BetterModelClassDiagram.png and b/docs/images/BetterModelClassDiagram.png differ diff --git a/docs/images/Clear-entries-warning-message.png b/docs/images/Clear-entries-warning-message.png new file mode 100644 index 00000000000..d51cd78c10c Binary files /dev/null and b/docs/images/Clear-entries-warning-message.png differ diff --git a/docs/images/CommitActivityDiagram.png b/docs/images/CommitActivityDiagram.png index c08c13f5c8b..0bf974108c8 100644 Binary files a/docs/images/CommitActivityDiagram.png and b/docs/images/CommitActivityDiagram.png differ diff --git a/docs/images/CopySequenceDiagram.png b/docs/images/CopySequenceDiagram.png new file mode 100644 index 00000000000..f6967f0388b Binary files /dev/null and b/docs/images/CopySequenceDiagram.png differ diff --git a/docs/images/DeleteFieldSequenceDiagram.png b/docs/images/DeleteFieldSequenceDiagram.png new file mode 100644 index 00000000000..732eb079266 Binary files /dev/null and b/docs/images/DeleteFieldSequenceDiagram.png differ diff --git a/docs/images/DeleteIndexSequenceDiagram.png b/docs/images/DeleteIndexSequenceDiagram.png new file mode 100644 index 00000000000..7dfdec79f56 Binary files /dev/null and b/docs/images/DeleteIndexSequenceDiagram.png differ diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png index fa327b39618..888f97d6f50 100644 Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ diff --git a/docs/images/EditSequenceDiagram.png b/docs/images/EditSequenceDiagram.png new file mode 100644 index 00000000000..f639d077453 Binary files /dev/null and b/docs/images/EditSequenceDiagram.png differ diff --git a/docs/images/Edit_data_warning_message.png b/docs/images/Edit_data_warning_message.png new file mode 100644 index 00000000000..dce1664c904 Binary files /dev/null and b/docs/images/Edit_data_warning_message.png differ diff --git a/docs/images/FindSequenceDiagram.png b/docs/images/FindSequenceDiagram.png new file mode 100644 index 00000000000..809c9482680 Binary files /dev/null and b/docs/images/FindSequenceDiagram.png differ diff --git a/docs/images/InternBuddy GUI Markup.pptx b/docs/images/InternBuddy GUI Markup.pptx new file mode 100644 index 00000000000..8f730640519 Binary files /dev/null and b/docs/images/InternBuddy GUI Markup.pptx differ diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png index 9e9ba9f79e5..35138ee640d 100644 Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index 04070af60d8..a8d8f85c68d 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/ParserClasses.png b/docs/images/ParserClasses.png index e7b4c8880cd..1122156e804 100644 Binary files a/docs/images/ParserClasses.png and b/docs/images/ParserClasses.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png index 2533a5c1af0..8d104730243 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..b9c9b4ad935 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..cea4068044b 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/UndoRedoState0.png b/docs/images/UndoRedoState0.png index 8f7538cd884..7812a55d57d 100644 Binary files a/docs/images/UndoRedoState0.png and b/docs/images/UndoRedoState0.png differ diff --git a/docs/images/UndoRedoState1.png b/docs/images/UndoRedoState1.png index df9908d0948..e214456808d 100644 Binary files a/docs/images/UndoRedoState1.png and b/docs/images/UndoRedoState1.png differ diff --git a/docs/images/UndoRedoState2.png b/docs/images/UndoRedoState2.png index 36519c1015b..8485ecb2d6c 100644 Binary files a/docs/images/UndoRedoState2.png and b/docs/images/UndoRedoState2.png differ diff --git a/docs/images/UndoRedoState3.png b/docs/images/UndoRedoState3.png index 19959d01712..94f1763743b 100644 Binary files a/docs/images/UndoRedoState3.png and b/docs/images/UndoRedoState3.png differ diff --git a/docs/images/UndoRedoState4.png b/docs/images/UndoRedoState4.png index 4c623e4f2c5..be74bf157b6 100644 Binary files a/docs/images/UndoRedoState4.png and b/docs/images/UndoRedoState4.png differ diff --git a/docs/images/UndoRedoState5.png b/docs/images/UndoRedoState5.png index 84ad2afa6bd..0977b7cf5e2 100644 Binary files a/docs/images/UndoRedoState5.png and b/docs/images/UndoRedoState5.png differ diff --git a/docs/images/UndoSequenceDiagram.png b/docs/images/UndoSequenceDiagram.png index 6addcd3a8d9..7eb6ca91857 100644 Binary files a/docs/images/UndoSequenceDiagram.png and b/docs/images/UndoSequenceDiagram.png differ diff --git a/docs/images/UpcomingSequenceDiagram.png b/docs/images/UpcomingSequenceDiagram.png new file mode 100644 index 00000000000..04d9ea7cf8d Binary files /dev/null and b/docs/images/UpcomingSequenceDiagram.png differ diff --git a/docs/images/ViewSequenceDiagram.png b/docs/images/ViewSequenceDiagram.png new file mode 100644 index 00000000000..d269dfe09cd Binary files /dev/null and b/docs/images/ViewSequenceDiagram.png differ diff --git a/docs/images/derricksaltfish.png b/docs/images/derricksaltfish.png new file mode 100644 index 00000000000..39d00073762 Binary files /dev/null and b/docs/images/derricksaltfish.png differ diff --git a/docs/images/dg-case-insensitive-add-after.png b/docs/images/dg-case-insensitive-add-after.png new file mode 100644 index 00000000000..da5dc0b56e9 Binary files /dev/null and b/docs/images/dg-case-insensitive-add-after.png differ diff --git a/docs/images/dg-case-insensitive-add-before.png b/docs/images/dg-case-insensitive-add-before.png new file mode 100644 index 00000000000..7d18bc6af0f Binary files /dev/null and b/docs/images/dg-case-insensitive-add-before.png differ diff --git a/docs/images/dg-case-insensitive-prefix-fix.png b/docs/images/dg-case-insensitive-prefix-fix.png new file mode 100644 index 00000000000..f98afdab898 Binary files /dev/null and b/docs/images/dg-case-insensitive-prefix-fix.png differ diff --git a/docs/images/dg-case-sensitive-find-after.png b/docs/images/dg-case-sensitive-find-after.png new file mode 100644 index 00000000000..ec1cffc2ab5 Binary files /dev/null and b/docs/images/dg-case-sensitive-find-after.png differ diff --git a/docs/images/dg-case-sensitive-find-before.png b/docs/images/dg-case-sensitive-find-before.png new file mode 100644 index 00000000000..3a7ecbb2fd3 Binary files /dev/null and b/docs/images/dg-case-sensitive-find-before.png differ diff --git a/docs/images/dg-case-sensitive-prefix-add-example.png b/docs/images/dg-case-sensitive-prefix-add-example.png new file mode 100644 index 00000000000..4464a99bc8c Binary files /dev/null and b/docs/images/dg-case-sensitive-prefix-add-example.png differ diff --git a/docs/images/dg-int-overflow-problem.png b/docs/images/dg-int-overflow-problem.png new file mode 100644 index 00000000000..8a54a125d98 Binary files /dev/null and b/docs/images/dg-int-overflow-problem.png differ diff --git a/docs/images/dg-int-overflow-solution.png b/docs/images/dg-int-overflow-solution.png new file mode 100644 index 00000000000..cf9f8e87a58 Binary files /dev/null and b/docs/images/dg-int-overflow-solution.png differ diff --git a/docs/images/dg-negative-int-overflow-solved.png b/docs/images/dg-negative-int-overflow-solved.png new file mode 100644 index 00000000000..7b3a430603a Binary files /dev/null and b/docs/images/dg-negative-int-overflow-solved.png differ diff --git a/docs/images/dg-positive-int-overflow-solved.png b/docs/images/dg-positive-int-overflow-solved.png new file mode 100644 index 00000000000..95a382ebbe4 Binary files /dev/null and b/docs/images/dg-positive-int-overflow-solved.png differ diff --git a/docs/images/eugenetangkj.png b/docs/images/eugenetangkj.png new file mode 100644 index 00000000000..b5c4177c0c9 Binary files /dev/null and b/docs/images/eugenetangkj.png differ diff --git a/docs/images/github-raw.png b/docs/images/github-raw.png new file mode 100644 index 00000000000..9dbac9423cc Binary files /dev/null and b/docs/images/github-raw.png differ diff --git a/docs/images/gui-markup.png b/docs/images/gui-markup.png new file mode 100644 index 00000000000..23ae6fcc5fb Binary files /dev/null and b/docs/images/gui-markup.png differ diff --git a/docs/images/help-window-changes.png b/docs/images/help-window-changes.png new file mode 100644 index 00000000000..0bd4c172262 Binary files /dev/null and b/docs/images/help-window-changes.png differ diff --git a/docs/images/internbuddy-computer.png b/docs/images/internbuddy-computer.png new file mode 100644 index 00000000000..e1724bc0a1e Binary files /dev/null and b/docs/images/internbuddy-computer.png differ diff --git a/docs/images/internbuddy-help.png b/docs/images/internbuddy-help.png new file mode 100644 index 00000000000..507a3b94b23 Binary files /dev/null and b/docs/images/internbuddy-help.png differ diff --git a/docs/images/internbuddy-hero.png b/docs/images/internbuddy-hero.png new file mode 100644 index 00000000000..c178020de00 Binary files /dev/null and b/docs/images/internbuddy-hero.png differ diff --git a/docs/images/internbuddy-json-sample.png b/docs/images/internbuddy-json-sample.png new file mode 100644 index 00000000000..a64e66a65f8 Binary files /dev/null and b/docs/images/internbuddy-json-sample.png differ diff --git a/docs/images/internbuddy-logo.png b/docs/images/internbuddy-logo.png new file mode 100644 index 00000000000..fff09590835 Binary files /dev/null and b/docs/images/internbuddy-logo.png differ diff --git a/docs/images/kohkaixun.png b/docs/images/kohkaixun.png new file mode 100644 index 00000000000..961179aec90 Binary files /dev/null and b/docs/images/kohkaixun.png differ diff --git a/docs/images/potty10.png b/docs/images/potty10.png new file mode 100644 index 00000000000..14d133f9e39 Binary files /dev/null and b/docs/images/potty10.png differ diff --git a/docs/images/seadragon2000341.png b/docs/images/seadragon2000341.png new file mode 100644 index 00000000000..f9be5af883f Binary files /dev/null and b/docs/images/seadragon2000341.png differ diff --git a/docs/images/team-mascots/InternBuddyArtist.png b/docs/images/team-mascots/InternBuddyArtist.png new file mode 100644 index 00000000000..13f64e2db6f Binary files /dev/null and b/docs/images/team-mascots/InternBuddyArtist.png differ diff --git a/docs/images/ug-add-example.png b/docs/images/ug-add-example.png new file mode 100644 index 00000000000..d27e55180cf Binary files /dev/null and b/docs/images/ug-add-example.png differ diff --git a/docs/images/ug-appendix-b-json-change.png b/docs/images/ug-appendix-b-json-change.png new file mode 100644 index 00000000000..604bd3ccd07 Binary files /dev/null and b/docs/images/ug-appendix-b-json-change.png differ diff --git a/docs/images/ug-appendix-b-json-example.png b/docs/images/ug-appendix-b-json-example.png new file mode 100644 index 00000000000..917979e87dc Binary files /dev/null and b/docs/images/ug-appendix-b-json-example.png differ diff --git a/docs/images/ug-clear-example.png b/docs/images/ug-clear-example.png new file mode 100644 index 00000000000..6272aa6defd Binary files /dev/null and b/docs/images/ug-clear-example.png differ diff --git a/docs/images/ug-delete-example.png b/docs/images/ug-delete-example.png new file mode 100644 index 00000000000..5c21cf1c456 Binary files /dev/null and b/docs/images/ug-delete-example.png differ diff --git a/docs/images/ug-edit-example.png b/docs/images/ug-edit-example.png new file mode 100644 index 00000000000..19076f27e4b Binary files /dev/null and b/docs/images/ug-edit-example.png differ diff --git a/docs/images/ug-find-example.png b/docs/images/ug-find-example.png new file mode 100644 index 00000000000..f9207a01fca Binary files /dev/null and b/docs/images/ug-find-example.png differ diff --git a/docs/images/ug-help-window.png b/docs/images/ug-help-window.png new file mode 100644 index 00000000000..430e491d683 Binary files /dev/null and b/docs/images/ug-help-window.png differ diff --git a/docs/images/ug-navigate-commands.png b/docs/images/ug-navigate-commands.png new file mode 100644 index 00000000000..963f9861c86 Binary files /dev/null and b/docs/images/ug-navigate-commands.png differ diff --git a/docs/images/ug-view-example.png b/docs/images/ug-view-example.png new file mode 100644 index 00000000000..fc6c40d409f Binary files /dev/null and b/docs/images/ug-view-example.png differ diff --git a/docs/images/ui-changes.png b/docs/images/ui-changes.png new file mode 100644 index 00000000000..ee8c041acdd Binary files /dev/null and b/docs/images/ui-changes.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..9e553003285 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,19 +1,38 @@ --- layout: page -title: AddressBook Level-3 +title: InternBuddy --- +[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/AY2223S2-CS2103T-T14-3/tp/actions) +[![codecov](https://codecov.io/gh/AY2223S2-CS2103T-T14-3/tp/branch/master/graph/badge.svg)](https://codecov.io/gh/AY2223S2-CS2103T-T14-3/tp)
+ -[![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) ![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). +InternBuddy is a desktop application for Computing undergraduates to manage their internship applications. +It is optimized for typing where it allows you to complete internship management tasks much more efficiently +via the keyboard as compared to using traditional Graphical User Interface (GUI) applications. +If you are a fast typist who is seeking a one-stop platform to systematically organise your internship +applications, then InternBuddy is the perfect buddy to accompany you during your internship hunt. + +InternBuddy runs using Java 11, and is available on the Windows, macOS and Linux operating systems. + -* 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 InternBuddy, head over to the [_Quick Start_ section of the **User Guide**](https://ay2223s2-cs2103t-t14-3.github.io/tp/UserGuide.html#quick-start). +* If you are interested about developing InternBuddy, the [**Developer Guide**](https://ay2223s2-cs2103t-t14-3.github.io/tp/DeveloperGuide.html) is a good place to start. -**Acknowledgements** +## Acknowledgements -* Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5) +* InternBuddy is written in **Java 11**. +* It is adapted from the [AddressBook Level 3](https://github.com/se-edu/addressbook-level3) project created by + the [SE-EDU initiative](https://se-education.org). +* Libraries and frameworks used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), + [JUnit5](https://github.com/junit-team/junit5) and [TestFX](https://github.com/TestFX/TestFX). +* GUI testing is implemented with references from [AddressBook Level 4](https://github.com/se-edu/addressbook-level4) + and [Please Hire Us](https://github.com/AY2223S1-CS2103T-W17-4/tp). We utilised code from these projects to set + up GUI testing and added our own test cases to test the UI components that we created. +* The feature of Navigating Through Past Commands is primarily adapted from [HackNet](https://github.com/AY2122S2-CS2103T-W13-3/tp), + but we added code modifications and test cases. +* The sections on explaining the formatting standards and GUI interface in the User and Developer Guides are + inspired by [Please Hire Us](https://github.com/AY2223S1-CS2103T-W17-4/tp). diff --git a/docs/team/derricksaltfish.md b/docs/team/derricksaltfish.md new file mode 100644 index 00000000000..6a5cccad5d4 --- /dev/null +++ b/docs/team/derricksaltfish.md @@ -0,0 +1,51 @@ +--- +layout: page +title: Ou Chuhao's Project Portfolio Page +--- + +### Project: InternBuddy + +InternBuddy provides a 1-stop platform for Computing undergraduates to manage and track their internship applications. It is optimized for typing which Computing undergraduates are comfortable and proficient in, allowing them to fully and efficiently exploit the application’s organisational capabilities. +The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC. + +Given below are my contributions to the project. + +* **Code contributed**: +My code contributions can be found on: +[RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=derricksaltfish&breakdown=true) + + +## Feature and Enhancements + +* **New Feature**: Added `copy` command [#130](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/130) + * What it does: Allows users to copy the information of an internship entry onto the clipboard of the computer. + * Justification: + * Uses `SwingUtilities.invokeLater()` to wrap the clipboard code in a `Runnable` object, ensures the clipboard operation to be safe to run from a test or other non-GUI context. + +* **Enhancements to existing features**: + * Update help information for the help box opened by command `help` and click on the Help button [#59](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/59) + * Update Load Data feature to fit for InternBuddy [#59](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/59) + +**Project Management** +* Reviewed and approved pull requests for merging. +* Testing of InternBuddy releases after each version evolution. + +## Documentation + +**Documentation** +* Side-Wide settings: + * Update site-wide settings in `[JAR file location]\docs\_config.yml` and `[JAR file location]\docs\index.md` [#19](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/19) +* User Guide: + * Initial update of command `exit`, Save data and Load data contents [#28](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/28) + * Update descriptions for Load data [#51](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/51) + * Update descriptions for Edit data [#51](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/51) + * Update descriptions for Clearing all Internship entries [#51](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/51) +* Developer Guide: + * Update content in common classes and AddressBook related content to InternBuddy [#99](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/99) + * Update of `copy` command with inclusion of UML sequence diagrams [#205](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/205) + * Update appendix A content [#104](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/104) + +## Others + +**Community** +* PRs reviewed (with non-trivial review comments): [#33](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/33) [#204](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/204) diff --git a/docs/team/eugenetangkj.md b/docs/team/eugenetangkj.md new file mode 100644 index 00000000000..93b848a48ba --- /dev/null +++ b/docs/team/eugenetangkj.md @@ -0,0 +1,101 @@ +--- +layout: page +title: Eugene Tang's Project Portfolio Page +--- + +## About InternBuddy +InternBuddy is a desktop application for Computing undergraduates to manage their internship applications. +It is optimized for typing where it allows you to complete internship management tasks much more efficiently +via the keyboard as compared to using traditional Graphical User Interface (GUI) applications. + +## Project Contributions +My code contributions can be found on +[RepoSense](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=eugenetangkj&breakdown=true&sort=groupTitle&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other). + +### Features and Enhancement +- **Refactored code to adapt the content of AB3 to the context of InternBuddy** + [\#37](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/37) + * Renamed classes and packages, such as from `Person` to `Internship`. + * Renamed variables. + * Redefined test cases to suit the context of internships instead of persons. + +- **Redesigned the GUI of InternBuddy** + [\#52](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/52), + [\#78](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/78), + [\#191](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/191) + * Created a new look for the List Panel. + * Implemented a new responsive View Panel for the viewing of internship information. + +- **Implemented feature to add optional field,** `COMMENT` + [\#79](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/79) + * Users can now add an optional comment to an internship entry via the `add` command. + * Editing of comments is made possible via the `edit` command. + * If the user did not include a value for the comment, the comment will have a default value of `NA`. + * Added test cases accordingly, such as in the class `CommentTest`. + +
+ +- **Implemented the** `view` **command** + [\#78](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/78) + * Users can now view the detailed information of a selected internship entry. + * Details are displayed in the View Panel. + * Apart from showing the internship information, I added a tips box that will appear + below the internship information. The contents of the tips box will change depending + on the status of the internship entry. + * Added test cases accordingly, such as in the class `InternshipDetailsCardTest`. + +- **Redesigned the Help Window** + [\#81](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/81) + [\#132](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/132) + * Inserted the command summary and hyperlink to InternBuddy's user guide in the Help Window. + * Created a new look for the Help Window. + +- **Co-implemented GUI testing for InternBuddy** + [\#52](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/52) + * With code references from [AddressBook Level 4](https://github.com/se-edu/addressbook-level4) + and [Please Hire Us](https://github.com/AY2223S1-CS2103T-W17-4/tp), I managed to implement + test cases for UI components such as in the class `InternshipCardTest`. + * Worked with my teammate, Christopher, to implement this. + * GUI testing improved code coverage for InternBuddy. + +### Documentation + +- **Contributed to User Guide** + [\#21](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/21), + [\#34](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/34), + [\#102](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/102), + [\#109](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/109), + [\#204](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/204) + * Documented the explanation for the different parts in InternBuddy's GUI. + * Wrote up the command information, explaining the format and constraints of commands and + fields in InternBuddy. + * Responsible for the write-up of the `list`, `add`, `view`, `help` and `exit` commands. + * Created the appendices to explain to users how to install Java 11, manually edit the `internbuddy.json` file + and populate InternBuddy with sample data. +- **Contributed to Developer Guide** + [\#23](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/23), + [\#82](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/82), + [\#84](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/84), + [\#86](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/86), + [\#119](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/119) + * Explained implementation and design considerations for `add` and `view` commands, supplementing the explanations with sequence diagrams. + * Defined product scope by identifying InternBuddy's target audience and value proposition, as well as drafted user stories. + * Added test cases for instructions on manual testing, such as for the `add` and `view` commands. + +### Others +- Provided non-trivial PR reviews for + [\#47](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/47), + [\#80](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/80), + [\#100](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/100), + [\#201](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/201). +- Designed the logo for InternBuddy. +- Contributed to CS2103T forum discussions for + [\#45](https://github.com/nus-cs2103-AY2223S2/forum/issues/45), + [\#99](https://github.com/nus-cs2103-AY2223S2/forum/issues/99), + [\#177](https://github.com/nus-cs2103-AY2223S2/forum/issues/177), + [\#252](https://github.com/nus-cs2103-AY2223S2/forum/issues/252), + [\#254](https://github.com/nus-cs2103-AY2223S2/forum/issues/254), + [\#266](https://github.com/nus-cs2103-AY2223S2/forum/issues/266), + [\#319](https://github.com/nus-cs2103-AY2223S2/forum/issues/319), + [\#322](https://github.com/nus-cs2103-AY2223S2/forum/issues/322). + diff --git a/docs/team/kohkaixun.md b/docs/team/kohkaixun.md new file mode 100644 index 00000000000..3b3e629e7d4 --- /dev/null +++ b/docs/team/kohkaixun.md @@ -0,0 +1,54 @@ +--- +layout: page +title: Kai Xun's Project Portfolio Page +--- + +### Project: InternBuddy + +InternBuddy provides a 1-stop platform for Computing undergraduates to manage and track their internship applications. It is optimized for typing which Computing undergraduates are comfortable and proficient in, allowing them to fully and efficiently exploit the application’s organisational capabilities. +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. + +My code contributions can be found [here](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=kohkaixun&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) on RepoSense. + +#### Features and Enhancements + +* **New Feature**: Implemented multiple iterations of the `find` command [#50](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/50) [#58](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/58) [#89](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/89) + * What it does: + * The first iteration of the `find` command [#50](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/50) would look for all internship with the matching `COMPANY NAME`, `STATUS` and `TAG` fields. Other than the `TAG` field, fields with multiple instances in the input only had the last instance considered. + * The second iteration [#58](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/58) took into account the `ROLE` field as well when searching for internships. InternBuddy also now took into consideration multiple instances for every field. + * The third iteration [#89](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/89) could now take into account the `DATE` field as well. Matching of user input to internship field was also changed to an exact but case-insensitive match. + * Justification + * In the first iteration [#50](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/50), if InternBuddy has a long list of internship entries, the user can quickly look for the desired entry using the `find` command instead of manually going through the entire list. + * In the second iteration [#58](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/58), the user can just enter one `find` command to search for entries with different information in the same field and compare them all on the same screen, instead of entering multiple `find` commands and being unable to view all these entries altogether at once. + * In the third iteration [#89](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/89), exact matching between inputs and internship fields will streamline search results, instead of keyword matching where entries could be filtered out due to a matching with a non-essential keyword within the input. + +* **New Feature**: Save past user input [#144](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/144) + * What it does: Keeps a record of all user input in the current run of InternBuddy and allows user to navigate through them using the up and down arrow keys. + * Justification: + * If the user is trying to enter multiple similar commands, after entering the first command, he or she can simply use the up arrow key to retrieve the last inputted command and make a quick edit instead of typing the whole command again. + * Suppose there was a past input that was of the wrong format, the user would be able to navigate to it and make edits before entering instead of typing everything out again + +* **Enhancements to existing features**: + * Made displayed indexing error messages more specific. [#131](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/131) [#133](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/133) [#216](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/216) + * Addressed integer overflow bug in displayed error messages, but enhancement was not merged after discovering it violated feature freeze. [#194](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/194) + +**Project Management** +* Set up certain issues. +* Reviewed and approved pull requests for merging. +* Testing of InternBuddy releases on MacOS environment. + +**Documentation** +* User Guide: + * Initial update of `list` and `delete` commands [#32](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/32) + * Update of `find` command according to new implementation [#123](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/123) + * Add and update of navigating through command history section [#149](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/149) [#202](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/202) +* Developer Guide: + * Wrote use cases for `list` command [#30](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/30) + * Wrote use cases for `edit` command [#30](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/30) + * Update of `find` command with inclusion of UML sequence diagrams [#88](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/88) [#90](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/90) [#95](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/95) [#123](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/123) [#203](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/203) + * Update set up and get started section [#107](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/107) + * Wrote design tweaks addressing bugs found in PE dry run [#202](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/202/files#diff-b50feaf9240709b6b02fb9584696b012c2a69feeba89e409952cc2f401f373fb) + +**Community** +* PRs reviewed (with non-trivial review comments): [#79](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/79), [#80](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/80) +* Contributed to forum discussions (example: [1](https://github.com/nus-cs2103-AY2223S2/forum/issues/340)) diff --git a/docs/team/potty10.md b/docs/team/potty10.md new file mode 100644 index 00000000000..bc8e3bff4c6 --- /dev/null +++ b/docs/team/potty10.md @@ -0,0 +1,77 @@ +--- +layout: page +title: Christopher's Project Portfolio Page +--- + +### Project: InternBuddy + +InternBuddy provides a 1-stop platform for Computing undergraduates to manage and track their internship applications. It is optimized for typing which Computing undergraduates are comfortable and proficient in, allowing them to fully and efficiently exploit the application’s organisational capabilities. +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. + +## Project Contributions +My code contributions can be found on +[RepoSense](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=potty10&breakdown=true) + + +### Features and Enhancement + +* **New Feature**: Refactored `delete` as `delete-index` command [#100](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/100) + * What it does: Allows users to delete multiple internships by their indexes. + * Justification: This feature allows the user to clear delete multiple internships from the list at once, instead of entering `delete` multiple times. Users can then + keep their internship list nice and tidy. +* **New Feature**: Added `delete-field` command [#100](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/100) + * What it does: Allows users to delete multiple internships by their fields. + * Justification: Users may want to mass delete internships from the list when internship application season is over, but still want to archive some for future reference. It is an improvement over `clear`, which deletes all internship entries. + +* **Co-implemented GUI testing for InternBuddy** + [\#198](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/198), + [\#57](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/57) + * With code references from [AB4](https://github.com/se-edu/addressbook-level4) + and [Please Hire Us](https://github.com/AY2223S1-CS2103T-W17-4/tp), I managed to implement + test cases for UI components such as in the class `InternshipCardTest` and `CommandBoxTest`. + * Worked with my teammate, Eugene, to implement this. + * GUI testing improved code coverage for InternBuddy. + +
+ +* **Enhancements to existing features**: + * Added date labels to UI, so that users understand what the dates mean. [\#57](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/57), [\#138](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/138) + * Added a feature where clicking on an internship entry updates the right panel with the selected internship. [\#150](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/150) + * Refactored Status as a set of final constant strings, mitigating misuse of Status strings. [\#57](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/57) + * Fixed a bug where the right panel does not reset after clear. [\##128](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/128) + +### Documentation + +* **About Us page** + [\#18](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/18) + * Collected my team photos and drafted the About Us page. + +* **Project management**: + * Managed release v1.3.2 on GitHub. + * PRs reviewed (with non-trivial review comments): + [\#118](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/118), + [\#58](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/58), + [\#32](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/32), + [\#30](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/30), + [\#20](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/20) + +* **Documentation**: + * User Guide: + * Contributed to Introduction, Quick Start, Notes about Features, FAQ [\#26](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/26) + * Contributed to implementation of `delete-index` command [\#121](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/121) + * Contributed to implementation of `delete-field` command [\#121](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/121) + * Fixed UG bugs from PE-D [#195](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/195) + * Developer Guide: + * Contributed to Acknowledgements, Non functional requirements and Glossary [\#31](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/31) + * Contributed to Introduction and About Developer Guide [\#106](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/106) + * Added implementation for `delete-field` command [#122](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/122) + * Updated UML sequence diagram for `delete-field` command [#94](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/94) + * Wrote design tweaks addressing bugs found in PE dry run [#201](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/201) + +* **Community**: + * Contributed to forum discussions for + [\#297](https://github.com/nus-cs2103-AY2223S2/forum/issues/297), + [\#287](https://github.com/nus-cs2103-AY2223S2/forum/issues/287), + [\#190](https://github.com/nus-cs2103-AY2223S2/forum/issues/190) + + diff --git a/docs/team/seadragon2000341.md b/docs/team/seadragon2000341.md new file mode 100644 index 00000000000..c60eee4e0b3 --- /dev/null +++ b/docs/team/seadragon2000341.md @@ -0,0 +1,59 @@ +--- +layout: page +title: Shawn's Project Portfolio Page +--- + +### Project: InternBuddy + +InternBuddy provides a 1-stop platform for Computing undergraduates to manage and track their internship applications. It is optimized for typing which Computing undergraduates are comfortable and proficient in, allowing them to fully and efficiently exploit the application’s organisational capabilities. +The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC. + +Given below are my contributions to the project. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=seadragon2000341&breakdown=true) + +* **New Feature**: Added `upcoming` command [#93](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/93) + * What it does: Allows users to find internships that have upcoming events (interviews/assessments) or deadlines (application/acceptance deadline). + * Justification: This feature improves the product because it provides a busy user with a convenient way to check his internship application schedule for the week. + + +* **Enhancements to existing features**: + * Updated constraints for `COMPANY_NAME` and `ROLE` fields [#126](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/126) + * Fixed inconsistencies with internship shown using the `LIST`command [#125](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/125) + * Changed equality/duplicate notion that was inherited from Address Book to suit InternBuddy [#124](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/124) + * Created a new `STATUS` field `ACCPETED` to complement existing statuses [#87](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/87) + * Updated `DATE` labels for `OFFERED` and `NEW` statuses [#87](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/87) + * Updated `TAG` validation to be at most 30 characters and cannot be an empty string [#47](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/47) + * Updated `STATUS` field inputs to be non case-sensitive [#39](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/39) + * Enhanced `DATE` validation to make sure the date is of the correct format and a valid date [#38](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/38) + + +* **Project management**: + * Created team's organisation and repository + * Set up the project's Continuous Integration and website + * Enable assertion [#76](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/76) + * Reviewed and approved PRs for merging + * Updated config.yml [#16](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/16) + * Set up certain milestones and issues + + +* **Documentation**: + * User Guide: + * Added implementation for `upcoming` command [#120](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/120) + * Updated implementation for `edit` command [#120](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/120), [#22](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/22) + * Updated implementation for `help` command [#22](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/22) + * Fixed UG bugs from PE-D [#187](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/187) + * Developer Guide: + * Added implementation for `upcoming` command [#122](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/122) + * Updated UML sequence diagram for `edit` command [#94](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/94) + * Updated implementation for `edit` command [#80](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/80) + * Wrote use cases for `add` command [#24](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/24/) + * Wrote use cases for `exit` command [#24](https://github.com/AY2223S2-CS2103T-T14-3/tp/pull/24/) + + + +* **Community**: + + * Contributed to forum discussions (examples: [1](https://github.com/nus-cs2103-AY2223S2/forum/issues/223), [2](https://github.com/nus-cs2103-AY2223S2/forum/issues/180), [3](https://github.com/nus-cs2103-AY2223S2/forum/issues/176), [4](https://github.com/nus-cs2103-AY2223S2/forum/issues/163), [5](https://github.com/nus-cs2103-AY2223S2/forum/issues/137), [6](https://github.com/nus-cs2103-AY2223S2/forum/issues/136), [7](https://github.com/nus-cs2103-AY2223S2/forum/issues/83), [8](https://github.com/nus-cs2103-AY2223S2/forum/issues/67), [9](https://github.com/nus-cs2103-AY2223S2/forum/issues/64), [10](https://github.com/nus-cs2103-AY2223S2/forum/issues/60), [11](https://github.com/nus-cs2103-AY2223S2/forum/issues/49), [12](https://github.com/nus-cs2103-AY2223S2/forum/issues/42). [13](https://github.com/nus-cs2103-AY2223S2/forum/issues/4)) + + diff --git a/docs/tutorials/AddRemark.md b/docs/tutorials/AddRemark.md index 880c701042f..95518e07f8c 100644 --- a/docs/tutorials/AddRemark.md +++ b/docs/tutorials/AddRemark.md @@ -23,9 +23,9 @@ For now, let’s keep `RemarkCommand` as simple as possible and print some outpu **`RemarkCommand.java`:** ``` java -package seedu.address.logic.commands; +package seedu.internship.logic.commands; -import seedu.address.model.Model; +import seedu.internship.model.Model; /** * Changes the remark of an existing person in the address book. @@ -91,7 +91,7 @@ Let’s change `RemarkCommand` to parse input from the user. We start by modifying the constructor of `RemarkCommand` to accept an `Index` and a `String`. While we are at it, let’s change the error message to echo the values. While this is not a replacement for tests, it is an obvious way to tell if our code is functioning as intended. ``` java -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.internship.commons.util.CollectionUtil.requireAllNonNull; //... public class RemarkCommand extends Command { //... @@ -142,7 +142,7 @@ Your code should look something like [this](https://github.com/se-edu/addressboo Now let’s move on to writing a parser that will extract the index and remark from the input provided by the user. -Create a `RemarkCommandParser` class in the `seedu.address.logic.parser` package. The class must extend the `Parser` interface. +Create a `RemarkCommandParser` class in the `seedu.internship.logic.parser` package. The class must extend the `Parser` interface. ![The relationship between Parser and RemarkCommandParser](../images/add-remark/ParserInterface.png) @@ -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.internship.model.internship`. 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.internship.ui.PersonCard`](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..8d1755a4047 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.internship.model.internship.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/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md index 4fb62a83ef6..fdc11df5169 100644 --- a/docs/tutorials/TracingCode.md +++ b/docs/tutorials/TracingCode.md @@ -39,7 +39,7 @@ In our case, we would want to begin the tracing at the very point where the App -According to the sequence diagram you saw earlier (and repeated above for reference), the `UI` component yields control to the `Logic` component through a method named `execute`. Searching through the code base for an `execute()` method that belongs to the `Logic` component yields a promising candidate in `seedu.address.logic.Logic`. +According to the sequence diagram you saw earlier (and repeated above for reference), the `UI` component yields control to the `Logic` component through a method named `execute`. Searching through the code base for an `execute()` method that belongs to the `Logic` component yields a promising candidate in `seedu.internship.logic.Logic`. @@ -48,7 +48,7 @@ According to the sequence diagram you saw earlier (and repeated above for refere :bulb: **Intellij Tip:** The ['**Search Everywhere**' feature](https://www.jetbrains.com/help/idea/searching-everywhere.html) can be used here. In particular, the '**Find Symbol**' ('Symbol' here refers to methods, variables, classes etc.) variant of that feature is quite useful here as we are looking for a _method_ named `execute`, not simply the text `execute`.
-A quick look at the `seedu.address.logic.Logic` (an extract given below) confirms that this indeed might be what we’re looking for. +A quick look at the `seedu.internship.logic.Logic` (an extract given below) confirms that this indeed might be what we’re looking for. ```java public interface Logic { diff --git a/internbuddy.json b/internbuddy.json new file mode 100644 index 00000000000..fa6309a797f --- /dev/null +++ b/internbuddy.json @@ -0,0 +1,52 @@ +{ + "internships" : [ { + "companyName" : "Apple", + "role" : "iOS Developer", + "status" : "applied", + "date" : "2023-03-20", + "comment" : "Yay! My dream company!", + "tagged" : [ "iOS" ] + }, { + "companyName" : "Amazon", + "role" : "Cloud Architect", + "status" : "new", + "date" : "2023-03-28", + "comment" : "Need to research more on cloud services.", + "tagged" : [ "Cloud Services", "AWS" ] + }, { + "companyName" : "Google", + "role" : "Software Engineer", + "status" : "assessment", + "date" : "2023-04-02", + "comment" : "Good company culture and environment.", + "tagged" : [ "Golang", "Back-end" ] + }, { + "companyName" : "Samsung", + "role" : "Android Developer", + "status" : "interview", + "date" : "2023-04-10", + "comment" : "To compare with Apple's offer again.", + "tagged" : [ "Mobile", "Android" ] + }, { + "companyName" : "Grab", + "role" : "Frontend Designer", + "status" : "offered", + "date" : "2023-03-27", + "comment" : "Good benefits. Can consider.", + "tagged" : [ "CSS", "React" ] + }, { + "companyName" : "Paypal", + "role" : "Product Designer", + "status" : "accepted", + "date" : "2023-03-26", + "comment" : "Starting work on 1 May. Excited!", + "tagged" : [ "UX", "UI" ] + }, { + "companyName" : "Facebook", + "role" : "Backend Developer", + "status" : "rejected", + "date" : "2023-03-15", + "comment" : "Rejected since I lack proficiency in SQL.", + "tagged" : [ "SQL" ] + } ] +} diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java deleted file mode 100644 index 1deb3a1e469..00000000000 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ /dev/null @@ -1,13 +0,0 @@ -package seedu.address.commons.core; - -/** - * Container for user visible messages. - */ -public class Messages { - - public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; - public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; - public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; - public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; - -} diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/address/commons/util/StringUtil.java deleted file mode 100644 index 61cc8c9a1cb..00000000000 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ /dev/null @@ -1,68 +0,0 @@ -package seedu.address.commons.util; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.Arrays; - -/** - * Helper functions for handling strings. - */ -public class StringUtil { - - /** - * Returns true if the {@code sentence} contains the {@code word}. - * Ignores case, but a full word match is required. - *
examples:
-     *       containsWordIgnoreCase("ABc def", "abc") == true
-     *       containsWordIgnoreCase("ABc def", "DEF") == true
-     *       containsWordIgnoreCase("ABc def", "AB") == false //not a full word match
-     *       
- * @param sentence cannot be null - * @param word cannot be null, cannot be empty, must be a single word - */ - public static boolean containsWordIgnoreCase(String sentence, String word) { - requireNonNull(sentence); - requireNonNull(word); - - String preppedWord = word.trim(); - checkArgument(!preppedWord.isEmpty(), "Word parameter cannot be empty"); - checkArgument(preppedWord.split("\\s+").length == 1, "Word parameter should be a single word"); - - String preppedSentence = sentence; - String[] wordsInPreppedSentence = preppedSentence.split("\\s+"); - - return Arrays.stream(wordsInPreppedSentence) - .anyMatch(preppedWord::equalsIgnoreCase); - } - - /** - * Returns a detailed message of the t, including the stack trace. - */ - public static String getDetails(Throwable t) { - requireNonNull(t); - StringWriter sw = new StringWriter(); - t.printStackTrace(new PrintWriter(sw)); - return t.getMessage() + "\n" + sw.toString(); - } - - /** - * Returns true if {@code s} represents a non-zero unsigned integer - * e.g. 1, 2, 3, ..., {@code Integer.MAX_VALUE}
- * Will return false for any other non-null string input - * e.g. empty string, "-1", "0", "+1", and " 2 " (untrimmed), "3 0" (contains whitespace), "1 a" (contains letters) - * @throws NullPointerException if {@code s} is null. - */ - public static boolean isNonZeroUnsignedInteger(String s) { - requireNonNull(s); - - try { - int value = Integer.parseInt(s); - return value > 0 && !s.startsWith("+"); // "+1" is successfully parsed by Integer#parseInt(String) - } catch (NumberFormatException nfe) { - return false; - } - } -} diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java deleted file mode 100644 index 92cd8fa605a..00000000000 --- a/src/main/java/seedu/address/logic/Logic.java +++ /dev/null @@ -1,50 +0,0 @@ -package seedu.address.logic; - -import java.nio.file.Path; - -import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; - -/** - * API of the Logic component - */ -public interface Logic { - /** - * Executes the command and returns the result. - * @param commandText The command as entered by the user. - * @return the result of the command execution. - * @throws CommandException If an error occurs during command execution. - * @throws ParseException If an error occurs during parsing. - */ - CommandResult execute(String commandText) throws CommandException, ParseException; - - /** - * Returns the AddressBook. - * - * @see seedu.address.model.Model#getAddressBook() - */ - ReadOnlyAddressBook getAddressBook(); - - /** Returns an unmodifiable view of the filtered list of persons */ - ObservableList getFilteredPersonList(); - - /** - * Returns the user prefs' address book file path. - */ - Path getAddressBookFilePath(); - - /** - * Returns the user prefs' GUI settings. - */ - GuiSettings getGuiSettings(); - - /** - * Set the user prefs' GUI settings. - */ - void setGuiSettings(GuiSettings guiSettings); -} diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java deleted file mode 100644 index 71656d7c5c8..00000000000 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ /dev/null @@ -1,67 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; - -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; - -/** - * Adds a person to the address book. - */ -public class AddCommand extends Command { - - public static final String COMMAND_WORD = "add"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. " - + "Parameters: " - + PREFIX_NAME + "NAME " - + PREFIX_PHONE + "PHONE " - + PREFIX_EMAIL + "EMAIL " - + PREFIX_ADDRESS + "ADDRESS " - + "[" + PREFIX_TAG + "TAG]...\n" - + "Example: " + COMMAND_WORD + " " - + PREFIX_NAME + "John Doe " - + PREFIX_PHONE + "98765432 " - + PREFIX_EMAIL + "johnd@example.com " - + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " - + PREFIX_TAG + "friends " - + PREFIX_TAG + "owesMoney"; - - public static final String MESSAGE_SUCCESS = "New person added: %1$s"; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book"; - - private final Person toAdd; - - /** - * Creates an AddCommand to add the specified {@code Person} - */ - public AddCommand(Person person) { - requireNonNull(person); - toAdd = person; - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - - if (model.hasPerson(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } - - model.addPerson(toAdd); - return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof AddCommand // instanceof handles nulls - && toAdd.equals(((AddCommand) other).toAdd)); - } -} diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java deleted file mode 100644 index 9c86b1fa6e4..00000000000 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ /dev/null @@ -1,23 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; - -import seedu.address.model.AddressBook; -import seedu.address.model.Model; - -/** - * Clears the address book. - */ -public class ClearCommand extends Command { - - public static final String COMMAND_WORD = "clear"; - public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; - - - @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.setAddressBook(new AddressBook()); - return new CommandResult(MESSAGE_SUCCESS); - } -} diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java deleted file mode 100644 index 02fd256acba..00000000000 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ /dev/null @@ -1,53 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; - -import java.util.List; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; - -/** - * Deletes a person identified using it's displayed index from the address book. - */ -public class DeleteCommand extends Command { - - public static final String COMMAND_WORD = "delete"; - - public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Deletes the person identified by the index number used in the displayed person list.\n" - + "Parameters: INDEX (must be a positive integer)\n" - + "Example: " + COMMAND_WORD + " 1"; - - public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s"; - - private final Index targetIndex; - - public DeleteCommand(Index targetIndex) { - this.targetIndex = targetIndex; - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); - - if (targetIndex.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - Person personToDelete = lastShownList.get(targetIndex.getZeroBased()); - model.deletePerson(personToDelete); - return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete)); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof DeleteCommand // instanceof handles nulls - && targetIndex.equals(((DeleteCommand) other).targetIndex)); // state check - } -} diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java deleted file mode 100644 index 7e36114902f..00000000000 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ /dev/null @@ -1,226 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.CollectionUtil; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Edits the details of an existing person in the address book. - */ -public class EditCommand extends Command { - - public static final String COMMAND_WORD = "edit"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified " - + "by the index number used in the displayed person list. " - + "Existing values will be overwritten by the input values.\n" - + "Parameters: INDEX (must be a positive integer) " - + "[" + PREFIX_NAME + "NAME] " - + "[" + PREFIX_PHONE + "PHONE] " - + "[" + PREFIX_EMAIL + "EMAIL] " - + "[" + PREFIX_ADDRESS + "ADDRESS] " - + "[" + PREFIX_TAG + "TAG]...\n" - + "Example: " + COMMAND_WORD + " 1 " - + PREFIX_PHONE + "91234567 " - + PREFIX_EMAIL + "johndoe@example.com"; - - public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s"; - public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book."; - - private final Index index; - private final EditPersonDescriptor editPersonDescriptor; - - /** - * @param index of the person in the filtered person list to edit - * @param editPersonDescriptor details to edit the person with - */ - public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) { - requireNonNull(index); - requireNonNull(editPersonDescriptor); - - this.index = index; - this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor); - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); - - if (index.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - Person personToEdit = lastShownList.get(index.getZeroBased()); - Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor); - - if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } - - model.setPerson(personToEdit, editedPerson); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson)); - } - - /** - * Creates and returns a {@code Person} with the details of {@code personToEdit} - * edited with {@code editPersonDescriptor}. - */ - private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) { - assert personToEdit != null; - - Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName()); - Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); - Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); - Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress()); - Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags()); - - return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof EditCommand)) { - return false; - } - - // state check - EditCommand e = (EditCommand) other; - return index.equals(e.index) - && editPersonDescriptor.equals(e.editPersonDescriptor); - } - - /** - * Stores the details to edit the person with. Each non-empty field value will replace the - * corresponding field value of the person. - */ - public static class EditPersonDescriptor { - private Name name; - private Phone phone; - private Email email; - private Address address; - private Set tags; - - public EditPersonDescriptor() {} - - /** - * Copy constructor. - * A defensive copy of {@code tags} is used internally. - */ - public EditPersonDescriptor(EditPersonDescriptor toCopy) { - setName(toCopy.name); - setPhone(toCopy.phone); - setEmail(toCopy.email); - setAddress(toCopy.address); - setTags(toCopy.tags); - } - - /** - * Returns true if at least one field is edited. - */ - public boolean isAnyFieldEdited() { - return CollectionUtil.isAnyNonNull(name, phone, email, address, tags); - } - - public void setName(Name name) { - this.name = name; - } - - public Optional getName() { - return Optional.ofNullable(name); - } - - public void setPhone(Phone phone) { - this.phone = phone; - } - - public Optional getPhone() { - return Optional.ofNullable(phone); - } - - public void setEmail(Email email) { - this.email = email; - } - - public Optional getEmail() { - return Optional.ofNullable(email); - } - - public void setAddress(Address address) { - this.address = address; - } - - public Optional
getAddress() { - return Optional.ofNullable(address); - } - - /** - * Sets {@code tags} to this object's {@code tags}. - * A defensive copy of {@code tags} is used internally. - */ - public void setTags(Set tags) { - this.tags = (tags != null) ? new HashSet<>(tags) : null; - } - - /** - * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - * Returns {@code Optional#empty()} if {@code tags} is null. - */ - public Optional> getTags() { - return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof EditPersonDescriptor)) { - return false; - } - - // state check - EditPersonDescriptor e = (EditPersonDescriptor) other; - - return getName().equals(e.getName()) - && getPhone().equals(e.getPhone()) - && getEmail().equals(e.getEmail()) - && getAddress().equals(e.getAddress()) - && getTags().equals(e.getTags()); - } - } -} diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java deleted file mode 100644 index d6b19b0a0de..00000000000 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ /dev/null @@ -1,42 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; - -import seedu.address.commons.core.Messages; -import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; - -/** - * Finds and lists all persons in address book whose name contains any of the argument keywords. - * Keyword matching is case insensitive. - */ -public class FindCommand extends Command { - - public static final String COMMAND_WORD = "find"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of " - + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" - + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" - + "Example: " + COMMAND_WORD + " alice bob charlie"; - - private final NameContainsKeywordsPredicate predicate; - - public FindCommand(NameContainsKeywordsPredicate predicate) { - this.predicate = predicate; - } - - @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.updateFilteredPersonList(predicate); - return new CommandResult( - String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof FindCommand // instanceof handles nulls - && predicate.equals(((FindCommand) other).predicate)); // state check - } -} diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java deleted file mode 100644 index 84be6ad2596..00000000000 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ /dev/null @@ -1,24 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; - -import seedu.address.model.Model; - -/** - * Lists all persons in the address book to the user. - */ -public class ListCommand extends Command { - - public static final String COMMAND_WORD = "list"; - - public static final String MESSAGE_SUCCESS = "Listed all persons"; - - - @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(MESSAGE_SUCCESS); - } -} diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java deleted file mode 100644 index 3b8bfa035e8..00000000000 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; - -import java.util.Set; -import java.util.stream.Stream; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Parses input arguments and creates a new AddCommand object - */ -public class AddCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the AddCommand - * and returns an AddCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public AddCommand parse(String args) throws ParseException { - ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); - - if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) - || !argMultimap.getPreamble().isEmpty()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); - } - - Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); - Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()); - Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()); - Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()); - Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); - - Person person = new Person(name, phone, email, address, tagList); - - return new AddCommand(person); - } - - /** - * Returns true if none of the prefixes contains empty {@code Optional} values in the given - * {@code ArgumentMultimap}. - */ - private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { - return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); - } - -} diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java deleted file mode 100644 index 75b1a9bf119..00000000000 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ /dev/null @@ -1,15 +0,0 @@ -package seedu.address.logic.parser; - -/** - * Contains Command Line Interface (CLI) syntax definitions common to multiple commands - */ -public class CliSyntax { - - /* Prefix definitions */ - public static final Prefix PREFIX_NAME = new Prefix("n/"); - public static final Prefix PREFIX_PHONE = new Prefix("p/"); - public static final Prefix PREFIX_EMAIL = new Prefix("e/"); - public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); - public static final Prefix PREFIX_TAG = new Prefix("t/"); - -} diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java deleted file mode 100644 index 522b93081cc..00000000000 --- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java +++ /dev/null @@ -1,29 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; - -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.DeleteCommand; -import seedu.address.logic.parser.exceptions.ParseException; - -/** - * Parses input arguments and creates a new DeleteCommand object - */ -public class DeleteCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the DeleteCommand - * and returns a DeleteCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public DeleteCommand parse(String args) throws ParseException { - try { - Index index = ParserUtil.parseIndex(args); - return new DeleteCommand(index); - } catch (ParseException pe) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe); - } - } - -} diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java deleted file mode 100644 index 845644b7dea..00000000000 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ /dev/null @@ -1,82 +0,0 @@ -package seedu.address.logic.parser; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; - -import java.util.Collection; -import java.util.Collections; -import java.util.Optional; -import java.util.Set; - -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.tag.Tag; - -/** - * Parses input arguments and creates a new EditCommand object - */ -public class EditCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the EditCommand - * and returns an EditCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public EditCommand parse(String args) throws ParseException { - requireNonNull(args); - ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); - - Index index; - - try { - index = ParserUtil.parseIndex(argMultimap.getPreamble()); - } catch (ParseException pe) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe); - } - - EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); - if (argMultimap.getValue(PREFIX_NAME).isPresent()) { - editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); - } - if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { - editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); - } - if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { - editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get())); - } - if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { - editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); - } - parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags); - - if (!editPersonDescriptor.isAnyFieldEdited()) { - throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); - } - - return new EditCommand(index, editPersonDescriptor); - } - - /** - * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty. - * If {@code tags} contain only one element which is an empty string, it will be parsed into a - * {@code Set} containing zero tags. - */ - private Optional> parseTagsForEdit(Collection tags) throws ParseException { - assert tags != null; - - if (tags.isEmpty()) { - return Optional.empty(); - } - Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; - return Optional.of(ParserUtil.parseTags(tagSet)); - } - -} diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java deleted file mode 100644 index 4fb71f23103..00000000000 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ /dev/null @@ -1,33 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; - -import java.util.Arrays; - -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; - -/** - * Parses input arguments and creates a new FindCommand object - */ -public class FindCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the FindCommand - * and returns a FindCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public FindCommand parse(String args) throws ParseException { - String trimmedArgs = args.trim(); - if (trimmedArgs.isEmpty()) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); - } - - String[] nameKeywords = trimmedArgs.split("\\s+"); - - return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); - } - -} diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java deleted file mode 100644 index b117acb9c55..00000000000 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ /dev/null @@ -1,124 +0,0 @@ -package seedu.address.logic.parser; - -import static java.util.Objects.requireNonNull; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Contains utility methods used for parsing strings in the various *Parser classes. - */ -public class ParserUtil { - - public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer."; - - /** - * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be - * trimmed. - * @throws ParseException if the specified index is invalid (not non-zero unsigned integer). - */ - public static Index parseIndex(String oneBasedIndex) throws ParseException { - String trimmedIndex = oneBasedIndex.trim(); - if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) { - throw new ParseException(MESSAGE_INVALID_INDEX); - } - return Index.fromOneBased(Integer.parseInt(trimmedIndex)); - } - - /** - * Parses a {@code String name} into a {@code Name}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code name} is invalid. - */ - public static Name parseName(String name) throws ParseException { - requireNonNull(name); - String trimmedName = name.trim(); - if (!Name.isValidName(trimmedName)) { - throw new ParseException(Name.MESSAGE_CONSTRAINTS); - } - return new Name(trimmedName); - } - - /** - * Parses a {@code String phone} into a {@code Phone}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code phone} is invalid. - */ - public static Phone parsePhone(String phone) throws ParseException { - requireNonNull(phone); - String trimmedPhone = phone.trim(); - if (!Phone.isValidPhone(trimmedPhone)) { - throw new ParseException(Phone.MESSAGE_CONSTRAINTS); - } - return new Phone(trimmedPhone); - } - - /** - * Parses a {@code String address} into an {@code Address}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code address} is invalid. - */ - public static Address parseAddress(String address) throws ParseException { - requireNonNull(address); - String trimmedAddress = address.trim(); - if (!Address.isValidAddress(trimmedAddress)) { - throw new ParseException(Address.MESSAGE_CONSTRAINTS); - } - return new Address(trimmedAddress); - } - - /** - * Parses a {@code String email} into an {@code Email}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code email} is invalid. - */ - public static Email parseEmail(String email) throws ParseException { - requireNonNull(email); - String trimmedEmail = email.trim(); - if (!Email.isValidEmail(trimmedEmail)) { - throw new ParseException(Email.MESSAGE_CONSTRAINTS); - } - return new Email(trimmedEmail); - } - - /** - * Parses a {@code String tag} into a {@code Tag}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code tag} is invalid. - */ - public static Tag parseTag(String tag) throws ParseException { - requireNonNull(tag); - String trimmedTag = tag.trim(); - if (!Tag.isValidTagName(trimmedTag)) { - throw new ParseException(Tag.MESSAGE_CONSTRAINTS); - } - return new Tag(trimmedTag); - } - - /** - * Parses {@code Collection tags} into a {@code Set}. - */ - public static Set parseTags(Collection tags) throws ParseException { - requireNonNull(tags); - final Set tagSet = new HashSet<>(); - for (String tagName : tags) { - tagSet.add(parseTag(tagName)); - } - return tagSet; - } -} diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java deleted file mode 100644 index 1a943a0781a..00000000000 --- a/src/main/java/seedu/address/model/AddressBook.java +++ /dev/null @@ -1,120 +0,0 @@ -package seedu.address.model; - -import static java.util.Objects.requireNonNull; - -import java.util.List; - -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.UniquePersonList; - -/** - * Wraps all data at the address-book level - * Duplicates are not allowed (by .isSamePerson comparison) - */ -public class AddressBook implements ReadOnlyAddressBook { - - private final UniquePersonList persons; - - /* - * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication - * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html - * - * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication - * among constructors. - */ - { - persons = new UniquePersonList(); - } - - public AddressBook() {} - - /** - * Creates an AddressBook using the Persons in the {@code toBeCopied} - */ - public AddressBook(ReadOnlyAddressBook toBeCopied) { - this(); - resetData(toBeCopied); - } - - //// list overwrite operations - - /** - * Replaces the contents of the person list with {@code persons}. - * {@code persons} must not contain duplicate persons. - */ - public void setPersons(List persons) { - this.persons.setPersons(persons); - } - - /** - * Resets the existing data of this {@code AddressBook} with {@code newData}. - */ - public void resetData(ReadOnlyAddressBook newData) { - requireNonNull(newData); - - setPersons(newData.getPersonList()); - } - - //// person-level operations - - /** - * Returns true if a person with the same identity as {@code person} exists in the address book. - */ - public boolean hasPerson(Person person) { - requireNonNull(person); - return persons.contains(person); - } - - /** - * Adds a person to the address book. - * The person must not already exist in the address book. - */ - public void addPerson(Person p) { - persons.add(p); - } - - /** - * Replaces the given person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. - */ - public void setPerson(Person target, Person editedPerson) { - requireNonNull(editedPerson); - - persons.setPerson(target, editedPerson); - } - - /** - * Removes {@code key} from this {@code AddressBook}. - * {@code key} must exist in the address book. - */ - public void removePerson(Person key) { - persons.remove(key); - } - - //// util methods - - @Override - public String toString() { - return persons.asUnmodifiableObservableList().size() + " persons"; - // TODO: refine later - } - - @Override - public ObservableList getPersonList() { - return persons.asUnmodifiableObservableList(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof AddressBook // instanceof handles nulls - && persons.equals(((AddressBook) other).persons)); - } - - @Override - public int hashCode() { - return persons.hashCode(); - } -} diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java deleted file mode 100644 index d54df471c1f..00000000000 --- a/src/main/java/seedu/address/model/Model.java +++ /dev/null @@ -1,87 +0,0 @@ -package seedu.address.model; - -import java.nio.file.Path; -import java.util.function.Predicate; - -import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.Person; - -/** - * The API of the Model component. - */ -public interface Model { - /** {@code Predicate} that always evaluate to true */ - Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; - - /** - * Replaces user prefs data with the data in {@code userPrefs}. - */ - void setUserPrefs(ReadOnlyUserPrefs userPrefs); - - /** - * Returns the user prefs. - */ - ReadOnlyUserPrefs getUserPrefs(); - - /** - * Returns the user prefs' GUI settings. - */ - GuiSettings getGuiSettings(); - - /** - * Sets the user prefs' GUI settings. - */ - void setGuiSettings(GuiSettings guiSettings); - - /** - * Returns the user prefs' address book file path. - */ - Path getAddressBookFilePath(); - - /** - * Sets the user prefs' address book file path. - */ - void setAddressBookFilePath(Path addressBookFilePath); - - /** - * Replaces address book data with the data in {@code addressBook}. - */ - void setAddressBook(ReadOnlyAddressBook addressBook); - - /** Returns the AddressBook */ - ReadOnlyAddressBook getAddressBook(); - - /** - * Returns true if a person with the same identity as {@code person} exists in the address book. - */ - boolean hasPerson(Person person); - - /** - * Deletes the given person. - * The person must exist in the address book. - */ - void deletePerson(Person target); - - /** - * Adds the given person. - * {@code person} must not already exist in the address book. - */ - void addPerson(Person person); - - /** - * Replaces the given person {@code target} with {@code editedPerson}. - * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. - */ - void setPerson(Person target, Person editedPerson); - - /** Returns an unmodifiable view of the filtered person list */ - ObservableList getFilteredPersonList(); - - /** - * Updates the filter of the filtered person list to filter by the given {@code predicate}. - * @throws NullPointerException if {@code predicate} is null. - */ - void updateFilteredPersonList(Predicate predicate); -} diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java deleted file mode 100644 index 86c1df298d7..00000000000 --- a/src/main/java/seedu/address/model/ModelManager.java +++ /dev/null @@ -1,150 +0,0 @@ -package seedu.address.model; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.nio.file.Path; -import java.util.function.Predicate; -import java.util.logging.Logger; - -import javafx.collections.ObservableList; -import javafx.collections.transformation.FilteredList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; - -/** - * Represents the in-memory model of the address book data. - */ -public class ModelManager implements Model { - private static final Logger logger = LogsCenter.getLogger(ModelManager.class); - - private final AddressBook addressBook; - private final UserPrefs userPrefs; - private final FilteredList filteredPersons; - - /** - * Initializes a ModelManager with the given addressBook and userPrefs. - */ - public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) { - requireAllNonNull(addressBook, userPrefs); - - logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs); - - this.addressBook = new AddressBook(addressBook); - this.userPrefs = new UserPrefs(userPrefs); - filteredPersons = new FilteredList<>(this.addressBook.getPersonList()); - } - - public ModelManager() { - this(new AddressBook(), new UserPrefs()); - } - - //=========== UserPrefs ================================================================================== - - @Override - public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { - requireNonNull(userPrefs); - this.userPrefs.resetData(userPrefs); - } - - @Override - public ReadOnlyUserPrefs getUserPrefs() { - return userPrefs; - } - - @Override - public GuiSettings getGuiSettings() { - return userPrefs.getGuiSettings(); - } - - @Override - public void setGuiSettings(GuiSettings guiSettings) { - requireNonNull(guiSettings); - userPrefs.setGuiSettings(guiSettings); - } - - @Override - public Path getAddressBookFilePath() { - return userPrefs.getAddressBookFilePath(); - } - - @Override - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - userPrefs.setAddressBookFilePath(addressBookFilePath); - } - - //=========== AddressBook ================================================================================ - - @Override - public void setAddressBook(ReadOnlyAddressBook addressBook) { - this.addressBook.resetData(addressBook); - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - return addressBook; - } - - @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return addressBook.hasPerson(person); - } - - @Override - public void deletePerson(Person target) { - addressBook.removePerson(target); - } - - @Override - public void addPerson(Person person) { - addressBook.addPerson(person); - updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - } - - @Override - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); - - addressBook.setPerson(target, editedPerson); - } - - //=========== Filtered Person List Accessors ============================================================= - - /** - * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of - * {@code versionedAddressBook} - */ - @Override - public ObservableList getFilteredPersonList() { - return filteredPersons; - } - - @Override - public void updateFilteredPersonList(Predicate predicate) { - requireNonNull(predicate); - filteredPersons.setPredicate(predicate); - } - - @Override - public boolean equals(Object obj) { - // short circuit if same object - if (obj == this) { - return true; - } - - // instanceof handles nulls - if (!(obj instanceof ModelManager)) { - return false; - } - - // state check - ModelManager other = (ModelManager) obj; - return addressBook.equals(other.addressBook) - && userPrefs.equals(other.userPrefs) - && filteredPersons.equals(other.filteredPersons); - } - -} diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java deleted file mode 100644 index 6ddc2cd9a29..00000000000 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ /dev/null @@ -1,17 +0,0 @@ -package seedu.address.model; - -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; - -/** - * Unmodifiable view of an address book - */ -public interface ReadOnlyAddressBook { - - /** - * Returns an unmodifiable view of the persons list. - * This list will not contain any duplicate persons. - */ - ObservableList getPersonList(); - -} diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/person/Address.java deleted file mode 100644 index 60472ca22a0..00000000000 --- a/src/main/java/seedu/address/model/person/Address.java +++ /dev/null @@ -1,57 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's address in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)} - */ -public class Address { - - public static final String MESSAGE_CONSTRAINTS = "Addresses can take any values, and it should not be blank"; - - /* - * The first character of the address must not be a whitespace, - * otherwise " " (a blank string) becomes a valid input. - */ - public static final String VALIDATION_REGEX = "[^\\s].*"; - - public final String value; - - /** - * Constructs an {@code Address}. - * - * @param address A valid address. - */ - public Address(String address) { - requireNonNull(address); - checkArgument(isValidAddress(address), MESSAGE_CONSTRAINTS); - value = address; - } - - /** - * Returns true if a given string is a valid email. - */ - public static boolean isValidAddress(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Address // instanceof handles nulls - && value.equals(((Address) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/person/Email.java deleted file mode 100644 index f866e7133de..00000000000 --- a/src/main/java/seedu/address/model/person/Email.java +++ /dev/null @@ -1,71 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's email in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)} - */ -public class Email { - - private static final String SPECIAL_CHARACTERS = "+_.-"; - public static final String MESSAGE_CONSTRAINTS = "Emails should be of the format local-part@domain " - + "and adhere to the following constraints:\n" - + "1. The local-part should only contain alphanumeric characters and these special characters, excluding " - + "the parentheses, (" + SPECIAL_CHARACTERS + "). 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" - + " - 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; - - public final String value; - - /** - * Constructs an {@code Email}. - * - * @param email A valid email address. - */ - public Email(String email) { - requireNonNull(email); - checkArgument(isValidEmail(email), MESSAGE_CONSTRAINTS); - value = email; - } - - /** - * Returns if a given string is a valid email. - */ - public static boolean isValidEmail(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Email // instanceof handles nulls - && value.equals(((Email) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/person/Name.java deleted file mode 100644 index 79244d71cf7..00000000000 --- a/src/main/java/seedu/address/model/person/Name.java +++ /dev/null @@ -1,59 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's name in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} - */ -public class Name { - - public static final String MESSAGE_CONSTRAINTS = - "Names should only contain alphanumeric characters and spaces, and it should not be blank"; - - /* - * The first character of the address must not be a whitespace, - * otherwise " " (a blank string) becomes a valid input. - */ - public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; - - public final String fullName; - - /** - * Constructs a {@code Name}. - * - * @param name A valid name. - */ - public Name(String name) { - requireNonNull(name); - checkArgument(isValidName(name), MESSAGE_CONSTRAINTS); - fullName = name; - } - - /** - * Returns true if a given string is a valid name. - */ - public static boolean isValidName(String test) { - return test.matches(VALIDATION_REGEX); - } - - - @Override - public String toString() { - return fullName; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Name // instanceof handles nulls - && fullName.equals(((Name) other).fullName)); // state check - } - - @Override - public int hashCode() { - return fullName.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java deleted file mode 100644 index c9b5868427c..00000000000 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ /dev/null @@ -1,31 +0,0 @@ -package seedu.address.model.person; - -import java.util.List; -import java.util.function.Predicate; - -import seedu.address.commons.util.StringUtil; - -/** - * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. - */ -public class NameContainsKeywordsPredicate implements Predicate { - private final List keywords; - - public NameContainsKeywordsPredicate(List keywords) { - this.keywords = keywords; - } - - @Override - public boolean test(Person person) { - return keywords.stream() - .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword)); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof NameContainsKeywordsPredicate // instanceof handles nulls - && keywords.equals(((NameContainsKeywordsPredicate) other).keywords)); // state check - } - -} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java deleted file mode 100644 index 8ff1d83fe89..00000000000 --- a/src/main/java/seedu/address/model/person/Person.java +++ /dev/null @@ -1,123 +0,0 @@ -package seedu.address.model.person; - -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import seedu.address.model.tag.Tag; - -/** - * Represents a Person in the address book. - * Guarantees: details are present and not null, field values are validated, immutable. - */ -public class Person { - - // Identity fields - private final Name name; - private final Phone phone; - private final Email email; - - // Data fields - private final Address address; - private final Set tags = new HashSet<>(); - - /** - * Every field must be present and not null. - */ - public Person(Name name, Phone phone, Email email, Address address, Set tags) { - requireAllNonNull(name, phone, email, address, tags); - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - this.tags.addAll(tags); - } - - public Name getName() { - return name; - } - - public Phone getPhone() { - return phone; - } - - public Email getEmail() { - return email; - } - - public Address getAddress() { - return address; - } - - /** - * Returns an immutable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - */ - public Set getTags() { - return Collections.unmodifiableSet(tags); - } - - /** - * Returns true if both persons have the same name. - * This defines a weaker notion of equality between two persons. - */ - public boolean isSamePerson(Person otherPerson) { - if (otherPerson == this) { - return true; - } - - return otherPerson != null - && otherPerson.getName().equals(getName()); - } - - /** - * Returns true if both persons have the same identity and data fields. - * This defines a stronger notion of equality between two persons. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof Person)) { - return false; - } - - Person otherPerson = (Person) other; - return otherPerson.getName().equals(getName()) - && otherPerson.getPhone().equals(getPhone()) - && otherPerson.getEmail().equals(getEmail()) - && otherPerson.getAddress().equals(getAddress()) - && otherPerson.getTags().equals(getTags()); - } - - @Override - public int hashCode() { - // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append(getName()) - .append("; Phone: ") - .append(getPhone()) - .append("; Email: ") - .append(getEmail()) - .append("; Address: ") - .append(getAddress()); - - Set tags = getTags(); - if (!tags.isEmpty()) { - builder.append("; Tags: "); - tags.forEach(builder::append); - } - return builder.toString(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/person/Phone.java deleted file mode 100644 index 872c76b382f..00000000000 --- a/src/main/java/seedu/address/model/person/Phone.java +++ /dev/null @@ -1,53 +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 phone number in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)} - */ -public class Phone { - - - public static final String MESSAGE_CONSTRAINTS = - "Phone numbers should only contain numbers, and it should be at least 3 digits long"; - public static final String VALIDATION_REGEX = "\\d{3,}"; - public final String value; - - /** - * Constructs a {@code Phone}. - * - * @param phone A valid phone number. - */ - public Phone(String phone) { - requireNonNull(phone); - checkArgument(isValidPhone(phone), MESSAGE_CONSTRAINTS); - value = phone; - } - - /** - * Returns true if a given string is a valid phone number. - */ - public static boolean isValidPhone(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 Phone // instanceof handles nulls - && value.equals(((Phone) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java deleted file mode 100644 index 0fee4fe57e6..00000000000 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ /dev/null @@ -1,137 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Iterator; -import java.util.List; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; - -/** - * A list of persons that enforces uniqueness between its elements and does not allow nulls. - * A person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, adding and updating of - * persons uses Person#isSamePerson(Person) for equality so as to ensure that the person being added or updated is - * unique in terms of identity in the UniquePersonList. However, the removal of a person uses Person#equals(Object) so - * as to ensure that the person with exactly the same fields will be removed. - * - * Supports a minimal set of list operations. - * - * @see Person#isSamePerson(Person) - */ -public class UniquePersonList implements Iterable { - - private final ObservableList internalList = FXCollections.observableArrayList(); - private final ObservableList internalUnmodifiableList = - FXCollections.unmodifiableObservableList(internalList); - - /** - * Returns true if the list contains an equivalent person as the given argument. - */ - public boolean contains(Person toCheck) { - requireNonNull(toCheck); - return internalList.stream().anyMatch(toCheck::isSamePerson); - } - - /** - * Adds a person to the list. - * The person must not already exist in the list. - */ - public void add(Person toAdd) { - requireNonNull(toAdd); - if (contains(toAdd)) { - throw new DuplicatePersonException(); - } - internalList.add(toAdd); - } - - /** - * Replaces the person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the list. - * The person identity of {@code editedPerson} must not be the same as another existing person in the list. - */ - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); - - int index = internalList.indexOf(target); - if (index == -1) { - throw new PersonNotFoundException(); - } - - if (!target.isSamePerson(editedPerson) && contains(editedPerson)) { - throw new DuplicatePersonException(); - } - - internalList.set(index, editedPerson); - } - - /** - * Removes the equivalent person from the list. - * The person must exist in the list. - */ - public void remove(Person toRemove) { - requireNonNull(toRemove); - if (!internalList.remove(toRemove)) { - throw new PersonNotFoundException(); - } - } - - public void setPersons(UniquePersonList replacement) { - requireNonNull(replacement); - internalList.setAll(replacement.internalList); - } - - /** - * Replaces the contents of this list with {@code persons}. - * {@code persons} must not contain duplicate persons. - */ - public void setPersons(List persons) { - requireAllNonNull(persons); - if (!personsAreUnique(persons)) { - throw new DuplicatePersonException(); - } - - internalList.setAll(persons); - } - - /** - * Returns the backing list as an unmodifiable {@code ObservableList}. - */ - public ObservableList asUnmodifiableObservableList() { - return internalUnmodifiableList; - } - - @Override - public Iterator iterator() { - return internalList.iterator(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof UniquePersonList // instanceof handles nulls - && internalList.equals(((UniquePersonList) other).internalList)); - } - - @Override - public int hashCode() { - return internalList.hashCode(); - } - - /** - * Returns true if {@code persons} contains only unique persons. - */ - private boolean personsAreUnique(List persons) { - for (int i = 0; i < persons.size() - 1; i++) { - for (int j = i + 1; j < persons.size(); j++) { - if (persons.get(i).isSamePerson(persons.get(j))) { - return false; - } - } - } - return true; - } -} diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java deleted file mode 100644 index d7290f59442..00000000000 --- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java +++ /dev/null @@ -1,11 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same - * identity). - */ -public class DuplicatePersonException extends RuntimeException { - public DuplicatePersonException() { - super("Operation would result in duplicate persons"); - } -} diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java deleted file mode 100644 index fa764426ca7..00000000000 --- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java +++ /dev/null @@ -1,6 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation is unable to find the specified person. - */ -public class PersonNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java deleted file mode 100644 index 1806da4facf..00000000000 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.model.util; - -import java.util.Arrays; -import java.util.Set; -import java.util.stream.Collectors; - -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Contains utility methods for populating {@code AddressBook} with sample data. - */ -public class SampleDataUtil { - public static Person[] getSamplePersons() { - return new Person[] { - new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), - new Address("Blk 30 Geylang Street 29, #06-40"), - getTagSet("friends")), - new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), - new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), - getTagSet("colleagues", "friends")), - new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), - new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), - getTagSet("neighbours")), - new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), - new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), - getTagSet("family")), - new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), - new Address("Blk 47 Tampines Street 20, #17-35"), - getTagSet("classmates")), - new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), - new Address("Blk 45 Aljunied Street 85, #11-31"), - getTagSet("colleagues")) - }; - } - - public static ReadOnlyAddressBook getSampleAddressBook() { - AddressBook sampleAb = new AddressBook(); - for (Person samplePerson : getSamplePersons()) { - sampleAb.addPerson(samplePerson); - } - return sampleAb; - } - - /** - * Returns a tag set containing the list of strings given. - */ - public static Set getTagSet(String... strings) { - return Arrays.stream(strings) - .map(Tag::new) - .collect(Collectors.toSet()); - } - -} diff --git a/src/main/java/seedu/address/storage/AddressBookStorage.java b/src/main/java/seedu/address/storage/AddressBookStorage.java deleted file mode 100644 index 4599182b3f9..00000000000 --- a/src/main/java/seedu/address/storage/AddressBookStorage.java +++ /dev/null @@ -1,45 +0,0 @@ -package seedu.address.storage; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; - -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; - -/** - * Represents a storage for {@link seedu.address.model.AddressBook}. - */ -public interface AddressBookStorage { - - /** - * Returns the file path of the data file. - */ - Path getAddressBookFilePath(); - - /** - * Returns AddressBook data as a {@link ReadOnlyAddressBook}. - * Returns {@code Optional.empty()} if storage file is not found. - * @throws DataConversionException if the data in storage is not in the expected format. - * @throws IOException if there was any problem when reading from the storage. - */ - Optional readAddressBook() throws DataConversionException, IOException; - - /** - * @see #getAddressBookFilePath() - */ - Optional readAddressBook(Path filePath) throws DataConversionException, IOException; - - /** - * Saves the given {@link ReadOnlyAddressBook} to the storage. - * @param addressBook cannot be null. - * @throws IOException if there was any problem writing to the file. - */ - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; - - /** - * @see #saveAddressBook(ReadOnlyAddressBook) - */ - void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException; - -} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java deleted file mode 100644 index a6321cec2ea..00000000000 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ /dev/null @@ -1,109 +0,0 @@ -package seedu.address.storage; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Jackson-friendly version of {@link Person}. - */ -class JsonAdaptedPerson { - - public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!"; - - private final String name; - private final String phone; - private final String email; - private final String address; - private final List tagged = new ArrayList<>(); - - /** - * Constructs a {@code JsonAdaptedPerson} with the given person details. - */ - @JsonCreator - public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone, - @JsonProperty("email") String email, @JsonProperty("address") String address, - @JsonProperty("tagged") List tagged) { - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - if (tagged != null) { - this.tagged.addAll(tagged); - } - } - - /** - * Converts a given {@code Person} into this class for Jackson use. - */ - public JsonAdaptedPerson(Person source) { - name = source.getName().fullName; - phone = source.getPhone().value; - email = source.getEmail().value; - address = source.getAddress().value; - tagged.addAll(source.getTags().stream() - .map(JsonAdaptedTag::new) - .collect(Collectors.toList())); - } - - /** - * Converts this Jackson-friendly adapted person object into the model's {@code Person} object. - * - * @throws IllegalValueException if there were any data constraints violated in the adapted person. - */ - public Person toModelType() throws IllegalValueException { - final List personTags = new ArrayList<>(); - for (JsonAdaptedTag tag : tagged) { - personTags.add(tag.toModelType()); - } - - if (name == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); - } - if (!Name.isValidName(name)) { - throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS); - } - final Name modelName = new Name(name); - - if (phone == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName())); - } - if (!Phone.isValidPhone(phone)) { - throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS); - } - final Phone modelPhone = new Phone(phone); - - if (email == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName())); - } - if (!Email.isValidEmail(email)) { - throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS); - } - final Email modelEmail = new Email(email); - - if (address == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); - } - if (!Address.isValidAddress(address)) { - throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); - } - final Address modelAddress = new Address(address); - - final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); - } - -} diff --git a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java b/src/main/java/seedu/address/storage/JsonAddressBookStorage.java deleted file mode 100644 index dfab9daaa0d..00000000000 --- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java +++ /dev/null @@ -1,80 +0,0 @@ -package seedu.address.storage; - -import static java.util.Objects.requireNonNull; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; -import java.util.logging.Logger; - -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.commons.util.FileUtil; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.ReadOnlyAddressBook; - -/** - * A class to access AddressBook data stored as a json file on the hard disk. - */ -public class JsonAddressBookStorage implements AddressBookStorage { - - private static final Logger logger = LogsCenter.getLogger(JsonAddressBookStorage.class); - - private Path filePath; - - public JsonAddressBookStorage(Path filePath) { - this.filePath = filePath; - } - - public Path getAddressBookFilePath() { - return filePath; - } - - @Override - public Optional readAddressBook() throws DataConversionException { - return readAddressBook(filePath); - } - - /** - * Similar to {@link #readAddressBook()}. - * - * @param filePath location of the data. Cannot be null. - * @throws DataConversionException if the file is not in the correct format. - */ - public Optional readAddressBook(Path filePath) throws DataConversionException { - requireNonNull(filePath); - - Optional jsonAddressBook = JsonUtil.readJsonFile( - filePath, JsonSerializableAddressBook.class); - if (!jsonAddressBook.isPresent()) { - return Optional.empty(); - } - - try { - return Optional.of(jsonAddressBook.get().toModelType()); - } catch (IllegalValueException ive) { - logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); - throw new DataConversionException(ive); - } - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, filePath); - } - - /** - * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)}. - * - * @param filePath location of the data. Cannot be null. - */ - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { - requireNonNull(addressBook); - requireNonNull(filePath); - - FileUtil.createIfMissing(filePath); - JsonUtil.saveJsonFile(new JsonSerializableAddressBook(addressBook), filePath); - } - -} diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java deleted file mode 100644 index 5efd834091d..00000000000 --- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.storage; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonRootName; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; - -/** - * An Immutable AddressBook that is serializable to JSON format. - */ -@JsonRootName(value = "addressbook") -class JsonSerializableAddressBook { - - public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s)."; - - private final List persons = new ArrayList<>(); - - /** - * Constructs a {@code JsonSerializableAddressBook} with the given persons. - */ - @JsonCreator - public JsonSerializableAddressBook(@JsonProperty("persons") List persons) { - this.persons.addAll(persons); - } - - /** - * Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use. - * - * @param source future changes to this will not affect the created {@code JsonSerializableAddressBook}. - */ - public JsonSerializableAddressBook(ReadOnlyAddressBook source) { - persons.addAll(source.getPersonList().stream().map(JsonAdaptedPerson::new).collect(Collectors.toList())); - } - - /** - * Converts this address book into the model's {@code AddressBook} object. - * - * @throws IllegalValueException if there were any data constraints violated. - */ - public AddressBook toModelType() throws IllegalValueException { - AddressBook addressBook = new AddressBook(); - for (JsonAdaptedPerson jsonAdaptedPerson : persons) { - Person person = jsonAdaptedPerson.toModelType(); - if (addressBook.hasPerson(person)) { - throw new IllegalValueException(MESSAGE_DUPLICATE_PERSON); - } - addressBook.addPerson(person); - } - return addressBook; - } - -} diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/seedu/address/storage/Storage.java deleted file mode 100644 index beda8bd9f11..00000000000 --- a/src/main/java/seedu/address/storage/Storage.java +++ /dev/null @@ -1,32 +0,0 @@ -package seedu.address.storage; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; - -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; - -/** - * API of the Storage component - */ -public interface Storage extends AddressBookStorage, UserPrefsStorage { - - @Override - Optional readUserPrefs() throws DataConversionException, IOException; - - @Override - void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; - - @Override - Path getAddressBookFilePath(); - - @Override - Optional readAddressBook() throws DataConversionException, IOException; - - @Override - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; - -} diff --git a/src/main/java/seedu/address/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/AppParameters.java b/src/main/java/seedu/internship/AppParameters.java similarity index 93% rename from src/main/java/seedu/address/AppParameters.java rename to src/main/java/seedu/internship/AppParameters.java index ab552c398f3..a42c1334095 100644 --- a/src/main/java/seedu/address/AppParameters.java +++ b/src/main/java/seedu/internship/AppParameters.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.internship; import java.nio.file.Path; import java.nio.file.Paths; @@ -7,8 +7,8 @@ import java.util.logging.Logger; import javafx.application.Application; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.util.FileUtil; +import seedu.internship.commons.core.LogsCenter; +import seedu.internship.commons.util.FileUtil; /** * Represents the parsed command-line parameters given to the application. diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/seedu/internship/Main.java similarity index 97% rename from src/main/java/seedu/address/Main.java rename to src/main/java/seedu/internship/Main.java index 052a5068631..9607b3f3827 100644 --- a/src/main/java/seedu/address/Main.java +++ b/src/main/java/seedu/internship/Main.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.internship; import javafx.application.Application; diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/internship/MainApp.java similarity index 66% rename from src/main/java/seedu/address/MainApp.java rename to src/main/java/seedu/internship/MainApp.java index 4133aaa0151..5887949852d 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/internship/MainApp.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.internship; import java.io.IOException; import java.nio.file.Path; @@ -7,36 +7,36 @@ import javafx.application.Application; import javafx.stage.Stage; -import seedu.address.commons.core.Config; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.core.Version; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.ConfigUtil; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; -import seedu.address.logic.LogicManager; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; -import seedu.address.model.util.SampleDataUtil; -import seedu.address.storage.AddressBookStorage; -import seedu.address.storage.JsonAddressBookStorage; -import seedu.address.storage.JsonUserPrefsStorage; -import seedu.address.storage.Storage; -import seedu.address.storage.StorageManager; -import seedu.address.storage.UserPrefsStorage; -import seedu.address.ui.Ui; -import seedu.address.ui.UiManager; +import seedu.internship.commons.core.Config; +import seedu.internship.commons.core.LogsCenter; +import seedu.internship.commons.core.Version; +import seedu.internship.commons.exceptions.DataConversionException; +import seedu.internship.commons.util.ConfigUtil; +import seedu.internship.commons.util.StringUtil; +import seedu.internship.logic.Logic; +import seedu.internship.logic.LogicManager; +import seedu.internship.model.InternBuddy; +import seedu.internship.model.Model; +import seedu.internship.model.ModelManager; +import seedu.internship.model.ReadOnlyInternBuddy; +import seedu.internship.model.ReadOnlyUserPrefs; +import seedu.internship.model.UserPrefs; +import seedu.internship.model.util.SampleDataUtil; +import seedu.internship.storage.InternBuddyStorage; +import seedu.internship.storage.JsonInternBuddyStorage; +import seedu.internship.storage.JsonUserPrefsStorage; +import seedu.internship.storage.Storage; +import seedu.internship.storage.StorageManager; +import seedu.internship.storage.UserPrefsStorage; +import seedu.internship.ui.Ui; +import seedu.internship.ui.UiManager; /** * Runs the application. */ 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, 1, true); private static final Logger logger = LogsCenter.getLogger(MainApp.class); @@ -48,7 +48,7 @@ public class MainApp extends Application { @Override public void init() throws Exception { - logger.info("=============================[ Initializing AddressBook ]==========================="); + logger.info("=============================[ Initializing InternBuddy ]==========================="); super.init(); AppParameters appParameters = AppParameters.parse(getParameters()); @@ -56,8 +56,8 @@ 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); + InternBuddyStorage internBuddyStorage = new JsonInternBuddyStorage(userPrefs.getInternBuddyFilePath()); + storage = new StorageManager(internBuddyStorage, userPrefsStorage); initLogging(config); @@ -69,25 +69,25 @@ public void init() throws Exception { } /** - * Returns a {@code ModelManager} with the data from {@code storage}'s address book and {@code userPrefs}.
- * The data from the sample address book will be used instead if {@code storage}'s address book is not found, - * or an empty address book will be used instead if errors occur when reading {@code storage}'s address book. + * Returns a {@code ModelManager} with the data from {@code storage}'s InternBuddy and {@code userPrefs}.
+ * The data from the sample InternBuddy will be used instead if {@code storage}'s InternBuddy is not found, + * or an empty InternBuddy will be used instead if errors occur when reading {@code storage}'s InternBuddy. */ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { - Optional addressBookOptional; - ReadOnlyAddressBook initialData; + Optional internBuddyOptional; + ReadOnlyInternBuddy initialData; try { - addressBookOptional = storage.readAddressBook(); - if (!addressBookOptional.isPresent()) { - logger.info("Data file not found. Will be starting with a sample AddressBook"); + internBuddyOptional = storage.readInternBuddy(); + if (!internBuddyOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample InternBuddy"); } - initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); + initialData = internBuddyOptional.orElseGet(SampleDataUtil::getSampleInternBuddy); } catch (DataConversionException e) { - logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Data file not in the correct format. Will be starting with an empty InternBuddy"); + initialData = new InternBuddy(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Problem while reading from the file. Will be starting with an empty InternBuddy"); + initialData = new InternBuddy(); } return new ModelManager(initialData, userPrefs); @@ -151,7 +151,7 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { + "Using default user prefs"); initializedPrefs = new UserPrefs(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); + logger.warning("Problem while reading from the file. Will be starting with an empty InternBuddy"); initializedPrefs = new UserPrefs(); } @@ -167,13 +167,13 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { @Override public void start(Stage primaryStage) { - logger.info("Starting AddressBook " + MainApp.VERSION); + logger.info("Starting InternBuddy " + MainApp.VERSION); ui.start(primaryStage); } @Override public void stop() { - logger.info("============================ [ Stopping Address Book ] ============================="); + logger.info("============================ [ Stopping InternBuddy ] ============================="); try { storage.saveUserPrefs(model.getUserPrefs()); } catch (IOException e) { diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/seedu/internship/commons/core/Config.java similarity index 97% rename from src/main/java/seedu/address/commons/core/Config.java rename to src/main/java/seedu/internship/commons/core/Config.java index 91145745521..768ab25ca89 100644 --- a/src/main/java/seedu/address/commons/core/Config.java +++ b/src/main/java/seedu/internship/commons/core/Config.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.internship.commons.core; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/internship/commons/core/GuiSettings.java similarity index 98% rename from src/main/java/seedu/address/commons/core/GuiSettings.java rename to src/main/java/seedu/internship/commons/core/GuiSettings.java index ba33653be67..7b903b3cf3a 100644 --- a/src/main/java/seedu/address/commons/core/GuiSettings.java +++ b/src/main/java/seedu/internship/commons/core/GuiSettings.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.internship.commons.core; import java.awt.Point; import java.io.Serializable; diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/internship/commons/core/LogsCenter.java similarity index 97% rename from src/main/java/seedu/address/commons/core/LogsCenter.java rename to src/main/java/seedu/internship/commons/core/LogsCenter.java index 431e7185e76..f5fe14b25b5 100644 --- a/src/main/java/seedu/address/commons/core/LogsCenter.java +++ b/src/main/java/seedu/internship/commons/core/LogsCenter.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.internship.commons.core; import java.io.IOException; import java.util.Arrays; @@ -18,7 +18,7 @@ public class LogsCenter { private static final int MAX_FILE_COUNT = 5; private static final int MAX_FILE_SIZE_IN_BYTES = (int) (Math.pow(2, 20) * 5); // 5MB - private static final String LOG_FILE = "addressbook.log"; + private static final String LOG_FILE = "InternBuddy.log"; private static Level currentLogLevel = Level.INFO; private static final Logger logger = LogsCenter.getLogger(LogsCenter.class); private static FileHandler fileHandler; diff --git a/src/main/java/seedu/internship/commons/core/Messages.java b/src/main/java/seedu/internship/commons/core/Messages.java new file mode 100644 index 00000000000..b7917b5e730 --- /dev/null +++ b/src/main/java/seedu/internship/commons/core/Messages.java @@ -0,0 +1,16 @@ +package seedu.internship.commons.core; + +/** + * Container for user visible messages. + */ +public class Messages { + + public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; + public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; + public static final String MESSAGE_INVALID_INTERNSHIP_DISPLAYED_INDEX = "The internship index provided is invalid"; + public static final String MESSAGE_OUT_OF_RANGE_INTERNSHIP_DISPLAYED_INDEX = "The internship index provided is " + + "out of range"; + public static final String MESSAGE_INTERNSHIP_LISTED_OVERVIEW = "%1$d internship/s listed!"; + public static final String MESSAGE_MISSING_ARGUMENTS = "This command requires at least 1 argument!\n%1$s"; + +} diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/seedu/internship/commons/core/Version.java similarity index 98% rename from src/main/java/seedu/address/commons/core/Version.java rename to src/main/java/seedu/internship/commons/core/Version.java index 12142ec1e32..aad8760aa9c 100644 --- a/src/main/java/seedu/address/commons/core/Version.java +++ b/src/main/java/seedu/internship/commons/core/Version.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.internship.commons.core; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/src/main/java/seedu/address/commons/core/index/Index.java b/src/main/java/seedu/internship/commons/core/index/Index.java similarity index 78% rename from src/main/java/seedu/address/commons/core/index/Index.java rename to src/main/java/seedu/internship/commons/core/index/Index.java index 19536439c09..adafe1fc8af 100644 --- a/src/main/java/seedu/address/commons/core/index/Index.java +++ b/src/main/java/seedu/internship/commons/core/index/Index.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core.index; +package seedu.internship.commons.core.index; /** * Represents a zero-based or one-based index. @@ -8,7 +8,7 @@ * base the other component is using for its index. However, after receiving the {@code Index}, that component can * convert it back to an int if the index will not be passed to a different component again. */ -public class Index { +public class Index implements Comparable { private int zeroBasedIndex; /** @@ -51,4 +51,20 @@ public boolean equals(Object other) { || (other instanceof Index // instanceof handles nulls && zeroBasedIndex == ((Index) other).zeroBasedIndex); // state check } + + @Override + public int compareTo(Index other) { + if (this.zeroBasedIndex == other.getZeroBased()) { + return 0; + } + if (this.zeroBasedIndex < other.getZeroBased()) { + return -1; + } + return 1; + } + @Override + public int hashCode() { + return this.zeroBasedIndex; + } + } diff --git a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java b/src/main/java/seedu/internship/commons/exceptions/DataConversionException.java similarity index 83% rename from src/main/java/seedu/address/commons/exceptions/DataConversionException.java rename to src/main/java/seedu/internship/commons/exceptions/DataConversionException.java index 1f689bd8e3f..8e253f780a6 100644 --- a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java +++ b/src/main/java/seedu/internship/commons/exceptions/DataConversionException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.internship.commons.exceptions; /** * Represents an error during conversion of data from one format to another diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java b/src/main/java/seedu/internship/commons/exceptions/IllegalValueException.java similarity index 92% rename from src/main/java/seedu/address/commons/exceptions/IllegalValueException.java rename to src/main/java/seedu/internship/commons/exceptions/IllegalValueException.java index 19124db485c..db2a91efab8 100644 --- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java +++ b/src/main/java/seedu/internship/commons/exceptions/IllegalValueException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.internship.commons.exceptions; /** * Signals that some given data does not fulfill some constraints. diff --git a/src/main/java/seedu/address/commons/util/AppUtil.java b/src/main/java/seedu/internship/commons/util/AppUtil.java similarity index 94% rename from src/main/java/seedu/address/commons/util/AppUtil.java rename to src/main/java/seedu/internship/commons/util/AppUtil.java index 87aa89c0326..5f4c78b483f 100644 --- a/src/main/java/seedu/address/commons/util/AppUtil.java +++ b/src/main/java/seedu/internship/commons/util/AppUtil.java @@ -1,9 +1,9 @@ -package seedu.address.commons.util; +package seedu.internship.commons.util; import static java.util.Objects.requireNonNull; import javafx.scene.image.Image; -import seedu.address.MainApp; +import seedu.internship.MainApp; /** * A container for App specific utility functions diff --git a/src/main/java/seedu/address/commons/util/CollectionUtil.java b/src/main/java/seedu/internship/commons/util/CollectionUtil.java similarity index 96% rename from src/main/java/seedu/address/commons/util/CollectionUtil.java rename to src/main/java/seedu/internship/commons/util/CollectionUtil.java index eafe4dfd681..717376b67d1 100644 --- a/src/main/java/seedu/address/commons/util/CollectionUtil.java +++ b/src/main/java/seedu/internship/commons/util/CollectionUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.internship.commons.util; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/commons/util/ConfigUtil.java b/src/main/java/seedu/internship/commons/util/ConfigUtil.java similarity index 76% rename from src/main/java/seedu/address/commons/util/ConfigUtil.java rename to src/main/java/seedu/internship/commons/util/ConfigUtil.java index f7f8a2bd44c..0225425de60 100644 --- a/src/main/java/seedu/address/commons/util/ConfigUtil.java +++ b/src/main/java/seedu/internship/commons/util/ConfigUtil.java @@ -1,11 +1,11 @@ -package seedu.address.commons.util; +package seedu.internship.commons.util; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.core.Config; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.internship.commons.core.Config; +import seedu.internship.commons.exceptions.DataConversionException; /** * A class for accessing the Config File. diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/seedu/internship/commons/util/FileUtil.java similarity index 98% rename from src/main/java/seedu/address/commons/util/FileUtil.java rename to src/main/java/seedu/internship/commons/util/FileUtil.java index b1e2767cdd9..670ee2a8be2 100644 --- a/src/main/java/seedu/address/commons/util/FileUtil.java +++ b/src/main/java/seedu/internship/commons/util/FileUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.internship.commons.util; import java.io.IOException; import java.nio.file.Files; diff --git a/src/main/java/seedu/address/commons/util/JsonUtil.java b/src/main/java/seedu/internship/commons/util/JsonUtil.java similarity index 97% rename from src/main/java/seedu/address/commons/util/JsonUtil.java rename to src/main/java/seedu/internship/commons/util/JsonUtil.java index 8ef609f055d..ee76bc61355 100644 --- a/src/main/java/seedu/address/commons/util/JsonUtil.java +++ b/src/main/java/seedu/internship/commons/util/JsonUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.internship.commons.util; import static java.util.Objects.requireNonNull; @@ -20,8 +20,8 @@ import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.internship.commons.core.LogsCenter; +import seedu.internship.commons.exceptions.DataConversionException; /** * Converts a Java object instance to JSON and vice versa diff --git a/src/main/java/seedu/internship/commons/util/StringUtil.java b/src/main/java/seedu/internship/commons/util/StringUtil.java new file mode 100644 index 00000000000..add8f423e40 --- /dev/null +++ b/src/main/java/seedu/internship/commons/util/StringUtil.java @@ -0,0 +1,105 @@ +package seedu.internship.commons.util; + +import static java.util.Objects.requireNonNull; +import static seedu.internship.commons.util.AppUtil.checkArgument; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Arrays; + +import seedu.internship.logic.parser.ParserUtil; +import seedu.internship.logic.parser.exceptions.ParseException; + +/** + * Helper functions for handling strings. + */ +public class StringUtil { + + /** + * Returns true if the {@code sentence} contains the {@code word}. + * Ignores case, but a full word match is required. + *
examples:
+     *       containsWordIgnoreCase("ABc def", "abc") == true
+     *       containsWordIgnoreCase("ABc def", "DEF") == true
+     *       containsWordIgnoreCase("ABc def", "AB") == false //not a full word match
+     *       
+ * @param sentence cannot be null + * @param word cannot be null, cannot be empty, must be a single word + */ + public static boolean containsWordIgnoreCase(String sentence, String word) { + requireNonNull(sentence); + requireNonNull(word); + + String preppedWord = word.trim(); + checkArgument(!preppedWord.isEmpty(), "Word parameter cannot be empty"); + checkArgument(preppedWord.split("\\s+").length == 1, "Word parameter should be a single word"); + + String preppedSentence = sentence; + String[] wordsInPreppedSentence = preppedSentence.split("\\s+"); + + return Arrays.stream(wordsInPreppedSentence) + .anyMatch(preppedWord::equalsIgnoreCase); + } + + /** + * Returns true if the {@code input} equals the {@code target}. + * Ignores case but both strings must match. + *
examples:
+     *       matchingStringIgnoreCase("abc def", "abc def") == true
+     *       matchingStringIgnoreCase("ABc dEf", "abc DEF") == true
+     *       matchingStringIgnoreCase("ABc def", "AB") == false //not a full string match
+     *       
+ * @param input cannot be null + * @param target cannot be null, cannot be empty + */ + public static boolean matchingStringIgnoreCase(String input, String target) { + requireNonNull(input); + requireNonNull(target); + + checkArgument(!target.isEmpty(), "Target cannot be empty!"); + assert(!target.isEmpty()); + + return target.equalsIgnoreCase(input); + } + + /** + * Returns a detailed message of the t, including the stack trace. + */ + public static String getDetails(Throwable t) { + requireNonNull(t); + StringWriter sw = new StringWriter(); + t.printStackTrace(new PrintWriter(sw)); + return t.getMessage() + "\n" + sw.toString(); + } + + /** + * Throws ParseException objects with error message {@code Messages.MESSAGE_INVALID_COMMAND_FORMAT} if {@code s} + * does not represent an integer or represents a positive integer with the plus sign "+" in front and + * {@code Messages.MESSAGE_INVALID_INTERNSHIP_DISPLAYED_INDEX} if {@code s} represents a negative integer. Else, + * does nothing. + * @param s String input + * @throws ParseException with error message {@code Messages.MESSAGE_INVALID_COMMAND_FORMAT} if {@code s} + * does not represent an integer or represents a positive integer with the plus sign "+" in front and + * {@code Messages.MESSAGE_INVALID_INTERNSHIP_DISPLAYED_INDEX} if {@code s} represents a negative integer. + */ + public static void nonZeroUnsignedIntegerCheck(String s) throws ParseException { + requireNonNull(s); + + int value; + + try { + value = Integer.parseInt(s); + } catch (NumberFormatException nfe) { + throw new ParseException(ParserUtil.MESSAGE_INVALID_INDEX_FORMAT); + } + + if (s.startsWith("+")) { + throw new ParseException(ParserUtil.MESSAGE_INVALID_POSITIVE_SIGNED_INDEX); + } + + if (value <= 0) { + throw new ParseException(ParserUtil.MESSAGE_INVALID_INDEX); + } + + } +} diff --git a/src/main/java/seedu/internship/logic/Logic.java b/src/main/java/seedu/internship/logic/Logic.java new file mode 100644 index 00000000000..bd310f46698 --- /dev/null +++ b/src/main/java/seedu/internship/logic/Logic.java @@ -0,0 +1,65 @@ +package seedu.internship.logic; + +import java.nio.file.Path; + +import javafx.collections.ObservableList; +import seedu.internship.commons.core.GuiSettings; +import seedu.internship.logic.commands.CommandResult; +import seedu.internship.logic.commands.exceptions.CommandException; +import seedu.internship.logic.parser.exceptions.ParseException; +import seedu.internship.model.Model; +import seedu.internship.model.ReadOnlyInternBuddy; +import seedu.internship.model.internship.Internship; + +/** + * API of the Logic component + */ +public interface Logic { + /** + * Executes the command and returns the result. + * @param commandText The command as entered by the user. + * @return the result of the command execution. + * @throws CommandException If an error occurs during command execution. + * @throws ParseException If an error occurs during parsing. + */ + CommandResult execute(String commandText) throws CommandException, ParseException; + + /** + * Returns the InternBuddy. + * + * @see seedu.internship.model.Model#getInternBuddy() + */ + ReadOnlyInternBuddy getInternBuddy(); + + /** Returns an unmodifiable view of the filtered list of internships */ + ObservableList getFilteredInternshipList(); + + /** + * Returns the user prefs' InternBuddy file path. + */ + Path getInternBuddyFilePath(); + + /** + * Returns the user prefs' GUI settings. + */ + GuiSettings getGuiSettings(); + + /** + * Set the user prefs' GUI settings. + */ + void setGuiSettings(GuiSettings guiSettings); + + /** + * Gets the selected internship. + * + * @return the currently selected Internship + */ + Internship getSelectedInternship(); + + /** + * Gets the model associated with the logic object. + * @return the model in the logic object. + */ + Model getModel(); + +} diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/internship/logic/LogicManager.java similarity index 51% rename from src/main/java/seedu/address/logic/LogicManager.java rename to src/main/java/seedu/internship/logic/LogicManager.java index 9d9c6d15bdc..060a03f3145 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/internship/logic/LogicManager.java @@ -1,21 +1,21 @@ -package seedu.address.logic; +package seedu.internship.logic; import java.io.IOException; import java.nio.file.Path; import java.util.logging.Logger; import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.logic.commands.Command; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.AddressBookParser; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.Model; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; -import seedu.address.storage.Storage; +import seedu.internship.commons.core.GuiSettings; +import seedu.internship.commons.core.LogsCenter; +import seedu.internship.logic.commands.Command; +import seedu.internship.logic.commands.CommandResult; +import seedu.internship.logic.commands.exceptions.CommandException; +import seedu.internship.logic.parser.InternBuddyParser; +import seedu.internship.logic.parser.exceptions.ParseException; +import seedu.internship.model.Model; +import seedu.internship.model.ReadOnlyInternBuddy; +import seedu.internship.model.internship.Internship; +import seedu.internship.storage.Storage; /** * The main LogicManager of the app. @@ -26,7 +26,7 @@ public class LogicManager implements Logic { private final Model model; private final Storage storage; - private final AddressBookParser addressBookParser; + private final InternBuddyParser internBuddyParser; /** * Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}. @@ -34,7 +34,7 @@ public class LogicManager implements Logic { public LogicManager(Model model, Storage storage) { this.model = model; this.storage = storage; - addressBookParser = new AddressBookParser(); + internBuddyParser = new InternBuddyParser(); } @Override @@ -42,11 +42,11 @@ public CommandResult execute(String commandText) throws CommandException, ParseE logger.info("----------------[USER COMMAND][" + commandText + "]"); CommandResult commandResult; - Command command = addressBookParser.parseCommand(commandText); + Command command = internBuddyParser.parseCommand(commandText); commandResult = command.execute(model); try { - storage.saveAddressBook(model.getAddressBook()); + storage.saveInternBuddy(model.getInternBuddy()); } catch (IOException ioe) { throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe); } @@ -55,18 +55,18 @@ public CommandResult execute(String commandText) throws CommandException, ParseE } @Override - public ReadOnlyAddressBook getAddressBook() { - return model.getAddressBook(); + public ReadOnlyInternBuddy getInternBuddy() { + return model.getInternBuddy(); } @Override - public ObservableList getFilteredPersonList() { - return model.getFilteredPersonList(); + public ObservableList getFilteredInternshipList() { + return model.getFilteredInternshipList(); } @Override - public Path getAddressBookFilePath() { - return model.getAddressBookFilePath(); + public Path getInternBuddyFilePath() { + return model.getInternBuddyFilePath(); } @Override @@ -78,4 +78,14 @@ public GuiSettings getGuiSettings() { public void setGuiSettings(GuiSettings guiSettings) { model.setGuiSettings(guiSettings); } + + @Override + public Internship getSelectedInternship() { + return model.getSelectedInternship(); + + } + @Override + public Model getModel() { + return this.model; + }; } diff --git a/src/main/java/seedu/internship/logic/commands/AddCommand.java b/src/main/java/seedu/internship/logic/commands/AddCommand.java new file mode 100644 index 00000000000..0a7024fe1fd --- /dev/null +++ b/src/main/java/seedu/internship/logic/commands/AddCommand.java @@ -0,0 +1,74 @@ +package seedu.internship.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_COMMENT; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_ROLE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_STATUS; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_TAG; + +import seedu.internship.logic.commands.exceptions.CommandException; +import seedu.internship.model.Model; +import seedu.internship.model.internship.Internship; + +/** + * Adds an internship to InternBuddy. + */ +public class AddCommand extends Command { + + public static final String COMMAND_WORD = "add"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an internship to InternBuddy. " + + "Fields: " + + PREFIX_COMPANY_NAME + "COMPANY_NAME " + + PREFIX_ROLE + "ROLE " + + PREFIX_STATUS + "STATUS " + + PREFIX_DATE + "DATE " + + "[" + PREFIX_COMMENT + "COMMENT] " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_COMPANY_NAME + "Google " + + PREFIX_ROLE + "Software Engineer " + + PREFIX_STATUS + "Applied " + + PREFIX_DATE + "2023-02-01 " + + PREFIX_COMMENT + "I like the company culture! " + + PREFIX_TAG + "Go " + + PREFIX_TAG + "Java"; + + public static final String MESSAGE_SUCCESS = "New internship added: %1$s"; + public static final String MESSAGE_DUPLICATE_INTERNSHIP = "This internship already exists in InternBuddy"; + + private final Internship toAdd; + + /** + * Creates an AddCommand to add the specified {@code Internship} + */ + public AddCommand(Internship internship) { + requireNonNull(internship); + toAdd = internship; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasInternship(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_INTERNSHIP); + } + + //Add internship + model.addInternship(toAdd); + //Update right panel + model.updateSelectedInternship(toAdd); + + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddCommand // instanceof handles nulls + && toAdd.equals(((AddCommand) other).toAdd)); + } +} diff --git a/src/main/java/seedu/internship/logic/commands/ClearCommand.java b/src/main/java/seedu/internship/logic/commands/ClearCommand.java new file mode 100644 index 00000000000..c05862a890b --- /dev/null +++ b/src/main/java/seedu/internship/logic/commands/ClearCommand.java @@ -0,0 +1,24 @@ +package seedu.internship.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.internship.model.InternBuddy; +import seedu.internship.model.Model; + +/** + * Clears the data in InternBuddy. + */ +public class ClearCommand extends Command { + + public static final String COMMAND_WORD = "clear"; + public static final String MESSAGE_SUCCESS = "InternBuddy data has been cleared!"; + + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.setInternBuddy(new InternBuddy()); + model.updateSelectedInternship(null); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/internship/logic/commands/Command.java similarity index 77% rename from src/main/java/seedu/address/logic/commands/Command.java rename to src/main/java/seedu/internship/logic/commands/Command.java index 64f18992160..1876f4e2b64 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/seedu/internship/logic/commands/Command.java @@ -1,7 +1,7 @@ -package seedu.address.logic.commands; +package seedu.internship.logic.commands; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; +import seedu.internship.logic.commands.exceptions.CommandException; +import seedu.internship.model.Model; /** * Represents a command with hidden internal logic and the ability to be executed. diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/internship/logic/commands/CommandResult.java similarity index 97% rename from src/main/java/seedu/address/logic/commands/CommandResult.java rename to src/main/java/seedu/internship/logic/commands/CommandResult.java index 92f900b7916..eb132ba2eef 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/internship/logic/commands/CommandResult.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.internship.logic.commands; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/internship/logic/commands/CopyCommand.java b/src/main/java/seedu/internship/logic/commands/CopyCommand.java new file mode 100644 index 00000000000..ee7b70e01af --- /dev/null +++ b/src/main/java/seedu/internship/logic/commands/CopyCommand.java @@ -0,0 +1,59 @@ +package seedu.internship.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.internship.commons.core.Messages; +import seedu.internship.commons.core.index.Index; +import seedu.internship.logic.commands.exceptions.CommandException; +import seedu.internship.model.Model; +import seedu.internship.model.internship.Internship; + +/** + * Copies the internship information identified using it's displayed index from InternBuddy. + */ +public class CopyCommand extends Command { + + public static final String COMMAND_WORD = "copy"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Copies the details of the internship identified by the index" + + " number used in the displayed internship list.\n" + + "Fields: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_COPY_INTERNSHIP_SUCCESS = "Copied Internship: %1$s"; + + private final Index targetIndex; + + public CopyCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredInternshipList(); + + //Checks for a valid index + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_OUT_OF_RANGE_INTERNSHIP_DISPLAYED_INDEX); + } + + //Gets the internship to copy + Internship internshipToCopy = lastShownList.get(targetIndex.getZeroBased()); + + //Functionality of the copy internship command + model.copyInternship(internshipToCopy); + + return new CommandResult(String.format(MESSAGE_COPY_INTERNSHIP_SUCCESS, internshipToCopy)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof CopyCommand // instanceof handles nulls + && targetIndex.equals(((CopyCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/internship/logic/commands/DeleteFieldCommand.java b/src/main/java/seedu/internship/logic/commands/DeleteFieldCommand.java new file mode 100644 index 00000000000..98cbe01c9d9 --- /dev/null +++ b/src/main/java/seedu/internship/logic/commands/DeleteFieldCommand.java @@ -0,0 +1,91 @@ +package seedu.internship.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_ROLE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_STATUS; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Arrays; +import java.util.List; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import seedu.internship.MainApp; +import seedu.internship.commons.core.LogsCenter; +import seedu.internship.logic.commands.exceptions.CommandException; +import seedu.internship.model.Model; +import seedu.internship.model.internship.Internship; +import seedu.internship.model.internship.InternshipContainsKeywordsPredicate; + + + + + +/** + * Deletes an internship identified using it's displayed index from InternBuddy. + */ +public class DeleteFieldCommand extends Command { + + public static final String COMMAND_WORD = "delete-field"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes all internships containing at least one of the inputs for every different field " + + "(COMPANY_NAME, ROLE, STATUS, DATE or TAG) that you have provided.\n" + + "Fields: [" + PREFIX_COMPANY_NAME + "COMPANY_NAME]... " + + "[" + PREFIX_ROLE + "ROLE]... " + + "[" + PREFIX_DATE + "DATE]... " + + "[" + PREFIX_STATUS + "STATUS]... " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_COMPANY_NAME + "Apple " + + PREFIX_COMPANY_NAME + "Google "; + + public static final String MESSAGE_DELETE_INTERNSHIP_SUCCESS = "%1$d internship/s have been deleted!"; + private static final Logger logger = LogsCenter.getLogger(MainApp.class); + private final InternshipContainsKeywordsPredicate predicate; + /** + * Create a DeleteFieldCommand object from the list of user supplied indexes and a predicate + * @param predicate A user supplied predicate to filter internships. + */ + public DeleteFieldCommand(InternshipContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredInternshipList(); + + int previousSize = lastShownList.size(); + + if (!this.predicate.isEmpty()) { + lastShownList = lastShownList.stream() + .filter(this.predicate) + .collect(Collectors.toList()); + } + + logger.info(String.format("Deleting internships: %s", Arrays.toString(lastShownList.toArray()))); + + for (Internship internshipToDelete: lastShownList) { + //Delete internship + model.deleteInternship(internshipToDelete); + //Update right panel + if (internshipToDelete.equals(model.getSelectedInternship())) { + model.updateSelectedInternship(null); + } + } + + int newSize = model.getFilteredInternshipList().size(); + + return new CommandResult(String.format(MESSAGE_DELETE_INTERNSHIP_SUCCESS, previousSize - newSize)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteFieldCommand // instanceof handles nulls + && predicate.equals(((DeleteFieldCommand) other).predicate)); + } +} diff --git a/src/main/java/seedu/internship/logic/commands/DeleteIndexCommand.java b/src/main/java/seedu/internship/logic/commands/DeleteIndexCommand.java new file mode 100644 index 00000000000..dd93f075d40 --- /dev/null +++ b/src/main/java/seedu/internship/logic/commands/DeleteIndexCommand.java @@ -0,0 +1,94 @@ +package seedu.internship.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import seedu.internship.MainApp; +import seedu.internship.commons.core.LogsCenter; +import seedu.internship.commons.core.Messages; +import seedu.internship.commons.core.index.Index; +import seedu.internship.logic.commands.exceptions.CommandException; +import seedu.internship.model.Model; +import seedu.internship.model.internship.Internship; + +/** + * Deletes an internship identified using it's displayed index from InternBuddy. + */ +public class DeleteIndexCommand extends Command { + + public static final String COMMAND_WORD = "delete-index"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes all internships identified by the index numbers used in the displayed internship list.\n" + + "Fields: INDEX [INDEX]... (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1 2"; + + public static final String MESSAGE_DELETE_INTERNSHIP_SUCCESS = "%1$d internships have been deleted!"; + private static final Logger logger = LogsCenter.getLogger(MainApp.class); + + private final List targetIndexes; + /** + * Create a DeleteIndexCommand object from the list of user supplied indexes and a predicate + * @param targetIndexes A list of {@code Index} that refers to the index of internship entries in list. + */ + public DeleteIndexCommand(List targetIndexes) { + this.targetIndexes = targetIndexes; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredInternshipList(); + + int previousSize = lastShownList.size(); + + // Remove duplicates + List uniqueTargetIndexes = new ArrayList<>(new HashSet<>(this.targetIndexes)); + + + // Sort list + Collections.sort(uniqueTargetIndexes); + + if (uniqueTargetIndexes.size() > 0 && uniqueTargetIndexes.get(uniqueTargetIndexes.size() - 1) + .getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_OUT_OF_RANGE_INTERNSHIP_DISPLAYED_INDEX); + } + + List internshipsToDelete = new ArrayList<>(lastShownList); + + if (uniqueTargetIndexes.size() > 0) { + internshipsToDelete = uniqueTargetIndexes.stream() + .map(index -> lastShownList.get(index.getZeroBased())) + .collect(Collectors.toList()); + } + + logger.info(String.format("Deleting internships: %s", Arrays.toString(internshipsToDelete.toArray()))); + + for (Internship internshipToDelete: internshipsToDelete) { + //Delete internship + model.deleteInternship(internshipToDelete); + //Update right panel + if (internshipToDelete.equals(model.getSelectedInternship())) { + model.updateSelectedInternship(null); + } + } + + int newSize = model.getFilteredInternshipList().size(); + + return new CommandResult(String.format(MESSAGE_DELETE_INTERNSHIP_SUCCESS, previousSize - newSize)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteIndexCommand // instanceof handles nulls + && new HashSet<>(this.targetIndexes).equals(new HashSet<>(((DeleteIndexCommand) other).targetIndexes))); + } +} diff --git a/src/main/java/seedu/internship/logic/commands/EditCommand.java b/src/main/java/seedu/internship/logic/commands/EditCommand.java new file mode 100644 index 00000000000..c163158fa4f --- /dev/null +++ b/src/main/java/seedu/internship/logic/commands/EditCommand.java @@ -0,0 +1,247 @@ +package seedu.internship.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_COMMENT; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_ROLE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_STATUS; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.internship.model.Model.PREDICATE_SHOW_ALL_INTERNSHIPS; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import seedu.internship.commons.core.Messages; +import seedu.internship.commons.core.index.Index; +import seedu.internship.commons.util.CollectionUtil; +import seedu.internship.logic.commands.exceptions.CommandException; +import seedu.internship.model.Model; +import seedu.internship.model.internship.Comment; +import seedu.internship.model.internship.CompanyName; +import seedu.internship.model.internship.Date; +import seedu.internship.model.internship.Internship; +import seedu.internship.model.internship.Role; +import seedu.internship.model.internship.Status; +import seedu.internship.model.tag.Tag; + +/** + * Edits the details of an existing internship in InternBuddy. + */ +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 internship identified " + + "by the index number used in the displayed internship list. " + + "Existing values will be overwritten by the input values.\n" + + "Fields: INDEX (must be a positive integer) " + + "[" + PREFIX_COMPANY_NAME + "COMPANY_NAME] " + + "[" + PREFIX_ROLE + "ROLE] " + + "[" + PREFIX_STATUS + "STATUS] " + + "[" + PREFIX_DATE + "DATE] " + + "[" + PREFIX_COMMENT + "COMMENT] " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_ROLE + "Backend Engineer " + + PREFIX_STATUS + "Interview"; + + 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_INTERNSHIP = "This internship already exists in InternBuddy."; + + private final Index index; + private final EditInternshipDescriptor editInternshipDescriptor; + + /** + * @param index of the internship in the filtered internship list to edit + * @param editInternshipDescriptor details to edit the internship with + */ + public EditCommand(Index index, EditInternshipDescriptor editInternshipDescriptor) { + requireNonNull(index); + requireNonNull(editInternshipDescriptor); + + this.index = index; + this.editInternshipDescriptor = new EditInternshipDescriptor(editInternshipDescriptor); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredInternshipList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_OUT_OF_RANGE_INTERNSHIP_DISPLAYED_INDEX); + } + + Internship internshipToEdit = lastShownList.get(index.getZeroBased()); + Internship editedInternship = createEditedInternship(internshipToEdit, editInternshipDescriptor); + + if (!internshipToEdit.isSameInternship(editedInternship) && model.hasInternship(editedInternship)) { + throw new CommandException(MESSAGE_DUPLICATE_INTERNSHIP); + } + + //Edit functionality + model.setInternship(internshipToEdit, editedInternship); + model.updateFilteredInternshipList(PREDICATE_SHOW_ALL_INTERNSHIPS); + model.updateSelectedInternship(editedInternship); + return new CommandResult(String.format(MESSAGE_EDIT_INTERNSHIP_SUCCESS, editedInternship)); + } + + /** + * Creates and returns a {@code Internship} with the details of {@code internshipToEdit} + * edited with {@code editInternshipDescriptor}. + */ + private static Internship createEditedInternship(Internship internshipToEdit, + EditInternshipDescriptor editInternshipDescriptor) { + + assert internshipToEdit != null; + CompanyName updatedCompanyName = editInternshipDescriptor.getCompanyName() + .orElse(internshipToEdit.getCompanyName()); + Role updatedRole = editInternshipDescriptor.getRole().orElse(internshipToEdit.getRole()); + Status updatedStatus = editInternshipDescriptor.getStatus().orElse(internshipToEdit.getStatus()); + Date updatedDate = editInternshipDescriptor.getDate().orElse(internshipToEdit.getDate()); + Comment updatedComment = editInternshipDescriptor.getComment().orElse(internshipToEdit.getComment()); + Set updatedTags = editInternshipDescriptor.getTags().orElse(internshipToEdit.getTags()); + + return new Internship(updatedCompanyName, updatedRole, updatedStatus, updatedDate, updatedComment, + updatedTags); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditCommand)) { + return false; + } + + // state check + EditCommand e = (EditCommand) other; + return index.equals(e.index) + && editInternshipDescriptor.equals(e.editInternshipDescriptor); + } + + /** + * 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 EditInternshipDescriptor { + private CompanyName companyName; + private Role role; + private Status status; + private Date date; + private Comment comment; + private Set tags; + + public EditInternshipDescriptor() {} + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public EditInternshipDescriptor(EditInternshipDescriptor toCopy) { + setCompanyName(toCopy.companyName); + setRole(toCopy.role); + setStatus(toCopy.status); + setDate(toCopy.date); + setComment(toCopy.comment); + setTags(toCopy.tags); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(companyName, role, status, date, comment, tags); + } + + public void setCompanyName(CompanyName companyName) { + this.companyName = companyName; + } + + public Optional getCompanyName() { + return Optional.ofNullable(companyName); + } + + public void setRole(Role role) { + this.role = role; + } + + public Optional getRole() { + return Optional.ofNullable(role); + } + + + public void setStatus(Status status) { + this.status = status; + } + + public Optional getStatus() { + return Optional.ofNullable(status); + } + + public void setDate(Date date) { + this.date = date; + } + + public Optional getDate() { + return Optional.ofNullable(date); + } + + public void setComment(Comment comment) { + this.comment = comment; + } + + public Optional getComment() { + return Optional.ofNullable(comment); + } + + /** + * Sets {@code tags} to this object's {@code tags}. + * A defensive copy of {@code tags} is used internally. + */ + public void setTags(Set tags) { + this.tags = (tags != null) ? new HashSet<>(tags) : null; + } + + /** + * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + * Returns {@code Optional#empty()} if {@code tags} is null. + */ + public Optional> getTags() { + return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditInternshipDescriptor)) { + return false; + } + + // state check + EditInternshipDescriptor e = (EditInternshipDescriptor) other; + + return getCompanyName().equals(e.getCompanyName()) + && getRole().equals(e.getRole()) + && getStatus().equals(e.getStatus()) + && getDate().equals(e.getDate()) + && getComment().equals(e.getComment()) + && getTags().equals(e.getTags()); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/internship/logic/commands/ExitCommand.java similarity index 75% rename from src/main/java/seedu/address/logic/commands/ExitCommand.java rename to src/main/java/seedu/internship/logic/commands/ExitCommand.java index 3dd85a8ba90..fa965bb9418 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/internship/logic/commands/ExitCommand.java @@ -1,6 +1,6 @@ -package seedu.address.logic.commands; +package seedu.internship.logic.commands; -import seedu.address.model.Model; +import seedu.internship.model.Model; /** * Terminates the program. @@ -9,7 +9,7 @@ public class ExitCommand extends Command { public static final String COMMAND_WORD = "exit"; - public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; + public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting InternBuddy as requested ..."; @Override public CommandResult execute(Model model) { diff --git a/src/main/java/seedu/internship/logic/commands/FindCommand.java b/src/main/java/seedu/internship/logic/commands/FindCommand.java new file mode 100644 index 00000000000..209b168eb7f --- /dev/null +++ b/src/main/java/seedu/internship/logic/commands/FindCommand.java @@ -0,0 +1,62 @@ +package seedu.internship.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_ROLE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_STATUS; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_TAG; + +import seedu.internship.commons.core.Messages; +import seedu.internship.model.Model; +import seedu.internship.model.internship.InternshipContainsKeywordsPredicate; + +/** + * Finds and lists all internships in InternBuddy which match with the given search terms + * 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 internships containing at least one of the " + + "inputs for every different field (COMPANY_NAME, ROLE, STATUS, DATE or TAG) that you have provided.\n" + + "Fields: " + + "[" + PREFIX_COMPANY_NAME + "COMPANY_NAME]... " + + "[" + PREFIX_ROLE + "ROLE]... " + + "[" + PREFIX_STATUS + "STATUS]... " + + "[" + PREFIX_DATE + "DATE]... " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_COMPANY_NAME + "Apple " + + PREFIX_COMPANY_NAME + "Google " + + PREFIX_ROLE + "Software Developer " + + PREFIX_ROLE + "App Developer " + + PREFIX_STATUS + "Applied " + + PREFIX_STATUS + "Offered " + + PREFIX_DATE + "2023-01-01 " + + PREFIX_DATE + "2023-02-02 " + + PREFIX_TAG + "Python " + + PREFIX_TAG + "Java"; + + private final InternshipContainsKeywordsPredicate predicate; + + public FindCommand(InternshipContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredInternshipList(predicate); + return new CommandResult( + String.format(Messages.MESSAGE_INTERNSHIP_LISTED_OVERVIEW, model.getFilteredInternshipList().size())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FindCommand // instanceof handles nulls + && predicate.equals(((FindCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/internship/logic/commands/HelpCommand.java similarity index 87% rename from src/main/java/seedu/address/logic/commands/HelpCommand.java rename to src/main/java/seedu/internship/logic/commands/HelpCommand.java index bf824f91bd0..d6278a3ad0b 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/internship/logic/commands/HelpCommand.java @@ -1,6 +1,6 @@ -package seedu.address.logic.commands; +package seedu.internship.logic.commands; -import seedu.address.model.Model; +import seedu.internship.model.Model; /** * Format full help instructions for every command for display. diff --git a/src/main/java/seedu/internship/logic/commands/ListCommand.java b/src/main/java/seedu/internship/logic/commands/ListCommand.java new file mode 100644 index 00000000000..ee049430373 --- /dev/null +++ b/src/main/java/seedu/internship/logic/commands/ListCommand.java @@ -0,0 +1,25 @@ +package seedu.internship.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.internship.model.Model.PREDICATE_SHOW_ALL_INTERNSHIPS; + +import seedu.internship.model.Model; + +/** + * Lists all internships in InternBuddy to the user. + */ +public class ListCommand extends Command { + + public static final String COMMAND_WORD = "list"; + + public static final String MESSAGE_SUCCESS = "Listed all internships"; + + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredInternshipList(PREDICATE_SHOW_ALL_INTERNSHIPS); + model.updateSelectedInternship(null); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/internship/logic/commands/UpcomingCommand.java b/src/main/java/seedu/internship/logic/commands/UpcomingCommand.java new file mode 100644 index 00000000000..f6555d0efaa --- /dev/null +++ b/src/main/java/seedu/internship/logic/commands/UpcomingCommand.java @@ -0,0 +1,31 @@ +package seedu.internship.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.internship.model.Model.PREDICATE_SHOW_UPCOMING_INTERNSHIPS; + +import seedu.internship.logic.commands.exceptions.CommandException; +import seedu.internship.model.Model; + + +/** + * Lists all internships that have deadlines in the upcoming week. + */ +public class UpcomingCommand extends Command { + + public static final String COMMAND_WORD = "upcoming"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Lists all internships with events or deadlines in the upcoming week\n" + + "No fields required. Any fields will be ignored.\n" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "Listed all internships with events or deadlines " + + "in the upcoming week"; + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + model.updateFilteredInternshipList(PREDICATE_SHOW_UPCOMING_INTERNSHIPS); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/internship/logic/commands/ViewCommand.java b/src/main/java/seedu/internship/logic/commands/ViewCommand.java new file mode 100644 index 00000000000..4aafefc0b63 --- /dev/null +++ b/src/main/java/seedu/internship/logic/commands/ViewCommand.java @@ -0,0 +1,57 @@ +package seedu.internship.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.internship.commons.core.Messages; +import seedu.internship.commons.core.index.Index; +import seedu.internship.logic.commands.exceptions.CommandException; +import seedu.internship.model.Model; +import seedu.internship.model.internship.Internship; + +/** + * Views an internship identified using it's displayed index from InternBuddy. + */ +public class ViewCommand extends Command { + + public static final String COMMAND_WORD = "view"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Views the internship identified by the index number used in the displayed internship list.\n" + + "Fields: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_VIEW_INTERNSHIP_SUCCESS = "Viewed Internship: %1$s"; + + private final Index targetIndex; + + public ViewCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredInternshipList(); + + //Checks for a valid index + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_OUT_OF_RANGE_INTERNSHIP_DISPLAYED_INDEX); + } + + //Gets the internship to view + Internship internshipToView = lastShownList.get(targetIndex.getZeroBased()); + + //Functionality of the view internship command + model.updateSelectedInternship(internshipToView); + return new CommandResult(String.format(MESSAGE_VIEW_INTERNSHIP_SUCCESS, internshipToView)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ViewCommand // instanceof handles nulls + && targetIndex.equals(((ViewCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/seedu/internship/logic/commands/exceptions/CommandException.java similarity index 89% rename from src/main/java/seedu/address/logic/commands/exceptions/CommandException.java rename to src/main/java/seedu/internship/logic/commands/exceptions/CommandException.java index a16bd14f2cd..19564e639bd 100644 --- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java +++ b/src/main/java/seedu/internship/logic/commands/exceptions/CommandException.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands.exceptions; +package seedu.internship.logic.commands.exceptions; /** * Represents an error which occurs during execution of a {@link Command}. diff --git a/src/main/java/seedu/internship/logic/parser/AddCommandParser.java b/src/main/java/seedu/internship/logic/parser/AddCommandParser.java new file mode 100644 index 00000000000..98bffcb305b --- /dev/null +++ b/src/main/java/seedu/internship/logic/parser/AddCommandParser.java @@ -0,0 +1,64 @@ +package seedu.internship.logic.parser; + +import static seedu.internship.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_COMMENT; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_ROLE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_STATUS; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Set; +import java.util.stream.Stream; + +import seedu.internship.logic.commands.AddCommand; +import seedu.internship.logic.parser.exceptions.ParseException; +import seedu.internship.model.internship.Comment; +import seedu.internship.model.internship.CompanyName; +import seedu.internship.model.internship.Date; +import seedu.internship.model.internship.Internship; +import seedu.internship.model.internship.Role; +import seedu.internship.model.internship.Status; +import seedu.internship.model.tag.Tag; + +/** + * Parses input arguments and creates a new AddCommand object + */ +public class AddCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddCommand + * and returns an AddCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_COMPANY_NAME, PREFIX_ROLE, PREFIX_STATUS, PREFIX_DATE, + PREFIX_COMMENT, PREFIX_TAG); + //Note that comment and tags are optional + if (!arePrefixesPresent(argMultimap, PREFIX_COMPANY_NAME, PREFIX_ROLE, PREFIX_STATUS, PREFIX_DATE) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } + + CompanyName companyName = ParserUtil.parseCompanyName(argMultimap.getValue(PREFIX_COMPANY_NAME).get()); + Role role = ParserUtil.parseRole(argMultimap.getValue(PREFIX_ROLE).get()); + Status status = ParserUtil.parseStatus(argMultimap.getValue(PREFIX_STATUS).get()); + Date date = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get()); + Comment comment = ParserUtil.parseComment(argMultimap.getOptionalValue(PREFIX_COMMENT).get()); + Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); + + Internship internship = new Internship(companyName, role, status, date, comment, tagList); + + return new AddCommand(internship); + } + + /** + * 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/ArgumentMultimap.java b/src/main/java/seedu/internship/logic/parser/ArgumentMultimap.java similarity index 86% rename from src/main/java/seedu/address/logic/parser/ArgumentMultimap.java rename to src/main/java/seedu/internship/logic/parser/ArgumentMultimap.java index 954c8e18f8e..96a00c19906 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java +++ b/src/main/java/seedu/internship/logic/parser/ArgumentMultimap.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.internship.logic.parser; import java.util.ArrayList; import java.util.HashMap; @@ -39,6 +39,14 @@ public Optional getValue(Prefix prefix) { return values.isEmpty() ? Optional.empty() : Optional.of(values.get(values.size() - 1)); } + /** + * Returns the last value of {@code prefix} for optional fields. + */ + public Optional getOptionalValue(Prefix prefix) { + List values = getAllValues(prefix); + return values.isEmpty() ? Optional.of("NA") : Optional.of(values.get(values.size() - 1)); + } + /** * Returns all values of {@code prefix}. * If the prefix does not exist or has no values, this will return an empty list. diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/seedu/internship/logic/parser/ArgumentTokenizer.java similarity index 99% rename from src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java rename to src/main/java/seedu/internship/logic/parser/ArgumentTokenizer.java index 5c9aebfa488..e4cf583c0ff 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java +++ b/src/main/java/seedu/internship/logic/parser/ArgumentTokenizer.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.internship.logic.parser; import java.util.ArrayList; import java.util.Arrays; diff --git a/src/main/java/seedu/internship/logic/parser/CliSyntax.java b/src/main/java/seedu/internship/logic/parser/CliSyntax.java new file mode 100644 index 00000000000..65c61380ce6 --- /dev/null +++ b/src/main/java/seedu/internship/logic/parser/CliSyntax.java @@ -0,0 +1,16 @@ +package seedu.internship.logic.parser; + +/** + * Contains Command Line Interface (CLI) syntax definitions common to multiple commands + */ +public class CliSyntax { + + /* Prefix definitions */ + public static final Prefix PREFIX_COMPANY_NAME = new Prefix("n/"); + public static final Prefix PREFIX_ROLE = new Prefix("r/"); + public static final Prefix PREFIX_STATUS = new Prefix("s/"); + public static final Prefix PREFIX_DATE = new Prefix("d/"); + public static final Prefix PREFIX_COMMENT = new Prefix("c/"); + public static final Prefix PREFIX_TAG = new Prefix("t/"); + +} diff --git a/src/main/java/seedu/internship/logic/parser/CopyCommandParser.java b/src/main/java/seedu/internship/logic/parser/CopyCommandParser.java new file mode 100644 index 00000000000..116d896a3fb --- /dev/null +++ b/src/main/java/seedu/internship/logic/parser/CopyCommandParser.java @@ -0,0 +1,30 @@ +package seedu.internship.logic.parser; + +import seedu.internship.commons.core.index.Index; +import seedu.internship.logic.commands.CopyCommand; +import seedu.internship.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new CopyCommand object + */ +public class CopyCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the CopyCommand + * and returns a CopyCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public CopyCommand parse(String args) throws ParseException { + Index index; + + try { + index = ParserUtil.parseIndex(args); + assert index.getZeroBased() > -1; + } catch (ParseException pe) { + ParseException e = ParserUtil.handleIndexException(pe, CopyCommand.MESSAGE_USAGE); + throw e; + } + return new CopyCommand(index); + } + +} diff --git a/src/main/java/seedu/internship/logic/parser/DeleteFieldCommandParser.java b/src/main/java/seedu/internship/logic/parser/DeleteFieldCommandParser.java new file mode 100644 index 00000000000..e792919a957 --- /dev/null +++ b/src/main/java/seedu/internship/logic/parser/DeleteFieldCommandParser.java @@ -0,0 +1,65 @@ +package seedu.internship.logic.parser; + +import static seedu.internship.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.internship.commons.core.Messages.MESSAGE_MISSING_ARGUMENTS; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_ROLE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_STATUS; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.internship.logic.parser.ParserUtil.parseCompanyNamesToString; +import static seedu.internship.logic.parser.ParserUtil.parseDatesToString; +import static seedu.internship.logic.parser.ParserUtil.parseRolesToString; +import static seedu.internship.logic.parser.ParserUtil.parseStatusesToString; +import static seedu.internship.logic.parser.ParserUtil.parseTagsToString; + +import java.util.List; + +import seedu.internship.logic.commands.DeleteFieldCommand; +import seedu.internship.logic.parser.exceptions.ParseException; +import seedu.internship.model.internship.InternshipContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new DeleteCommand object + */ +public class DeleteFieldCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteFieldCommand + * and returns a DeleteFieldCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteFieldCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_COMPANY_NAME, PREFIX_ROLE, PREFIX_STATUS, PREFIX_DATE, + PREFIX_TAG); + + if (!argMultimap.getPreamble().isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteFieldCommand.MESSAGE_USAGE)); + } + + if (argMultimap.getValue(PREFIX_COMPANY_NAME).isEmpty() + && argMultimap.getValue(PREFIX_ROLE).isEmpty() + && argMultimap.getValue(PREFIX_STATUS).isEmpty() + && argMultimap.getValue(PREFIX_DATE).isEmpty() + && argMultimap.getValue(PREFIX_TAG).isEmpty()) { + throw new ParseException(String.format(MESSAGE_MISSING_ARGUMENTS, DeleteFieldCommand.MESSAGE_USAGE)); + } + + List nameList = parseCompanyNamesToString(argMultimap.getAllValues(PREFIX_COMPANY_NAME)); + List roleList = parseRolesToString(argMultimap.getAllValues(PREFIX_ROLE)); + List statusList = parseStatusesToString(argMultimap.getAllValues(PREFIX_STATUS)); + List dateList = parseDatesToString(argMultimap.getAllValues(PREFIX_DATE)); + List tagList = parseTagsToString(argMultimap.getAllValues(PREFIX_TAG)); + + InternshipContainsKeywordsPredicate newPredicate = new InternshipContainsKeywordsPredicate(nameList, + roleList, statusList, dateList, tagList); + + + return new DeleteFieldCommand(newPredicate); + } + + + +} diff --git a/src/main/java/seedu/internship/logic/parser/DeleteIndexCommandParser.java b/src/main/java/seedu/internship/logic/parser/DeleteIndexCommandParser.java new file mode 100644 index 00000000000..f49bf231196 --- /dev/null +++ b/src/main/java/seedu/internship/logic/parser/DeleteIndexCommandParser.java @@ -0,0 +1,39 @@ +package seedu.internship.logic.parser; + +import static seedu.internship.commons.core.Messages.MESSAGE_MISSING_ARGUMENTS; + +import java.util.List; + +import seedu.internship.commons.core.index.Index; +import seedu.internship.logic.commands.DeleteIndexCommand; +import seedu.internship.logic.parser.exceptions.ParseException; + + +/** + * Parses input arguments and creates a new DeleteCommand object + */ +public class DeleteIndexCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteIndexCommand + * and returns a DeleteIndexCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteIndexCommand parse(String args) throws ParseException { + if (args.trim().isEmpty()) { + throw new ParseException(String.format(MESSAGE_MISSING_ARGUMENTS, DeleteIndexCommand.MESSAGE_USAGE)); + } + + List indexes; + + try { + indexes = ParserUtil.parseIndexes(args); + assert indexes != null; + } catch (ParseException pe) { + ParseException e = ParserUtil.handleIndexException(pe, DeleteIndexCommand.MESSAGE_USAGE); + throw e; + } + + return new DeleteIndexCommand(indexes); + } +} diff --git a/src/main/java/seedu/internship/logic/parser/EditCommandParser.java b/src/main/java/seedu/internship/logic/parser/EditCommandParser.java new file mode 100644 index 00000000000..e5505989e9e --- /dev/null +++ b/src/main/java/seedu/internship/logic/parser/EditCommandParser.java @@ -0,0 +1,117 @@ +package seedu.internship.logic.parser; + + +import static java.util.Objects.requireNonNull; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_COMMENT; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_ROLE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_STATUS; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Collection; +import java.util.Collections; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.Set; +import java.util.logging.Logger; + +import seedu.internship.commons.core.LogsCenter; +import seedu.internship.commons.core.index.Index; +import seedu.internship.logic.commands.EditCommand; +import seedu.internship.logic.commands.EditCommand.EditInternshipDescriptor; +import seedu.internship.logic.parser.exceptions.ParseException; +import seedu.internship.model.tag.Tag; + +/** + * Parses input arguments and creates a new EditCommand object + */ +public class EditCommandParser implements Parser { + + + /** + * Parses the given {@code String} of arguments in the context of the EditCommand + * and returns an EditCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + + private static final Logger logger = LogsCenter.getLogger(EditCommandParser.class); + + /** + * Parses arguments to construct an {@code EditCommand} + * + * @param args Raw user input. + * @return an {@code EditCommand} encapsulating the user's input. + * @throws ParseException if the user's input cannot be parsed into a valid {@code EditCommand}. + */ + public EditCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_COMPANY_NAME, PREFIX_STATUS, PREFIX_ROLE, PREFIX_DATE, + PREFIX_COMMENT, PREFIX_TAG); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + ParseException e = ParserUtil.handleIndexException(pe, EditCommand.MESSAGE_USAGE); + throw e; + } + + EditInternshipDescriptor editInternshipDescriptor = new EditCommand.EditInternshipDescriptor(); + if (argMultimap.getValue(PREFIX_COMPANY_NAME).isPresent()) { + editInternshipDescriptor.setCompanyName(ParserUtil.parseCompanyName(argMultimap.getValue( + PREFIX_COMPANY_NAME).get())); + } + if (argMultimap.getValue(PREFIX_ROLE).isPresent()) { + editInternshipDescriptor.setRole(ParserUtil.parseRole(argMultimap.getValue(PREFIX_ROLE).get())); + } + if (argMultimap.getValue(PREFIX_STATUS).isPresent()) { + editInternshipDescriptor.setStatus(ParserUtil.parseStatus(argMultimap.getValue(PREFIX_STATUS).get())); + } + if (argMultimap.getValue(PREFIX_DATE).isPresent()) { + editInternshipDescriptor.setDate(ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get())); + } + if (argMultimap.getValue(PREFIX_COMMENT).isPresent()) { + String commentContent = ""; + try { + String userInput = argMultimap.getValue(PREFIX_COMMENT).get(); + if (userInput.equals("")) { + logger.info("User entered empty comment string, should reset comment to NA"); + commentContent = "NA"; + } else { + commentContent = userInput; + } + } catch (NoSuchElementException emptyComment) { + //Should not reach here + assert(false); + } + editInternshipDescriptor.setComment(ParserUtil.parseComment(commentContent)); + } + + parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editInternshipDescriptor::setTags); + + if (!editInternshipDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); + } + + return new EditCommand(index, editInternshipDescriptor); + } + + /** + * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty. + * If {@code tags} contain only one element which is an empty string, it will be parsed into a + * {@code Set} containing zero tags. + */ + private Optional> parseTagsForEdit(Collection tags) throws ParseException { + assert tags != null; + + if (tags.isEmpty()) { + return Optional.empty(); + } + Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; + return Optional.of(ParserUtil.parseTags(tagSet)); + } + +} diff --git a/src/main/java/seedu/internship/logic/parser/FindCommandParser.java b/src/main/java/seedu/internship/logic/parser/FindCommandParser.java new file mode 100644 index 00000000000..240b929acb8 --- /dev/null +++ b/src/main/java/seedu/internship/logic/parser/FindCommandParser.java @@ -0,0 +1,65 @@ +package seedu.internship.logic.parser; + +import static seedu.internship.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_ROLE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_STATUS; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.internship.logic.parser.ParserUtil.parseCompanyNamesToString; +import static seedu.internship.logic.parser.ParserUtil.parseDatesToString; +import static seedu.internship.logic.parser.ParserUtil.parseRolesToString; +import static seedu.internship.logic.parser.ParserUtil.parseStatusesToString; +import static seedu.internship.logic.parser.ParserUtil.parseTagsToString; + +import java.util.List; +import java.util.logging.Logger; + +import seedu.internship.commons.core.LogsCenter; +import seedu.internship.logic.commands.FindCommand; +import seedu.internship.logic.parser.exceptions.ParseException; +import seedu.internship.model.internship.InternshipContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new FindCommand object + */ +public class FindCommandParser implements Parser { + private static final Logger logger = LogsCenter.getLogger(FindCommandParser.class); + + + /** + * Parses the given {@code String} of arguments in the context of the FindCommand + * and returns a FindCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FindCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_COMPANY_NAME, PREFIX_ROLE, PREFIX_STATUS, PREFIX_DATE, + PREFIX_TAG); + + if (!argMultimap.getPreamble().isEmpty()) { + FindCommandParser.logger.info("User inputted find command with non-empty preamble."); + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + } + + if (!argMultimap.getValue(PREFIX_COMPANY_NAME).isPresent() + && !argMultimap.getValue(PREFIX_ROLE).isPresent() + && !argMultimap.getValue(PREFIX_STATUS).isPresent() + && !argMultimap.getValue(PREFIX_DATE).isPresent() + && !argMultimap.getValue(PREFIX_TAG).isPresent()) { + FindCommandParser.logger.info("User inputted find command with no fields"); + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + } + //@@author kohkaixun + List nameList = parseCompanyNamesToString(argMultimap.getAllValues(PREFIX_COMPANY_NAME)); + List roleList = parseRolesToString(argMultimap.getAllValues(PREFIX_ROLE)); + List statusList = parseStatusesToString(argMultimap.getAllValues(PREFIX_STATUS)); + List dateList = parseDatesToString(argMultimap.getAllValues(PREFIX_DATE)); + List tagList = parseTagsToString(argMultimap.getAllValues(PREFIX_TAG)); + //@@author + return new FindCommand(new InternshipContainsKeywordsPredicate(nameList, roleList, statusList, dateList, + tagList)); + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/internship/logic/parser/InternBuddyParser.java similarity index 53% rename from src/main/java/seedu/address/logic/parser/AddressBookParser.java rename to src/main/java/seedu/internship/logic/parser/InternBuddyParser.java index 1e466792b46..1063fb00f95 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/internship/logic/parser/InternBuddyParser.java @@ -1,26 +1,30 @@ -package seedu.address.logic.parser; +package seedu.internship.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.internship.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.internship.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; +import seedu.internship.logic.commands.AddCommand; +import seedu.internship.logic.commands.ClearCommand; +import seedu.internship.logic.commands.Command; +import seedu.internship.logic.commands.CopyCommand; +import seedu.internship.logic.commands.DeleteFieldCommand; +import seedu.internship.logic.commands.DeleteIndexCommand; +import seedu.internship.logic.commands.EditCommand; +import seedu.internship.logic.commands.ExitCommand; +import seedu.internship.logic.commands.FindCommand; +import seedu.internship.logic.commands.HelpCommand; +import seedu.internship.logic.commands.ListCommand; +import seedu.internship.logic.commands.UpcomingCommand; +import seedu.internship.logic.commands.ViewCommand; +import seedu.internship.logic.parser.exceptions.ParseException; /** * Parses user input. */ -public class AddressBookParser { +public class InternBuddyParser { /** * Used for initial separation of command word and args. @@ -50,8 +54,11 @@ public Command parseCommand(String userInput) throws ParseException { case EditCommand.COMMAND_WORD: return new EditCommandParser().parse(arguments); - case DeleteCommand.COMMAND_WORD: - return new DeleteCommandParser().parse(arguments); + case DeleteIndexCommand.COMMAND_WORD: + return new DeleteIndexCommandParser().parse(arguments); + + case DeleteFieldCommand.COMMAND_WORD: + return new DeleteFieldCommandParser().parse(arguments); case ClearCommand.COMMAND_WORD: return new ClearCommand(); @@ -68,6 +75,15 @@ public Command parseCommand(String userInput) throws ParseException { case HelpCommand.COMMAND_WORD: return new HelpCommand(); + case ViewCommand.COMMAND_WORD: + return new ViewCommandParser().parse(arguments); + + case CopyCommand.COMMAND_WORD: + return new CopyCommandParser().parse(arguments); + + case UpcomingCommand.COMMAND_WORD: + return new UpcomingCommand(); + default: throw new ParseException(MESSAGE_UNKNOWN_COMMAND); } diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/seedu/internship/logic/parser/Parser.java similarity index 71% rename from src/main/java/seedu/address/logic/parser/Parser.java rename to src/main/java/seedu/internship/logic/parser/Parser.java index d6551ad8e3f..244a87311cd 100644 --- a/src/main/java/seedu/address/logic/parser/Parser.java +++ b/src/main/java/seedu/internship/logic/parser/Parser.java @@ -1,7 +1,7 @@ -package seedu.address.logic.parser; +package seedu.internship.logic.parser; -import seedu.address.logic.commands.Command; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.internship.logic.commands.Command; +import seedu.internship.logic.parser.exceptions.ParseException; /** * Represents a Parser that is able to parse user input into a {@code Command} of type {@code T}. diff --git a/src/main/java/seedu/internship/logic/parser/ParserUtil.java b/src/main/java/seedu/internship/logic/parser/ParserUtil.java new file mode 100644 index 00000000000..93dd2a41894 --- /dev/null +++ b/src/main/java/seedu/internship/logic/parser/ParserUtil.java @@ -0,0 +1,324 @@ +package seedu.internship.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.internship.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.internship.commons.core.Messages.MESSAGE_INVALID_INTERNSHIP_DISPLAYED_INDEX; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import seedu.internship.commons.core.index.Index; +import seedu.internship.commons.util.StringUtil; +import seedu.internship.logic.parser.exceptions.ParseException; +import seedu.internship.model.internship.Comment; +import seedu.internship.model.internship.CompanyName; +import seedu.internship.model.internship.Date; +import seedu.internship.model.internship.Role; +import seedu.internship.model.internship.Status; +import seedu.internship.model.tag.Tag; + +/** + * Contains utility methods used for parsing strings in the various *Parser classes. + */ +public class ParserUtil { + + public static final String MESSAGE_INVALID_INDEX = "Index is smaller than 1."; + public static final String MESSAGE_INVALID_POSITIVE_SIGNED_INDEX = "Index has positive sign."; + public static final String MESSAGE_INVALID_INDEX_FORMAT = "Index is not an integer."; + + /** + * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be + * trimmed. + * @throws ParseException if the specified index is invalid (not non-zero unsigned integer). + */ + public static Index parseIndex(String oneBasedIndex) throws ParseException { + String trimmedIndex = oneBasedIndex.trim(); + try { + StringUtil.nonZeroUnsignedIntegerCheck(trimmedIndex); + } catch (ParseException pe) { + throw pe; + } + return Index.fromOneBased(Integer.parseInt(trimmedIndex)); + } + /** + * Parses {@code oneBasedIndexes} into a {@code List} and returns it. Leading and trailing whitespaces will + * be trimmed. + * @throws ParseException if any specified index is invalid (not non-zero unsigned integer). + */ + public static List parseIndexes(String oneBasedIndexes) throws ParseException { + List tokens = Arrays.asList(oneBasedIndexes.split("\\s+")) + .stream() + .filter(s -> !s.isEmpty()) + .collect(Collectors.toList()); + List result = new ArrayList<>(); + for (String index: tokens) { + result.add(parseIndex(index)); + } + + return result; + } + + /** + * Checks index exception error messages and returns corresponding ParseException + * @param pe ParseException from parseIndex or parseIndexes method + * @param commandFormatMessage Command format message to be contained in ParseException if to be used + * @return New ParseException with the corresponding error message according to message in ParseException given + */ + public static ParseException handleIndexException(ParseException pe, String commandFormatMessage) { + if (pe.getMessage().equals(ParserUtil.MESSAGE_INVALID_INDEX)) { + return new ParseException(MESSAGE_INVALID_INTERNSHIP_DISPLAYED_INDEX); + } + return new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, commandFormatMessage), pe); + } + + /** + * Parses a {@code String companyName} into a {@code CompanyName}. + * Leading and trailing whitespaces will be trimmed. + * + * @param companyName The raw company name string. + * @return a {@CompanyName} object encapsulating the company name. + * @throws ParseException if the given {@code companyName} is invalid. + */ + 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 CompanyName(trimmedName); + } + + /** + * Parses a {@code String role} into a {@code Role}. + * Leading and trailing whitespaces will be trimmed. + * + * @param role The raw role string to be parsed. + * @return the {@code Role} object encapsulating the role. + * @throws ParseException if the given {@code role} is invalid. + */ + public static Role parseRole(String role) throws ParseException { + requireNonNull(role); + String trimmedRole = role.trim(); + if (!Role.isValidRole(trimmedRole)) { + throw new ParseException(Role.MESSAGE_CONSTRAINTS); + } + return new Role(trimmedRole); + } + + /** + * Parses a {@code String status} into a {@code Status}. + * Leading and trailing whitespaces will be trimmed. + * + * @param status The raw status string to be parsed. + * @return the {@code Status} object encapsulating the status. + * @throws ParseException if the given {@code status} is invalid. + */ + public static Status parseStatus(String status) throws ParseException { + requireNonNull(status); + String trimmedStatus = status.trim(); + if (!Status.isValidStatus(trimmedStatus)) { + throw new ParseException(Status.MESSAGE_CONSTRAINTS); + } + return new Status(trimmedStatus); + } + + /** + * Parses a {@code String date} into an {@code Date}. + * Leading and trailing whitespaces will be trimmed. + * + * @param date The raw date string to be parsed. + * @return the {@code Date} object encapsulating the date. + * @throws ParseException if the given {@code date} is invalid. + */ + public static Date parseDate(String date) throws ParseException { + requireNonNull(date); + String trimmedDate = date.trim(); + if (!Date.isValidDate(trimmedDate)) { + throw new ParseException(Date.MESSAGE_CONSTRAINTS); + } + return new Date(trimmedDate); + } + + /** + * Parses a {@code String comment} into an {@code Comment}. + * Leading and trailing whitespaces will be trimmed. + * + * @param commentContent The raw comment to be parsed. + * @return a {@code Comment} object encapsulating the comment content. + * @throws ParseException if the given {@code comment} is invalid. + */ + public static Comment parseComment(String commentContent) throws ParseException { + String trimmedComment = commentContent.trim(); + if (!Comment.isValidComment(trimmedComment)) { + throw new ParseException(Comment.MESSAGE_CONSTRAINTS); + } + return new Comment(trimmedComment); + } + + + /** + * Parses a {@code String tag} into a {@code Tag}. + * Leading and trailing whitespaces will be trimmed. + * + * @param tag The raw tag string to be parsed. + * @return the {@code Tag} encapsulating the tag string. + * @throws ParseException if the given {@code tag} is invalid. + */ + public static Tag parseTag(String tag) throws ParseException { + requireNonNull(tag); + String trimmedTag = tag.trim(); + if (!Tag.isValidTagName(trimmedTag)) { + throw new ParseException(Tag.MESSAGE_CONSTRAINTS); + } + return new Tag(trimmedTag); + } + + /** + * Parses {@code Collection tags} into a {@code Set}. + * + * @param companyNames The collection of company name strings to be parsed. + * @return a set of {@code CompanyName} objects encapsulating the company name strings. + */ + public static Set parseCompanyNames(Collection companyNames) throws ParseException { + requireNonNull(companyNames); + final Set companyNameSet = new HashSet<>(); + for (String tagName : companyNames) { + companyNameSet.add(parseCompanyName(tagName)); + } + return companyNameSet; + } + + /** + * Parses {@code Collection tags} into a {@code Set}. + * + * @param roles The collection of role strings to be parsed. + * @return a set of {@code Role} objects encapsulating the role strings. + */ + public static Set parseRoles(Collection roles) throws ParseException { + requireNonNull(roles); + final Set roleSet = new HashSet<>(); + for (String role : roles) { + roleSet.add(parseRole(role)); + } + return roleSet; + } + + /** + * Parses {@code Collection tags} into a {@code Set}. + * + * @param statuses The collection of status strings to be parsed. + * @return a set of {@code Tag} objects encapsulating the status strings. + */ + public static Set parseStatuses(Collection statuses) throws ParseException { + requireNonNull(statuses); + final Set statusSet = new HashSet<>(); + for (String status : statuses) { + statusSet.add(parseStatus(status)); + } + return statusSet; + } + + /** + * Parses {@code Collection tags} into a {@code Set}. + * + * @param dates The collection of dates strings to be parsed. + * @return a set of {@code Date} objects encapsulating the date strings. + */ + public static Set parseDates(Collection dates) throws ParseException { + requireNonNull(dates); + final Set dateSet = new HashSet<>(); + for (String date : dates) { + dateSet.add(parseDate(date)); + } + return dateSet; + } + + /** + * Parses {@code Collection tags} into a {@code Set}. + * + * @param tags The collection of tag strings to be parsed. + * @return a set of {@code Tag} objects encapsulating the tag strings. + */ + public static Set parseTags(Collection tags) throws ParseException { + requireNonNull(tags); + final Set tagSet = new HashSet<>(); + for (String tagName : tags) { + tagSet.add(parseTag(tagName)); + } + return tagSet; + } + + //@@author kohkaixun + + /** + * Parses {@code List unparsedNames} into a {@code List} of parsed company names. + * @param unparsedNames The list of company name strings to be parsed. + * @return The list of company name strings that has the correct format. + * @throws ParseException if any of the given company names in the list cannot be parsed. + */ + public static List parseCompanyNamesToString(List unparsedNames) throws ParseException { + List parsedNames = ParserUtil.parseCompanyNames(unparsedNames).stream() + .map(name -> name.fullCompanyName) + .collect(Collectors.toList()); + return parsedNames; + } + + /** + * Parses {@code List unparsedRoles} into a {@code List} of parsed role names. + * @param unparsedRoles The list of role strings to be parsed. + * @return The list of role strings that has the correct format. + * @throws ParseException if any of the given roles in the list cannot be parsed. + */ + public static List parseRolesToString(List unparsedRoles) throws ParseException { + List parsedRoles = ParserUtil.parseRoles(unparsedRoles).stream() + .map(role -> role.fullRole) + .collect(Collectors.toList()); + return parsedRoles; + } + + /** + * Parses {@code List unparsedStatuses} into a {@code List} of parsed statuses. + * @param unparsedStatuses The list of status strings to be parsed. + * @return The list of status strings that has the correct format. + * @throws ParseException if any of the given statuses in the list cannot be parsed. + */ + public static List parseStatusesToString(List unparsedStatuses) throws ParseException { + List parsedStatuses = ParserUtil.parseStatuses(unparsedStatuses).stream() + .map(status -> status.fullStatus) + .collect(Collectors.toList()); + return parsedStatuses; + } + + /** + * Parses {@code List unparsedDates} into a {@code List} of parsed dates. + * @param unparsedDates The list of date strings to be parsed. + * @return The list of date strings that has the correct format. + * @throws ParseException if any of the given dates in the list cannot be parsed. + */ + public static List parseDatesToString(List unparsedDates) throws ParseException { + List parsedDates = ParserUtil.parseDates(unparsedDates).stream() + .map(date -> date.fullDate) + .collect(Collectors.toList()); + return parsedDates; + } + + /** + * Parses {@code List unparsedTags} into a {@code List} of parsed tags. + * @param unparsedTags The list of tag strings to be parsed. + * @return The list of tag strings that has the correct format. + * @throws ParseException if any of the given tags in the list cannot be parsed. + */ + public static List parseTagsToString(List unparsedTags) throws ParseException { + List parsedTags = ParserUtil.parseTags(unparsedTags).stream() + .map(tag -> tag.tagName) + .collect(Collectors.toList()); + return parsedTags; + } + //@@author +} diff --git a/src/main/java/seedu/address/logic/parser/Prefix.java b/src/main/java/seedu/internship/logic/parser/Prefix.java similarity index 95% rename from src/main/java/seedu/address/logic/parser/Prefix.java rename to src/main/java/seedu/internship/logic/parser/Prefix.java index c859d5fa5db..3e88e7fd39d 100644 --- a/src/main/java/seedu/address/logic/parser/Prefix.java +++ b/src/main/java/seedu/internship/logic/parser/Prefix.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.internship.logic.parser; /** * A prefix that marks the beginning of an argument in an arguments string. diff --git a/src/main/java/seedu/internship/logic/parser/ViewCommandParser.java b/src/main/java/seedu/internship/logic/parser/ViewCommandParser.java new file mode 100644 index 00000000000..13bab4c1d5e --- /dev/null +++ b/src/main/java/seedu/internship/logic/parser/ViewCommandParser.java @@ -0,0 +1,30 @@ +package seedu.internship.logic.parser; + +import seedu.internship.commons.core.index.Index; +import seedu.internship.logic.commands.ViewCommand; +import seedu.internship.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new ViewCommand object + */ +public class ViewCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ViewCommand + * and returns a ViewCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ViewCommand parse(String args) throws ParseException { + Index index; + + try { + index = ParserUtil.parseIndex(args); + } catch (ParseException pe) { + ParseException e = ParserUtil.handleIndexException(pe, ViewCommand.MESSAGE_USAGE); + throw e; + } + + return new ViewCommand(index); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java b/src/main/java/seedu/internship/logic/parser/exceptions/ParseException.java similarity index 72% rename from src/main/java/seedu/address/logic/parser/exceptions/ParseException.java rename to src/main/java/seedu/internship/logic/parser/exceptions/ParseException.java index 158a1a54c1c..b72f0f2b459 100644 --- a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java +++ b/src/main/java/seedu/internship/logic/parser/exceptions/ParseException.java @@ -1,6 +1,6 @@ -package seedu.address.logic.parser.exceptions; +package seedu.internship.logic.parser.exceptions; -import seedu.address.commons.exceptions.IllegalValueException; +import seedu.internship.commons.exceptions.IllegalValueException; /** * Represents a parse error encountered by a parser. diff --git a/src/main/java/seedu/internship/model/InternBuddy.java b/src/main/java/seedu/internship/model/InternBuddy.java new file mode 100644 index 00000000000..120e156e0a9 --- /dev/null +++ b/src/main/java/seedu/internship/model/InternBuddy.java @@ -0,0 +1,151 @@ +package seedu.internship.model; + +import static java.util.Objects.requireNonNull; + +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; +import java.util.List; +import javax.swing.SwingUtilities; + +import javafx.collections.ObservableList; +import seedu.internship.model.internship.Internship; +import seedu.internship.model.internship.UniqueInternshipList; + +/** + * Wraps all data at the InternBuddy level + * Duplicates are not allowed (by .isSameInternship comparison) + */ +public class InternBuddy implements ReadOnlyInternBuddy { + + private final UniqueInternshipList internships; + + /* + * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication + * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html + * + * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication + * among constructors. + */ + { + internships = new UniqueInternshipList(); + } + + public InternBuddy() {} + + /** + * Creates an InternBuddy using the Internships in the {@code toBeCopied} + */ + public InternBuddy(ReadOnlyInternBuddy toBeCopied) { + this(); + resetData(toBeCopied); + } + + //// list overwrite operations + + /** + * Replaces the contents of the Internship list with {@code internships}. + * {@code internships} must not contain duplicate internships. + */ + public void setInternships(List internships) { + this.internships.setInternships(internships); + } + + /** + * Resets the existing data of this {@code InternBuddy} with {@code newData}. + */ + public void resetData(ReadOnlyInternBuddy newData) { + requireNonNull(newData); + + setInternships(newData.getInternshipList()); + } + + //// internship-level operations + + /** + * Returns true if an internship with the same identity as {@code internship} exists in InternBuddy. + */ + public boolean hasInternship(Internship internship) { + requireNonNull(internship); + return internships.contains(internship); + } + + /** + * Adds an internship to InternBuddy + * The internship must not already exist in InternBuddy. + */ + public void addInternship(Internship p) { + internships.add(p); + } + + /** + * Replaces the given internship {@code target} in the list with {@code editedInternship}. + * {@code target} must exist in the intern buddy. + * The internship identity of {@code editedInternship} must not be the same as another existing internship + * in InternBuddy. + */ + public void setInternship(Internship target, Internship editedInternship) { + requireNonNull(editedInternship); + + internships.setInternship(target, editedInternship); + } + + /** + * Removes {@code key} from this {@code InternBuddy}. + * {@code key} must exist in InternBuddy. + */ + public void removeInternship(Internship key) { + internships.remove(key); + } + + /** + * Gets {@code key} from this {@code InternBuddy}. + * {@code key} must exist in InternBuddy. + * + */ + public void viewInternship(Internship key) { + internships.view(key); + } + + + /** + * Copies the content of the specific internship to the clipboard + * Gets {@code key} from this {@code InternBuddy}. + * {@code key} must exist in InternBuddy. + */ + public void copyInternship(Internship key) { + String content = "Company Name: " + key.toString(); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + StringSelection selection = new StringSelection(content); + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(selection, null); + } + }); + } + + //// util methods + + @Override + public String toString() { + return internships.asUnmodifiableObservableList().size() + " internships"; + // TODO: refine later + } + + @Override + public ObservableList getInternshipList() { + return internships.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof InternBuddy // instanceof handles nulls + && internships.equals(((InternBuddy) other).internships)); + } + + @Override + public int hashCode() { + return internships.hashCode(); + } +} diff --git a/src/main/java/seedu/internship/model/Model.java b/src/main/java/seedu/internship/model/Model.java new file mode 100644 index 00000000000..28d1d08a8c1 --- /dev/null +++ b/src/main/java/seedu/internship/model/Model.java @@ -0,0 +1,136 @@ +package seedu.internship.model; + +import java.nio.file.Path; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; + +import javafx.collections.ObservableList; +import seedu.internship.commons.core.GuiSettings; +import seedu.internship.model.internship.Internship; + +/** + * The API of the Model component. + */ +public interface Model { + /** {@code Predicate} that always evaluate to true */ + Predicate PREDICATE_SHOW_ALL_INTERNSHIPS = unused -> true; + + /** {@code Predicate} that shows upcoming internships */ + Predicate PREDICATE_SHOW_UPCOMING_INTERNSHIPS = internship -> { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + LocalDate now = LocalDate.now(); + LocalDate nextWeek = now.plusWeeks(1); + LocalDate deadline = LocalDate.parse(internship.getDate().toString(), formatter); + String status = internship.getStatus().toString(); + List acceptedStatuses = new ArrayList<>(Arrays.asList( + "new", "offered", "assessment", "interview")); + return !deadline.isBefore(now) && deadline.isBefore(nextWeek) && acceptedStatuses.contains(status); + }; + + /** + * Replaces user prefs data with the data in {@code userPrefs}. + */ + void setUserPrefs(ReadOnlyUserPrefs userPrefs); + + /** + * Returns the user prefs. + */ + ReadOnlyUserPrefs getUserPrefs(); + + /** + * Returns the user prefs' GUI settings. + */ + GuiSettings getGuiSettings(); + + /** + * Sets the user prefs' GUI settings. + */ + void setGuiSettings(GuiSettings guiSettings); + + /** + * Returns the user prefs' InternBuddy file path. + */ + Path getInternBuddyFilePath(); + + /** + * Sets the user prefs' InternBuddy book file path. + */ + void setInternBuddyFilePath(Path InternBuddyFilePath); + + /** + * Replaces InternBuddy data with the data in {@code internbuddy}. + */ + void setInternBuddy(ReadOnlyInternBuddy internBuddy); + + /** Returns the InternBuddy */ + ReadOnlyInternBuddy getInternBuddy(); + + /** + * Returns true if an internship with the same identity as {@code Internship} exists in InternBuddy. + */ + boolean hasInternship(Internship internship); + + /** + * Deletes the given internship. + * The internship must exist in InternBuddy. + */ + void deleteInternship(Internship target); + + /** + * Views the given internship. + * The {@code internship} must exist in InternBuddy. + */ + void viewInternship(Internship target); + + /** + * Adds the given internship. + * {@code internship} must not already exist in InternBuddy. + */ + void addInternship(Internship internship); + + /** + * Replaces the given internship {@code target} with {@code editedInternship}. + * {@code target} must exist in InternBuddy. + * The internship identity of {@code editedInternship} must not be the same as another existing internship in + * InternBuddy. + * + * @param target The given internship to be replaced. + * @param editedInternship The internship to replace the original one. + */ + void setInternship(Internship target, Internship editedInternship); + + /** Returns an unmodifiable view of the filtered internship list */ + ObservableList getFilteredInternshipList(); + + /** + * 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); + + /** + * Gets the internship that is currently being selected for viewing + * + * @return the currently selected internship + */ + Internship getSelectedInternship(); + + + /** + * Updates the internship that is currently being selected for viewing + * + * @param target The internship that is selected for viewing + */ + void updateSelectedInternship(Internship target); + + /** + * Copies the content of the internship onto the clipboard of the computer + * {@code internship} must not already exist in InternBuddy. + */ + + void copyInternship(Internship target); +} diff --git a/src/main/java/seedu/internship/model/ModelManager.java b/src/main/java/seedu/internship/model/ModelManager.java new file mode 100644 index 00000000000..d5cb29a317e --- /dev/null +++ b/src/main/java/seedu/internship/model/ModelManager.java @@ -0,0 +1,175 @@ +package seedu.internship.model; + +import static java.util.Objects.requireNonNull; +import static seedu.internship.commons.util.CollectionUtil.requireAllNonNull; + +import java.nio.file.Path; +import java.util.function.Predicate; +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import seedu.internship.commons.core.GuiSettings; +import seedu.internship.commons.core.LogsCenter; +import seedu.internship.model.internship.Internship; + +/** + * Represents the in-memory model of InternBuddy data. + */ +public class ModelManager implements Model { + private static final Logger logger = LogsCenter.getLogger(ModelManager.class); + + private final InternBuddy internBuddy; + private final UserPrefs userPrefs; + private final FilteredList filteredInternships; + private Internship selectedInternship; + + /** + * Initializes a ModelManager with the given internBuddy and userPrefs. + */ + public ModelManager(ReadOnlyInternBuddy internBuddy, ReadOnlyUserPrefs userPrefs) { + requireAllNonNull(internBuddy, userPrefs); + + logger.fine("Initializing with InternBuddy: " + internBuddy + " and user prefs " + userPrefs); + + this.internBuddy = new InternBuddy(internBuddy); + this.userPrefs = new UserPrefs(userPrefs); + filteredInternships = new FilteredList<>(this.internBuddy.getInternshipList()); + selectedInternship = null; + } + + public ModelManager() { + this(new InternBuddy(), new UserPrefs()); + } + + //=========== UserPrefs ================================================================================== + + @Override + public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { + requireNonNull(userPrefs); + this.userPrefs.resetData(userPrefs); + } + + @Override + public ReadOnlyUserPrefs getUserPrefs() { + return userPrefs; + } + + @Override + public GuiSettings getGuiSettings() { + return userPrefs.getGuiSettings(); + } + + @Override + public void setGuiSettings(GuiSettings guiSettings) { + requireNonNull(guiSettings); + userPrefs.setGuiSettings(guiSettings); + } + + @Override + public Path getInternBuddyFilePath() { + return userPrefs.getInternBuddyFilePath(); + } + + @Override + public void setInternBuddyFilePath(Path internBuddyFilePath) { + requireNonNull(internBuddyFilePath); + userPrefs.setInternBuddyFilePath(internBuddyFilePath); + } + + //=========== InternBuddy ================================================================================ + + @Override + public void setInternBuddy(ReadOnlyInternBuddy internBuddy) { + this.internBuddy.resetData(internBuddy); + } + + @Override + public ReadOnlyInternBuddy getInternBuddy() { + return internBuddy; + } + + @Override + public boolean hasInternship(Internship internship) { + requireNonNull(internship); + return internBuddy.hasInternship(internship); + } + + @Override + public void deleteInternship(Internship target) { + internBuddy.removeInternship(target); + } + + @Override + public void viewInternship(Internship target) { + internBuddy.viewInternship(target); + } + + @Override + public void addInternship(Internship internship) { + internBuddy.addInternship(internship); + updateFilteredInternshipList(PREDICATE_SHOW_ALL_INTERNSHIPS); + } + + @Override + public void setInternship(Internship target, Internship editedInternship) { + requireAllNonNull(target, editedInternship); + + internBuddy.setInternship(target, editedInternship); + } + + //=========== Filtered Internship List Accessors ============================================================= + + /** + * Returns an unmodifiable view of the list of {@code Internship} backed by the internal list of + * {@code versionedInternBuddy} + */ + @Override + public ObservableList getFilteredInternshipList() { + return filteredInternships; + } + + @Override + public void updateFilteredInternshipList(Predicate predicate) { + requireNonNull(predicate); + filteredInternships.setPredicate(predicate); + } + + @Override + public boolean equals(Object obj) { + // short circuit if same object + if (obj == this) { + return true; + } + + // instanceof handles nulls + if (!(obj instanceof ModelManager)) { + return false; + } + + + // state check + ModelManager other = (ModelManager) obj; + return internBuddy.equals(other.internBuddy) + && userPrefs.equals(other.userPrefs) + && filteredInternships.equals(other.filteredInternships); + } + + //=========== Selected internship ============================================================= + + @Override + public Internship getSelectedInternship() { + return this.selectedInternship; + } + + @Override + public void updateSelectedInternship(Internship target) { + this.selectedInternship = target; + } + + @Override + public void copyInternship(Internship target) { + internBuddy.copyInternship(target); + } + +} diff --git a/src/main/java/seedu/internship/model/ReadOnlyInternBuddy.java b/src/main/java/seedu/internship/model/ReadOnlyInternBuddy.java new file mode 100644 index 00000000000..092e5a43a1a --- /dev/null +++ b/src/main/java/seedu/internship/model/ReadOnlyInternBuddy.java @@ -0,0 +1,17 @@ +package seedu.internship.model; + +import javafx.collections.ObservableList; +import seedu.internship.model.internship.Internship; + +/** + * Unmodifiable view of an InternBuddy + */ +public interface ReadOnlyInternBuddy { + + /** + * Returns an unmodifiable view of the internships list. + * This list will not contain any duplicate internships + */ + ObservableList getInternshipList(); + +} diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/seedu/internship/model/ReadOnlyUserPrefs.java similarity index 56% rename from src/main/java/seedu/address/model/ReadOnlyUserPrefs.java rename to src/main/java/seedu/internship/model/ReadOnlyUserPrefs.java index befd58a4c73..02e61b9f961 100644 --- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java +++ b/src/main/java/seedu/internship/model/ReadOnlyUserPrefs.java @@ -1,8 +1,8 @@ -package seedu.address.model; +package seedu.internship.model; import java.nio.file.Path; -import seedu.address.commons.core.GuiSettings; +import seedu.internship.commons.core.GuiSettings; /** * Unmodifiable view of user prefs. @@ -11,6 +11,6 @@ public interface ReadOnlyUserPrefs { GuiSettings getGuiSettings(); - Path getAddressBookFilePath(); + Path getInternBuddyFilePath(); } diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/internship/model/UserPrefs.java similarity index 71% rename from src/main/java/seedu/address/model/UserPrefs.java rename to src/main/java/seedu/internship/model/UserPrefs.java index 25a5fd6eab9..cdaa9426c5b 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/internship/model/UserPrefs.java @@ -1,4 +1,4 @@ -package seedu.address.model; +package seedu.internship.model; import static java.util.Objects.requireNonNull; @@ -6,7 +6,7 @@ import java.nio.file.Paths; import java.util.Objects; -import seedu.address.commons.core.GuiSettings; +import seedu.internship.commons.core.GuiSettings; /** * Represents User's preferences. @@ -14,7 +14,7 @@ public class UserPrefs implements ReadOnlyUserPrefs { private GuiSettings guiSettings = new GuiSettings(); - private Path addressBookFilePath = Paths.get("data" , "addressbook.json"); + private Path internBuddyFilePath = Paths.get("data" , "internbuddy.json"); /** * Creates a {@code UserPrefs} with default values. @@ -35,7 +35,7 @@ public UserPrefs(ReadOnlyUserPrefs userPrefs) { public void resetData(ReadOnlyUserPrefs newUserPrefs) { requireNonNull(newUserPrefs); setGuiSettings(newUserPrefs.getGuiSettings()); - setAddressBookFilePath(newUserPrefs.getAddressBookFilePath()); + setInternBuddyFilePath(newUserPrefs.getInternBuddyFilePath()); } public GuiSettings getGuiSettings() { @@ -46,14 +46,13 @@ public void setGuiSettings(GuiSettings guiSettings) { requireNonNull(guiSettings); this.guiSettings = guiSettings; } - - public Path getAddressBookFilePath() { - return addressBookFilePath; + public Path getInternBuddyFilePath() { + return internBuddyFilePath; } - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - this.addressBookFilePath = addressBookFilePath; + public void setInternBuddyFilePath(Path internBuddyFilePath) { + requireNonNull(internBuddyFilePath); + this.internBuddyFilePath = internBuddyFilePath; } @Override @@ -68,19 +67,19 @@ public boolean equals(Object other) { UserPrefs o = (UserPrefs) other; return guiSettings.equals(o.guiSettings) - && addressBookFilePath.equals(o.addressBookFilePath); + && internBuddyFilePath.equals(o.internBuddyFilePath); } @Override public int hashCode() { - return Objects.hash(guiSettings, addressBookFilePath); + return Objects.hash(guiSettings, internBuddyFilePath); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Gui Settings : " + guiSettings); - sb.append("\nLocal data file location : " + addressBookFilePath); + sb.append("\nLocal data file location : " + internBuddyFilePath); return sb.toString(); } diff --git a/src/main/java/seedu/internship/model/internship/Comment.java b/src/main/java/seedu/internship/model/internship/Comment.java new file mode 100644 index 00000000000..fbb30299137 --- /dev/null +++ b/src/main/java/seedu/internship/model/internship/Comment.java @@ -0,0 +1,59 @@ +package seedu.internship.model.internship; + +import static java.util.Objects.requireNonNull; +import static seedu.internship.commons.util.AppUtil.checkArgument; + +/** + * Represents a comment in InternBuddy. + * Guarantees: immutable; name is valid as declared in {@link #isValidComment(String)} + */ +public class Comment { + + public static final String MESSAGE_CONSTRAINTS = "Comments should not blank."; + public static final String VALIDATION_REGEX = ".*"; + + public final String commentContent; + + /** + * Constructs a {@code Comment} + * + * @param commentContent The string content of the comment + */ + public Comment(String commentContent) { + requireNonNull(commentContent); + checkArgument(isValidComment(commentContent), MESSAGE_CONSTRAINTS); + this.commentContent = commentContent; + } + + /** + * Returns true if a given string is valid content for a comment + * + * @param test The string to be tested for comment validity. + */ + public static boolean isValidComment(String test) { + if (test.isEmpty() || !test.matches(VALIDATION_REGEX)) { + return false; + } + return true; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Comment // instanceof handles nulls + && commentContent.equals(((Comment) other).commentContent)); // state check + } + + @Override + public int hashCode() { + return commentContent.hashCode(); + } + + /** + * Format state as text for viewing. + */ + public String toString() { + return '[' + commentContent + ']'; + } + +} diff --git a/src/main/java/seedu/internship/model/internship/CompanyName.java b/src/main/java/seedu/internship/model/internship/CompanyName.java new file mode 100644 index 00000000000..deddf199aa3 --- /dev/null +++ b/src/main/java/seedu/internship/model/internship/CompanyName.java @@ -0,0 +1,79 @@ +package seedu.internship.model.internship; + +import static java.util.Objects.requireNonNull; +import static seedu.internship.commons.util.AppUtil.checkArgument; + +/** + * Represents an Internship's company name in InternBuddy. + * Guarantees: immutable; is valid as declared in {@link #isValidCompanyName(String)} + */ +public class CompanyName { + + public static final String MESSAGE_CONSTRAINTS = + "Company names should not be blank, and should be at most 50 characters"; + + /* + * The first character of the company name must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "^.{1,50}$"; + + public final String fullCompanyName; + + /** + * Constructs a {@code CompanyName}. + * + * @param companyName A valid company name. + */ + public CompanyName(String companyName) { + requireNonNull(companyName); + checkArgument(isValidCompanyName(companyName), MESSAGE_CONSTRAINTS); + fullCompanyName = companyName; + } + + /** + * Returns true if a given string is a valid name. + * + * @param test The regex to test for a valid name. + * @return true if the given input String is a valid company name + */ + public static boolean isValidCompanyName(String test) { + return test.matches(VALIDATION_REGEX); + } + + + /** + * Returns the String representation of the company name. + * + * @return a string corresponding to the company name. + */ + @Override + public String toString() { + return fullCompanyName; + } + + /** + * Determines if an object is equal to the current Internship object. + * + * @param other The other object to be compared with. + * @return true if the object is equal to the current Internship entry, and false otherwise. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof CompanyName // instanceof handles nulls + && fullCompanyName.toLowerCase().equals(( + (CompanyName) other).fullCompanyName.toLowerCase())); // state check + } + + /** + * Generates the hashcode for the company's name. + * + * @return the hash code representing the company name. + */ + @Override + public int hashCode() { + return fullCompanyName.hashCode(); + } + +} diff --git a/src/main/java/seedu/internship/model/internship/Date.java b/src/main/java/seedu/internship/model/internship/Date.java new file mode 100644 index 00000000000..b445c698cd1 --- /dev/null +++ b/src/main/java/seedu/internship/model/internship/Date.java @@ -0,0 +1,84 @@ +package seedu.internship.model.internship; + +import static java.util.Objects.requireNonNull; +import static seedu.internship.commons.util.AppUtil.checkArgument; + +import java.text.SimpleDateFormat; + +/** + * Represents an Internship's date in InternBuddy. The date is associated with the status of the Internship. + * For example, if the Internship's status is ASSESSMENT, then the date will be inferred as the date of assessment. + * Guarantees: immutable; is valid as declared in {@link #isValidDate(String)} + */ +public class Date { + public static final String VALIDATION_REGEX = "^\\d{4}-\\d{2}-\\d{2}$"; + + public static final String MESSAGE_CONSTRAINTS = "Date should be of the format YYYY-MM-DD, " + + "and it should be a valid date"; + + public final String fullDate; + + /** + * Constructs an {@code Date}. + * + * @param date A valid date; + */ + public Date(String date) { + requireNonNull(date); + checkArgument(isValidDate(date), MESSAGE_CONSTRAINTS); + fullDate = date; + } + + /** + * Returns if a given string is a valid date. + * + * @returns true if a given string is a valid date string, else returns false. + */ + public static boolean isValidDate(String test) { + if (!test.matches(VALIDATION_REGEX)) { + return false; + } + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + df.setLenient(false); + try { + df.parse(test); + return true; + } catch (Exception ex) { + return false; + } + } + + /** + * Returns a String representation for the Date. + * + * @returns a string representing the date. + */ + @Override + public String toString() { + return fullDate; + } + + /** + * Determines if another object is equal to the current {@code Date} object. + * + * @param other The other object to compare with. + * @return true if the other object is a {@code Date} object with the same date string. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Date // instanceof handles nulls + && fullDate.equals(((Date) other).fullDate)); // state check + } + + /** + * Gets the hashcode for the date represented by this {@code Date} object. + * + * @return the hashcode for this {@code Date} object. + */ + @Override + public int hashCode() { + return fullDate.hashCode(); + } + +} diff --git a/src/main/java/seedu/internship/model/internship/Internship.java b/src/main/java/seedu/internship/model/internship/Internship.java new file mode 100644 index 00000000000..1503f10a49a --- /dev/null +++ b/src/main/java/seedu/internship/model/internship/Internship.java @@ -0,0 +1,181 @@ +package seedu.internship.model.internship; + +import static seedu.internship.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import seedu.internship.model.tag.Tag; + +/** + * Represents an Internship in InternBuddy. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Internship { + + // Identity fields + private final CompanyName companyName; + private final Role role; + + // Data fields + private final Status status; + + private final Date date; + + private final Comment comment; + private final Set tags = new HashSet<>(); + + /** + * Constructs an Internship instance where every field must be present and not null. + * + * @param companyName The name of the company associated with the Internship. + * @param role The role or job position associated with the Internship. + * @param status The status of the Internship entry. + * @param date The date of the Internship entry. + * @param comment The comment of the Internship entry. + * @param tags The set of tags associated with the Internship. + */ + public Internship(CompanyName companyName, Role role, Status status, Date date, Comment comment, Set tags) { + requireAllNonNull(companyName, role, status, date, tags); + this.companyName = companyName; + this.role = role; + this.status = status; + this.date = date; + this.comment = comment; + this.tags.addAll(tags); + } + + /** + * Gets the company name for the Internship entry. + * + * @return the company name associated with the Internship entry. + */ + public CompanyName getCompanyName() { + return companyName; + } + + /** + * Gets the role for the Internship entry. + * + * @return the role associated with the Internship entry. + */ + public Role getRole() { + return role; + } + + /** + * Gets the status for the Internship entry. + * + * @return the status associated with the Internship entry. + */ + public Status getStatus() { + return status; + } + + /** + * Gets the date for the Internship entry. + * + * @return the date associated with the Internship entry. + */ + public Date getDate() { + return date; + } + + /** + * Gets the comment for the Internship entry. + * + * @return the comment associated with the Internship entry. + */ + public Comment getComment() { + return comment; + } + + /** + * Returns an immutable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + * + * @return the set of tags associated with this Internship. + */ + public Set getTags() { + return Collections.unmodifiableSet(tags); + } + + /** + * Returns true if both Internships have the same company name. + * This defines a weaker notion of equality between two Internships + * + * @param otherInternship The Internship to compare with. + * @return true if both Internships have the same company name. + */ + public boolean isSameInternship(Internship otherInternship) { + if (otherInternship == this) { + return true; + } + + return otherInternship != null + && otherInternship.getCompanyName().equals(getCompanyName()) + && otherInternship.getStatus().equals(getStatus()) + && otherInternship.getDate().equals(getDate()) + && otherInternship.getRole().equals(getRole()); + } + + /** + * Returns true if both internships have the same identity and data fields. + * This defines a stronger notion of equality between two Internships. + * + * @param other The other Internship to compare with. + * @return true if both internships have the same identity and data fields. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Internship)) { + return false; + } + + Internship otherInternship = (Internship) other; + + return otherInternship.getCompanyName().equals(getCompanyName()) + && otherInternship.getRole().equals(getRole()) + && otherInternship.getStatus().equals(getStatus()) + && otherInternship.getDate().equals(getDate()) + && otherInternship.getComment().equals(getComment()) + && otherInternship.getTags().equals(getTags()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(companyName, role, status, date, comment, tags); + } + + @Override + public String toString() { + String status = getStatus().toString(); + final StringBuilder builder = new StringBuilder(); + builder.append(getCompanyName()) + .append("; Role: ") + .append(getRole()) + .append("; Status: ") + .append(status.substring(0, 1).toUpperCase() + status.substring(1)) + .append("; Date: ") + .append(getDate()) + .append("; Comment: ") + .append(getComment()); + + + + Set tags = getTags(); + if (!tags.isEmpty()) { + builder.append("; Tags: "); + tags.forEach(builder::append); + } + return builder.toString(); + } + +} diff --git a/src/main/java/seedu/internship/model/internship/InternshipContainsKeywordsPredicate.java b/src/main/java/seedu/internship/model/internship/InternshipContainsKeywordsPredicate.java new file mode 100644 index 00000000000..b329102a2b3 --- /dev/null +++ b/src/main/java/seedu/internship/model/internship/InternshipContainsKeywordsPredicate.java @@ -0,0 +1,132 @@ +package seedu.internship.model.internship; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.internship.commons.util.StringUtil; + +/** + * Tests that a {@code Internship}'s {@code CompanyName} matches any of the keywords given. + */ +public class InternshipContainsKeywordsPredicate implements Predicate { + private final List nameKeywords; + private final List roleKeywords; + private final List statusKeywords; + private final List keyDates; + private final List tagKeywords; + + /** + * Constructs a {@code CompanyNameContainsKeywordsPredicate} instance. + * + * @param nameKeywords The keywords to check for in the Internship's company name. + */ + public InternshipContainsKeywordsPredicate(List nameKeywords, List roleKeywords, + List statusKeywords, List keyDates, + List tagKeywords) { + this.nameKeywords = nameKeywords; + this.roleKeywords = roleKeywords; + this.statusKeywords = statusKeywords; + this.keyDates = keyDates; + this.tagKeywords = tagKeywords; + } + + /** + * Tests if an Internship's company name matches any of the keywords. + * + * @param internship the input Internship whose name will be checked. + * @return true if there is a match with any of the keyword, else false. + */ + @Override + public boolean test(Internship internship) { + boolean noNameKeywords = this.nameKeywords.isEmpty(); + boolean noRoleKeywords = this.roleKeywords.isEmpty(); + boolean noStatusKeywords = this.statusKeywords.isEmpty(); + boolean noKeyDates = this.keyDates.isEmpty(); + boolean noTagKeywords = this.tagKeywords.isEmpty(); + + if (noNameKeywords && noRoleKeywords && noStatusKeywords && noKeyDates && noTagKeywords) { + return false; + } + + return this.checkName(noNameKeywords, internship) + && this.checkRole(noRoleKeywords, internship) + && this.checkStatus(noStatusKeywords, internship) + && this.checkDate(noKeyDates, internship) + && this.checkTags(noTagKeywords, internship); + } + + private boolean checkName(boolean noNameKeywords, Internship internship) { + if (!noNameKeywords) { + return this.nameKeywords.stream() + .anyMatch(keyword -> StringUtil.matchingStringIgnoreCase( + internship.getCompanyName().fullCompanyName, keyword)); + } + return true; + } + + private boolean checkRole(boolean noRoleKeywords, Internship internship) { + if (!noRoleKeywords) { + return this.roleKeywords.stream() + .anyMatch(keyword -> StringUtil.matchingStringIgnoreCase( + internship.getRole().fullRole, keyword)); + } + return true; + } + + private boolean checkStatus(boolean noStatusKeywords, Internship internship) { + if (!noStatusKeywords) { + return this.statusKeywords.stream() + .anyMatch(keyword -> StringUtil.matchingStringIgnoreCase( + internship.getStatus().fullStatus, keyword)); + } + return true; + } + + private boolean checkDate(boolean noKeyDates, Internship internship) { + if (!noKeyDates) { + return this.keyDates.stream() + .anyMatch(keyword -> StringUtil.matchingStringIgnoreCase( + internship.getDate().fullDate, keyword)); + } + return true; + } + + private boolean checkTags(boolean noTagKeywords, Internship internship) { + if (!noTagKeywords) { + return this.tagKeywords.stream() + .anyMatch(keyword -> internship.getTags().stream() + .map(tag -> tag.tagName) + .anyMatch(tagName -> StringUtil.matchingStringIgnoreCase(tagName, keyword))); + } + return true; + } + + /** + * Determines if an object is equal to the current {@code CompanyNameContainsKeywordsPredicate} instance. + * + * @param other The other object to compare with + * @return true if both are {@code CompanyNameContainsKeywordsPredicate} instances with the same set + * of keywords to check for. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof InternshipContainsKeywordsPredicate // instanceof handles nulls + && nameKeywords.equals(((InternshipContainsKeywordsPredicate) other).nameKeywords) // state check + && roleKeywords.equals(((InternshipContainsKeywordsPredicate) other).roleKeywords) + && statusKeywords.equals(((InternshipContainsKeywordsPredicate) other).statusKeywords) + && keyDates.equals(((InternshipContainsKeywordsPredicate) other).keyDates) + && tagKeywords.equals(((InternshipContainsKeywordsPredicate) other).tagKeywords)); + } + + /** + * Determines if the predicate contains no conditions. + * + * @return true if predicate contains no conditions. + */ + public boolean isEmpty() { + return this.nameKeywords.isEmpty() && this.roleKeywords.isEmpty() && this.statusKeywords.isEmpty() + && this.tagKeywords.isEmpty() && this.keyDates.isEmpty(); + } + +} diff --git a/src/main/java/seedu/internship/model/internship/Role.java b/src/main/java/seedu/internship/model/internship/Role.java new file mode 100644 index 00000000000..9ba8cc1e22c --- /dev/null +++ b/src/main/java/seedu/internship/model/internship/Role.java @@ -0,0 +1,78 @@ +package seedu.internship.model.internship; + +import static java.util.Objects.requireNonNull; +import static seedu.internship.commons.util.AppUtil.checkArgument; + +/** + * Represents an Internship's role in InternBuddy. + * Guarantees: immutable; is valid as declared in {@link #isValidRole(String)} + */ +public class Role { + + + public static final String MESSAGE_CONSTRAINTS = + "Roles should not be blank, and should be at most 50 characters"; + + /* + * The first character of the role must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "^.{1,50}$"; + + public final String fullRole; + + /** + * Constructs a {@code Role}. + * + * @param role A valid role to be associated with an Internship. + */ + public Role(String role) { + requireNonNull(role); + checkArgument(isValidRole(role), MESSAGE_CONSTRAINTS); + this.fullRole = role; + } + + /** + * Returns true if a given string is a valid role. + * + * @param test The string to test against the regex. + * @return true if the given string corresponds to a valid string for a role, else returns false. + */ + public static boolean isValidRole(String test) { + return test.matches(VALIDATION_REGEX); + } + + /** + * Returns the String representation of the Role. + * + * @return a String representing the role. + */ + @Override + public String toString() { + return fullRole; + } + + /** + * Determines if another object is equal to this Role object. + * + * @param other The other object to compare with. + * @return true if the other object is a Role object with the same role string. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Role // instanceof handles nulls + && fullRole.toLowerCase().equals(((Role) other).fullRole.toLowerCase())); // state check + } + + /** + * Gets the hash code of the Role object. + * + * @return the hash code for the role represented by the Role object. + */ + @Override + public int hashCode() { + return fullRole.hashCode(); + } + +} diff --git a/src/main/java/seedu/internship/model/internship/Status.java b/src/main/java/seedu/internship/model/internship/Status.java new file mode 100644 index 00000000000..eb7078d7be3 --- /dev/null +++ b/src/main/java/seedu/internship/model/internship/Status.java @@ -0,0 +1,95 @@ +package seedu.internship.model.internship; + +import static java.util.Objects.requireNonNull; +import static seedu.internship.commons.util.AppUtil.checkArgument; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + +/** + * Represents an Internship's status in InternBuddy + * Guarantees: immutable; is valid as declared in {@link #isValidStatus(String)} + */ +public class Status { + + public static final String MESSAGE_CONSTRAINTS = + "Status should only be one of the following: New, Applied, Assessment, Interview, Offered, " + + "Accepted or Rejected. It should not be blank too."; + + public static final String NEW = "new"; + public static final String APPLIED = "applied"; + public static final String ASSESSMENT = "assessment"; + public static final String INTERVIEW = "interview"; + public static final String OFFERED = "offered"; + public static final String REJECTED = "rejected"; + public static final String ACCEPTED = "accepted"; + //A set of valid statuses + public static final List LIST_OF_VALID_STATUSES = + Arrays.asList(NEW, APPLIED, ASSESSMENT, INTERVIEW, OFFERED, REJECTED, ACCEPTED); + public static final HashSet SET_OF_VALID_STATUSES = new HashSet(LIST_OF_VALID_STATUSES); + + public final String fullStatus; + + /** + * Constructs a {@code Status}. + * + * @param status A valid status to be associated with an Internship. + */ + public Status(String status) { + requireNonNull(status); + status = status.toLowerCase(); + checkArgument(isValidStatus(status), MESSAGE_CONSTRAINTS); + this.fullStatus = status; + } + + + /** + * Returns true if a given string is a valid status + * + * @param test The string to check for. + * @return true if the given string corresponds to a valid string for a role, else returns false. + * @throws NullPointerException if a null status is passed in + */ + public static boolean isValidStatus(String test) { + if (test == null) { + throw new NullPointerException(); + } + return SET_OF_VALID_STATUSES.contains(test.toLowerCase()); + } + + /** + * Returns the String representation of the Role. + * + * @return a String representing the role. + */ + @Override + public String toString() { + return this.fullStatus; + } + + + /** + * Determines if another object is equal to this Status object. + * + * @param other The other object to compare with. + * @return true if the other object is a Status object with the same status string. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Status // instanceof handles nulls + && fullStatus.equals(((Status) other).fullStatus)); // state check + } + + /** + * Gets the hash code of the Status object. + * + * @return the hash code for the status represented by the Status object. + */ + @Override + public int hashCode() { + return fullStatus.hashCode(); + } +} + diff --git a/src/main/java/seedu/internship/model/internship/UniqueInternshipList.java b/src/main/java/seedu/internship/model/internship/UniqueInternshipList.java new file mode 100644 index 00000000000..6cf23ab7f8b --- /dev/null +++ b/src/main/java/seedu/internship/model/internship/UniqueInternshipList.java @@ -0,0 +1,154 @@ +package seedu.internship.model.internship; + +import static java.util.Objects.requireNonNull; +import static seedu.internship.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.internship.model.internship.exceptions.DuplicateInternshipException; +import seedu.internship.model.internship.exceptions.InternshipNotFoundException; + +/** + * A list of Internships that enforces uniqueness between its elements and does not allow nulls. + * An Internship is considered unique by comparing using {@code Internship#isSameInternship(Internship)}. As such, + * adding and updating of internships uses Internship#isSameInternship(Internship) for equality to ensure that the + * internship being added or updated is unique in terms of identity in the UniqueInternshipList. However, the removal + * of an internship uses Internship#equals(Object) so to ensure that an Internship with exactly the same fields will be + * removed. + * + * Supports a minimal set of list operations. + * + * @see Internship#isSameInternship(Internship) + */ +public class UniqueInternshipList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent internship as the given argument. + */ + public boolean contains(Internship toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameInternship); + } + + /** + * Adds an internship to the list. + * The internship must not already exist in the list. + */ + public void add(Internship toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateInternshipException(); + } + internalList.add(toAdd); + } + + /** + * Replaces the internship {@code target} in the list with {@code editedInternship}. + * {@code target} must exist in the list. + * The internship identity of {@code editedInternship} must not be the same as another existing internship in + * the list. + */ + public void setInternship(Internship target, Internship editedInternship) { + requireAllNonNull(target, editedInternship); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new InternshipNotFoundException(); + } + + if (!target.isSameInternship(editedInternship) && contains(editedInternship)) { + throw new DuplicateInternshipException(); + } + + internalList.set(index, editedInternship); + } + + /** + * Removes the equivalent internship from the list. + * The internship must exist in the list. + */ + public void remove(Internship toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new InternshipNotFoundException(); + } + } + + /** + * Views the equivalent internship from the list. + * The internship must exist in the list. + * + * @param toView The internship to be viewed + */ + public void view(Internship toView) { + requireNonNull(toView); + if (!contains(toView)) { + //Desired internship cannot be found + throw new InternshipNotFoundException(); + } + + } + + public void setInternships(UniqueInternshipList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code internships}. + * {@code internships} must not contain duplicate internships + */ + public void setInternships(List internships) { + requireAllNonNull(internships); + if (!internshipsAreUnique(internships)) { + throw new DuplicateInternshipException(); + } + + internalList.setAll(internships); + } + + /** + * 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 UniqueInternshipList // instanceof handles nulls + && internalList.equals(((UniqueInternshipList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code internships} contains only unique internships. + */ + private boolean internshipsAreUnique(List internships) { + for (int i = 0; i < internships.size() - 1; i++) { + for (int j = i + 1; j < internships.size(); j++) { + if (internships.get(i).isSameInternship(internships.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/seedu/internship/model/internship/exceptions/DuplicateInternshipException.java b/src/main/java/seedu/internship/model/internship/exceptions/DuplicateInternshipException.java new file mode 100644 index 00000000000..8005d2a1813 --- /dev/null +++ b/src/main/java/seedu/internship/model/internship/exceptions/DuplicateInternshipException.java @@ -0,0 +1,11 @@ +package seedu.internship.model.internship.exceptions; + +/** + * Signals that the operation will result in duplicate Internships (Internships are considered duplicates if they have + * the same identity). + */ +public class DuplicateInternshipException extends RuntimeException { + public DuplicateInternshipException() { + super("Operation would result in duplicate internships."); + } +} diff --git a/src/main/java/seedu/internship/model/internship/exceptions/InternshipNotFoundException.java b/src/main/java/seedu/internship/model/internship/exceptions/InternshipNotFoundException.java new file mode 100644 index 00000000000..b8759d9467f --- /dev/null +++ b/src/main/java/seedu/internship/model/internship/exceptions/InternshipNotFoundException.java @@ -0,0 +1,6 @@ +package seedu.internship.model.internship.exceptions; + +/** + * Signals that the operation is unable to find the specified internship + */ +public class InternshipNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/internship/model/tag/Tag.java similarity index 67% rename from src/main/java/seedu/address/model/tag/Tag.java rename to src/main/java/seedu/internship/model/tag/Tag.java index b0ea7e7dad7..f31e9473008 100644 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ b/src/main/java/seedu/internship/model/tag/Tag.java @@ -1,16 +1,17 @@ -package seedu.address.model.tag; +package seedu.internship.model.tag; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.internship.commons.util.AppUtil.checkArgument; /** - * Represents a Tag in the address book. + * Represents a tag in InternBuddy. * 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 static final String MESSAGE_CONSTRAINTS = "Tags should not be blank, and " + + "should be at most 30 characters"; + public static final String VALIDATION_REGEX = "^.{1,30}$"; public final String tagName; @@ -27,9 +28,14 @@ public Tag(String tagName) { /** * Returns true if a given string is a valid tag name. + * + * @param test The string to be tested for tag validity. */ public static boolean isValidTagName(String test) { - return test.matches(VALIDATION_REGEX); + if (test.isEmpty() || !test.matches(VALIDATION_REGEX)) { + return false; + } + return true; } @Override diff --git a/src/main/java/seedu/internship/model/util/SampleDataUtil.java b/src/main/java/seedu/internship/model/util/SampleDataUtil.java new file mode 100644 index 00000000000..29d838cf9f9 --- /dev/null +++ b/src/main/java/seedu/internship/model/util/SampleDataUtil.java @@ -0,0 +1,63 @@ +package seedu.internship.model.util; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +import seedu.internship.model.InternBuddy; +import seedu.internship.model.ReadOnlyInternBuddy; +import seedu.internship.model.internship.Comment; +import seedu.internship.model.internship.CompanyName; +import seedu.internship.model.internship.Date; +import seedu.internship.model.internship.Internship; +import seedu.internship.model.internship.Role; +import seedu.internship.model.internship.Status; +import seedu.internship.model.tag.Tag; + +/** + * Contains utility methods for populating {@code InternBuddy} with sample data. + */ +public class SampleDataUtil { + public static Internship[] getSampleInternships() { + return new Internship[] { + new Internship(new CompanyName("Apple"), new Role("iOS Developer"), new Status("applied"), + new Date("2023-03-20"), new Comment("Yay! My dream company!"), getTagSet("iOS")), + new Internship(new CompanyName("Amazon"), new Role("Cloud Architect"), new Status("new"), + new Date("2023-03-28"), new Comment("Need to research more on cloud services."), + getTagSet("AWS", "Cloud Services")), + new Internship(new CompanyName("Google"), new Role("Software Engineer"), new Status("assessment"), + new Date("2023-04-02"), new Comment("Good company culture and environment."), + getTagSet("Golang", "Back-end")), + new Internship(new CompanyName("Samsung"), new Role("Android Developer"), new Status("interview"), + new Date("2023-04-10"), new Comment("To compare with Apple's offer again."), + getTagSet("Android", "Mobile")), + new Internship(new CompanyName("Grab"), new Role("Frontend Designer"), new Status("offered"), + new Date("2023-03-27"), new Comment("Good benefits. Can consider."), + getTagSet("React", "CSS")), + new Internship(new CompanyName("Paypal"), new Role("Product Designer"), new Status("Accepted"), + new Date("2023-03-26"), new Comment("Starting work on 1 May. Excited!"), + getTagSet("UI", "UX")), + new Internship(new CompanyName("Facebook"), new Role("Backend Developer"), new Status("rejected"), + new Date("2023-03-15"), new Comment("Rejected since I lack proficiency in SQL."), + getTagSet("SQL")), + }; + } + + public static ReadOnlyInternBuddy getSampleInternBuddy() { + InternBuddy sampleAb = new InternBuddy(); + for (Internship sampleInternship : getSampleInternships()) { + sampleAb.addInternship(sampleInternship); + } + return sampleAb; + } + + /** + * Returns a tag set containing the list of strings given. + */ + public static Set getTagSet(String... strings) { + return Arrays.stream(strings) + .map(Tag::new) + .collect(Collectors.toSet()); + } + +} diff --git a/src/main/java/seedu/internship/storage/CommandHistory.java b/src/main/java/seedu/internship/storage/CommandHistory.java new file mode 100644 index 00000000000..f3cf7bf57ff --- /dev/null +++ b/src/main/java/seedu/internship/storage/CommandHistory.java @@ -0,0 +1,57 @@ +package seedu.internship.storage; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +// @@author kohkaixun-reused +// Solution below adapted from +// https://github.com/AY2122S2-CS2103T-W13-3/tp/blob/master/src/main/java/seedu/address/storage/UserInputHistory.java +/** + * CommandHistory keeps track of past commands inputted. + */ +public class CommandHistory { + private final List list; + private int index; + private final String emptyString = ""; + + /** + * Creates new CommandHistory object + */ + public CommandHistory() { + this.list = new ArrayList<>(Arrays.asList(this.emptyString)); + this.index = 0; + } + + /** + * Add new input to CommandHistory object + * @param newInput The string representing the new input + */ + public void addInput(String newInput) { + this.list.add(this.list.size() - 1, newInput); + this.index = this.list.size() - 1; + } + + /** + * Gets the next older input. If there is not older input, the oldest input is returned again. + * @return The next older input or if that doesn't exist, the oldest input. + */ + public String getOlderInput() { + if (this.index > 0) { + this.index--; + } + return this.list.get(this.index); + } + + /** + * Gets the next most recent input. If there isn't any more recent inputs, the most recent input is returned again. + * @return The next most recent input or if that doesn't exist, the most recent input. + */ + public String getNewerInput() { + if (this.index < this.list.size() - 1) { + this.index++; + } + return this.list.get(this.index); + } +} +// @@author diff --git a/src/main/java/seedu/internship/storage/InternBuddyStorage.java b/src/main/java/seedu/internship/storage/InternBuddyStorage.java new file mode 100644 index 00000000000..56e38724699 --- /dev/null +++ b/src/main/java/seedu/internship/storage/InternBuddyStorage.java @@ -0,0 +1,45 @@ +package seedu.internship.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.internship.commons.exceptions.DataConversionException; +import seedu.internship.model.ReadOnlyInternBuddy; + +/** + * Represents a storage for {@link seedu.internship.model.InternBuddy}. + */ +public interface InternBuddyStorage { + + /** + * Returns the file path of the data file. + */ + Path getInternBuddyFilePath(); + + /** + * Returns InternBuddy data as a {@link ReadOnlyInternBuddy}. + * 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 readInternBuddy() throws DataConversionException, IOException; + + /** + * @see #getInternBuddyFilePath() + */ + Optional readInternBuddy(Path filePath) throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyInternBuddy} to the storage. + * @param internBuddy cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveInternBuddy(ReadOnlyInternBuddy internBuddy) throws IOException; + + /** + * @see #saveInternBuddy(ReadOnlyInternBuddy) + */ + void saveInternBuddy(ReadOnlyInternBuddy internBuddy, Path filePath) throws IOException; + +} diff --git a/src/main/java/seedu/internship/storage/JsonAdaptedInternship.java b/src/main/java/seedu/internship/storage/JsonAdaptedInternship.java new file mode 100644 index 00000000000..860c3125161 --- /dev/null +++ b/src/main/java/seedu/internship/storage/JsonAdaptedInternship.java @@ -0,0 +1,123 @@ +package seedu.internship.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.internship.commons.exceptions.IllegalValueException; +import seedu.internship.model.internship.Comment; +import seedu.internship.model.internship.CompanyName; +import seedu.internship.model.internship.Date; +import seedu.internship.model.internship.Internship; +import seedu.internship.model.internship.Role; +import seedu.internship.model.internship.Status; +import seedu.internship.model.tag.Tag; + +/** + * Jackson-friendly version of {@link Internship}. + */ +class JsonAdaptedInternship { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Internship's %s field is missing!"; + + private final String companyName; + private final String role; + private final String status; + private final String date; + private final String comment; + private final List tagged = new ArrayList<>(); + + /** + * Constructs a {@code JsonAdaptedInternship} with the given internship details. + */ + @JsonCreator + public JsonAdaptedInternship(@JsonProperty("companyName") String companyName, @JsonProperty("role") String role, + @JsonProperty("status") String status, @JsonProperty("date") String date, + @JsonProperty("comment") String comment, @JsonProperty("tagged") List tagged) { + this.companyName = companyName; + this.role = role; + this.status = status; + this.date = date; + if (comment == null) { + this.comment = ""; + } else { + this.comment = comment; + } + if (tagged != null) { + this.tagged.addAll(tagged); + } + } + + /** + * Converts a given {@code Internship} into this class for Jackson use. + */ + public JsonAdaptedInternship(Internship source) { + companyName = source.getCompanyName().fullCompanyName; + role = source.getRole().fullRole; + status = source.getStatus().toString(); + date = source.getDate().fullDate; + comment = source.getComment().commentContent; + tagged.addAll(source.getTags().stream() + .map(JsonAdaptedTag::new) + .collect(Collectors.toList())); + } + + /** + * Converts this Jackson-friendly adapted internship object into the model's {@code Internship} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted internship + */ + public Internship toModelType() throws IllegalValueException { + final List internshipTags = new ArrayList<>(); + for (JsonAdaptedTag tag : tagged) { + internshipTags.add(tag.toModelType()); + } + + 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); + } + final CompanyName modelCompanyName = new CompanyName(companyName); + + if (role == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Role.class.getSimpleName())); + } + if (!Role.isValidRole(role)) { + throw new IllegalValueException(Role.MESSAGE_CONSTRAINTS); + } + final Role modelRole = new Role(role); + + if (status == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Status.class.getSimpleName())); + } + if (!Status.isValidStatus(status)) { + throw new IllegalValueException(Status.MESSAGE_CONSTRAINTS); + } + final Status modelStatus = new Status(status); + + if (date == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Date.class.getSimpleName())); + } + if (!Date.isValidDate(date)) { + throw new IllegalValueException(Date.MESSAGE_CONSTRAINTS); + } + final Date modelDate = new Date(date); + + + if (!Comment.isValidComment(comment)) { + throw new IllegalValueException(Comment.MESSAGE_CONSTRAINTS); + } + final Comment modelComment = new Comment(comment); + + final Set modelTags = new HashSet<>(internshipTags); + return new Internship(modelCompanyName, modelRole, modelStatus, modelDate, modelComment, modelTags); + } +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/seedu/internship/storage/JsonAdaptedTag.java similarity index 88% rename from src/main/java/seedu/address/storage/JsonAdaptedTag.java rename to src/main/java/seedu/internship/storage/JsonAdaptedTag.java index 0df22bdb754..5d1e60030c5 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java +++ b/src/main/java/seedu/internship/storage/JsonAdaptedTag.java @@ -1,10 +1,10 @@ -package seedu.address.storage; +package seedu.internship.storage; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.tag.Tag; +import seedu.internship.commons.exceptions.IllegalValueException; +import seedu.internship.model.tag.Tag; /** * Jackson-friendly version of {@link Tag}. diff --git a/src/main/java/seedu/internship/storage/JsonInternBuddyStorage.java b/src/main/java/seedu/internship/storage/JsonInternBuddyStorage.java new file mode 100644 index 00000000000..e235476af3f --- /dev/null +++ b/src/main/java/seedu/internship/storage/JsonInternBuddyStorage.java @@ -0,0 +1,80 @@ +package seedu.internship.storage; + +import static java.util.Objects.requireNonNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.internship.commons.core.LogsCenter; +import seedu.internship.commons.exceptions.DataConversionException; +import seedu.internship.commons.exceptions.IllegalValueException; +import seedu.internship.commons.util.FileUtil; +import seedu.internship.commons.util.JsonUtil; +import seedu.internship.model.ReadOnlyInternBuddy; + +/** + * A class to access InternBuddy data stored as a json file on the hard disk. + */ +public class JsonInternBuddyStorage implements InternBuddyStorage { + + private static final Logger logger = LogsCenter.getLogger(JsonInternBuddyStorage.class); + + private Path filePath; + + public JsonInternBuddyStorage(Path filePath) { + this.filePath = filePath; + } + + public Path getInternBuddyFilePath() { + return filePath; + } + + @Override + public Optional readInternBuddy() throws DataConversionException { + return readInternBuddy(filePath); + } + + /** + * Similar to {@link #readInternBuddy()}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readInternBuddy(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional jsonInternBuddy = JsonUtil.readJsonFile( + filePath, JsonSerializableInternBuddy.class); + if (!jsonInternBuddy.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonInternBuddy.get().toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + @Override + public void saveInternBuddy(ReadOnlyInternBuddy internBuddy) throws IOException { + saveInternBuddy(internBuddy, filePath); + } + + /** + * Similar to {@link #saveInternBuddy(ReadOnlyInternBuddy)}. + * + * @param filePath location of the data. Cannot be null. + */ + public void saveInternBuddy(ReadOnlyInternBuddy internBuddy, Path filePath) throws IOException { + requireNonNull(internBuddy); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonSerializableInternBuddy(internBuddy), filePath); + } + +} diff --git a/src/main/java/seedu/internship/storage/JsonSerializableInternBuddy.java b/src/main/java/seedu/internship/storage/JsonSerializableInternBuddy.java new file mode 100644 index 00000000000..db63dbafda0 --- /dev/null +++ b/src/main/java/seedu/internship/storage/JsonSerializableInternBuddy.java @@ -0,0 +1,61 @@ +package seedu.internship.storage; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; + +import seedu.internship.commons.exceptions.IllegalValueException; +import seedu.internship.model.InternBuddy; +import seedu.internship.model.ReadOnlyInternBuddy; +import seedu.internship.model.internship.Internship; + +/** + * An Immutable InternBuddy that is serializable to JSON format. + */ +@JsonRootName(value = "internbuddy") +class JsonSerializableInternBuddy { + + public static final String MESSAGE_DUPLICATE_INTERNSHIP = "Internships list contains duplicate internship(s)."; + + private final List internships = new ArrayList<>(); + + /** + * Constructs a {@code JsonSerializableInternBuddy} with the given internships + */ + @JsonCreator + public JsonSerializableInternBuddy(@JsonProperty("internships") List internships) { + this.internships.addAll(internships); + } + + /** + * Converts a given {@code ReadOnlyInternBuddy} into this class for Jackson use. + * + * @param source future changes to this will not affect the created {@code JsonSerializableInternBuddy}. + */ + public JsonSerializableInternBuddy(ReadOnlyInternBuddy source) { + internships.addAll(source.getInternshipList().stream().map(JsonAdaptedInternship::new) + .collect(Collectors.toList())); + } + + /** + * Converts this InternBuddy into the model's {@code InternBuddy} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public InternBuddy toModelType() throws IllegalValueException { + InternBuddy internBuddy = new InternBuddy(); + for (JsonAdaptedInternship jsonAdaptedInternship : internships) { + Internship internship = jsonAdaptedInternship.toModelType(); + if (internBuddy.hasInternship(internship)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_INTERNSHIP); + } + internBuddy.addInternship(internship); + } + return internBuddy; + } + +} diff --git a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java b/src/main/java/seedu/internship/storage/JsonUserPrefsStorage.java similarity index 82% rename from src/main/java/seedu/address/storage/JsonUserPrefsStorage.java rename to src/main/java/seedu/internship/storage/JsonUserPrefsStorage.java index bc2bbad84aa..6dcd0db1da5 100644 --- a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java +++ b/src/main/java/seedu/internship/storage/JsonUserPrefsStorage.java @@ -1,13 +1,13 @@ -package seedu.address.storage; +package seedu.internship.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import seedu.internship.commons.exceptions.DataConversionException; +import seedu.internship.commons.util.JsonUtil; +import seedu.internship.model.ReadOnlyUserPrefs; +import seedu.internship.model.UserPrefs; /** * A class to access UserPrefs stored in the hard disk as a json file diff --git a/src/main/java/seedu/internship/storage/Storage.java b/src/main/java/seedu/internship/storage/Storage.java new file mode 100644 index 00000000000..606d9de6926 --- /dev/null +++ b/src/main/java/seedu/internship/storage/Storage.java @@ -0,0 +1,32 @@ +package seedu.internship.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.internship.commons.exceptions.DataConversionException; +import seedu.internship.model.ReadOnlyInternBuddy; +import seedu.internship.model.ReadOnlyUserPrefs; +import seedu.internship.model.UserPrefs; + +/** + * API of the Storage component + */ +public interface Storage extends InternBuddyStorage, UserPrefsStorage { + + @Override + Optional readUserPrefs() throws DataConversionException, IOException; + + @Override + void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; + + @Override + Path getInternBuddyFilePath(); + + @Override + Optional readInternBuddy() throws DataConversionException, IOException; + + @Override + void saveInternBuddy(ReadOnlyInternBuddy internBuddy) throws IOException; + +} diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/internship/storage/StorageManager.java similarity index 50% rename from src/main/java/seedu/address/storage/StorageManager.java rename to src/main/java/seedu/internship/storage/StorageManager.java index 6cfa0162164..31cac596166 100644 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ b/src/main/java/seedu/internship/storage/StorageManager.java @@ -1,30 +1,30 @@ -package seedu.address.storage; +package seedu.internship.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; import java.util.logging.Logger; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import seedu.internship.commons.core.LogsCenter; +import seedu.internship.commons.exceptions.DataConversionException; +import seedu.internship.model.ReadOnlyInternBuddy; +import seedu.internship.model.ReadOnlyUserPrefs; +import seedu.internship.model.UserPrefs; /** - * Manages storage of AddressBook data in local storage. + * Manages storage of InternBuddy data in local storage. */ public class StorageManager implements Storage { private static final Logger logger = LogsCenter.getLogger(StorageManager.class); - private AddressBookStorage addressBookStorage; + private InternBuddyStorage internBuddyStorage; private UserPrefsStorage userPrefsStorage; /** - * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}. + * Creates a {@code StorageManager} with the given {@code InternBuddyStorage} and {@code UserPrefStorage}. */ - public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) { - this.addressBookStorage = addressBookStorage; + public StorageManager(InternBuddyStorage internBuddyStorage, UserPrefsStorage userPrefsStorage) { + this.internBuddyStorage = internBuddyStorage; this.userPrefsStorage = userPrefsStorage; } @@ -46,33 +46,33 @@ public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { } - // ================ AddressBook methods ============================== + // ================ InternBuddy methods ============================== @Override - public Path getAddressBookFilePath() { - return addressBookStorage.getAddressBookFilePath(); + public Path getInternBuddyFilePath() { + return internBuddyStorage.getInternBuddyFilePath(); } @Override - public Optional readAddressBook() throws DataConversionException, IOException { - return readAddressBook(addressBookStorage.getAddressBookFilePath()); + public Optional readInternBuddy() throws DataConversionException, IOException { + return readInternBuddy(internBuddyStorage.getInternBuddyFilePath()); } @Override - public Optional readAddressBook(Path filePath) throws DataConversionException, IOException { + public Optional readInternBuddy(Path filePath) throws DataConversionException, IOException { logger.fine("Attempting to read data from file: " + filePath); - return addressBookStorage.readAddressBook(filePath); + return internBuddyStorage.readInternBuddy(filePath); } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath()); + public void saveInternBuddy(ReadOnlyInternBuddy internBuddy) throws IOException { + saveInternBuddy(internBuddy, internBuddyStorage.getInternBuddyFilePath()); } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { + public void saveInternBuddy(ReadOnlyInternBuddy internBuddy, Path filePath) throws IOException { logger.fine("Attempting to write to data file: " + filePath); - addressBookStorage.saveAddressBook(addressBook, filePath); + internBuddyStorage.saveInternBuddy(internBuddy, filePath); } } diff --git a/src/main/java/seedu/address/storage/UserPrefsStorage.java b/src/main/java/seedu/internship/storage/UserPrefsStorage.java similarity index 70% rename from src/main/java/seedu/address/storage/UserPrefsStorage.java rename to src/main/java/seedu/internship/storage/UserPrefsStorage.java index 29eef178dbc..1436161a5aa 100644 --- a/src/main/java/seedu/address/storage/UserPrefsStorage.java +++ b/src/main/java/seedu/internship/storage/UserPrefsStorage.java @@ -1,15 +1,15 @@ -package seedu.address.storage; +package seedu.internship.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import seedu.internship.commons.exceptions.DataConversionException; +import seedu.internship.model.ReadOnlyUserPrefs; +import seedu.internship.model.UserPrefs; /** - * Represents a storage for {@link seedu.address.model.UserPrefs}. + * Represents a storage for {@link seedu.internship.model.UserPrefs}. */ public interface UserPrefsStorage { @@ -27,7 +27,7 @@ public interface UserPrefsStorage { Optional readUserPrefs() throws DataConversionException, IOException; /** - * Saves the given {@link seedu.address.model.ReadOnlyUserPrefs} to the storage. + * Saves the given {@link seedu.internship.model.ReadOnlyUserPrefs} to the storage. * @param userPrefs cannot be null. * @throws IOException if there was any problem writing to the file. */ diff --git a/src/main/java/seedu/address/ui/CommandBox.java b/src/main/java/seedu/internship/ui/CommandBox.java similarity index 53% rename from src/main/java/seedu/address/ui/CommandBox.java rename to src/main/java/seedu/internship/ui/CommandBox.java index 9e75478664b..60fb9c0a4a7 100644 --- a/src/main/java/seedu/address/ui/CommandBox.java +++ b/src/main/java/seedu/internship/ui/CommandBox.java @@ -1,12 +1,15 @@ -package seedu.address.ui; +package seedu.internship.ui; +import javafx.application.Platform; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.control.TextField; +import javafx.scene.input.KeyEvent; import javafx.scene.layout.Region; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.internship.logic.commands.CommandResult; +import seedu.internship.logic.commands.exceptions.CommandException; +import seedu.internship.logic.parser.exceptions.ParseException; +import seedu.internship.storage.CommandHistory; /** * The UI component that is responsible for receiving user command inputs. @@ -17,6 +20,7 @@ public class CommandBox extends UiPart { private static final String FXML = "CommandBox.fxml"; private final CommandExecutor commandExecutor; + private final CommandHistory commandHistory; @FXML private TextField commandTextField; @@ -29,6 +33,12 @@ 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()); + this.commandHistory = new CommandHistory(); + // @@author kohkaixun-reused + // Reused from + // https://github.com/AY2122S2-CS2103T-W13-3/tp/blob/master/src/main/java/seedu/address/ui/CommandBox.java + this.commandTextField.addEventFilter(KeyEvent.KEY_PRESSED, this::handleUpDownButtonEvent); + // @@author } /** @@ -41,6 +51,8 @@ private void handleCommandEntered() { return; } + this.commandHistory.addInput(commandText); + try { commandExecutor.execute(commandText); commandTextField.setText(""); @@ -49,6 +61,39 @@ private void handleCommandEntered() { } } + @FXML + private void handleUpDownButtonEvent(KeyEvent event) { + // @@author kohkaixun-reused + // Reused from + // https://github.com/AY2122S2-CS2103T-W13-3/tp/blob/master/src/main/java/seedu/address/ui/CommandBox.java + if (event.getCode().isArrowKey()) { + switch (event.getCode()) { + case UP: + String olderInput = this.commandHistory.getOlderInput(); + commandTextField.setText(olderInput); + // @@author kohkaixun-reused + // Reused from + // https://stackoverflow.com/q/8293774 + Platform.runLater(new Runnable() { + @Override + public void run() { + commandTextField.end(); + } + }); + // @@author kohkaixun-reused + break; + + case DOWN: + commandTextField.setText(this.commandHistory.getNewerInput()); + break; + + default: + break; + } + } + // @@author + } + /** * Sets the command box style to use the default style. */ @@ -77,7 +122,7 @@ public interface CommandExecutor { /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see seedu.internship.logic.Logic#execute(String) */ CommandResult execute(String commandText) throws CommandException, ParseException; } diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/internship/ui/HelpWindow.java similarity index 65% rename from src/main/java/seedu/address/ui/HelpWindow.java rename to src/main/java/seedu/internship/ui/HelpWindow.java index 3f16b2fcf26..61a8bcfce7f 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/seedu/internship/ui/HelpWindow.java @@ -1,31 +1,31 @@ -package seedu.address.ui; +package seedu.internship.ui; +import java.awt.Desktop; +import java.net.URI; import java.util.logging.Logger; import javafx.fxml.FXML; import javafx.scene.control.Button; -import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.stage.Stage; -import seedu.address.commons.core.LogsCenter; +import seedu.internship.commons.core.LogsCenter; /** * Controller for a help page */ public class HelpWindow extends UiPart { - public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html"; - public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL; - + public static final String USERGUIDE_URL = "https://ay2223s2-cs2103t-t14-3.github.io/tp/UserGuide.html"; private static final Logger logger = LogsCenter.getLogger(HelpWindow.class); private static final String FXML = "HelpWindow.fxml"; @FXML private Button copyButton; - @FXML - private Label helpMessage; + private ImageView helpImage; /** * Creates a new HelpWindow. @@ -34,7 +34,6 @@ public class HelpWindow extends UiPart { */ public HelpWindow(Stage root) { super(FXML, root); - helpMessage.setText(HELP_MESSAGE); } /** @@ -64,6 +63,7 @@ public HelpWindow() { */ public void show() { logger.fine("Showing help page about the application."); + helpImage.setImage(new Image(this.getClass().getResourceAsStream("/images/internbuddy-help.png"))); getRoot().show(); getRoot().centerOnScreen(); } @@ -99,4 +99,27 @@ private void copyUrl() { url.putString(USERGUIDE_URL); clipboard.setContent(url); } + + /** + * Opens the user guide in a browser + */ + @FXML + private void goToUserGuide() { + //@author eugenetangkj-reused + //Adapted with modifications from + // https://stackoverflow.com/questions/5226212/how-to-open-the-default-webbrowser-using-java + if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { + try { + Desktop.getDesktop().browse(new URI(USERGUIDE_URL)); + } catch (Exception e) { + //Should not reach here + e.printStackTrace(); + assert (false); + } + } + } + + public ImageView getHelpImage() { + return this.helpImage; + } } diff --git a/src/main/java/seedu/internship/ui/InternshipCard.java b/src/main/java/seedu/internship/ui/InternshipCard.java new file mode 100644 index 00000000000..6fdf2f7dd7d --- /dev/null +++ b/src/main/java/seedu/internship/ui/InternshipCard.java @@ -0,0 +1,166 @@ +package seedu.internship.ui; + +import static seedu.internship.model.internship.Status.ACCEPTED; +import static seedu.internship.model.internship.Status.APPLIED; +import static seedu.internship.model.internship.Status.ASSESSMENT; +import static seedu.internship.model.internship.Status.INTERVIEW; +import static seedu.internship.model.internship.Status.NEW; +import static seedu.internship.model.internship.Status.OFFERED; +import static seedu.internship.model.internship.Status.REJECTED; + +import java.util.Comparator; +import java.util.HashMap; + +import javafx.fxml.FXML; +import javafx.geometry.Insets; +import javafx.scene.control.Label; +import javafx.scene.layout.Background; +import javafx.scene.layout.BackgroundFill; +import javafx.scene.layout.CornerRadii; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import javafx.scene.paint.Color; +import seedu.internship.model.internship.Internship; + + +/** + * A UI component that displays information of a {@code Internship}. + */ +public class InternshipCard extends UiPart { + public static final String ROLE_LABEL = "Role: "; + private static final String FXML = "InternshipListCard.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 Internship internship; + + @FXML + private HBox cardPane; + @FXML + private Label companyName; + @FXML + private Label id; + @FXML + private Label role; + @FXML + private Label date; + @FXML + private FlowPane tags; + @FXML + private Label statusLabel; + + + /** + * Creates a {@code InternshipCode} with the given {@code Internship} and index to display. + */ + public InternshipCard(Internship internship, int displayedIndex) { + super(FXML); + this.internship = internship; + //Add Id + id.setText(displayedIndex + ". "); + + //Add Company Name + companyName.setText(internship.getCompanyName().fullCompanyName); + + //Add Role + role.setText(ROLE_LABEL + internship.getRole().fullRole); + + //Add Date + String dateLabel = getDateLabel(internship.getStatus().toString()); + date.setText(dateLabel + internship.getDate().fullDate); + + //Add Tags + internship.getTags().stream() + .sorted(Comparator.comparing(tag -> tag.tagName)) + .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + + + //Set up status label + String statusString = internship.getStatus().toString(); + HashMap colorMap = setupColours(); + Color statusColor = colorMap.get(statusString); + statusLabel.setText(statusString.toUpperCase()); + statusLabel.setBackground(new Background(new BackgroundFill( + statusColor, new CornerRadii(10), new Insets(-5)))); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof InternshipCard)) { + return false; + } + + // state check + InternshipCard card = (InternshipCard) other; + return id.getText().equals(card.id.getText()) + && internship.equals(card.internship); + } + + + /** + * Initialises the colours associated with the status label. + * + * @return a hashmap containing the colors associated with each status type + */ + public static HashMap setupColours() { + //Hashmap that stores the colours associated with each status + HashMap colorMap = new HashMap(); + colorMap.put(ACCEPTED, Color.rgb(42, 174, 79, 1.0)); + colorMap.put(REJECTED, Color.rgb(250, 68, 68, 1.0)); + colorMap.put(OFFERED, Color.rgb(42, 174, 166, 1.0)); + colorMap.put(INTERVIEW, Color.rgb(126, 68, 250, 1.0)); + colorMap.put(ASSESSMENT, Color.rgb(250, 68, 155, 1.0)); + colorMap.put(APPLIED, Color.rgb(68, 170, 250, 1.0)); + colorMap.put(NEW, Color.rgb(250, 155, 68, 1.0)); + return colorMap; + } + + + /** + * Returns the label for the date field in Internship Card. + * + * @param statusString The current status of the associated Internship. + * + * @return the corresponding String as a label for the date. + */ + public static String getDateLabel(String statusString) { + String dateLabel; + switch (statusString) { + case ACCEPTED: + dateLabel = "Date of Acceptance: "; + break; + case REJECTED: + dateLabel = "Date of Rejection: "; + break; + case OFFERED: + dateLabel = "Deadline of Offer Acceptance: "; + break; + case INTERVIEW: + dateLabel = "Date of Interview: "; + break; + case APPLIED: + dateLabel = "Date Applied: "; + break; + case ASSESSMENT: + dateLabel = "Date of Assessment: "; + break; + + default: + dateLabel = "Deadline of Application: "; + } + return dateLabel; + } +} diff --git a/src/main/java/seedu/internship/ui/InternshipDetailsCard.java b/src/main/java/seedu/internship/ui/InternshipDetailsCard.java new file mode 100644 index 00000000000..fce32059ca1 --- /dev/null +++ b/src/main/java/seedu/internship/ui/InternshipDetailsCard.java @@ -0,0 +1,167 @@ +package seedu.internship.ui; + +import static seedu.internship.model.internship.Status.ACCEPTED; +import static seedu.internship.model.internship.Status.APPLIED; +import static seedu.internship.model.internship.Status.ASSESSMENT; +import static seedu.internship.model.internship.Status.INTERVIEW; +import static seedu.internship.model.internship.Status.OFFERED; +import static seedu.internship.model.internship.Status.REJECTED; + +import java.util.Comparator; +import java.util.HashMap; + +import javafx.fxml.FXML; +import javafx.geometry.Insets; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.scene.layout.Background; +import javafx.scene.layout.BackgroundFill; +import javafx.scene.layout.CornerRadii; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.text.Text; +import seedu.internship.model.internship.Internship; + + +/** + * A UI component that displays information of a {@code Internship}. + */ +public class InternshipDetailsCard extends UiPart { + public static final String ROLE_LABEL = "Role: "; + + private static final String FXML = "InternshipDetailsCard.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 Internship internship; + + @FXML + private VBox cardPane; + @FXML + private Text companyName; + + @FXML + private Text role; + @FXML + private Label date; + @FXML + private Text comment; + @FXML + private FlowPane tags; + @FXML + private Label statusLabel; + @FXML + private VBox tipBox; + @FXML + private Text tips; + + /** + * A UI component that displays the detailed information and tips of a {@code Internship}. + */ + public InternshipDetailsCard(Internship internship, Scene scene) { + super(FXML); + this.internship = internship; + + //Add Company Name + companyName.setText(internship.getCompanyName().fullCompanyName); + + //Add Role + role.setText(ROLE_LABEL + internship.getRole().fullRole); + + //Add Date + String dateLabel = InternshipCard.getDateLabel(internship.getStatus().toString()); + date.setText(dateLabel + internship.getDate().fullDate); + + //Add Comment + comment.setText(internship.getComment().commentContent); + //@@author eugenetangkj-reused + //Reused with modifications from https://stackoverflow.com/questions/29315469/javafx-resize-text-with-window + comment.wrappingWidthProperty().bind(scene.widthProperty().multiply(0.4)); + + //Add Tags + internship.getTags().stream() + .sorted(Comparator.comparing(tag -> tag.tagName)) + .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + + + //Set up status label + String statusString = internship.getStatus().toString(); + HashMap colorMap = InternshipCard.setupColours(); + Color statusColor = colorMap.get(statusString); + statusLabel.setText(statusString.toUpperCase()); + statusLabel.setBackground(new Background(new BackgroundFill( + statusColor, new CornerRadii(10), new Insets(-5)))); + + //Set up tips + tips.setText(getTips()); + + //@@author eugenetangkj-reused + //Reused with modifications from https://stackoverflow.com/questions/29315469/javafx-resize-text-with-window + companyName.wrappingWidthProperty().bind(scene.widthProperty().multiply(0.3)); + role.wrappingWidthProperty().bind(scene.widthProperty().multiply(0.3)); + tips.wrappingWidthProperty().bind(scene.widthProperty().multiply(0.4)); + + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof InternshipDetailsCard)) { + return false; + } + + // state check with comparison of company name, role and date + InternshipDetailsCard details = (InternshipDetailsCard) other; + return internship.equals(details.internship); + } + + + /** + * Gets the corresponding tips according to the status + * + * @return the tips for a specific status + */ + public String getTips() { + switch (this.internship.getStatus().toString()) { + case APPLIED: + return "While waiting for the company's response, you can try applying to other companies as well" + + " to have a higher chance of landing an internship."; + case ASSESSMENT: + return "Practice makes perfect! Visit sites such as HackerRank and LeetCode to practice your algorithms" + + " and problem-solving skills. You could also attempt the practices under a time trial to give" + + " you a better sense of the actual coding assignment."; + case INTERVIEW: + return "Be natural! The role of the interviewer is not to put you in a tight position, but rather to" + + " learn more about who you are as a person. It's good if you could share what makes you special" + + " and about your personalised experience that makes you suitable for the job."; + case OFFERED: + return "Congratulations! Your hard work has paid off. Remember to read through the details of the" + + " letter of offer such as job scope and working hours before committing to the offer."; + case REJECTED: + return "Fret not! The process of landing an internship is not a smooth-sailing one, and failures are" + + " part of the journey. Continue your search and you will eventually a suitable internship." + + " Fighting!"; + case ACCEPTED: + return "Congratulations! This is a chance to build new skills, make connections, and explore your " + + "interests in a real-world setting. Embrace every moment of this journey and " + + "don't be afraid to ask questions, seek guidance, and take risks."; + default: + return "If possible, try to apply early because once companies receive applications, they would start" + + " screening for potential candidates. Also, remember to do a thorough check of your resume" + + " before sending out your application."; + } + } +} diff --git a/src/main/java/seedu/internship/ui/InternshipListPanel.java b/src/main/java/seedu/internship/ui/InternshipListPanel.java new file mode 100644 index 00000000000..66d99f36f74 --- /dev/null +++ b/src/main/java/seedu/internship/ui/InternshipListPanel.java @@ -0,0 +1,64 @@ +package seedu.internship.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.internship.commons.core.LogsCenter; +import seedu.internship.model.Model; +import seedu.internship.model.internship.Internship; + +/** + * Panel containing the list of internships + */ +public class InternshipListPanel extends UiPart { + private static final String FXML = "InternshipListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(InternshipListPanel.class); + + private Model model; + + private MainWindow mainWindow; + @FXML + private ListView internshipListView; + + /** + * Creates a {@code InternshipListPanel} with the given {@code ObservableList}. + */ + public InternshipListPanel(ObservableList internshipList, Model selectedModel, + MainWindow selectedMainWindow) { + super(FXML); + internshipListView.setItems(internshipList); + internshipListView.setCellFactory(listView -> new InternshipListViewCell()); + model = selectedModel; + mainWindow = selectedMainWindow; + // @@author potty10-reused + // Reused from https://stackoverflow.com/a/34646172 + // with minor modifications + internshipListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { + model.updateSelectedInternship(newValue); + mainWindow.updateRightPanel(); + }); + // @@author + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Internship} using a {@code InternshipCard}. + */ + class InternshipListViewCell extends ListCell { + @Override + protected void updateItem(Internship internship, boolean empty) { + super.updateItem(internship, empty); + + if (empty || internship == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new InternshipCard(internship, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/internship/ui/MainWindow.java similarity index 69% rename from src/main/java/seedu/address/ui/MainWindow.java rename to src/main/java/seedu/internship/ui/MainWindow.java index 9106c3aa6e5..c0cee87eb88 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/internship/ui/MainWindow.java @@ -1,21 +1,27 @@ -package seedu.address.ui; +package seedu.internship.ui; import java.util.logging.Logger; import javafx.event.ActionEvent; import javafx.fxml.FXML; +import javafx.geometry.Pos; import javafx.scene.control.MenuItem; +import javafx.scene.control.ScrollPane; import javafx.scene.control.TextInputControl; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyEvent; import javafx.scene.layout.StackPane; +import javafx.scene.text.Text; import javafx.stage.Stage; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.logic.Logic; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.internship.commons.core.GuiSettings; +import seedu.internship.commons.core.LogsCenter; +import seedu.internship.logic.Logic; +import seedu.internship.logic.commands.CommandResult; +import seedu.internship.logic.commands.exceptions.CommandException; +import seedu.internship.logic.parser.exceptions.ParseException; +import seedu.internship.model.internship.Internship; /** * The Main Window. Provides the basic application layout containing @@ -31,9 +37,10 @@ public class MainWindow extends UiPart { private Logic logic; // Independent Ui parts residing in this Ui container - private PersonListPanel personListPanel; + private InternshipListPanel internshipListPanel; private ResultDisplay resultDisplay; private HelpWindow helpWindow; + private InternshipDetailsCard internshipDetailsCard; @FXML private StackPane commandBoxPlaceholder; @@ -42,13 +49,24 @@ public class MainWindow extends UiPart { private MenuItem helpMenuItem; @FXML - private StackPane personListPanelPlaceholder; + private StackPane internshipListPanelPlaceholder; + + @FXML + private StackPane internshipDetailsPanelPlaceholder; @FXML private StackPane resultDisplayPlaceholder; @FXML private StackPane statusbarPlaceholder; + @FXML + private ScrollPane rightPanel; + @FXML + private Text introOne; + @FXML + private Text introTwo; + @FXML + private ImageView introThree; /** * Creates a {@code MainWindow} with the given {@code Stage} and {@code Logic}. @@ -66,6 +84,9 @@ public MainWindow(Stage primaryStage, Logic logic) { setAccelerators(); helpWindow = new HelpWindow(); + + introThree.setImage(new Image(this.getClass().getResourceAsStream("/images/internbuddy-hero.png"))); + } public Stage getPrimaryStage() { @@ -110,13 +131,13 @@ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { * Fills up all the placeholders of this window. */ void fillInnerParts() { - personListPanel = new PersonListPanel(logic.getFilteredPersonList()); - personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); + internshipListPanel = new InternshipListPanel(logic.getFilteredInternshipList(), logic.getModel(), this); + internshipListPanelPlaceholder.getChildren().add(internshipListPanel.getRoot()); resultDisplay = new ResultDisplay(); resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); - StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath()); + StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getInternBuddyFilePath()); statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot()); CommandBox commandBox = new CommandBox(this::executeCommand); @@ -147,6 +168,8 @@ public void handleHelp() { } } + + void show() { primaryStage.show(); } @@ -163,20 +186,17 @@ private void handleExit() { primaryStage.hide(); } - public PersonListPanel getPersonListPanel() { - return personListPanel; - } - /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see seedu.internship.logic.Logic#execute(String) */ private CommandResult executeCommand(String commandText) throws CommandException, ParseException { try { CommandResult commandResult = logic.execute(commandText); logger.info("Result: " + commandResult.getFeedbackToUser()); resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser()); + updateRightPanel(); if (commandResult.isShowHelp()) { handleHelp(); @@ -193,4 +213,27 @@ private CommandResult executeCommand(String commandText) throws CommandException throw e; } } + + /** + * Updates the right panel whenever user calls a valid view command + */ + public void updateRightPanel() { + Internship selectedInternship = logic.getSelectedInternship(); + if (selectedInternship != null) { + //Update with internship information + internshipDetailsCard = new InternshipDetailsCard(selectedInternship, primaryStage.getScene()); + internshipDetailsPanelPlaceholder.getChildren().clear(); + internshipDetailsPanelPlaceholder.getChildren().add(internshipDetailsCard.getRoot()); + internshipDetailsPanelPlaceholder.setAlignment(Pos.TOP_CENTER); + } else { + //Reset to original introduction information + internshipDetailsPanelPlaceholder.getChildren().clear(); + internshipDetailsPanelPlaceholder.getChildren().add(introOne); + internshipDetailsPanelPlaceholder.getChildren().add(introTwo); + internshipDetailsPanelPlaceholder.getChildren().add(introThree); + internshipDetailsPanelPlaceholder.setAlignment(Pos.CENTER); + } + } + + } diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/seedu/internship/ui/ResultDisplay.java similarity index 95% rename from src/main/java/seedu/address/ui/ResultDisplay.java rename to src/main/java/seedu/internship/ui/ResultDisplay.java index 7d98e84eedf..cf984381e7d 100644 --- a/src/main/java/seedu/address/ui/ResultDisplay.java +++ b/src/main/java/seedu/internship/ui/ResultDisplay.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.internship.ui; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/seedu/internship/ui/StatusBarFooter.java similarity index 95% rename from src/main/java/seedu/address/ui/StatusBarFooter.java rename to src/main/java/seedu/internship/ui/StatusBarFooter.java index b577f829423..36ed9eb5054 100644 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ b/src/main/java/seedu/internship/ui/StatusBarFooter.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.internship.ui; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/main/java/seedu/address/ui/Ui.java b/src/main/java/seedu/internship/ui/Ui.java similarity index 84% rename from src/main/java/seedu/address/ui/Ui.java rename to src/main/java/seedu/internship/ui/Ui.java index 17aa0b494fe..f2cfb260e20 100644 --- a/src/main/java/seedu/address/ui/Ui.java +++ b/src/main/java/seedu/internship/ui/Ui.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.internship.ui; import javafx.stage.Stage; diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/internship/ui/UiManager.java similarity index 90% rename from src/main/java/seedu/address/ui/UiManager.java rename to src/main/java/seedu/internship/ui/UiManager.java index fdf024138bc..6833d9b06e4 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/seedu/internship/ui/UiManager.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.internship.ui; import java.util.logging.Logger; @@ -7,10 +7,10 @@ import javafx.scene.control.Alert.AlertType; import javafx.scene.image.Image; import javafx.stage.Stage; -import seedu.address.MainApp; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; +import seedu.internship.MainApp; +import seedu.internship.commons.core.LogsCenter; +import seedu.internship.commons.util.StringUtil; +import seedu.internship.logic.Logic; /** * The manager of the UI component. @@ -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/internbuddy_32.png"; private Logic logic; private MainWindow mainWindow; diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/seedu/internship/ui/UiPart.java similarity index 97% rename from src/main/java/seedu/address/ui/UiPart.java rename to src/main/java/seedu/internship/ui/UiPart.java index fc820e01a9c..837ff5a4cb4 100644 --- a/src/main/java/seedu/address/ui/UiPart.java +++ b/src/main/java/seedu/internship/ui/UiPart.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.internship.ui; import static java.util.Objects.requireNonNull; @@ -6,7 +6,7 @@ import java.net.URL; import javafx.fxml.FXMLLoader; -import seedu.address.MainApp; +import seedu.internship.MainApp; /** * Represents a distinct part of the UI. e.g. Windows, dialogs, panels, status bars, etc. diff --git a/src/main/resources/images/internbuddy-help.png b/src/main/resources/images/internbuddy-help.png new file mode 100644 index 00000000000..507a3b94b23 Binary files /dev/null and b/src/main/resources/images/internbuddy-help.png differ diff --git a/src/main/resources/images/internbuddy-hero.png b/src/main/resources/images/internbuddy-hero.png new file mode 100644 index 00000000000..c178020de00 Binary files /dev/null and b/src/main/resources/images/internbuddy-hero.png differ diff --git a/src/main/resources/images/internbuddy_32.png b/src/main/resources/images/internbuddy_32.png new file mode 100644 index 00000000000..029280b3d3c Binary files /dev/null and b/src/main/resources/images/internbuddy_32.png differ diff --git a/src/main/resources/images/light_bulb.png b/src/main/resources/images/light_bulb.png new file mode 100644 index 00000000000..2135cc72bee Binary files /dev/null and b/src/main/resources/images/light_bulb.png differ diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 36e6b001cd8..93fccc43ace 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -113,7 +113,7 @@ .list-cell:filled:selected #cardPane { -fx-border-color: #3e7b91; - -fx-border-width: 1; + -fx-border-width: 0; } .list-cell .label { @@ -149,7 +149,7 @@ .result-display { -fx-background-color: transparent; -fx-font-family: "Segoe UI Light"; - -fx-font-size: 13pt; + -fx-font-size: 11pt; -fx-text-fill: white; } @@ -350,3 +350,35 @@ -fx-background-radius: 2; -fx-font-size: 11; } + +#statusLabel { + -fx-font-size: 14; +} + +.whiteCompanyNameLabel { + -fx-text-fill: white; + -fx-font-size: 20; +} + +.whiteLabel { + -fx-text-fill: white; +} + +.whiteText { + -fx-text-fill: white; +} + +.tipsTitle { + -fx-text-fill: black; + -fx-font-size: 16; +} + +.tipsText { + -fx-font-size: 14; +} + +.commentField { + -fx-text-fill: blue; +} + + diff --git a/src/main/resources/view/HelpWindow.fxml b/src/main/resources/view/HelpWindow.fxml index 5dea0adef70..35722b9db58 100644 --- a/src/main/resources/view/HelpWindow.fxml +++ b/src/main/resources/view/HelpWindow.fxml @@ -4,9 +4,17 @@ + + + + + + + + @@ -19,18 +27,413 @@ - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/InternshipDetailsCard.fxml b/src/main/resources/view/InternshipDetailsCard.fxml new file mode 100644 index 00000000000..22ece3ebba4 --- /dev/null +++ b/src/main/resources/view/InternshipDetailsCard.fxml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/InternshipListCard.fxml b/src/main/resources/view/InternshipListCard.fxml new file mode 100644 index 00000000000..396505c630c --- /dev/null +++ b/src/main/resources/view/InternshipListCard.fxml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/PersonListPanel.fxml b/src/main/resources/view/InternshipListPanel.fxml similarity index 76% rename from src/main/resources/view/PersonListPanel.fxml rename to src/main/resources/view/InternshipListPanel.fxml index 8836d323cc5..ec14fcc91f7 100644 --- a/src/main/resources/view/PersonListPanel.fxml +++ b/src/main/resources/view/InternshipListPanel.fxml @@ -4,5 +4,5 @@ - + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index a431648f6c0..6c4ee7028ec 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -6,13 +6,20 @@ - + + + + + + + + + - + @@ -33,25 +40,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/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json deleted file mode 100644 index 6a4d2b7181c..00000000000 --- a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "persons": [ { - "name": "Valid Person", - "phone": "9482424", - "email": "hans@example.com", - "address": "4th street" - }, { - "name": "Person With Invalid Phone Field", - "phone": "948asdf2424", - "email": "hans@example.com", - "address": "4th street" - } ] -} diff --git a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json deleted file mode 100644 index ccd21f7d1a9..00000000000 --- a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "persons": [ { - "name": "Person with invalid name field: Ha!ns Mu@ster", - "phone": "9482424", - "email": "hans@example.com", - "address": "4th street" - } ] -} diff --git a/src/test/data/JsonInternBuddyStorageTest/invalidAndValidInternshipInternBuddy.json b/src/test/data/JsonInternBuddyStorageTest/invalidAndValidInternshipInternBuddy.json new file mode 100644 index 00000000000..782a2677cee --- /dev/null +++ b/src/test/data/JsonInternBuddyStorageTest/invalidAndValidInternshipInternBuddy.json @@ -0,0 +1,13 @@ +{ + "internships": [ { + "companyName": "Valid Internship", + "role": "Software Engineer", + "status": "applied", + "date": "2023-02-01" + }, { + "companyName": "Internship With Invalid Status Field", + "role": "Software Engineer", + "status": "not-a-status", + "date": "2023-02-01" + } ] +} diff --git a/src/test/data/JsonInternBuddyStorageTest/invalidInternshipInternBuddy.json b/src/test/data/JsonInternBuddyStorageTest/invalidInternshipInternBuddy.json new file mode 100644 index 00000000000..45b09481feb --- /dev/null +++ b/src/test/data/JsonInternBuddyStorageTest/invalidInternshipInternBuddy.json @@ -0,0 +1,8 @@ +{ + "internships": [ { + "companyName": "Internship with invalid name field: Go!gle M@ets Te_ams", + "role": "Software Engineer", + "status": "applied", + "date": "2023-02-01" + } ] +} diff --git a/src/test/data/JsonAddressBookStorageTest/notJsonFormatAddressBook.json b/src/test/data/JsonInternBuddyStorageTest/notJsonFormatInternBuddy.json similarity index 100% rename from src/test/data/JsonAddressBookStorageTest/notJsonFormatAddressBook.json rename to src/test/data/JsonInternBuddyStorageTest/notJsonFormatInternBuddy.json diff --git a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json deleted file mode 100644 index 48831cc7674..00000000000 --- a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "persons": [ { - "name": "Alice Pauline", - "phone": "94351253", - "email": "alice@example.com", - "address": "123, Jurong West Ave 6, #08-111", - "tagged": [ "friends" ] - }, { - "name": "Alice Pauline", - "phone": "94351253", - "email": "pauline@example.com", - "address": "4th street" - } ] -} diff --git a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json deleted file mode 100644 index ad3f135ae42..00000000000 --- a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "persons": [ { - "name": "Hans Muster", - "phone": "9482424", - "email": "invalid@email!3e", - "address": "4th street" - } ] -} diff --git a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json deleted file mode 100644 index f10eddee12e..00000000000 --- a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "_comment": "AddressBook save file which contains the same Person values as in TypicalPersons#getTypicalAddressBook()", - "persons" : [ { - "name" : "Alice Pauline", - "phone" : "94351253", - "email" : "alice@example.com", - "address" : "123, Jurong West Ave 6, #08-111", - "tagged" : [ "friends" ] - }, { - "name" : "Benson Meier", - "phone" : "98765432", - "email" : "johnd@example.com", - "address" : "311, Clementi Ave 2, #02-25", - "tagged" : [ "owesMoney", "friends" ] - }, { - "name" : "Carl Kurz", - "phone" : "95352563", - "email" : "heinz@example.com", - "address" : "wall street", - "tagged" : [ ] - }, { - "name" : "Daniel Meier", - "phone" : "87652533", - "email" : "cornelia@example.com", - "address" : "10th street", - "tagged" : [ "friends" ] - }, { - "name" : "Elle Meyer", - "phone" : "9482224", - "email" : "werner@example.com", - "address" : "michegan ave", - "tagged" : [ ] - }, { - "name" : "Fiona Kunz", - "phone" : "9482427", - "email" : "lydia@example.com", - "address" : "little tokyo", - "tagged" : [ ] - }, { - "name" : "George Best", - "phone" : "9482442", - "email" : "anna@example.com", - "address" : "4th street", - "tagged" : [ ] - } ] -} diff --git a/src/test/data/JsonSerializableInternBuddyTest/duplicateInternshipInternBuddy.json b/src/test/data/JsonSerializableInternBuddyTest/duplicateInternshipInternBuddy.json new file mode 100644 index 00000000000..9c875a828e3 --- /dev/null +++ b/src/test/data/JsonSerializableInternBuddyTest/duplicateInternshipInternBuddy.json @@ -0,0 +1,17 @@ +{ + "internships": [ { + "companyName": "Google", + "role": "Software Engineer", + "status": "applied", + "date": "2023-02-01", + "comment": "My dream company", + "tagged": [ "Go" ] + }, { + "companyName": "Google", + "role": "Software Engineer", + "status": "applied", + "date": "2023-02-01", + "comment": "Not my dream company", + "tagged": [ "Java" ] + } ] +} diff --git a/src/test/data/JsonSerializableInternBuddyTest/invalidInternshipInternBuddy.json b/src/test/data/JsonSerializableInternBuddyTest/invalidInternshipInternBuddy.json new file mode 100644 index 00000000000..fd0bcc3ac5c --- /dev/null +++ b/src/test/data/JsonSerializableInternBuddyTest/invalidInternshipInternBuddy.json @@ -0,0 +1,10 @@ +{ + "internships": [ { + "companyName": "Google", + "role": "Software Engineer", + "status": "applied", + "date": "23rd January 2023", + "comment": "I love Google", + "tagged": [ "Go" ] + } ] +} diff --git a/src/test/data/JsonSerializableInternBuddyTest/typicalInternshipInternBuddy.json b/src/test/data/JsonSerializableInternBuddyTest/typicalInternshipInternBuddy.json new file mode 100644 index 00000000000..fa9f2bf4176 --- /dev/null +++ b/src/test/data/JsonSerializableInternBuddyTest/typicalInternshipInternBuddy.json @@ -0,0 +1,49 @@ +{ + "_comment": "InternBuddy save file which contains the same Internship values as in TypicalInternships#getTypicalInternBuddy()", + "internships" : [ { + "companyName" : "Amazon", + "role" : "Cloud Architect", + "status" : "assessment", + "date" : "2023-02-01", + "comment": "I love Amazon!", + "tagged" : [ "aws", "back" ] + }, { + "companyName" : "Food Panda", + "role" : "Back end Developer", + "status" : "assessment", + "comment": "I love Food Panda!", + "date" : "2023-02-02" + }, { + "companyName" : "Goldman", + "role" : "Cyber Security Analyst", + "status" : "offered", + "comment": "I love Goldman!", + "date" : "2023-02-03" + }, { + "companyName" : "Grab", + "role" : "Front end Engineer", + "status" : "rejected", + "comment": "I love Grab!", + "date" : "2023-02-04" + }, { + "companyName" : "Riot Games", + "role" : "Game Client Developer", + "status" : "interview", + "date" : "2023-02-05", + "comment": "I love Riot Games!", + "tagged" : [ "game", "developer" ] + }, { + "companyName" : "Samsung", + "role" : "Android Developer", + "status" : "applied", + "comment": "I love Samsung!", + "date" : "2023-02-06" + }, { + "companyName" : "Supercell Games", + "role" : "Game Designer", + "status" : "new", + "date" : "2023-02-07", + "comment": "I love Supercell Games!", + "tagged" : [ "design", "game" ] + } ] +} diff --git a/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json b/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json index 1037548a9cd..ad8da7d273a 100644 --- a/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json +++ b/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json @@ -9,5 +9,5 @@ "z" : 99 } }, - "addressBookFilePath" : "addressbook.json" + "internBuddyFilePath" : "internbuddy.json" } diff --git a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json index b819bed900a..3816f2ff2bf 100644 --- a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json +++ b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json @@ -7,5 +7,5 @@ "y" : 100 } }, - "addressBookFilePath" : "addressbook.json" + "internBuddyFilePath" : "internbuddy.json" } diff --git a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java b/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java deleted file mode 100644 index cb8714bb055..00000000000 --- a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package seedu.address.logic.commands; - -import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; -import seedu.address.testutil.PersonBuilder; - -/** - * Contains integration tests (interaction with the Model) for {@code AddCommand}. - */ -public class AddCommandIntegrationTest { - - private Model model; - - @BeforeEach - public void setUp() { - model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - } - - @Test - public void execute_newPerson_success() { - Person validPerson = new PersonBuilder().build(); - - Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - expectedModel.addPerson(validPerson); - - assertCommandSuccess(new AddCommand(validPerson), model, - String.format(AddCommand.MESSAGE_SUCCESS, validPerson), expectedModel); - } - - @Test - public void execute_duplicatePerson_throwsCommandException() { - Person personInList = model.getAddressBook().getPersonList().get(0); - assertCommandFailure(new AddCommand(personInList), model, AddCommand.MESSAGE_DUPLICATE_PERSON); - } - -} diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java deleted file mode 100644 index 5865713d5dd..00000000000 --- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java +++ /dev/null @@ -1,194 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.function.Predicate; - -import org.junit.jupiter.api.Test; - -import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.person.Person; -import seedu.address.testutil.PersonBuilder; - -public class AddCommandTest { - - @Test - public void constructor_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new AddCommand(null)); - } - - @Test - public void execute_personAcceptedByModel_addSuccessful() throws Exception { - ModelStubAcceptingPersonAdded modelStub = new ModelStubAcceptingPersonAdded(); - Person validPerson = new PersonBuilder().build(); - - CommandResult commandResult = new AddCommand(validPerson).execute(modelStub); - - assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, validPerson), commandResult.getFeedbackToUser()); - assertEquals(Arrays.asList(validPerson), modelStub.personsAdded); - } - - @Test - public void execute_duplicatePerson_throwsCommandException() { - Person validPerson = new PersonBuilder().build(); - AddCommand addCommand = new AddCommand(validPerson); - ModelStub modelStub = new ModelStubWithPerson(validPerson); - - assertThrows(CommandException.class, AddCommand.MESSAGE_DUPLICATE_PERSON, () -> addCommand.execute(modelStub)); - } - - @Test - public void equals() { - Person alice = new PersonBuilder().withName("Alice").build(); - Person bob = new PersonBuilder().withName("Bob").build(); - AddCommand addAliceCommand = new AddCommand(alice); - AddCommand addBobCommand = new AddCommand(bob); - - // same object -> returns true - assertTrue(addAliceCommand.equals(addAliceCommand)); - - // same values -> returns true - AddCommand addAliceCommandCopy = new AddCommand(alice); - assertTrue(addAliceCommand.equals(addAliceCommandCopy)); - - // different types -> returns false - assertFalse(addAliceCommand.equals(1)); - - // null -> returns false - assertFalse(addAliceCommand.equals(null)); - - // different person -> returns false - assertFalse(addAliceCommand.equals(addBobCommand)); - } - - /** - * A default model stub that have all of the methods failing. - */ - private class ModelStub implements Model { - @Override - public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { - throw new AssertionError("This method should not be called."); - } - - @Override - public ReadOnlyUserPrefs getUserPrefs() { - throw new AssertionError("This method should not be called."); - } - - @Override - public GuiSettings getGuiSettings() { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setGuiSettings(GuiSettings guiSettings) { - throw new AssertionError("This method should not be called."); - } - - @Override - public Path getAddressBookFilePath() { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setAddressBookFilePath(Path addressBookFilePath) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void addPerson(Person person) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setAddressBook(ReadOnlyAddressBook newData) { - throw new AssertionError("This method should not be called."); - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - throw new AssertionError("This method should not be called."); - } - - @Override - public boolean hasPerson(Person person) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void deletePerson(Person target) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setPerson(Person target, Person editedPerson) { - throw new AssertionError("This method should not be called."); - } - - @Override - public ObservableList getFilteredPersonList() { - throw new AssertionError("This method should not be called."); - } - - @Override - public void updateFilteredPersonList(Predicate predicate) { - throw new AssertionError("This method should not be called."); - } - } - - /** - * A Model stub that contains a single person. - */ - private class ModelStubWithPerson extends ModelStub { - private final Person person; - - ModelStubWithPerson(Person person) { - requireNonNull(person); - this.person = person; - } - - @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return this.person.isSamePerson(person); - } - } - - /** - * A Model stub that always accept the person being added. - */ - private class ModelStubAcceptingPersonAdded extends ModelStub { - final ArrayList personsAdded = new ArrayList<>(); - - @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return personsAdded.stream().anyMatch(person::isSamePerson); - } - - @Override - public void addPerson(Person person) { - requireNonNull(person); - personsAdded.add(person); - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - return new AddressBook(); - } - } - -} diff --git a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java b/src/test/java/seedu/address/logic/commands/ClearCommandTest.java deleted file mode 100644 index 80d9110c03a..00000000000 --- a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package seedu.address.logic.commands; - -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import org.junit.jupiter.api.Test; - -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; - -public class ClearCommandTest { - - @Test - public void execute_emptyAddressBook_success() { - Model model = new ModelManager(); - Model expectedModel = new ModelManager(); - - assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); - } - - @Test - public void execute_nonEmptyAddressBook_success() { - Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - expectedModel.setAddressBook(new AddressBook()); - - assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); - } - -} diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java deleted file mode 100644 index 643a1d08069..00000000000 --- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java +++ /dev/null @@ -1,128 +0,0 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import static seedu.address.testutil.Assert.assertThrows; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.model.person.Person; -import seedu.address.testutil.EditPersonDescriptorBuilder; - -/** - * Contains helper methods for testing commands. - */ -public class CommandTestUtil { - - public static final String VALID_NAME_AMY = "Amy Bee"; - public static final String VALID_NAME_BOB = "Bob Choo"; - public static final String VALID_PHONE_AMY = "11111111"; - public static final String VALID_PHONE_BOB = "22222222"; - public static final String VALID_EMAIL_AMY = "amy@example.com"; - public static final String VALID_EMAIL_BOB = "bob@example.com"; - public static final String VALID_ADDRESS_AMY = "Block 312, Amy Street 1"; - public static final String VALID_ADDRESS_BOB = "Block 123, Bobby Street 3"; - public static final String VALID_TAG_HUSBAND = "husband"; - public static final String VALID_TAG_FRIEND = "friend"; - - public static final String NAME_DESC_AMY = " " + PREFIX_NAME + VALID_NAME_AMY; - public static final String NAME_DESC_BOB = " " + PREFIX_NAME + VALID_NAME_BOB; - public static final String PHONE_DESC_AMY = " " + PREFIX_PHONE + VALID_PHONE_AMY; - public static final String PHONE_DESC_BOB = " " + PREFIX_PHONE + VALID_PHONE_BOB; - public static final String EMAIL_DESC_AMY = " " + PREFIX_EMAIL + VALID_EMAIL_AMY; - public static final String EMAIL_DESC_BOB = " " + PREFIX_EMAIL + VALID_EMAIL_BOB; - public static final String ADDRESS_DESC_AMY = " " + PREFIX_ADDRESS + VALID_ADDRESS_AMY; - public static final String ADDRESS_DESC_BOB = " " + PREFIX_ADDRESS + VALID_ADDRESS_BOB; - public static final String TAG_DESC_FRIEND = " " + PREFIX_TAG + VALID_TAG_FRIEND; - public static final String TAG_DESC_HUSBAND = " " + PREFIX_TAG + VALID_TAG_HUSBAND; - - public static final String INVALID_NAME_DESC = " " + PREFIX_NAME + "James&"; // '&' not allowed in names - public static final String INVALID_PHONE_DESC = " " + PREFIX_PHONE + "911a"; // 'a' not allowed in phones - public static final String INVALID_EMAIL_DESC = " " + PREFIX_EMAIL + "bob!yahoo"; // missing '@' symbol - public static final String INVALID_ADDRESS_DESC = " " + PREFIX_ADDRESS; // empty string not allowed for addresses - public static final String INVALID_TAG_DESC = " " + PREFIX_TAG + "hubby*"; // '*' not allowed in tags - - public static final String PREAMBLE_WHITESPACE = "\t \r \n"; - public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble"; - - public static final EditCommand.EditPersonDescriptor DESC_AMY; - public static final EditCommand.EditPersonDescriptor DESC_BOB; - - static { - DESC_AMY = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY) - .withPhone(VALID_PHONE_AMY).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY) - .withTags(VALID_TAG_FRIEND).build(); - DESC_BOB = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB) - .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB) - .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build(); - } - - /** - * Executes the given {@code command}, confirms that
- * - the returned {@link CommandResult} matches {@code expectedCommandResult}
- * - the {@code actualModel} matches {@code expectedModel} - */ - public static void assertCommandSuccess(Command command, Model actualModel, CommandResult expectedCommandResult, - Model expectedModel) { - try { - CommandResult result = command.execute(actualModel); - assertEquals(expectedCommandResult, result); - assertEquals(expectedModel, actualModel); - } catch (CommandException ce) { - throw new AssertionError("Execution of command should not fail.", ce); - } - } - - /** - * Convenience wrapper to {@link #assertCommandSuccess(Command, Model, CommandResult, Model)} - * that takes a string {@code expectedMessage}. - */ - public static void assertCommandSuccess(Command command, Model actualModel, String expectedMessage, - Model expectedModel) { - CommandResult expectedCommandResult = new CommandResult(expectedMessage); - assertCommandSuccess(command, actualModel, expectedCommandResult, expectedModel); - } - - /** - * Executes the given {@code command}, confirms that
- * - a {@code CommandException} is thrown
- * - the CommandException message matches {@code expectedMessage}
- * - the address book, filtered person list and selected person in {@code actualModel} remain unchanged - */ - public static void assertCommandFailure(Command command, Model actualModel, String expectedMessage) { - // we are unable to defensively copy the model for comparison later, so we can - // only do so by copying its components. - AddressBook expectedAddressBook = new AddressBook(actualModel.getAddressBook()); - List expectedFilteredList = new ArrayList<>(actualModel.getFilteredPersonList()); - - assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel)); - assertEquals(expectedAddressBook, actualModel.getAddressBook()); - assertEquals(expectedFilteredList, actualModel.getFilteredPersonList()); - } - /** - * Updates {@code model}'s filtered list to show only the person at the given {@code targetIndex} in the - * {@code model}'s address book. - */ - public static void showPersonAtIndex(Model model, Index targetIndex) { - assertTrue(targetIndex.getZeroBased() < model.getFilteredPersonList().size()); - - Person person = model.getFilteredPersonList().get(targetIndex.getZeroBased()); - final String[] splitName = person.getName().fullName.split("\\s+"); - model.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(splitName[0]))); - - assertEquals(1, model.getFilteredPersonList().size()); - } - -} diff --git a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java deleted file mode 100644 index 45a8c910ba1..00000000000 --- a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java +++ /dev/null @@ -1,109 +0,0 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; - -/** - * Contains integration tests (interaction with the Model) and unit tests for - * {@code DeleteCommand}. - */ -public class DeleteCommandTest { - - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - - @Test - public void execute_validIndexUnfilteredList_success() { - Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON); - - String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); - - ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - expectedModel.deletePerson(personToDelete); - - assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_invalidIndexUnfilteredList_throwsCommandException() { - Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); - DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex); - - assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - @Test - public void execute_validIndexFilteredList_success() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - - Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON); - - String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); - - Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - expectedModel.deletePerson(personToDelete); - showNoPerson(expectedModel); - - assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_invalidIndexFilteredList_throwsCommandException() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - - Index outOfBoundIndex = INDEX_SECOND_PERSON; - // ensures that outOfBoundIndex is still in bounds of address book list - assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size()); - - DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex); - - assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - @Test - public void equals() { - DeleteCommand deleteFirstCommand = new DeleteCommand(INDEX_FIRST_PERSON); - DeleteCommand deleteSecondCommand = new DeleteCommand(INDEX_SECOND_PERSON); - - // same object -> returns true - assertTrue(deleteFirstCommand.equals(deleteFirstCommand)); - - // same values -> returns true - DeleteCommand deleteFirstCommandCopy = new DeleteCommand(INDEX_FIRST_PERSON); - assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy)); - - // different types -> returns false - assertFalse(deleteFirstCommand.equals(1)); - - // null -> returns false - assertFalse(deleteFirstCommand.equals(null)); - - // different person -> returns false - assertFalse(deleteFirstCommand.equals(deleteSecondCommand)); - } - - /** - * Updates {@code model}'s filtered list to show no one. - */ - private void showNoPerson(Model model) { - model.updateFilteredPersonList(p -> false); - - assertTrue(model.getFilteredPersonList().isEmpty()); - } -} diff --git a/src/test/java/seedu/address/logic/commands/EditCommandTest.java b/src/test/java/seedu/address/logic/commands/EditCommandTest.java deleted file mode 100644 index 214c6c2507b..00000000000 --- a/src/test/java/seedu/address/logic/commands/EditCommandTest.java +++ /dev/null @@ -1,173 +0,0 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; -import seedu.address.testutil.EditPersonDescriptorBuilder; -import seedu.address.testutil.PersonBuilder; - -/** - * Contains integration tests (interaction with the Model) and unit tests for EditCommand. - */ -public class EditCommandTest { - - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - - @Test - public void execute_allFieldsSpecifiedUnfilteredList_success() { - Person editedPerson = new PersonBuilder().build(); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(editedPerson).build(); - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, descriptor); - - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson); - - assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_someFieldsSpecifiedUnfilteredList_success() { - Index indexLastPerson = Index.fromOneBased(model.getFilteredPersonList().size()); - Person lastPerson = model.getFilteredPersonList().get(indexLastPerson.getZeroBased()); - - PersonBuilder personInList = new PersonBuilder(lastPerson); - Person editedPerson = personInList.withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB) - .withTags(VALID_TAG_HUSBAND).build(); - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB) - .withPhone(VALID_PHONE_BOB).withTags(VALID_TAG_HUSBAND).build(); - EditCommand editCommand = new EditCommand(indexLastPerson, descriptor); - - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - expectedModel.setPerson(lastPerson, editedPerson); - - assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_noFieldSpecifiedUnfilteredList_success() { - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, new EditPersonDescriptor()); - Person editedPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - - assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_filteredList_success() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - - Person personInFilteredList = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - Person editedPerson = new PersonBuilder(personInFilteredList).withName(VALID_NAME_BOB).build(); - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, - new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build()); - - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson); - - assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_duplicatePersonUnfilteredList_failure() { - Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(firstPerson).build(); - EditCommand editCommand = new EditCommand(INDEX_SECOND_PERSON, descriptor); - - assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_PERSON); - } - - @Test - public void execute_duplicatePersonFilteredList_failure() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - - // edit person in filtered list into a duplicate in address book - Person personInList = model.getAddressBook().getPersonList().get(INDEX_SECOND_PERSON.getZeroBased()); - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, - new EditPersonDescriptorBuilder(personInList).build()); - - assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_PERSON); - } - - @Test - public void execute_invalidPersonIndexUnfilteredList_failure() { - Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build(); - EditCommand editCommand = new EditCommand(outOfBoundIndex, descriptor); - - assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - /** - * Edit filtered list where index is larger than size of filtered list, - * but smaller than size of address book - */ - @Test - public void execute_invalidPersonIndexFilteredList_failure() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - Index outOfBoundIndex = INDEX_SECOND_PERSON; - // ensures that outOfBoundIndex is still in bounds of address book list - assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size()); - - EditCommand editCommand = new EditCommand(outOfBoundIndex, - new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build()); - - assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - @Test - public void equals() { - final EditCommand standardCommand = new EditCommand(INDEX_FIRST_PERSON, DESC_AMY); - - // same values -> returns true - EditPersonDescriptor copyDescriptor = new EditPersonDescriptor(DESC_AMY); - EditCommand commandWithSameValues = new EditCommand(INDEX_FIRST_PERSON, copyDescriptor); - assertTrue(standardCommand.equals(commandWithSameValues)); - - // same object -> returns true - assertTrue(standardCommand.equals(standardCommand)); - - // null -> returns false - assertFalse(standardCommand.equals(null)); - - // different types -> returns false - assertFalse(standardCommand.equals(new ClearCommand())); - - // different index -> returns false - assertFalse(standardCommand.equals(new EditCommand(INDEX_SECOND_PERSON, DESC_AMY))); - - // different descriptor -> returns false - assertFalse(standardCommand.equals(new EditCommand(INDEX_FIRST_PERSON, DESC_BOB))); - } - -} diff --git a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java b/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java deleted file mode 100644 index e0288792e72..00000000000 --- a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.testutil.EditPersonDescriptorBuilder; - -public class EditPersonDescriptorTest { - - @Test - public void equals() { - // same values -> returns true - EditPersonDescriptor descriptorWithSameValues = new EditPersonDescriptor(DESC_AMY); - assertTrue(DESC_AMY.equals(descriptorWithSameValues)); - - // same object -> returns true - assertTrue(DESC_AMY.equals(DESC_AMY)); - - // null -> returns false - assertFalse(DESC_AMY.equals(null)); - - // different types -> returns false - assertFalse(DESC_AMY.equals(5)); - - // different values -> returns false - assertFalse(DESC_AMY.equals(DESC_BOB)); - - // different name -> returns false - EditPersonDescriptor editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withName(VALID_NAME_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different phone -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withPhone(VALID_PHONE_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different email -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withEmail(VALID_EMAIL_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different address -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withAddress(VALID_ADDRESS_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different tags -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withTags(VALID_TAG_HUSBAND).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - } -} diff --git a/src/test/java/seedu/address/logic/commands/FindCommandTest.java b/src/test/java/seedu/address/logic/commands/FindCommandTest.java deleted file mode 100644 index 9b15db28bbb..00000000000 --- a/src/test/java/seedu/address/logic/commands/FindCommandTest.java +++ /dev/null @@ -1,83 +0,0 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.commons.core.Messages.MESSAGE_PERSONS_LISTED_OVERVIEW; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.CARL; -import static seedu.address.testutil.TypicalPersons.ELLE; -import static seedu.address.testutil.TypicalPersons.FIONA; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import java.util.Arrays; -import java.util.Collections; - -import org.junit.jupiter.api.Test; - -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.NameContainsKeywordsPredicate; - -/** - * Contains integration tests (interaction with the Model) for {@code FindCommand}. - */ -public class FindCommandTest { - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - private Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - - @Test - public void equals() { - NameContainsKeywordsPredicate firstPredicate = - new NameContainsKeywordsPredicate(Collections.singletonList("first")); - NameContainsKeywordsPredicate secondPredicate = - new NameContainsKeywordsPredicate(Collections.singletonList("second")); - - FindCommand findFirstCommand = new FindCommand(firstPredicate); - FindCommand findSecondCommand = new FindCommand(secondPredicate); - - // same object -> returns true - assertTrue(findFirstCommand.equals(findFirstCommand)); - - // same values -> returns true - FindCommand findFirstCommandCopy = new FindCommand(firstPredicate); - assertTrue(findFirstCommand.equals(findFirstCommandCopy)); - - // different types -> returns false - assertFalse(findFirstCommand.equals(1)); - - // null -> returns false - assertFalse(findFirstCommand.equals(null)); - - // different person -> returns false - assertFalse(findFirstCommand.equals(findSecondCommand)); - } - - @Test - public void execute_zeroKeywords_noPersonFound() { - String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 0); - NameContainsKeywordsPredicate predicate = preparePredicate(" "); - FindCommand command = new FindCommand(predicate); - expectedModel.updateFilteredPersonList(predicate); - assertCommandSuccess(command, model, expectedMessage, expectedModel); - assertEquals(Collections.emptyList(), model.getFilteredPersonList()); - } - - @Test - public void execute_multipleKeywords_multiplePersonsFound() { - String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 3); - NameContainsKeywordsPredicate predicate = preparePredicate("Kurz Elle Kunz"); - FindCommand command = new FindCommand(predicate); - expectedModel.updateFilteredPersonList(predicate); - assertCommandSuccess(command, model, expectedMessage, expectedModel); - assertEquals(Arrays.asList(CARL, ELLE, FIONA), model.getFilteredPersonList()); - } - - /** - * Parses {@code userInput} into a {@code NameContainsKeywordsPredicate}. - */ - private NameContainsKeywordsPredicate preparePredicate(String userInput) { - return new NameContainsKeywordsPredicate(Arrays.asList(userInput.split("\\s+"))); - } -} diff --git a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java deleted file mode 100644 index 5cf487d7ebb..00000000000 --- a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java +++ /dev/null @@ -1,141 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY; -import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalPersons.AMY; -import static seedu.address.testutil.TypicalPersons.BOB; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -import seedu.address.testutil.PersonBuilder; - -public class AddCommandParserTest { - private AddCommandParser parser = new AddCommandParser(); - - @Test - public void parse_allFieldsPresent_success() { - Person expectedPerson = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND).build(); - - // whitespace only preamble - assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple names - last name accepted - assertParseSuccess(parser, NAME_DESC_AMY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple phones - last phone accepted - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_AMY + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple emails - last email accepted - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_AMY + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple addresses - last address accepted - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_AMY - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple tags - all accepted - Person expectedPersonMultipleTags = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND) - .build(); - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, new AddCommand(expectedPersonMultipleTags)); - } - - @Test - public void parse_optionalFieldsMissing_success() { - // zero tags - Person expectedPerson = new PersonBuilder(AMY).withTags().build(); - assertParseSuccess(parser, NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY + ADDRESS_DESC_AMY, - new AddCommand(expectedPerson)); - } - - @Test - public void parse_compulsoryFieldMissing_failure() { - String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); - - // missing name prefix - assertParseFailure(parser, VALID_NAME_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB, - expectedMessage); - - // missing phone prefix - assertParseFailure(parser, NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB, - expectedMessage); - - // missing email prefix - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB + ADDRESS_DESC_BOB, - expectedMessage); - - // missing address prefix - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + VALID_ADDRESS_BOB, - expectedMessage); - - // all prefixes missing - assertParseFailure(parser, VALID_NAME_BOB + VALID_PHONE_BOB + VALID_EMAIL_BOB + VALID_ADDRESS_BOB, - expectedMessage); - } - - @Test - public void parse_invalidValue_failure() { - // invalid name - assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Name.MESSAGE_CONSTRAINTS); - - // invalid phone - assertParseFailure(parser, NAME_DESC_BOB + INVALID_PHONE_DESC + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Phone.MESSAGE_CONSTRAINTS); - - // invalid email - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + INVALID_EMAIL_DESC + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Email.MESSAGE_CONSTRAINTS); - - // invalid address - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Address.MESSAGE_CONSTRAINTS); - - // invalid tag - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + INVALID_TAG_DESC + VALID_TAG_FRIEND, Tag.MESSAGE_CONSTRAINTS); - - // two invalid values, only first invalid value reported - assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC, - Name.MESSAGE_CONSTRAINTS); - - // non-empty preamble - assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, - String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); - } -} diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java deleted file mode 100644 index d9659205b57..00000000000 --- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java +++ /dev/null @@ -1,101 +0,0 @@ -package seedu.address.logic.parser; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.ClearCommand; -import seedu.address.logic.commands.DeleteCommand; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.logic.commands.ExitCommand; -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.commands.HelpCommand; -import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.model.person.Person; -import seedu.address.testutil.EditPersonDescriptorBuilder; -import seedu.address.testutil.PersonBuilder; -import seedu.address.testutil.PersonUtil; - -public class AddressBookParserTest { - - private final AddressBookParser parser = new AddressBookParser(); - - @Test - public void parseCommand_add() throws Exception { - Person person = new PersonBuilder().build(); - AddCommand command = (AddCommand) parser.parseCommand(PersonUtil.getAddCommand(person)); - assertEquals(new AddCommand(person), command); - } - - @Test - public void parseCommand_clear() throws Exception { - assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD) instanceof ClearCommand); - assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD + " 3") instanceof ClearCommand); - } - - @Test - public void parseCommand_delete() throws Exception { - DeleteCommand command = (DeleteCommand) parser.parseCommand( - DeleteCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased()); - assertEquals(new DeleteCommand(INDEX_FIRST_PERSON), command); - } - - @Test - public void parseCommand_edit() throws Exception { - Person person = new PersonBuilder().build(); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(person).build(); - EditCommand command = (EditCommand) parser.parseCommand(EditCommand.COMMAND_WORD + " " - + INDEX_FIRST_PERSON.getOneBased() + " " + PersonUtil.getEditPersonDescriptorDetails(descriptor)); - assertEquals(new EditCommand(INDEX_FIRST_PERSON, descriptor), command); - } - - @Test - public void parseCommand_exit() throws Exception { - assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD) instanceof ExitCommand); - assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD + " 3") instanceof ExitCommand); - } - - @Test - public void parseCommand_find() throws Exception { - List keywords = Arrays.asList("foo", "bar", "baz"); - FindCommand command = (FindCommand) parser.parseCommand( - FindCommand.COMMAND_WORD + " " + keywords.stream().collect(Collectors.joining(" "))); - assertEquals(new FindCommand(new NameContainsKeywordsPredicate(keywords)), command); - } - - @Test - public void parseCommand_help() throws Exception { - assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD) instanceof HelpCommand); - assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD + " 3") instanceof HelpCommand); - } - - @Test - public void parseCommand_list() throws Exception { - assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD) instanceof ListCommand); - assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD + " 3") instanceof ListCommand); - } - - @Test - public void parseCommand_unrecognisedInput_throwsParseException() { - assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE), () - -> parser.parseCommand("")); - } - - @Test - public void parseCommand_unknownCommand_throwsParseException() { - assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, () -> parser.parseCommand("unknownCommand")); - } -} diff --git a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java deleted file mode 100644 index 27eaec84450..00000000000 --- a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.DeleteCommand; - -/** - * As we are only doing white-box testing, our test cases do not cover path variations - * outside of the DeleteCommand code. For example, inputs "1" and "1 abc" take the - * same path through the DeleteCommand, and therefore we test only one of them. - * The path variation for those two cases occur inside the ParserUtil, and - * therefore should be covered by the ParserUtilTest. - */ -public class DeleteCommandParserTest { - - private DeleteCommandParser parser = new DeleteCommandParser(); - - @Test - public void parse_validArgs_returnsDeleteCommand() { - assertParseSuccess(parser, "1", new DeleteCommand(INDEX_FIRST_PERSON)); - } - - @Test - public void parse_invalidArgs_throwsParseException() { - assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE)); - } -} diff --git a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java deleted file mode 100644 index 2ff31522486..00000000000 --- a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java +++ /dev/null @@ -1,211 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_THIRD_PERSON; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -import seedu.address.testutil.EditPersonDescriptorBuilder; - -public class EditCommandParserTest { - - private static final String TAG_EMPTY = " " + PREFIX_TAG; - - private static final String MESSAGE_INVALID_FORMAT = - String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE); - - private EditCommandParser parser = new EditCommandParser(); - - @Test - public void parse_missingParts_failure() { - // no index specified - assertParseFailure(parser, VALID_NAME_AMY, MESSAGE_INVALID_FORMAT); - - // no field specified - assertParseFailure(parser, "1", EditCommand.MESSAGE_NOT_EDITED); - - // no index and no field specified - assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT); - } - - @Test - public void parse_invalidPreamble_failure() { - // negative index - assertParseFailure(parser, "-5" + NAME_DESC_AMY, MESSAGE_INVALID_FORMAT); - - // zero index - assertParseFailure(parser, "0" + NAME_DESC_AMY, MESSAGE_INVALID_FORMAT); - - // invalid arguments being parsed as preamble - assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT); - - // invalid prefix being parsed as preamble - assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT); - } - - @Test - public void parse_invalidValue_failure() { - assertParseFailure(parser, "1" + INVALID_NAME_DESC, Name.MESSAGE_CONSTRAINTS); // invalid name - assertParseFailure(parser, "1" + INVALID_PHONE_DESC, Phone.MESSAGE_CONSTRAINTS); // invalid phone - assertParseFailure(parser, "1" + INVALID_EMAIL_DESC, Email.MESSAGE_CONSTRAINTS); // invalid email - assertParseFailure(parser, "1" + INVALID_ADDRESS_DESC, Address.MESSAGE_CONSTRAINTS); // invalid address - assertParseFailure(parser, "1" + INVALID_TAG_DESC, Tag.MESSAGE_CONSTRAINTS); // invalid tag - - // invalid phone followed by valid email - assertParseFailure(parser, "1" + INVALID_PHONE_DESC + EMAIL_DESC_AMY, Phone.MESSAGE_CONSTRAINTS); - - // valid phone followed by invalid phone. The test case for invalid phone followed by valid phone - // is tested at {@code parse_invalidValueFollowedByValidValue_success()} - assertParseFailure(parser, "1" + PHONE_DESC_BOB + INVALID_PHONE_DESC, Phone.MESSAGE_CONSTRAINTS); - - // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Person} being edited, - // parsing it together with a valid tag results in error - assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_DESC_HUSBAND + TAG_EMPTY, Tag.MESSAGE_CONSTRAINTS); - assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_EMPTY + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS); - assertParseFailure(parser, "1" + TAG_EMPTY + TAG_DESC_FRIEND + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS); - - // multiple invalid values, but only the first invalid value is captured - assertParseFailure(parser, "1" + INVALID_NAME_DESC + INVALID_EMAIL_DESC + VALID_ADDRESS_AMY + VALID_PHONE_AMY, - Name.MESSAGE_CONSTRAINTS); - } - - @Test - public void parse_allFieldsSpecified_success() { - Index targetIndex = INDEX_SECOND_PERSON; - String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + TAG_DESC_HUSBAND - + EMAIL_DESC_AMY + ADDRESS_DESC_AMY + NAME_DESC_AMY + TAG_DESC_FRIEND; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY) - .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY) - .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_someFieldsSpecified_success() { - Index targetIndex = INDEX_FIRST_PERSON; - String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + EMAIL_DESC_AMY; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB) - .withEmail(VALID_EMAIL_AMY).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_oneFieldSpecified_success() { - // name - Index targetIndex = INDEX_THIRD_PERSON; - String userInput = targetIndex.getOneBased() + NAME_DESC_AMY; - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // phone - userInput = targetIndex.getOneBased() + PHONE_DESC_AMY; - descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_AMY).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // email - userInput = targetIndex.getOneBased() + EMAIL_DESC_AMY; - descriptor = new EditPersonDescriptorBuilder().withEmail(VALID_EMAIL_AMY).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // address - userInput = targetIndex.getOneBased() + ADDRESS_DESC_AMY; - descriptor = new EditPersonDescriptorBuilder().withAddress(VALID_ADDRESS_AMY).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // tags - userInput = targetIndex.getOneBased() + TAG_DESC_FRIEND; - descriptor = new EditPersonDescriptorBuilder().withTags(VALID_TAG_FRIEND).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_multipleRepeatedFields_acceptsLast() { - Index targetIndex = INDEX_FIRST_PERSON; - String userInput = targetIndex.getOneBased() + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY - + TAG_DESC_FRIEND + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY + TAG_DESC_FRIEND - + PHONE_DESC_BOB + ADDRESS_DESC_BOB + EMAIL_DESC_BOB + TAG_DESC_HUSBAND; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB) - .withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND) - .build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_invalidValueFollowedByValidValue_success() { - // no other valid values specified - Index targetIndex = INDEX_FIRST_PERSON; - String userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + PHONE_DESC_BOB; - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // other valid values specified - userInput = targetIndex.getOneBased() + EMAIL_DESC_BOB + INVALID_PHONE_DESC + ADDRESS_DESC_BOB - + PHONE_DESC_BOB; - descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB) - .withAddress(VALID_ADDRESS_BOB).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_resetTags_success() { - Index targetIndex = INDEX_THIRD_PERSON; - String userInput = targetIndex.getOneBased() + TAG_EMPTY; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withTags().build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } -} diff --git a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java deleted file mode 100644 index 70f4f0e79c4..00000000000 --- a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; - -import java.util.Arrays; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.FindCommand; -import seedu.address.model.person.NameContainsKeywordsPredicate; - -public class FindCommandParserTest { - - private FindCommandParser parser = new FindCommandParser(); - - @Test - public void parse_emptyArg_throwsParseException() { - assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); - } - - @Test - public void parse_validArgs_returnsFindCommand() { - // no leading and trailing whitespaces - FindCommand expectedFindCommand = - new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob"))); - assertParseSuccess(parser, "Alice Bob", expectedFindCommand); - - // multiple whitespaces between keywords - assertParseSuccess(parser, " \n Alice \n \t Bob \t", expectedFindCommand); - } - -} diff --git a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java deleted file mode 100644 index 4256788b1a7..00000000000 --- a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java +++ /dev/null @@ -1,196 +0,0 @@ -package seedu.address.logic.parser; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.parser.ParserUtil.MESSAGE_INVALID_INDEX; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -public class ParserUtilTest { - private static final String INVALID_NAME = "R@chel"; - private static final String INVALID_PHONE = "+651234"; - private static final String INVALID_ADDRESS = " "; - private static final String INVALID_EMAIL = "example.com"; - private static final String INVALID_TAG = "#friend"; - - private static final String VALID_NAME = "Rachel Walker"; - private static final String VALID_PHONE = "123456"; - private static final String VALID_ADDRESS = "123 Main Street #0505"; - private static final String VALID_EMAIL = "rachel@example.com"; - private static final String VALID_TAG_1 = "friend"; - private static final String VALID_TAG_2 = "neighbour"; - - private static final String WHITESPACE = " \t\r\n"; - - @Test - public void parseIndex_invalidInput_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseIndex("10 a")); - } - - @Test - public void parseIndex_outOfRangeInput_throwsParseException() { - assertThrows(ParseException.class, MESSAGE_INVALID_INDEX, () - -> ParserUtil.parseIndex(Long.toString(Integer.MAX_VALUE + 1))); - } - - @Test - public void parseIndex_validInput_success() throws Exception { - // No whitespaces - assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex("1")); - - // Leading and trailing whitespaces - assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex(" 1 ")); - } - - @Test - public void parseName_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseName((String) null)); - } - - @Test - public void parseName_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseName(INVALID_NAME)); - } - - @Test - public void parseName_validValueWithoutWhitespace_returnsName() throws Exception { - Name expectedName = new Name(VALID_NAME); - assertEquals(expectedName, ParserUtil.parseName(VALID_NAME)); - } - - @Test - public void parseName_validValueWithWhitespace_returnsTrimmedName() throws Exception { - String nameWithWhitespace = WHITESPACE + VALID_NAME + WHITESPACE; - Name expectedName = new Name(VALID_NAME); - assertEquals(expectedName, ParserUtil.parseName(nameWithWhitespace)); - } - - @Test - public void parsePhone_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parsePhone((String) null)); - } - - @Test - public void parsePhone_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parsePhone(INVALID_PHONE)); - } - - @Test - public void parsePhone_validValueWithoutWhitespace_returnsPhone() throws Exception { - Phone expectedPhone = new Phone(VALID_PHONE); - assertEquals(expectedPhone, ParserUtil.parsePhone(VALID_PHONE)); - } - - @Test - public void parsePhone_validValueWithWhitespace_returnsTrimmedPhone() throws Exception { - String phoneWithWhitespace = WHITESPACE + VALID_PHONE + WHITESPACE; - Phone expectedPhone = new Phone(VALID_PHONE); - assertEquals(expectedPhone, ParserUtil.parsePhone(phoneWithWhitespace)); - } - - @Test - public void parseAddress_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseAddress((String) null)); - } - - @Test - public void parseAddress_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseAddress(INVALID_ADDRESS)); - } - - @Test - public void parseAddress_validValueWithoutWhitespace_returnsAddress() throws Exception { - Address expectedAddress = new Address(VALID_ADDRESS); - assertEquals(expectedAddress, ParserUtil.parseAddress(VALID_ADDRESS)); - } - - @Test - public void parseAddress_validValueWithWhitespace_returnsTrimmedAddress() throws Exception { - String addressWithWhitespace = WHITESPACE + VALID_ADDRESS + WHITESPACE; - Address expectedAddress = new Address(VALID_ADDRESS); - assertEquals(expectedAddress, ParserUtil.parseAddress(addressWithWhitespace)); - } - - @Test - public void parseEmail_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseEmail((String) null)); - } - - @Test - public void parseEmail_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseEmail(INVALID_EMAIL)); - } - - @Test - public void parseEmail_validValueWithoutWhitespace_returnsEmail() throws Exception { - Email expectedEmail = new Email(VALID_EMAIL); - assertEquals(expectedEmail, ParserUtil.parseEmail(VALID_EMAIL)); - } - - @Test - public void parseEmail_validValueWithWhitespace_returnsTrimmedEmail() throws Exception { - String emailWithWhitespace = WHITESPACE + VALID_EMAIL + WHITESPACE; - Email expectedEmail = new Email(VALID_EMAIL); - assertEquals(expectedEmail, ParserUtil.parseEmail(emailWithWhitespace)); - } - - @Test - public void parseTag_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseTag(null)); - } - - @Test - public void parseTag_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseTag(INVALID_TAG)); - } - - @Test - public void parseTag_validValueWithoutWhitespace_returnsTag() throws Exception { - Tag expectedTag = new Tag(VALID_TAG_1); - assertEquals(expectedTag, ParserUtil.parseTag(VALID_TAG_1)); - } - - @Test - public void parseTag_validValueWithWhitespace_returnsTrimmedTag() throws Exception { - String tagWithWhitespace = WHITESPACE + VALID_TAG_1 + WHITESPACE; - Tag expectedTag = new Tag(VALID_TAG_1); - assertEquals(expectedTag, ParserUtil.parseTag(tagWithWhitespace)); - } - - @Test - public void parseTags_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseTags(null)); - } - - @Test - public void parseTags_collectionWithInvalidTags_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, INVALID_TAG))); - } - - @Test - public void parseTags_emptyCollection_returnsEmptySet() throws Exception { - assertTrue(ParserUtil.parseTags(Collections.emptyList()).isEmpty()); - } - - @Test - public void parseTags_collectionWithValidTags_returnsTagSet() throws Exception { - Set actualTagSet = ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, VALID_TAG_2)); - Set expectedTagSet = new HashSet(Arrays.asList(new Tag(VALID_TAG_1), new Tag(VALID_TAG_2))); - - assertEquals(expectedTagSet, actualTagSet); - } -} diff --git a/src/test/java/seedu/address/model/AddressBookTest.java b/src/test/java/seedu/address/model/AddressBookTest.java deleted file mode 100644 index 87782528ecd..00000000000 --- a/src/test/java/seedu/address/model/AddressBookTest.java +++ /dev/null @@ -1,102 +0,0 @@ -package seedu.address.model; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.testutil.PersonBuilder; - -public class AddressBookTest { - - private final AddressBook addressBook = new AddressBook(); - - @Test - public void constructor() { - assertEquals(Collections.emptyList(), addressBook.getPersonList()); - } - - @Test - public void resetData_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> addressBook.resetData(null)); - } - - @Test - public void resetData_withValidReadOnlyAddressBook_replacesData() { - AddressBook newData = getTypicalAddressBook(); - addressBook.resetData(newData); - assertEquals(newData, addressBook); - } - - @Test - public void resetData_withDuplicatePersons_throwsDuplicatePersonException() { - // Two persons with the same identity fields - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - List newPersons = Arrays.asList(ALICE, editedAlice); - AddressBookStub newData = new AddressBookStub(newPersons); - - assertThrows(DuplicatePersonException.class, () -> addressBook.resetData(newData)); - } - - @Test - public void hasPerson_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> addressBook.hasPerson(null)); - } - - @Test - public void hasPerson_personNotInAddressBook_returnsFalse() { - assertFalse(addressBook.hasPerson(ALICE)); - } - - @Test - public void hasPerson_personInAddressBook_returnsTrue() { - addressBook.addPerson(ALICE); - assertTrue(addressBook.hasPerson(ALICE)); - } - - @Test - public void hasPerson_personWithSameIdentityFieldsInAddressBook_returnsTrue() { - addressBook.addPerson(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - assertTrue(addressBook.hasPerson(editedAlice)); - } - - @Test - public void getPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> addressBook.getPersonList().remove(0)); - } - - /** - * A stub ReadOnlyAddressBook whose persons list can violate interface constraints. - */ - private static class AddressBookStub implements ReadOnlyAddressBook { - private final ObservableList persons = FXCollections.observableArrayList(); - - AddressBookStub(Collection persons) { - this.persons.setAll(persons); - } - - @Override - public ObservableList getPersonList() { - return persons; - } - } - -} diff --git a/src/test/java/seedu/address/model/ModelManagerTest.java b/src/test/java/seedu/address/model/ModelManagerTest.java deleted file mode 100644 index 2cf1418d116..00000000000 --- a/src/test/java/seedu/address/model/ModelManagerTest.java +++ /dev/null @@ -1,132 +0,0 @@ -package seedu.address.model; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.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))); - } -} diff --git a/src/test/java/seedu/address/model/person/AddressTest.java b/src/test/java/seedu/address/model/person/AddressTest.java deleted file mode 100644 index dcd3be87b3a..00000000000 --- a/src/test/java/seedu/address/model/person/AddressTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class AddressTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Address(null)); - } - - @Test - public void constructor_invalidAddress_throwsIllegalArgumentException() { - String invalidAddress = ""; - assertThrows(IllegalArgumentException.class, () -> new Address(invalidAddress)); - } - - @Test - public void isValidAddress() { - // null address - assertThrows(NullPointerException.class, () -> Address.isValidAddress(null)); - - // invalid addresses - assertFalse(Address.isValidAddress("")); // empty string - assertFalse(Address.isValidAddress(" ")); // spaces only - - // valid addresses - assertTrue(Address.isValidAddress("Blk 456, Den Road, #01-355")); - assertTrue(Address.isValidAddress("-")); // one character - assertTrue(Address.isValidAddress("Leng Inc; 1234 Market St; San Francisco CA 2349879; USA")); // long address - } -} diff --git a/src/test/java/seedu/address/model/person/EmailTest.java b/src/test/java/seedu/address/model/person/EmailTest.java deleted file mode 100644 index bbcc6c8c98e..00000000000 --- a/src/test/java/seedu/address/model/person/EmailTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class EmailTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Email(null)); - } - - @Test - public void constructor_invalidEmail_throwsIllegalArgumentException() { - String invalidEmail = ""; - assertThrows(IllegalArgumentException.class, () -> new Email(invalidEmail)); - } - - @Test - public void isValidEmail() { - // null email - assertThrows(NullPointerException.class, () -> Email.isValidEmail(null)); - - // blank email - assertFalse(Email.isValidEmail("")); // empty string - assertFalse(Email.isValidEmail(" ")); // spaces only - - // missing parts - assertFalse(Email.isValidEmail("@example.com")); // missing local part - assertFalse(Email.isValidEmail("peterjackexample.com")); // missing '@' symbol - assertFalse(Email.isValidEmail("peterjack@")); // missing domain name - - // invalid parts - assertFalse(Email.isValidEmail("peterjack@-")); // invalid domain name - assertFalse(Email.isValidEmail("peterjack@exam_ple.com")); // underscore in domain name - assertFalse(Email.isValidEmail("peter jack@example.com")); // spaces in local part - assertFalse(Email.isValidEmail("peterjack@exam ple.com")); // spaces in domain name - assertFalse(Email.isValidEmail(" peterjack@example.com")); // leading space - assertFalse(Email.isValidEmail("peterjack@example.com ")); // trailing space - assertFalse(Email.isValidEmail("peterjack@@example.com")); // double '@' symbol - assertFalse(Email.isValidEmail("peter@jack@example.com")); // '@' symbol in local part - assertFalse(Email.isValidEmail("-peterjack@example.com")); // local part starts with a hyphen - assertFalse(Email.isValidEmail("peterjack-@example.com")); // local part ends with a hyphen - assertFalse(Email.isValidEmail("peter..jack@example.com")); // local part has two consecutive periods - assertFalse(Email.isValidEmail("peterjack@example@com")); // '@' symbol in domain name - assertFalse(Email.isValidEmail("peterjack@.example.com")); // domain name starts with a period - assertFalse(Email.isValidEmail("peterjack@example.com.")); // domain name ends with a period - assertFalse(Email.isValidEmail("peterjack@-example.com")); // domain name starts with a hyphen - assertFalse(Email.isValidEmail("peterjack@example.com-")); // domain name ends with a hyphen - assertFalse(Email.isValidEmail("peterjack@example.c")); // top level domain has less than two chars - - // valid email - assertTrue(Email.isValidEmail("PeterJack_1190@example.com")); // underscore in local part - 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 - assertTrue(Email.isValidEmail("e1234567@u.nus.edu")); // more than one period in domain - } -} diff --git a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java deleted file mode 100644 index f136664e017..00000000000 --- a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import seedu.address.testutil.PersonBuilder; - -public class NameContainsKeywordsPredicateTest { - - @Test - public void equals() { - List firstPredicateKeywordList = Collections.singletonList("first"); - List secondPredicateKeywordList = Arrays.asList("first", "second"); - - NameContainsKeywordsPredicate firstPredicate = new NameContainsKeywordsPredicate(firstPredicateKeywordList); - NameContainsKeywordsPredicate secondPredicate = new NameContainsKeywordsPredicate(secondPredicateKeywordList); - - // same object -> returns true - assertTrue(firstPredicate.equals(firstPredicate)); - - // same values -> returns true - NameContainsKeywordsPredicate firstPredicateCopy = new NameContainsKeywordsPredicate(firstPredicateKeywordList); - assertTrue(firstPredicate.equals(firstPredicateCopy)); - - // different types -> returns false - assertFalse(firstPredicate.equals(1)); - - // null -> returns false - assertFalse(firstPredicate.equals(null)); - - // different person -> returns false - assertFalse(firstPredicate.equals(secondPredicate)); - } - - @Test - public void test_nameContainsKeywords_returnsTrue() { - // One keyword - NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Collections.singletonList("Alice")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build())); - - // Multiple keywords - predicate = new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build())); - - // Only one matching keyword - predicate = new NameContainsKeywordsPredicate(Arrays.asList("Bob", "Carol")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Carol").build())); - - // Mixed-case keywords - predicate = new NameContainsKeywordsPredicate(Arrays.asList("aLIce", "bOB")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build())); - } - - @Test - public void test_nameDoesNotContainKeywords_returnsFalse() { - // Zero keywords - NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Collections.emptyList()); - assertFalse(predicate.test(new PersonBuilder().withName("Alice").build())); - - // Non-matching keyword - predicate = new NameContainsKeywordsPredicate(Arrays.asList("Carol")); - assertFalse(predicate.test(new PersonBuilder().withName("Alice Bob").build())); - - // Keywords match phone, email and address, but does not match name - predicate = new NameContainsKeywordsPredicate(Arrays.asList("12345", "alice@email.com", "Main", "Street")); - assertFalse(predicate.test(new PersonBuilder().withName("Alice").withPhone("12345") - .withEmail("alice@email.com").withAddress("Main Street").build())); - } -} diff --git a/src/test/java/seedu/address/model/person/NameTest.java b/src/test/java/seedu/address/model/person/NameTest.java deleted file mode 100644 index c9801392874..00000000000 --- a/src/test/java/seedu/address/model/person/NameTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class NameTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Name(null)); - } - - @Test - public void constructor_invalidName_throwsIllegalArgumentException() { - String invalidName = ""; - assertThrows(IllegalArgumentException.class, () -> new Name(invalidName)); - } - - @Test - public void isValidName() { - // null name - assertThrows(NullPointerException.class, () -> Name.isValidName(null)); - - // invalid name - assertFalse(Name.isValidName("")); // empty string - assertFalse(Name.isValidName(" ")); // spaces only - assertFalse(Name.isValidName("^")); // only non-alphanumeric characters - assertFalse(Name.isValidName("peter*")); // contains non-alphanumeric characters - - // valid name - assertTrue(Name.isValidName("peter jack")); // alphabets only - assertTrue(Name.isValidName("12345")); // numbers only - assertTrue(Name.isValidName("peter the 2nd")); // alphanumeric characters - assertTrue(Name.isValidName("Capital Tan")); // with capital letters - assertTrue(Name.isValidName("David Roger Jackson Ray Jr 2nd")); // long names - } -} diff --git a/src/test/java/seedu/address/model/person/PersonTest.java b/src/test/java/seedu/address/model/person/PersonTest.java deleted file mode 100644 index b29c097cfd4..00000000000 --- a/src/test/java/seedu/address/model/person/PersonTest.java +++ /dev/null @@ -1,91 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BOB; - -import org.junit.jupiter.api.Test; - -import seedu.address.testutil.PersonBuilder; - -public class PersonTest { - - @Test - public void asObservableList_modifyList_throwsUnsupportedOperationException() { - Person person = new PersonBuilder().build(); - assertThrows(UnsupportedOperationException.class, () -> person.getTags().remove(0)); - } - - @Test - public void isSamePerson() { - // same object -> returns true - assertTrue(ALICE.isSamePerson(ALICE)); - - // null -> returns false - assertFalse(ALICE.isSamePerson(null)); - - // same name, all other attributes different -> returns true - Person editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB) - .withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND).build(); - assertTrue(ALICE.isSamePerson(editedAlice)); - - // different name, all other attributes same -> returns false - editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build(); - assertFalse(ALICE.isSamePerson(editedAlice)); - - // name differs in case, all other attributes same -> returns false - Person editedBob = new PersonBuilder(BOB).withName(VALID_NAME_BOB.toLowerCase()).build(); - assertFalse(BOB.isSamePerson(editedBob)); - - // name has trailing spaces, all other attributes same -> returns false - String nameWithTrailingSpaces = VALID_NAME_BOB + " "; - editedBob = new PersonBuilder(BOB).withName(nameWithTrailingSpaces).build(); - assertFalse(BOB.isSamePerson(editedBob)); - } - - @Test - public void equals() { - // same values -> returns true - Person aliceCopy = new PersonBuilder(ALICE).build(); - assertTrue(ALICE.equals(aliceCopy)); - - // same object -> returns true - assertTrue(ALICE.equals(ALICE)); - - // null -> returns false - assertFalse(ALICE.equals(null)); - - // different type -> returns false - assertFalse(ALICE.equals(5)); - - // different person -> returns false - assertFalse(ALICE.equals(BOB)); - - // different name -> returns false - Person editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different phone -> returns false - editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different email -> returns false - editedAlice = new PersonBuilder(ALICE).withEmail(VALID_EMAIL_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different address -> returns false - editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different tags -> returns false - editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND).build(); - assertFalse(ALICE.equals(editedAlice)); - } -} diff --git a/src/test/java/seedu/address/model/person/PhoneTest.java b/src/test/java/seedu/address/model/person/PhoneTest.java deleted file mode 100644 index 8dd52766a5f..00000000000 --- a/src/test/java/seedu/address/model/person/PhoneTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class PhoneTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Phone(null)); - } - - @Test - public void constructor_invalidPhone_throwsIllegalArgumentException() { - String invalidPhone = ""; - assertThrows(IllegalArgumentException.class, () -> new Phone(invalidPhone)); - } - - @Test - public void isValidPhone() { - // null phone number - assertThrows(NullPointerException.class, () -> Phone.isValidPhone(null)); - - // invalid phone numbers - assertFalse(Phone.isValidPhone("")); // empty string - assertFalse(Phone.isValidPhone(" ")); // spaces only - assertFalse(Phone.isValidPhone("91")); // less than 3 numbers - assertFalse(Phone.isValidPhone("phone")); // non-numeric - assertFalse(Phone.isValidPhone("9011p041")); // alphabets within digits - assertFalse(Phone.isValidPhone("9312 1534")); // spaces within digits - - // valid phone numbers - assertTrue(Phone.isValidPhone("911")); // exactly 3 numbers - assertTrue(Phone.isValidPhone("93121534")); - assertTrue(Phone.isValidPhone("124293842033123")); // long phone numbers - } -} diff --git a/src/test/java/seedu/address/model/person/UniquePersonListTest.java b/src/test/java/seedu/address/model/person/UniquePersonListTest.java deleted file mode 100644 index 1cc5fe9e0fe..00000000000 --- a/src/test/java/seedu/address/model/person/UniquePersonListTest.java +++ /dev/null @@ -1,170 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BOB; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; -import seedu.address.testutil.PersonBuilder; - -public class UniquePersonListTest { - - private final UniquePersonList uniquePersonList = new UniquePersonList(); - - @Test - public void contains_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.contains(null)); - } - - @Test - public void contains_personNotInList_returnsFalse() { - assertFalse(uniquePersonList.contains(ALICE)); - } - - @Test - public void contains_personInList_returnsTrue() { - uniquePersonList.add(ALICE); - assertTrue(uniquePersonList.contains(ALICE)); - } - - @Test - public void contains_personWithSameIdentityFieldsInList_returnsTrue() { - uniquePersonList.add(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - assertTrue(uniquePersonList.contains(editedAlice)); - } - - @Test - public void add_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.add(null)); - } - - @Test - public void add_duplicatePerson_throwsDuplicatePersonException() { - uniquePersonList.add(ALICE); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.add(ALICE)); - } - - @Test - public void setPerson_nullTargetPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPerson(null, ALICE)); - } - - @Test - public void setPerson_nullEditedPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPerson(ALICE, null)); - } - - @Test - public void setPerson_targetPersonNotInList_throwsPersonNotFoundException() { - assertThrows(PersonNotFoundException.class, () -> uniquePersonList.setPerson(ALICE, ALICE)); - } - - @Test - public void setPerson_editedPersonIsSamePerson_success() { - uniquePersonList.add(ALICE); - uniquePersonList.setPerson(ALICE, ALICE); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(ALICE); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPerson_editedPersonHasSameIdentity_success() { - uniquePersonList.add(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - uniquePersonList.setPerson(ALICE, editedAlice); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(editedAlice); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPerson_editedPersonHasDifferentIdentity_success() { - uniquePersonList.add(ALICE); - uniquePersonList.setPerson(ALICE, BOB); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(BOB); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPerson_editedPersonHasNonUniqueIdentity_throwsDuplicatePersonException() { - uniquePersonList.add(ALICE); - uniquePersonList.add(BOB); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.setPerson(ALICE, BOB)); - } - - @Test - public void remove_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.remove(null)); - } - - @Test - public void remove_personDoesNotExist_throwsPersonNotFoundException() { - assertThrows(PersonNotFoundException.class, () -> uniquePersonList.remove(ALICE)); - } - - @Test - public void remove_existingPerson_removesPerson() { - uniquePersonList.add(ALICE); - uniquePersonList.remove(ALICE); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPersons_nullUniquePersonList_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((UniquePersonList) null)); - } - - @Test - public void setPersons_uniquePersonList_replacesOwnListWithProvidedUniquePersonList() { - uniquePersonList.add(ALICE); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(BOB); - uniquePersonList.setPersons(expectedUniquePersonList); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPersons_nullList_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((List) null)); - } - - @Test - public void setPersons_list_replacesOwnListWithProvidedList() { - uniquePersonList.add(ALICE); - List personList = Collections.singletonList(BOB); - uniquePersonList.setPersons(personList); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(BOB); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPersons_listWithDuplicatePersons_throwsDuplicatePersonException() { - List listWithDuplicatePersons = Arrays.asList(ALICE, ALICE); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.setPersons(listWithDuplicatePersons)); - } - - @Test - public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () - -> uniquePersonList.asUnmodifiableObservableList().remove(0)); - } -} diff --git a/src/test/java/seedu/address/model/tag/TagTest.java b/src/test/java/seedu/address/model/tag/TagTest.java deleted file mode 100644 index 64d07d79ee2..00000000000 --- a/src/test/java/seedu/address/model/tag/TagTest.java +++ /dev/null @@ -1,26 +0,0 @@ -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/storage/JsonAdaptedPersonTest.java b/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java deleted file mode 100644 index 83b11331cdb..00000000000 --- a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package seedu.address.storage; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.storage.JsonAdaptedPerson.MISSING_FIELD_MESSAGE_FORMAT; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.BENSON; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; - -public class JsonAdaptedPersonTest { - private static final String INVALID_NAME = "R@chel"; - private static final String INVALID_PHONE = "+651234"; - private static final String INVALID_ADDRESS = " "; - private static final String INVALID_EMAIL = "example.com"; - private static final String INVALID_TAG = "#friend"; - - private static final String VALID_NAME = BENSON.getName().toString(); - private static final String VALID_PHONE = BENSON.getPhone().toString(); - private static final String VALID_EMAIL = BENSON.getEmail().toString(); - private static final String VALID_ADDRESS = BENSON.getAddress().toString(); - private static final List VALID_TAGS = BENSON.getTags().stream() - .map(JsonAdaptedTag::new) - .collect(Collectors.toList()); - - @Test - public void toModelType_validPersonDetails_returnsPerson() throws Exception { - JsonAdaptedPerson person = new JsonAdaptedPerson(BENSON); - assertEquals(BENSON, person.toModelType()); - } - - @Test - public void toModelType_invalidName_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(INVALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = Name.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullName_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(null, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidPhone_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, INVALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = Phone.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullPhone_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, null, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidEmail_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, INVALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = Email.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullEmail_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, null, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidAddress_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, INVALID_ADDRESS, VALID_TAGS); - String expectedMessage = Address.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullAddress_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, null, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidTags_throwsIllegalValueException() { - List invalidTags = new ArrayList<>(VALID_TAGS); - invalidTags.add(new JsonAdaptedTag(INVALID_TAG)); - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, invalidTags); - assertThrows(IllegalValueException.class, person::toModelType); - } - -} diff --git a/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java b/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java deleted file mode 100644 index ac3c3af9566..00000000000 --- a/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package seedu.address.storage; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.HOON; -import static seedu.address.testutil.TypicalPersons.IDA; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; - -public class JsonAddressBookStorageTest { - private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonAddressBookStorageTest"); - - @TempDir - public Path testFolder; - - @Test - public void readAddressBook_nullFilePath_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> readAddressBook(null)); - } - - private java.util.Optional readAddressBook(String filePath) throws Exception { - return new JsonAddressBookStorage(Paths.get(filePath)).readAddressBook(addToTestDataPathIfNotNull(filePath)); - } - - private Path addToTestDataPathIfNotNull(String prefsFileInTestDataFolder) { - return prefsFileInTestDataFolder != null - ? TEST_DATA_FOLDER.resolve(prefsFileInTestDataFolder) - : null; - } - - @Test - public void read_missingFile_emptyResult() throws Exception { - assertFalse(readAddressBook("NonExistentFile.json").isPresent()); - } - - @Test - public void read_notJsonFormat_exceptionThrown() { - assertThrows(DataConversionException.class, () -> readAddressBook("notJsonFormatAddressBook.json")); - } - - @Test - public void readAddressBook_invalidPersonAddressBook_throwDataConversionException() { - assertThrows(DataConversionException.class, () -> readAddressBook("invalidPersonAddressBook.json")); - } - - @Test - public void readAddressBook_invalidAndValidPersonAddressBook_throwDataConversionException() { - assertThrows(DataConversionException.class, () -> readAddressBook("invalidAndValidPersonAddressBook.json")); - } - - @Test - public void readAndSaveAddressBook_allInOrder_success() throws Exception { - Path filePath = testFolder.resolve("TempAddressBook.json"); - AddressBook original = getTypicalAddressBook(); - JsonAddressBookStorage jsonAddressBookStorage = new JsonAddressBookStorage(filePath); - - // Save in new file and read back - jsonAddressBookStorage.saveAddressBook(original, filePath); - ReadOnlyAddressBook readBack = jsonAddressBookStorage.readAddressBook(filePath).get(); - assertEquals(original, new AddressBook(readBack)); - - // Modify data, overwrite exiting file, and read back - original.addPerson(HOON); - original.removePerson(ALICE); - jsonAddressBookStorage.saveAddressBook(original, filePath); - readBack = jsonAddressBookStorage.readAddressBook(filePath).get(); - assertEquals(original, new AddressBook(readBack)); - - // Save and read without specifying file path - original.addPerson(IDA); - jsonAddressBookStorage.saveAddressBook(original); // file path not specified - readBack = jsonAddressBookStorage.readAddressBook().get(); // file path not specified - assertEquals(original, new AddressBook(readBack)); - - } - - @Test - public void saveAddressBook_nullAddressBook_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> saveAddressBook(null, "SomeFile.json")); - } - - /** - * Saves {@code addressBook} at the specified {@code filePath}. - */ - private void saveAddressBook(ReadOnlyAddressBook addressBook, String filePath) { - try { - new JsonAddressBookStorage(Paths.get(filePath)) - .saveAddressBook(addressBook, addToTestDataPathIfNotNull(filePath)); - } catch (IOException ioe) { - throw new AssertionError("There should not be an error writing to the file.", ioe); - } - } - - @Test - public void saveAddressBook_nullFilePath_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> saveAddressBook(new AddressBook(), null)); - } -} diff --git a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java b/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java deleted file mode 100644 index 188c9058d20..00000000000 --- a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package seedu.address.storage; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.testutil.Assert.assertThrows; - -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.AddressBook; -import seedu.address.testutil.TypicalPersons; - -public class JsonSerializableAddressBookTest { - - private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonSerializableAddressBookTest"); - private static final Path TYPICAL_PERSONS_FILE = TEST_DATA_FOLDER.resolve("typicalPersonsAddressBook.json"); - private static final Path INVALID_PERSON_FILE = TEST_DATA_FOLDER.resolve("invalidPersonAddressBook.json"); - private static final Path DUPLICATE_PERSON_FILE = TEST_DATA_FOLDER.resolve("duplicatePersonAddressBook.json"); - - @Test - public void toModelType_typicalPersonsFile_success() throws Exception { - JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(TYPICAL_PERSONS_FILE, - JsonSerializableAddressBook.class).get(); - AddressBook addressBookFromFile = dataFromFile.toModelType(); - AddressBook typicalPersonsAddressBook = TypicalPersons.getTypicalAddressBook(); - assertEquals(addressBookFromFile, typicalPersonsAddressBook); - } - - @Test - public void toModelType_invalidPersonFile_throwsIllegalValueException() throws Exception { - JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(INVALID_PERSON_FILE, - JsonSerializableAddressBook.class).get(); - assertThrows(IllegalValueException.class, dataFromFile::toModelType); - } - - @Test - public void toModelType_duplicatePersons_throwsIllegalValueException() throws Exception { - JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(DUPLICATE_PERSON_FILE, - JsonSerializableAddressBook.class).get(); - assertThrows(IllegalValueException.class, JsonSerializableAddressBook.MESSAGE_DUPLICATE_PERSON, - dataFromFile::toModelType); - } - -} diff --git a/src/test/java/seedu/address/testutil/AddressBookBuilder.java b/src/test/java/seedu/address/testutil/AddressBookBuilder.java deleted file mode 100644 index d53799fd110..00000000000 --- a/src/test/java/seedu/address/testutil/AddressBookBuilder.java +++ /dev/null @@ -1,34 +0,0 @@ -package seedu.address.testutil; - -import seedu.address.model.AddressBook; -import seedu.address.model.person.Person; - -/** - * A utility class to help with building Addressbook objects. - * Example usage:
- * {@code AddressBook ab = new AddressBookBuilder().withPerson("John", "Doe").build();} - */ -public class AddressBookBuilder { - - private AddressBook addressBook; - - public AddressBookBuilder() { - addressBook = new AddressBook(); - } - - public AddressBookBuilder(AddressBook addressBook) { - this.addressBook = addressBook; - } - - /** - * Adds a new {@code Person} to the {@code AddressBook} that we are building. - */ - public AddressBookBuilder withPerson(Person person) { - addressBook.addPerson(person); - return this; - } - - public AddressBook build() { - return addressBook; - } -} diff --git a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java b/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java deleted file mode 100644 index 4584bd5044e..00000000000 --- a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java +++ /dev/null @@ -1,87 +0,0 @@ -package seedu.address.testutil; - -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * A utility class to help with building EditPersonDescriptor objects. - */ -public class EditPersonDescriptorBuilder { - - private EditPersonDescriptor descriptor; - - public EditPersonDescriptorBuilder() { - descriptor = new EditPersonDescriptor(); - } - - public EditPersonDescriptorBuilder(EditPersonDescriptor descriptor) { - this.descriptor = new EditPersonDescriptor(descriptor); - } - - /** - * Returns an {@code EditPersonDescriptor} with fields containing {@code person}'s details - */ - public EditPersonDescriptorBuilder(Person person) { - descriptor = new EditPersonDescriptor(); - descriptor.setName(person.getName()); - descriptor.setPhone(person.getPhone()); - descriptor.setEmail(person.getEmail()); - descriptor.setAddress(person.getAddress()); - descriptor.setTags(person.getTags()); - } - - /** - * Sets the {@code Name} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withName(String name) { - descriptor.setName(new Name(name)); - return this; - } - - /** - * Sets the {@code Phone} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withPhone(String phone) { - descriptor.setPhone(new Phone(phone)); - return this; - } - - /** - * Sets the {@code Email} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withEmail(String email) { - descriptor.setEmail(new Email(email)); - return this; - } - - /** - * Sets the {@code Address} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withAddress(String address) { - descriptor.setAddress(new Address(address)); - return this; - } - - /** - * Parses the {@code tags} into a {@code Set} and set it to the {@code EditPersonDescriptor} - * that we are building. - */ - public EditPersonDescriptorBuilder withTags(String... tags) { - Set tagSet = Stream.of(tags).map(Tag::new).collect(Collectors.toSet()); - descriptor.setTags(tagSet); - return this; - } - - public EditPersonDescriptor build() { - return descriptor; - } -} diff --git a/src/test/java/seedu/address/testutil/PersonBuilder.java b/src/test/java/seedu/address/testutil/PersonBuilder.java deleted file mode 100644 index 6be381d39ba..00000000000 --- a/src/test/java/seedu/address/testutil/PersonBuilder.java +++ /dev/null @@ -1,96 +0,0 @@ -package seedu.address.testutil; - -import java.util.HashSet; -import java.util.Set; - -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -import seedu.address.model.util.SampleDataUtil; - -/** - * A utility class to help with building Person objects. - */ -public class PersonBuilder { - - public static final String DEFAULT_NAME = "Amy Bee"; - public static final String DEFAULT_PHONE = "85355255"; - public static final String DEFAULT_EMAIL = "amy@gmail.com"; - public static final String DEFAULT_ADDRESS = "123, Jurong West Ave 6, #08-111"; - - private Name name; - private Phone phone; - private Email email; - private Address address; - private Set tags; - - /** - * Creates a {@code PersonBuilder} with the default details. - */ - public PersonBuilder() { - name = new Name(DEFAULT_NAME); - phone = new Phone(DEFAULT_PHONE); - email = new Email(DEFAULT_EMAIL); - address = new Address(DEFAULT_ADDRESS); - tags = new HashSet<>(); - } - - /** - * Initializes the PersonBuilder with the data of {@code personToCopy}. - */ - public PersonBuilder(Person personToCopy) { - name = personToCopy.getName(); - phone = personToCopy.getPhone(); - email = personToCopy.getEmail(); - address = personToCopy.getAddress(); - tags = new HashSet<>(personToCopy.getTags()); - } - - /** - * Sets the {@code Name} of the {@code Person} that we are building. - */ - public PersonBuilder withName(String name) { - this.name = new Name(name); - return this; - } - - /** - * Parses the {@code tags} into a {@code Set} and set it to the {@code Person} that we are building. - */ - public PersonBuilder withTags(String ... tags) { - this.tags = SampleDataUtil.getTagSet(tags); - return this; - } - - /** - * Sets the {@code Address} of the {@code Person} that we are building. - */ - public PersonBuilder withAddress(String address) { - this.address = new Address(address); - return this; - } - - /** - * Sets the {@code Phone} of the {@code Person} that we are building. - */ - public PersonBuilder withPhone(String phone) { - this.phone = new Phone(phone); - return this; - } - - /** - * Sets the {@code Email} of the {@code Person} that we are building. - */ - public PersonBuilder withEmail(String email) { - this.email = new Email(email); - return this; - } - - public Person build() { - return new Person(name, phone, email, address, tags); - } - -} diff --git a/src/test/java/seedu/address/testutil/PersonUtil.java b/src/test/java/seedu/address/testutil/PersonUtil.java deleted file mode 100644 index 90849945183..00000000000 --- a/src/test/java/seedu/address/testutil/PersonUtil.java +++ /dev/null @@ -1,62 +0,0 @@ -package seedu.address.testutil; - -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; - -import java.util.Set; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.person.Person; -import seedu.address.model.tag.Tag; - -/** - * A utility class for Person. - */ -public class PersonUtil { - - /** - * Returns an add command string for adding the {@code person}. - */ - public static String getAddCommand(Person person) { - return AddCommand.COMMAND_WORD + " " + getPersonDetails(person); - } - - /** - * Returns the part of command string for the given {@code person}'s details. - */ - public static String getPersonDetails(Person person) { - StringBuilder sb = new StringBuilder(); - sb.append(PREFIX_NAME + person.getName().fullName + " "); - sb.append(PREFIX_PHONE + person.getPhone().value + " "); - sb.append(PREFIX_EMAIL + person.getEmail().value + " "); - sb.append(PREFIX_ADDRESS + person.getAddress().value + " "); - person.getTags().stream().forEach( - s -> sb.append(PREFIX_TAG + s.tagName + " ") - ); - return sb.toString(); - } - - /** - * Returns the part of command string for the given {@code EditPersonDescriptor}'s details. - */ - public static String getEditPersonDescriptorDetails(EditPersonDescriptor descriptor) { - StringBuilder sb = new StringBuilder(); - descriptor.getName().ifPresent(name -> sb.append(PREFIX_NAME).append(name.fullName).append(" ")); - descriptor.getPhone().ifPresent(phone -> sb.append(PREFIX_PHONE).append(phone.value).append(" ")); - descriptor.getEmail().ifPresent(email -> sb.append(PREFIX_EMAIL).append(email.value).append(" ")); - descriptor.getAddress().ifPresent(address -> sb.append(PREFIX_ADDRESS).append(address.value).append(" ")); - if (descriptor.getTags().isPresent()) { - Set tags = descriptor.getTags().get(); - if (tags.isEmpty()) { - sb.append(PREFIX_TAG); - } else { - tags.forEach(s -> sb.append(PREFIX_TAG).append(s.tagName).append(" ")); - } - } - return sb.toString(); - } -} diff --git a/src/test/java/seedu/address/testutil/TypicalIndexes.java b/src/test/java/seedu/address/testutil/TypicalIndexes.java deleted file mode 100644 index 1e613937657..00000000000 --- a/src/test/java/seedu/address/testutil/TypicalIndexes.java +++ /dev/null @@ -1,12 +0,0 @@ -package seedu.address.testutil; - -import seedu.address.commons.core.index.Index; - -/** - * A utility class containing a list of {@code Index} objects to be used in tests. - */ -public class TypicalIndexes { - public static final Index INDEX_FIRST_PERSON = Index.fromOneBased(1); - public static final Index INDEX_SECOND_PERSON = Index.fromOneBased(2); - public static final Index INDEX_THIRD_PERSON = Index.fromOneBased(3); -} diff --git a/src/test/java/seedu/address/testutil/TypicalPersons.java b/src/test/java/seedu/address/testutil/TypicalPersons.java deleted file mode 100644 index fec76fb7129..00000000000 --- a/src/test/java/seedu/address/testutil/TypicalPersons.java +++ /dev/null @@ -1,76 +0,0 @@ -package seedu.address.testutil; - -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import seedu.address.model.AddressBook; -import seedu.address.model.person.Person; - -/** - * A utility class containing a list of {@code Person} objects to be used in tests. - */ -public class TypicalPersons { - - public static final Person ALICE = new PersonBuilder().withName("Alice Pauline") - .withAddress("123, Jurong West Ave 6, #08-111").withEmail("alice@example.com") - .withPhone("94351253") - .withTags("friends").build(); - public static final Person BENSON = new PersonBuilder().withName("Benson Meier") - .withAddress("311, Clementi Ave 2, #02-25") - .withEmail("johnd@example.com").withPhone("98765432") - .withTags("owesMoney", "friends").build(); - public static final Person CARL = new PersonBuilder().withName("Carl Kurz").withPhone("95352563") - .withEmail("heinz@example.com").withAddress("wall street").build(); - public static final Person DANIEL = new PersonBuilder().withName("Daniel Meier").withPhone("87652533") - .withEmail("cornelia@example.com").withAddress("10th street").withTags("friends").build(); - public static final Person ELLE = new PersonBuilder().withName("Elle Meyer").withPhone("9482224") - .withEmail("werner@example.com").withAddress("michegan ave").build(); - public static final Person FIONA = new PersonBuilder().withName("Fiona Kunz").withPhone("9482427") - .withEmail("lydia@example.com").withAddress("little tokyo").build(); - public static final Person GEORGE = new PersonBuilder().withName("George Best").withPhone("9482442") - .withEmail("anna@example.com").withAddress("4th street").build(); - - // Manually added - public static final Person HOON = new PersonBuilder().withName("Hoon Meier").withPhone("8482424") - .withEmail("stefan@example.com").withAddress("little india").build(); - public static final Person IDA = new PersonBuilder().withName("Ida Mueller").withPhone("8482131") - .withEmail("hans@example.com").withAddress("chicago ave").build(); - - // Manually added - Person's details found in {@code CommandTestUtil} - public static final Person AMY = new PersonBuilder().withName(VALID_NAME_AMY).withPhone(VALID_PHONE_AMY) - .withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY).withTags(VALID_TAG_FRIEND).build(); - public static final Person BOB = new PersonBuilder().withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB) - .withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND) - .build(); - - public static final String KEYWORD_MATCHING_MEIER = "Meier"; // A keyword that matches MEIER - - private TypicalPersons() {} // prevents instantiation - - /** - * Returns an {@code AddressBook} with all the typical persons. - */ - public static AddressBook getTypicalAddressBook() { - AddressBook ab = new AddressBook(); - for (Person person : getTypicalPersons()) { - ab.addPerson(person); - } - return ab; - } - - public static List getTypicalPersons() { - return new ArrayList<>(Arrays.asList(ALICE, BENSON, CARL, DANIEL, ELLE, FIONA, GEORGE)); - } -} diff --git a/src/test/java/seedu/address/AppParametersTest.java b/src/test/java/seedu/internship/AppParametersTest.java similarity index 98% rename from src/test/java/seedu/address/AppParametersTest.java rename to src/test/java/seedu/internship/AppParametersTest.java index 61326b2d31a..c2263bf789e 100644 --- a/src/test/java/seedu/address/AppParametersTest.java +++ b/src/test/java/seedu/internship/AppParametersTest.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.internship; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/seedu/internship/MainAppTest.java b/src/test/java/seedu/internship/MainAppTest.java new file mode 100644 index 00000000000..83d2c2fdb02 --- /dev/null +++ b/src/test/java/seedu/internship/MainAppTest.java @@ -0,0 +1,15 @@ +package seedu.internship; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import seedu.internship.commons.core.Version; + +public class MainAppTest { + @Test + public void getVersionNumber_correct() { + assertEquals(MainApp.VERSION, new Version(1, 4, 1, true)); + + } +} diff --git a/src/test/java/seedu/address/commons/core/ConfigTest.java b/src/test/java/seedu/internship/commons/core/ConfigTest.java similarity index 94% rename from src/test/java/seedu/address/commons/core/ConfigTest.java rename to src/test/java/seedu/internship/commons/core/ConfigTest.java index 07cd7f73d53..52615e9ec5b 100644 --- a/src/test/java/seedu/address/commons/core/ConfigTest.java +++ b/src/test/java/seedu/internship/commons/core/ConfigTest.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.internship.commons.core; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/src/test/java/seedu/address/commons/core/VersionTest.java b/src/test/java/seedu/internship/commons/core/VersionTest.java similarity index 97% rename from src/test/java/seedu/address/commons/core/VersionTest.java rename to src/test/java/seedu/internship/commons/core/VersionTest.java index 495cd231554..f63704cae21 100644 --- a/src/test/java/seedu/address/commons/core/VersionTest.java +++ b/src/test/java/seedu/internship/commons/core/VersionTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.core; +package seedu.internship.commons.core; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.internship.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/commons/core/index/IndexTest.java b/src/test/java/seedu/internship/commons/core/index/IndexTest.java similarity index 72% rename from src/test/java/seedu/address/commons/core/index/IndexTest.java rename to src/test/java/seedu/internship/commons/core/index/IndexTest.java index a3ec6f8e747..f26a47ee1a0 100644 --- a/src/test/java/seedu/address/commons/core/index/IndexTest.java +++ b/src/test/java/seedu/internship/commons/core/index/IndexTest.java @@ -1,9 +1,9 @@ -package seedu.address.commons.core.index; +package seedu.internship.commons.core.index; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.internship.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; @@ -39,22 +39,22 @@ public void createZeroBasedIndex() { @Test public void equals() { - final Index fifthPersonIndex = Index.fromOneBased(5); + final Index fifthInternshipIndex = Index.fromOneBased(5); // same values -> returns true - assertTrue(fifthPersonIndex.equals(Index.fromOneBased(5))); - assertTrue(fifthPersonIndex.equals(Index.fromZeroBased(4))); + assertTrue(fifthInternshipIndex.equals(Index.fromOneBased(5))); + assertTrue(fifthInternshipIndex.equals(Index.fromZeroBased(4))); // same object -> returns true - assertTrue(fifthPersonIndex.equals(fifthPersonIndex)); + assertTrue(fifthInternshipIndex.equals(fifthInternshipIndex)); // null -> returns false - assertFalse(fifthPersonIndex.equals(null)); + assertFalse(fifthInternshipIndex.equals(null)); // different types -> returns false - assertFalse(fifthPersonIndex.equals(5.0f)); + assertFalse(fifthInternshipIndex.equals(5.0f)); // different index -> returns false - assertFalse(fifthPersonIndex.equals(Index.fromOneBased(1))); + assertFalse(fifthInternshipIndex.equals(Index.fromOneBased(1))); } } diff --git a/src/test/java/seedu/address/commons/util/AppUtilTest.java b/src/test/java/seedu/internship/commons/util/AppUtilTest.java similarity index 84% rename from src/test/java/seedu/address/commons/util/AppUtilTest.java rename to src/test/java/seedu/internship/commons/util/AppUtilTest.java index 594de1e6365..5eee347d897 100644 --- a/src/test/java/seedu/address/commons/util/AppUtilTest.java +++ b/src/test/java/seedu/internship/commons/util/AppUtilTest.java @@ -1,7 +1,7 @@ -package seedu.address.commons.util; +package seedu.internship.commons.util; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.internship.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; @@ -9,7 +9,7 @@ public class AppUtilTest { @Test public void getImage_exitingImage() { - assertNotNull(AppUtil.getImage("/images/address_book_32.png")); + assertNotNull(AppUtil.getImage("/images/internbuddy_32.png")); } @Test diff --git a/src/test/java/seedu/address/commons/util/CollectionUtilTest.java b/src/test/java/seedu/internship/commons/util/CollectionUtilTest.java similarity index 96% rename from src/test/java/seedu/address/commons/util/CollectionUtilTest.java rename to src/test/java/seedu/internship/commons/util/CollectionUtilTest.java index b467a3dc025..f81f67cbda5 100644 --- a/src/test/java/seedu/address/commons/util/CollectionUtilTest.java +++ b/src/test/java/seedu/internship/commons/util/CollectionUtilTest.java @@ -1,9 +1,9 @@ -package seedu.address.commons.util; +package seedu.internship.commons.util; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.internship.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.internship.testutil.Assert.assertThrows; import java.util.Arrays; import java.util.Collection; diff --git a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java b/src/test/java/seedu/internship/commons/util/ConfigUtilTest.java similarity index 94% rename from src/test/java/seedu/address/commons/util/ConfigUtilTest.java rename to src/test/java/seedu/internship/commons/util/ConfigUtilTest.java index d2ab2839a52..6e329363137 100644 --- a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java +++ b/src/test/java/seedu/internship/commons/util/ConfigUtilTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.util; +package seedu.internship.commons.util; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.internship.testutil.Assert.assertThrows; import java.io.IOException; import java.nio.file.Path; @@ -13,8 +13,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.commons.core.Config; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.internship.commons.core.Config; +import seedu.internship.commons.exceptions.DataConversionException; public class ConfigUtilTest { diff --git a/src/test/java/seedu/address/commons/util/FileUtilTest.java b/src/test/java/seedu/internship/commons/util/FileUtilTest.java similarity index 84% rename from src/test/java/seedu/address/commons/util/FileUtilTest.java rename to src/test/java/seedu/internship/commons/util/FileUtilTest.java index 1fe5478c756..fcda2a32cb9 100644 --- a/src/test/java/seedu/address/commons/util/FileUtilTest.java +++ b/src/test/java/seedu/internship/commons/util/FileUtilTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.util; +package seedu.internship.commons.util; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.internship.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/commons/util/JsonUtilTest.java b/src/test/java/seedu/internship/commons/util/JsonUtilTest.java similarity index 92% rename from src/test/java/seedu/address/commons/util/JsonUtilTest.java rename to src/test/java/seedu/internship/commons/util/JsonUtilTest.java index d4907539dee..3dbdd7c418b 100644 --- a/src/test/java/seedu/address/commons/util/JsonUtilTest.java +++ b/src/test/java/seedu/internship/commons/util/JsonUtilTest.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.internship.commons.util; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -7,8 +7,8 @@ import org.junit.jupiter.api.Test; -import seedu.address.testutil.SerializableTestClass; -import seedu.address.testutil.TestUtil; +import seedu.internship.testutil.SerializableTestClass; +import seedu.internship.testutil.TestUtil; /** * Tests JSON Read and Write diff --git a/src/test/java/seedu/address/commons/util/StringUtilTest.java b/src/test/java/seedu/internship/commons/util/StringUtilTest.java similarity index 62% rename from src/test/java/seedu/address/commons/util/StringUtilTest.java rename to src/test/java/seedu/internship/commons/util/StringUtilTest.java index c56d407bf3f..121ae184382 100644 --- a/src/test/java/seedu/address/commons/util/StringUtilTest.java +++ b/src/test/java/seedu/internship/commons/util/StringUtilTest.java @@ -1,48 +1,75 @@ -package seedu.address.commons.util; +package seedu.internship.commons.util; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.internship.testutil.Assert.assertThrows; import java.io.FileNotFoundException; import org.junit.jupiter.api.Test; +import seedu.internship.logic.parser.ParserUtil; +import seedu.internship.logic.parser.exceptions.ParseException; + public class StringUtilTest { - //---------------- Tests for isNonZeroUnsignedInteger -------------------------------------- + //---------------- Tests for nonZeroUnsignedIntegerCheck -------------------------------------- @Test - public void isNonZeroUnsignedInteger() { + public void nonZeroUnsignedIntegerCheck() { // EP: empty strings - assertFalse(StringUtil.isNonZeroUnsignedInteger("")); // Boundary value - assertFalse(StringUtil.isNonZeroUnsignedInteger(" ")); - + this.nonZeroUnsignedIntegerCheckAssertFail("", ParserUtil.MESSAGE_INVALID_INDEX_FORMAT); // Boundary value + this.nonZeroUnsignedIntegerCheckAssertFail(" ", ParserUtil.MESSAGE_INVALID_INDEX_FORMAT); // EP: not a number - assertFalse(StringUtil.isNonZeroUnsignedInteger("a")); - assertFalse(StringUtil.isNonZeroUnsignedInteger("aaa")); + this.nonZeroUnsignedIntegerCheckAssertFail("a", ParserUtil.MESSAGE_INVALID_INDEX_FORMAT); + this.nonZeroUnsignedIntegerCheckAssertFail("aaa", ParserUtil.MESSAGE_INVALID_INDEX_FORMAT); // EP: zero - assertFalse(StringUtil.isNonZeroUnsignedInteger("0")); + this.nonZeroUnsignedIntegerCheckAssertFail("0", ParserUtil.MESSAGE_INVALID_INDEX); // EP: zero as prefix - assertTrue(StringUtil.isNonZeroUnsignedInteger("01")); + this.nonZeroUnsignedIntegerCheckAssertPass("01"); - // EP: signed numbers - assertFalse(StringUtil.isNonZeroUnsignedInteger("-1")); - assertFalse(StringUtil.isNonZeroUnsignedInteger("+1")); + // EP: signed negative numbers + this.nonZeroUnsignedIntegerCheckAssertFail("-1", ParserUtil.MESSAGE_INVALID_INDEX); + this.nonZeroUnsignedIntegerCheckAssertFail("-100", ParserUtil.MESSAGE_INVALID_INDEX); + + // EP: signed positive numbers + this.nonZeroUnsignedIntegerCheckAssertFail("+1", ParserUtil.MESSAGE_INVALID_POSITIVE_SIGNED_INDEX); + this.nonZeroUnsignedIntegerCheckAssertFail("+100", ParserUtil.MESSAGE_INVALID_POSITIVE_SIGNED_INDEX); // EP: numbers with white space - assertFalse(StringUtil.isNonZeroUnsignedInteger(" 10 ")); // Leading/trailing spaces - assertFalse(StringUtil.isNonZeroUnsignedInteger("1 0")); // Spaces in the middle + + // Leading/trailing spaces + this.nonZeroUnsignedIntegerCheckAssertFail(" 10 ", ParserUtil.MESSAGE_INVALID_INDEX_FORMAT); + // Spaces in the middle + this.nonZeroUnsignedIntegerCheckAssertFail("1 0", ParserUtil.MESSAGE_INVALID_INDEX_FORMAT); // EP: number larger than Integer.MAX_VALUE - assertFalse(StringUtil.isNonZeroUnsignedInteger(Long.toString(Integer.MAX_VALUE + 1))); + nonZeroUnsignedIntegerCheckAssertFail(Long.toString(Integer.MAX_VALUE + 1), ParserUtil.MESSAGE_INVALID_INDEX); // EP: valid numbers, should return true - assertTrue(StringUtil.isNonZeroUnsignedInteger("1")); // Boundary value - assertTrue(StringUtil.isNonZeroUnsignedInteger("10")); + this.nonZeroUnsignedIntegerCheckAssertPass("1"); // Boundary value + this.nonZeroUnsignedIntegerCheckAssertPass("10"); + } + + private static void nonZeroUnsignedIntegerCheckAssertPass(String input) { + try { + StringUtil.nonZeroUnsignedIntegerCheck(input); + } catch (ParseException pe) { + throw new IllegalArgumentException("Invalid userInput.", pe); + } + } + + private static void nonZeroUnsignedIntegerCheckAssertFail(String input, String expectedMessage) { + try { + StringUtil.nonZeroUnsignedIntegerCheck(input); + throw new AssertionError("The expected ParseException was not thrown."); + } catch (ParseException pe) { + assertEquals(expectedMessage, pe.getMessage()); + } } diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/internship/logic/LogicManagerTest.java similarity index 55% rename from src/test/java/seedu/address/logic/LogicManagerTest.java rename to src/test/java/seedu/internship/logic/LogicManagerTest.java index ad923ac249a..0e1e465e466 100644 --- a/src/test/java/seedu/address/logic/LogicManagerTest.java +++ b/src/test/java/seedu/internship/logic/LogicManagerTest.java @@ -1,14 +1,15 @@ -package seedu.address.logic; +package seedu.internship.logic; import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.AMY; +import static seedu.internship.commons.core.Messages.MESSAGE_OUT_OF_RANGE_INTERNSHIP_DISPLAYED_INDEX; +import static seedu.internship.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.internship.logic.commands.CommandTestUtil.COMMENT_DESC_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.COMPANY_NAME_DESC_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.DATE_DESC_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.ROLE_DESC_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.STATUS_DESC_APPLE; +import static seedu.internship.testutil.Assert.assertThrows; +import static seedu.internship.testutil.TypicalInternships.APPLE; import java.io.IOException; import java.nio.file.Path; @@ -17,20 +18,20 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; -import seedu.address.storage.JsonAddressBookStorage; -import seedu.address.storage.JsonUserPrefsStorage; -import seedu.address.storage.StorageManager; -import seedu.address.testutil.PersonBuilder; +import seedu.internship.logic.commands.AddCommand; +import seedu.internship.logic.commands.CommandResult; +import seedu.internship.logic.commands.ListCommand; +import seedu.internship.logic.commands.exceptions.CommandException; +import seedu.internship.logic.parser.exceptions.ParseException; +import seedu.internship.model.Model; +import seedu.internship.model.ModelManager; +import seedu.internship.model.ReadOnlyInternBuddy; +import seedu.internship.model.UserPrefs; +import seedu.internship.model.internship.Internship; +import seedu.internship.storage.JsonInternBuddyStorage; +import seedu.internship.storage.JsonUserPrefsStorage; +import seedu.internship.storage.StorageManager; +import seedu.internship.testutil.InternshipBuilder; public class LogicManagerTest { private static final IOException DUMMY_IO_EXCEPTION = new IOException("dummy exception"); @@ -43,10 +44,10 @@ public class LogicManagerTest { @BeforeEach public void setUp() { - JsonAddressBookStorage addressBookStorage = - new JsonAddressBookStorage(temporaryFolder.resolve("addressBook.json")); + JsonInternBuddyStorage internBuddyStorage = + new JsonInternBuddyStorage(temporaryFolder.resolve("internBuddy.json")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("userPrefs.json")); - StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage); + StorageManager storage = new StorageManager(internBuddyStorage, userPrefsStorage); logic = new LogicManager(model, storage); } @@ -58,8 +59,8 @@ public void execute_invalidCommandFormat_throwsParseException() { @Test public void execute_commandExecutionError_throwsCommandException() { - String deleteCommand = "delete 9"; - assertCommandException(deleteCommand, MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + String deleteCommand = "delete-index 9"; + assertCommandException(deleteCommand, MESSAGE_OUT_OF_RANGE_INTERNSHIP_DISPLAYED_INDEX); } @Test @@ -70,27 +71,43 @@ public void execute_validCommand_success() throws Exception { @Test public void execute_storageThrowsIoException_throwsCommandException() { - // Setup LogicManager with JsonAddressBookIoExceptionThrowingStub - JsonAddressBookStorage addressBookStorage = - new JsonAddressBookIoExceptionThrowingStub(temporaryFolder.resolve("ioExceptionAddressBook.json")); + // Setup LogicManager with JsonInternBuddyIoExceptionThrowingStub + JsonInternBuddyStorage internBuddyStorage = + new JsonInternBuddyIoExceptionThrowingStub(temporaryFolder.resolve("ioExceptionInternBuddy.json")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("ioExceptionUserPrefs.json")); - StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage); + StorageManager storage = new StorageManager(internBuddyStorage, userPrefsStorage); logic = new LogicManager(model, storage); // Execute add command - String addCommand = AddCommand.COMMAND_WORD + NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY - + ADDRESS_DESC_AMY; - Person expectedPerson = new PersonBuilder(AMY).withTags().build(); + String addCommand = AddCommand.COMMAND_WORD + COMPANY_NAME_DESC_APPLE + ROLE_DESC_APPLE + + STATUS_DESC_APPLE + DATE_DESC_APPLE + COMMENT_DESC_APPLE; + Internship expectedInternship = new InternshipBuilder(APPLE).withTags().build(); ModelManager expectedModel = new ModelManager(); - expectedModel.addPerson(expectedPerson); + expectedModel.addInternship(expectedInternship); String expectedMessage = LogicManager.FILE_OPS_ERROR_MESSAGE + DUMMY_IO_EXCEPTION; assertCommandFailure(addCommand, CommandException.class, expectedMessage, expectedModel); } @Test - public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredPersonList().remove(0)); + public void getFilteredInternshipList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredInternshipList().remove(0)); + } + + @Test + public void getInitialSelectedInternship_equalsNull_success() { + assertEquals(logic.getSelectedInternship(), null); + } + + @Test + public void getNewSelectedInternship_equals_success() { + model.updateSelectedInternship(APPLE); + assertEquals(logic.getSelectedInternship(), APPLE); + } + + @Test + public void getModel_success() { + assertEquals(logic.getModel(), model); } /** @@ -129,7 +146,7 @@ private void assertCommandException(String inputCommand, String expectedMessage) */ private void assertCommandFailure(String inputCommand, Class expectedException, String expectedMessage) { - Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + Model expectedModel = new ModelManager(model.getInternBuddy(), new UserPrefs()); assertCommandFailure(inputCommand, expectedException, expectedMessage, expectedModel); } @@ -146,16 +163,26 @@ private void assertCommandFailure(String inputCommand, Class new AddCommand(null)); + } + + @Test + public void execute_internshipAcceptedByModel_addSuccessful() throws Exception { + ModelStubAcceptingInternshipAdded modelStub = new ModelStubAcceptingInternshipAdded(); + Internship validInternship = new InternshipBuilder().build(); + + CommandResult commandResult = new AddCommand(validInternship).execute(modelStub); + + assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, validInternship), commandResult.getFeedbackToUser()); + assertEquals(Arrays.asList(validInternship), modelStub.internshipsAdded); + } + + @Test + public void execute_duplicateInternship_throwsCommandException() { + Internship validInternship = new InternshipBuilder().build(); + AddCommand addCommand = new AddCommand(validInternship); + ModelStub modelStub = new ModelStubWithInternship(validInternship); + + assertThrows(CommandException.class, AddCommand.MESSAGE_DUPLICATE_INTERNSHIP, () -> addCommand + .execute(modelStub)); + } + + @Test + public void equals() { + Internship apple = new InternshipBuilder().withCompanyName("Apple").build(); + Internship google = new InternshipBuilder().withCompanyName("Google").build(); + AddCommand addAppleCommand = new AddCommand(apple); + AddCommand addGoogleCommand = new AddCommand(google); + + // same object -> returns true + assertTrue(addAppleCommand.equals(addAppleCommand)); + + // same values -> returns true + AddCommand addAppleCommandCopy = new AddCommand(apple); + assertTrue(addAppleCommand.equals(addAppleCommandCopy)); + + // different types -> returns false + assertFalse(addAppleCommand.equals(1)); + + // null -> returns false + assertFalse(addAppleCommand.equals(null)); + + // different internship -> returns false + assertFalse(addAppleCommand.equals(addGoogleCommand)); + } + + /** + * A default model stub that have all of the methods failing. + */ + private class ModelStub implements Model { + @Override + public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ReadOnlyUserPrefs getUserPrefs() { + throw new AssertionError("This method should not be called."); + } + + @Override + public GuiSettings getGuiSettings() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setGuiSettings(GuiSettings guiSettings) { + throw new AssertionError("This method should not be called."); + } + + @Override + public Path getInternBuddyFilePath() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setInternBuddyFilePath(Path internBuddyFilePath) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addInternship(Internship internship) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setInternBuddy(ReadOnlyInternBuddy newData) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void viewInternship(Internship target) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void copyInternship(Internship target) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ReadOnlyInternBuddy getInternBuddy() { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean hasInternship(Internship internship) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void deleteInternship(Internship target) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setInternship(Internship target, Internship editedInternship) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ObservableList getFilteredInternshipList() { + 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 Internship getSelectedInternship() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateSelectedInternship(Internship target) { + //This will be called to update the right panel + assert(true); + } + } + + /** + * A Model stub that contains a single internship. + */ + private class ModelStubWithInternship extends ModelStub { + private final Internship internship; + + ModelStubWithInternship(Internship internship) { + requireNonNull(internship); + this.internship = internship; + } + + @Override + public boolean hasInternship(Internship internship) { + requireNonNull(internship); + return this.internship.isSameInternship(internship); + } + } + + /** + * A Model stub that always accept the internship being added. + */ + private class ModelStubAcceptingInternshipAdded extends ModelStub { + final ArrayList internshipsAdded = new ArrayList<>(); + + @Override + public boolean hasInternship(Internship internship) { + requireNonNull(internship); + return internshipsAdded.stream().anyMatch(internship::isSameInternship); + } + + @Override + public void addInternship(Internship internship) { + requireNonNull(internship); + internshipsAdded.add(internship); + } + + @Override + public ReadOnlyInternBuddy getInternBuddy() { + return new InternBuddy(); + } + } + +} diff --git a/src/test/java/seedu/internship/logic/commands/ClearCommandTest.java b/src/test/java/seedu/internship/logic/commands/ClearCommandTest.java new file mode 100644 index 00000000000..457051815e6 --- /dev/null +++ b/src/test/java/seedu/internship/logic/commands/ClearCommandTest.java @@ -0,0 +1,32 @@ +package seedu.internship.logic.commands; + +import static seedu.internship.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.internship.testutil.TypicalInternships.getTypicalInternBuddy; + +import org.junit.jupiter.api.Test; + +import seedu.internship.model.InternBuddy; +import seedu.internship.model.Model; +import seedu.internship.model.ModelManager; +import seedu.internship.model.UserPrefs; + +public class ClearCommandTest { + + @Test + public void execute_emptyInternBuddy_success() { + Model model = new ModelManager(); + Model expectedModel = new ModelManager(); + + assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_nonEmptyInternBuddy_success() { + Model model = new ModelManager(getTypicalInternBuddy(), new UserPrefs()); + Model expectedModel = new ModelManager(getTypicalInternBuddy(), new UserPrefs()); + expectedModel.setInternBuddy(new InternBuddy()); + + assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); + } + +} diff --git a/src/test/java/seedu/address/logic/commands/CommandResultTest.java b/src/test/java/seedu/internship/logic/commands/CommandResultTest.java similarity index 71% rename from src/test/java/seedu/address/logic/commands/CommandResultTest.java rename to src/test/java/seedu/internship/logic/commands/CommandResultTest.java index 4f3eb46e9ef..7d8ce1c3fe6 100644 --- a/src/test/java/seedu/address/logic/commands/CommandResultTest.java +++ b/src/test/java/seedu/internship/logic/commands/CommandResultTest.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.internship.logic.commands; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -51,4 +51,26 @@ public void hashcode() { // different exit value -> returns different hashcode assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true).hashCode()); } + + @Test + public void getShowHelp_success() { + // Case 1: showHelp is true + CommandResult commandResult = new CommandResult("feedback", true, true); + assertEquals(commandResult.isShowHelp(), true); + + // Case 2: showHelp is false + commandResult = new CommandResult("feedback", false, true); + assertEquals(commandResult.isShowHelp(), false); + } + + @Test + public void getExit_success() { + // Case 1: showHelp is true + CommandResult commandResult = new CommandResult("feedback", true, true); + assertEquals(commandResult.isExit(), true); + + // Case 2: showHelp is false + commandResult = new CommandResult("feedback", true, false); + assertEquals(commandResult.isExit(), false); + } } diff --git a/src/test/java/seedu/internship/logic/commands/CommandTestUtil.java b/src/test/java/seedu/internship/logic/commands/CommandTestUtil.java new file mode 100644 index 00000000000..322d5c239ba --- /dev/null +++ b/src/test/java/seedu/internship/logic/commands/CommandTestUtil.java @@ -0,0 +1,184 @@ +package seedu.internship.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_COMMENT; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_ROLE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_STATUS; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.internship.testutil.Assert.assertThrows; +import static seedu.internship.testutil.TypicalIndexes.INDEX_FIRST_INTERNSHIP; +import static seedu.internship.testutil.TypicalIndexes.INDEX_SECOND_INTERNSHIP; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import seedu.internship.commons.core.index.Index; +import seedu.internship.logic.commands.exceptions.CommandException; +import seedu.internship.model.InternBuddy; +import seedu.internship.model.Model; +import seedu.internship.model.internship.Internship; +import seedu.internship.model.internship.InternshipContainsKeywordsPredicate; +import seedu.internship.testutil.EditInternshipDescriptorBuilder; + +/** + * Contains helper methods for testing commands. + */ +public class CommandTestUtil { + + public static final String VALID_COMPANY_NAME_APPLE = "Apple"; + public static final String VALID_COMPANY_NAME_GOOGLE = "Google"; + public static final String VALID_ROLE_APPLE = "Mobile Developer"; + public static final String VALID_ROLE_GOOGLE = "Software Developer"; + public static final String VALID_STATUS_APPLE = "interview"; + public static final String VALID_STATUS_GOOGLE = "applied"; + public static final String VALID_DATE_APPLE = "2023-02-01"; + public static final String VALID_DATE_GOOGLE = "2023-02-03"; + public static final String VALID_COMMENT_APPLE = "I love Apple!"; + public static final String VALID_COMMENT_GOOGLE = "I love Google!"; + public static final String VALID_TAG_FRONT = "frontend"; + public static final String VALID_TAG_BACK = "backend"; + + public static final String COMPANY_NAME_DESC_APPLE = " " + PREFIX_COMPANY_NAME + VALID_COMPANY_NAME_APPLE; + public static final String COMPANY_NAME_DESC_GOOGLE = " " + PREFIX_COMPANY_NAME + VALID_COMPANY_NAME_GOOGLE; + public static final String ROLE_DESC_APPLE = " " + PREFIX_ROLE + VALID_ROLE_APPLE; + public static final String ROLE_DESC_GOOGLE = " " + PREFIX_ROLE + VALID_ROLE_GOOGLE; + public static final String STATUS_DESC_APPLE = " " + PREFIX_STATUS + VALID_STATUS_APPLE; + public static final String STATUS_DESC_GOOGLE = " " + PREFIX_STATUS + VALID_STATUS_GOOGLE; + public static final String DATE_DESC_APPLE = " " + PREFIX_DATE + VALID_DATE_APPLE; + public static final String DATE_DESC_GOOGLE = " " + PREFIX_DATE + VALID_DATE_GOOGLE; + public static final String COMMENT_DESC_APPLE = " " + PREFIX_COMMENT + VALID_COMMENT_APPLE; + public static final String COMMENT_DESC_GOOGLE = " " + PREFIX_COMMENT + VALID_COMMENT_GOOGLE; + public static final String TAG_DESC_FRONT = " " + PREFIX_TAG + VALID_TAG_FRONT; + public static final String TAG_DESC_BACK = " " + PREFIX_TAG + VALID_TAG_BACK; + public static final String INVALID_COMPANY_NAME = "This Company name has more than fifty characters hmm"; + public static final String INVALID_ROLE = "This role has more than fifty characters so it is wrong"; + + // Company names cannot be blank + public static final String INVALID_COMPANY_NAME_DESC = " " + PREFIX_COMPANY_NAME + INVALID_COMPANY_NAME; + // Roles cannot be blank + public static final String INVALID_ROLE_DESC = " " + PREFIX_ROLE + INVALID_ROLE; + // pending is not a possible value for status + public static final String INVALID_STATUS_DESC = " " + PREFIX_STATUS + "pending"; + // invalid date format where dates have to be yyyy-MM-dd + public static final String INVALID_DATE_DESC = " " + PREFIX_DATE + "23-02-02"; + + // Comment cannot be empty + public static final String INVALID_COMMENT_DESC = " " + PREFIX_COMMENT + ""; + + // tag more than 30 characters + public static final String INVALID_TAG_DESC = " " + PREFIX_TAG + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + public static final String PREAMBLE_WHITESPACE = "\t \r \n"; + public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble"; + + public static final InternshipContainsKeywordsPredicate EMPTY_PREDICATE = + new InternshipContainsKeywordsPredicate(new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), + new ArrayList<>(), new ArrayList<>()); + + public static final InternshipContainsKeywordsPredicate SIMPLE_PREDICATE = + new InternshipContainsKeywordsPredicate(Arrays.asList("Amazon"), new ArrayList<>(), + new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + + public static final InternshipContainsKeywordsPredicate ONE_FIELD_PREDICATE = + new InternshipContainsKeywordsPredicate(Arrays.asList("Amazon", "Goldman"), new ArrayList<>(), + new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + + public static final InternshipContainsKeywordsPredicate MULTIPLE_FIELD_PREDICATE = + new InternshipContainsKeywordsPredicate(Arrays.asList("Amazon"), Arrays.asList("Cloud Architect"), + new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + + public static final InternshipContainsKeywordsPredicate COMPLEX_PREDICATE = + new InternshipContainsKeywordsPredicate(Arrays.asList("Amazon", "Goldman"), + Arrays.asList("Cloud Architect"), new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + + public static final List MULTIPLE_INDEX_LIST = new ArrayList<>(Arrays.asList(INDEX_FIRST_INTERNSHIP, + INDEX_SECOND_INTERNSHIP)); + public static final List NON_EMPTY_INDEXLIST = new ArrayList<>(Arrays.asList(INDEX_FIRST_INTERNSHIP)); + public static final List EMPTY_INDEXLIST = new ArrayList<>(); + + public static final EditCommand.EditInternshipDescriptor DESC_APPLE; + public static final EditCommand.EditInternshipDescriptor DESC_GOOGLE; + public static final EditCommand.EditInternshipDescriptor DESC_APPLE_DIFFERENT_COMMENT; + + static { + DESC_APPLE = new EditInternshipDescriptorBuilder().withCompanyName(VALID_COMPANY_NAME_APPLE) + .withRole(VALID_ROLE_APPLE).withStatus(VALID_STATUS_APPLE).withDate(VALID_DATE_APPLE) + .withComment(VALID_COMMENT_APPLE).withTags(VALID_TAG_FRONT).build(); + DESC_GOOGLE = new EditInternshipDescriptorBuilder().withCompanyName(VALID_COMPANY_NAME_GOOGLE) + .withRole(VALID_ROLE_GOOGLE).withStatus(VALID_STATUS_GOOGLE).withDate(VALID_DATE_GOOGLE) + .withComment(VALID_COMMENT_GOOGLE).withTags(VALID_TAG_BACK, VALID_TAG_FRONT).build(); + DESC_APPLE_DIFFERENT_COMMENT = new EditInternshipDescriptorBuilder().withCompanyName(VALID_COMPANY_NAME_APPLE) + .withRole(VALID_ROLE_APPLE).withStatus(VALID_STATUS_APPLE).withDate(VALID_DATE_APPLE) + .withComment(COMMENT_DESC_GOOGLE).withTags(VALID_TAG_FRONT).build(); + } + + /** + * Executes the given {@code command}, confirms that
+ * - the returned {@link CommandResult} matches {@code expectedCommandResult}
+ * - the {@code actualModel} matches {@code expectedModel} + */ + public static void assertCommandSuccess(Command command, Model actualModel, CommandResult expectedCommandResult, + Model expectedModel) { + try { + CommandResult result = command.execute(actualModel); + assertEquals(expectedCommandResult, result); + assertEquals(expectedModel, actualModel); + } catch (CommandException ce) { + throw new AssertionError("Execution of command should not fail.", ce); + } + } + + /** + * Convenience wrapper to {@link #assertCommandSuccess(Command, Model, CommandResult, Model)} + * that takes a string {@code expectedMessage}. + */ + public static void assertCommandSuccess(Command command, Model actualModel, String expectedMessage, + Model expectedModel) { + CommandResult expectedCommandResult = new CommandResult(expectedMessage); + assertCommandSuccess(command, actualModel, expectedCommandResult, expectedModel); + } + + /** + * Executes the given {@code command}, confirms that
+ * - a {@code CommandException} is thrown
+ * - the CommandException message matches {@code expectedMessage}
+ * - the InternBuddy, 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. + InternBuddy expectedInternBuddy = new InternBuddy(actualModel.getInternBuddy()); + List expectedFilteredList = new ArrayList<>(actualModel.getFilteredInternshipList()); + + assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel)); + assertEquals(expectedInternBuddy, actualModel.getInternBuddy()); + assertEquals(expectedFilteredList, actualModel.getFilteredInternshipList()); + } + /** + * Updates {@code model}'s filtered list to show only the internship at the given {@code targetIndex} in the + * {@code model}'s InternBuddy. + */ + public static void showInternshipAtIndex(Model model, Index targetIndex) { + assertTrue(targetIndex.getZeroBased() < model.getFilteredInternshipList().size()); + + Internship internship = model.getFilteredInternshipList().get(targetIndex.getZeroBased()); + final List nameList = Arrays.asList(internship.getCompanyName().fullCompanyName); + final List roleList = Arrays.asList(internship.getRole().fullRole); + final List statusList = Arrays.asList(internship.getStatus().fullStatus); + final List dateList = Arrays.asList(internship.getDate().fullDate); + final List tagList = internship.getTags().isEmpty() + ? Collections.emptyList() + : Arrays.asList(internship.getTags().stream() + .map(tag -> tag.tagName) + .findFirst().get()); + model.updateFilteredInternshipList(new InternshipContainsKeywordsPredicate(nameList, roleList, statusList, + dateList, tagList)); + + assertEquals(1, model.getFilteredInternshipList().size()); + } + +} diff --git a/src/test/java/seedu/internship/logic/commands/CopyCommandTest.java b/src/test/java/seedu/internship/logic/commands/CopyCommandTest.java new file mode 100644 index 00000000000..fd7303b4246 --- /dev/null +++ b/src/test/java/seedu/internship/logic/commands/CopyCommandTest.java @@ -0,0 +1,104 @@ +package seedu.internship.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.internship.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.internship.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.internship.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.internship.testutil.TypicalIndexes.INDEX_FIRST_INTERNSHIP; +import static seedu.internship.testutil.TypicalIndexes.INDEX_SECOND_INTERNSHIP; +import static seedu.internship.testutil.TypicalInternships.getTypicalInternBuddy; + +import org.junit.jupiter.api.Test; + +import seedu.internship.commons.core.Messages; +import seedu.internship.commons.core.index.Index; +import seedu.internship.model.Model; +import seedu.internship.model.ModelManager; +import seedu.internship.model.UserPrefs; +import seedu.internship.model.internship.Internship; + +/** + * Contains integration tests (interaction with the Model) and unit tests for + * {@code CopyCommand}. + */ +public class CopyCommandTest { + + + + private Model model = new ModelManager(getTypicalInternBuddy(), new UserPrefs()); + + @Test + public void execute_validIndexUnfilteredList_success() { + Internship internshipToCopy = model.getFilteredInternshipList().get(INDEX_FIRST_INTERNSHIP.getZeroBased()); + CopyCommand copyCommand = new CopyCommand(INDEX_FIRST_INTERNSHIP); + + String expectedMessage = String.format(CopyCommand.MESSAGE_COPY_INTERNSHIP_SUCCESS, internshipToCopy); + + ModelManager expectedModel = new ModelManager(model.getInternBuddy(), new UserPrefs()); + expectedModel.copyInternship(internshipToCopy); + + assertCommandSuccess(copyCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredInternshipList().size() + 1); + CopyCommand copyCommand = new CopyCommand(outOfBoundIndex); + + assertCommandFailure(copyCommand, model, Messages.MESSAGE_OUT_OF_RANGE_INTERNSHIP_DISPLAYED_INDEX); + } + + @Test + public void execute_validIndexFilteredList_success() { + showInternshipAtIndex(model, INDEX_FIRST_INTERNSHIP); + + Internship internshipToCopy = model.getFilteredInternshipList().get(INDEX_FIRST_INTERNSHIP.getZeroBased()); + CopyCommand copyCommand = new CopyCommand(INDEX_FIRST_INTERNSHIP); + + String expectedMessage = String.format(CopyCommand.MESSAGE_COPY_INTERNSHIP_SUCCESS, internshipToCopy); + + + Model expectedModel = new ModelManager(model.getInternBuddy(), new UserPrefs()); + showInternshipAtIndex(expectedModel, INDEX_FIRST_INTERNSHIP); + expectedModel.copyInternship(internshipToCopy); + + assertCommandSuccess(copyCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showInternshipAtIndex(model, INDEX_FIRST_INTERNSHIP); + + Index outOfBoundIndex = INDEX_SECOND_INTERNSHIP; + // ensures that outOfBoundIndex is still in bounds of internship list + assertTrue(outOfBoundIndex.getZeroBased() < model.getInternBuddy().getInternshipList().size()); + + CopyCommand copyCommand = new CopyCommand(outOfBoundIndex); + + assertCommandFailure(copyCommand, model, Messages.MESSAGE_OUT_OF_RANGE_INTERNSHIP_DISPLAYED_INDEX); + } + + @Test + public void equals() { + CopyCommand copyFirstCommand = new CopyCommand(INDEX_FIRST_INTERNSHIP); + CopyCommand copySecondCommand = new CopyCommand(INDEX_SECOND_INTERNSHIP); + + // same object -> returns true + assertTrue(copyFirstCommand.equals(copyFirstCommand)); + + // same values -> returns true + CopyCommand copyFirstCommandCopy = new CopyCommand(INDEX_FIRST_INTERNSHIP); + assertTrue(copyFirstCommand.equals(copyFirstCommandCopy)); + + // different types -> returns false + assertFalse(copyFirstCommand.equals(1)); + + // null -> returns false + assertFalse(copyFirstCommand.equals(null)); + + // different internship -> returns false + assertFalse(copyFirstCommand.equals(copySecondCommand)); + } + +} diff --git a/src/test/java/seedu/internship/logic/commands/DeleteFieldCommandTest.java b/src/test/java/seedu/internship/logic/commands/DeleteFieldCommandTest.java new file mode 100644 index 00000000000..6329d98c13c --- /dev/null +++ b/src/test/java/seedu/internship/logic/commands/DeleteFieldCommandTest.java @@ -0,0 +1,234 @@ +package seedu.internship.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.internship.logic.commands.CommandTestUtil.COMPLEX_PREDICATE; +import static seedu.internship.logic.commands.CommandTestUtil.MULTIPLE_FIELD_PREDICATE; +import static seedu.internship.logic.commands.CommandTestUtil.ONE_FIELD_PREDICATE; +import static seedu.internship.logic.commands.CommandTestUtil.SIMPLE_PREDICATE; +import static seedu.internship.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.internship.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.internship.logic.commands.DeleteFieldCommand.MESSAGE_DELETE_INTERNSHIP_SUCCESS; +import static seedu.internship.testutil.TypicalIndexes.INDEX_FIRST_INTERNSHIP; +import static seedu.internship.testutil.TypicalInternships.getTypicalInternBuddy; + +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +import seedu.internship.model.Model; +import seedu.internship.model.ModelManager; +import seedu.internship.model.UserPrefs; +import seedu.internship.model.internship.Internship; + + + + +/** + * Contains integration tests (interaction with the Model) and unit tests for + * {@code DeleteCommand}. + */ +public class DeleteFieldCommandTest { + private Model model = new ModelManager(getTypicalInternBuddy(), new UserPrefs()); + + @Test + public void execute_simplePredicateUnfilteredList_success() { + List internshipsToDelete = model.getFilteredInternshipList().stream() + .filter(SIMPLE_PREDICATE) + .collect(Collectors.toList()); + + DeleteFieldCommand deleteCommand = new DeleteFieldCommand(SIMPLE_PREDICATE); + + String expectedMessage = String.format(MESSAGE_DELETE_INTERNSHIP_SUCCESS, 1); + + ModelManager expectedModel = new ModelManager(model.getInternBuddy(), new UserPrefs()); + for (Internship internshipToDelete: internshipsToDelete) { + expectedModel.deleteInternship(internshipToDelete); + } + + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_oneFieldPredicateUnfilteredList_success() { + List internshipsToDelete = model.getFilteredInternshipList().stream() + .filter(ONE_FIELD_PREDICATE) + .collect(Collectors.toList()); + + DeleteFieldCommand deleteCommand = new DeleteFieldCommand(ONE_FIELD_PREDICATE); + + String expectedMessage = String.format(MESSAGE_DELETE_INTERNSHIP_SUCCESS, 2); + + ModelManager expectedModel = new ModelManager(model.getInternBuddy(), new UserPrefs()); + + for (Internship internshipToDelete: internshipsToDelete) { + expectedModel.deleteInternship(internshipToDelete); + } + + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_multipleFieldPredicateUnfilteredList_success() { + List internshipsToDelete = model.getFilteredInternshipList().stream() + .filter(MULTIPLE_FIELD_PREDICATE) + .collect(Collectors.toList()); + + DeleteFieldCommand deleteCommand = new DeleteFieldCommand(MULTIPLE_FIELD_PREDICATE); + + String expectedMessage = String.format(MESSAGE_DELETE_INTERNSHIP_SUCCESS, 1); + + ModelManager expectedModel = new ModelManager(model.getInternBuddy(), new UserPrefs()); + + for (Internship internshipToDelete: internshipsToDelete) { + expectedModel.deleteInternship(internshipToDelete); + } + + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_complexFieldPredicateUnfilteredList_success() { + List internshipsToDelete = model.getFilteredInternshipList().stream() + .filter(COMPLEX_PREDICATE) + .collect(Collectors.toList()); + + DeleteFieldCommand deleteCommand = new DeleteFieldCommand(COMPLEX_PREDICATE); + + String expectedMessage = String.format(MESSAGE_DELETE_INTERNSHIP_SUCCESS, 1); + + ModelManager expectedModel = new ModelManager(model.getInternBuddy(), new UserPrefs()); + + for (Internship internshipToDelete: internshipsToDelete) { + expectedModel.deleteInternship(internshipToDelete); + } + + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_multipleFieldFilteredList_success() { + showInternshipAtIndex(model, INDEX_FIRST_INTERNSHIP); + + List internshipsToDelete = model.getFilteredInternshipList().stream() + .filter(MULTIPLE_FIELD_PREDICATE) + .collect(Collectors.toList()); + + DeleteFieldCommand deleteCommand = new DeleteFieldCommand(MULTIPLE_FIELD_PREDICATE); + + String expectedMessage = String.format(DeleteFieldCommand.MESSAGE_DELETE_INTERNSHIP_SUCCESS, 1); + + Model expectedModel = new ModelManager(model.getInternBuddy(), new UserPrefs()); + + for (Internship internshipToDelete: internshipsToDelete) { + expectedModel.deleteInternship(internshipToDelete); + } + + showNoInternship(expectedModel); + + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_complexFieldFilteredList_success() { + showInternshipAtIndex(model, INDEX_FIRST_INTERNSHIP); + + List internshipsToDelete = model.getFilteredInternshipList().stream() + .filter(COMPLEX_PREDICATE) + .collect(Collectors.toList()); + + DeleteFieldCommand deleteCommand = new DeleteFieldCommand(COMPLEX_PREDICATE); + + String expectedMessage = String.format(DeleteFieldCommand.MESSAGE_DELETE_INTERNSHIP_SUCCESS, 1); + + Model expectedModel = new ModelManager(model.getInternBuddy(), new UserPrefs()); + + for (Internship internshipToDelete: internshipsToDelete) { + expectedModel.deleteInternship(internshipToDelete); + } + + showNoInternship(expectedModel); + + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_oneFieldPredicateFilteredList_success() { + showInternshipAtIndex(model, INDEX_FIRST_INTERNSHIP); + + List internshipsToDelete = model.getFilteredInternshipList().stream() + .filter(ONE_FIELD_PREDICATE) + .collect(Collectors.toList()); + + DeleteFieldCommand deleteCommand = new DeleteFieldCommand(ONE_FIELD_PREDICATE); + + String expectedMessage = String.format(DeleteFieldCommand.MESSAGE_DELETE_INTERNSHIP_SUCCESS, 1); + + Model expectedModel = new ModelManager(model.getInternBuddy(), new UserPrefs()); + + for (Internship internshipToDelete: internshipsToDelete) { + expectedModel.deleteInternship(internshipToDelete); + } + + showNoInternship(expectedModel); + + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_simplePredicateFilteredList_success() { + showInternshipAtIndex(model, INDEX_FIRST_INTERNSHIP); + + List internshipsToDelete = model.getFilteredInternshipList().stream() + .filter(SIMPLE_PREDICATE) + .collect(Collectors.toList()); + + DeleteFieldCommand deleteCommand = new DeleteFieldCommand(SIMPLE_PREDICATE); + + String expectedMessage = String.format(DeleteFieldCommand.MESSAGE_DELETE_INTERNSHIP_SUCCESS, 1); + + Model expectedModel = new ModelManager(model.getInternBuddy(), new UserPrefs()); + + for (Internship internshipToDelete: internshipsToDelete) { + expectedModel.deleteInternship(internshipToDelete); + } + + showNoInternship(expectedModel); + + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + } + + + + @Test + public void equals() { + DeleteFieldCommand deleteFirstCommand = new DeleteFieldCommand(SIMPLE_PREDICATE); + DeleteFieldCommand deleteSecondCommand = new DeleteFieldCommand(ONE_FIELD_PREDICATE); + + // same object -> returns true + assertTrue(deleteFirstCommand.equals(deleteFirstCommand)); + + // same values -> returns true + DeleteFieldCommand deleteFirstCommandCopy = new DeleteFieldCommand(SIMPLE_PREDICATE); + assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy)); + + // different types -> returns false + assertFalse(deleteFirstCommand.equals(1)); + + // null -> returns false + assertFalse(deleteFirstCommand.equals(null)); + + // different internship -> returns false + assertFalse(deleteFirstCommand.equals(deleteSecondCommand)); + } + + /** + * Updates {@code model}'s filtered list to show no internship entry. + */ + private void showNoInternship(Model model) { + model.updateFilteredInternshipList(p -> false); + + assertTrue(model.getFilteredInternshipList().isEmpty()); + } +} diff --git a/src/test/java/seedu/internship/logic/commands/DeleteIndexCommandTest.java b/src/test/java/seedu/internship/logic/commands/DeleteIndexCommandTest.java new file mode 100644 index 00000000000..71109e70b78 --- /dev/null +++ b/src/test/java/seedu/internship/logic/commands/DeleteIndexCommandTest.java @@ -0,0 +1,153 @@ +package seedu.internship.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.internship.logic.commands.CommandTestUtil.NON_EMPTY_INDEXLIST; +import static seedu.internship.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.internship.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.internship.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.internship.logic.commands.DeleteIndexCommand.MESSAGE_DELETE_INTERNSHIP_SUCCESS; +import static seedu.internship.testutil.TypicalIndexes.INDEX_FIRST_INTERNSHIP; +import static seedu.internship.testutil.TypicalIndexes.INDEX_SECOND_INTERNSHIP; +import static seedu.internship.testutil.TypicalInternships.getTypicalInternBuddy; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.internship.commons.core.Messages; +import seedu.internship.commons.core.index.Index; +import seedu.internship.model.Model; +import seedu.internship.model.ModelManager; +import seedu.internship.model.UserPrefs; +import seedu.internship.model.internship.Internship; + +/** + * Contains integration tests (interaction with the Model) and unit tests for + * {@code DeleteCommand}. + */ +public class DeleteIndexCommandTest { + private Model model = new ModelManager(getTypicalInternBuddy(), new UserPrefs()); + + @Test + public void execute_validIndexUnfilteredList_success() { + Internship internshipToDelete = model.getFilteredInternshipList().get(INDEX_FIRST_INTERNSHIP.getZeroBased()); + DeleteIndexCommand deleteCommand = new DeleteIndexCommand(NON_EMPTY_INDEXLIST); + String expectedMessage = String.format(MESSAGE_DELETE_INTERNSHIP_SUCCESS, 1); + + ModelManager expectedModel = new ModelManager(model.getInternBuddy(), new UserPrefs()); + expectedModel.deleteInternship(internshipToDelete); + + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredInternshipList().size() + 1); + List indexList = new ArrayList<>(Arrays.asList(outOfBoundIndex)); + DeleteIndexCommand deleteCommand = new DeleteIndexCommand(indexList); + + assertCommandFailure(deleteCommand, model, Messages.MESSAGE_OUT_OF_RANGE_INTERNSHIP_DISPLAYED_INDEX); + } + + @Test + public void execute_validIndexFilteredList_success() { + showInternshipAtIndex(model, INDEX_FIRST_INTERNSHIP); + + Internship internshipToDelete = model.getFilteredInternshipList().get(INDEX_FIRST_INTERNSHIP.getZeroBased()); + DeleteIndexCommand deleteCommand = new DeleteIndexCommand(NON_EMPTY_INDEXLIST); + + String expectedMessage = String.format(MESSAGE_DELETE_INTERNSHIP_SUCCESS, 1); + + Model expectedModel = new ModelManager(model.getInternBuddy(), new UserPrefs()); + expectedModel.deleteInternship(internshipToDelete); + showNoInternship(expectedModel); + + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showInternshipAtIndex(model, INDEX_FIRST_INTERNSHIP); + + Index outOfBoundIndex = INDEX_SECOND_INTERNSHIP; + // ensures that outOfBoundIndex is still in bounds of InternBuddy list + assertTrue(outOfBoundIndex.getZeroBased() < model.getInternBuddy().getInternshipList().size()); + + List indexList = new ArrayList<>(Arrays.asList(outOfBoundIndex)); + + DeleteIndexCommand deleteCommand = new DeleteIndexCommand(indexList); + + assertCommandFailure(deleteCommand, model, Messages.MESSAGE_OUT_OF_RANGE_INTERNSHIP_DISPLAYED_INDEX); + } + + @Test + public void equals() { + DeleteIndexCommand deleteFirstCommand = new DeleteIndexCommand(NON_EMPTY_INDEXLIST); + List indexList = new ArrayList<>(Arrays.asList(Index.fromZeroBased(1))); + DeleteIndexCommand deleteSecondCommand = new DeleteIndexCommand(indexList); + + // same object -> returns true + assertTrue(deleteFirstCommand.equals(deleteFirstCommand)); + + // same values -> returns true + DeleteIndexCommand deleteFirstCommandCopy = new DeleteIndexCommand(NON_EMPTY_INDEXLIST); + assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy)); + + // different types -> returns false + assertFalse(deleteFirstCommand.equals(1)); + + // null -> returns false + assertFalse(deleteFirstCommand.equals(null)); + + // different internship -> returns false + assertFalse(deleteFirstCommand.equals(deleteSecondCommand)); + } + + + // @@author eugenetangkj - reused + // Reused with modifications from my teammate, Christopher, in order to cover the + // conditional case that is not covered within test cases + @Test + public void updateRightPanel_success() { + // Case 1: Right panel is null, should not update + Internship internshipToDelete = model.getFilteredInternshipList().get(INDEX_FIRST_INTERNSHIP.getZeroBased()); + Internship internshipNotDeleted = model.getFilteredInternshipList().get(INDEX_SECOND_INTERNSHIP.getZeroBased()); + DeleteIndexCommand deleteCommand = new DeleteIndexCommand(NON_EMPTY_INDEXLIST); + String expectedMessage = String.format(MESSAGE_DELETE_INTERNSHIP_SUCCESS, 1); + + ModelManager expectedModel = new ModelManager(model.getInternBuddy(), new UserPrefs()); + model.updateSelectedInternship(internshipNotDeleted); + expectedModel.deleteInternship(internshipToDelete); + + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + assertEquals(model.getSelectedInternship(), internshipNotDeleted); + + + // Case 2: Right panel has internship that is deleted, should reset to original welcome message + internshipToDelete = model.getFilteredInternshipList().get(INDEX_FIRST_INTERNSHIP.getZeroBased()); + internshipNotDeleted = model.getFilteredInternshipList().get(INDEX_SECOND_INTERNSHIP.getZeroBased()); + deleteCommand = new DeleteIndexCommand(NON_EMPTY_INDEXLIST); + expectedMessage = String.format(MESSAGE_DELETE_INTERNSHIP_SUCCESS, 1); + + expectedModel = new ModelManager(model.getInternBuddy(), new UserPrefs()); + model.updateSelectedInternship(internshipToDelete); + expectedModel.deleteInternship(internshipToDelete); + + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + assertEquals(model.getSelectedInternship(), null); + } + + /** + * Updates {@code model}'s filtered list to show no internship entry. + */ + private void showNoInternship(Model model) { + model.updateFilteredInternshipList(p -> false); + + assertTrue(model.getFilteredInternshipList().isEmpty()); + } + +} diff --git a/src/test/java/seedu/internship/logic/commands/EditCommandTest.java b/src/test/java/seedu/internship/logic/commands/EditCommandTest.java new file mode 100644 index 00000000000..98a42b07742 --- /dev/null +++ b/src/test/java/seedu/internship/logic/commands/EditCommandTest.java @@ -0,0 +1,183 @@ +package seedu.internship.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.internship.logic.commands.CommandTestUtil.DESC_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.DESC_APPLE_DIFFERENT_COMMENT; +import static seedu.internship.logic.commands.CommandTestUtil.DESC_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_ROLE_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_TAG_FRONT; +import static seedu.internship.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.internship.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.internship.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.internship.testutil.TypicalIndexes.INDEX_FIRST_INTERNSHIP; +import static seedu.internship.testutil.TypicalIndexes.INDEX_SECOND_INTERNSHIP; +import static seedu.internship.testutil.TypicalInternships.getTypicalInternBuddy; + +import org.junit.jupiter.api.Test; + +import seedu.internship.commons.core.Messages; +import seedu.internship.commons.core.index.Index; +import seedu.internship.logic.commands.EditCommand.EditInternshipDescriptor; +import seedu.internship.model.InternBuddy; +import seedu.internship.model.Model; +import seedu.internship.model.ModelManager; +import seedu.internship.model.UserPrefs; +import seedu.internship.model.internship.Internship; +import seedu.internship.testutil.EditInternshipDescriptorBuilder; +import seedu.internship.testutil.InternshipBuilder; + +/** + * Contains integration tests (interaction with the Model) and unit tests for EditCommand. + */ +public class EditCommandTest { + + private Model model = new ModelManager(getTypicalInternBuddy(), new UserPrefs()); + + @Test + public void execute_allFieldsSpecifiedUnfilteredList_success() { + Internship editedInternship = new InternshipBuilder().build(); + EditInternshipDescriptor descriptor = new EditInternshipDescriptorBuilder(editedInternship).build(); + EditCommand editCommand = new EditCommand(INDEX_FIRST_INTERNSHIP, descriptor); + + String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_INTERNSHIP_SUCCESS, editedInternship); + + Model expectedModel = new ModelManager(new InternBuddy(model.getInternBuddy()), new UserPrefs()); + expectedModel.setInternship(model.getFilteredInternshipList().get(0), editedInternship); + + assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_someFieldsSpecifiedUnfilteredList_success() { + Index indexLastInternship = Index.fromOneBased(model.getFilteredInternshipList().size()); + Internship lastInternship = model.getFilteredInternshipList().get(indexLastInternship.getZeroBased()); + + InternshipBuilder internshipInList = new InternshipBuilder(lastInternship); + Internship editedInternship = internshipInList.withCompanyName(VALID_COMPANY_NAME_GOOGLE) + .withRole(VALID_ROLE_GOOGLE) + .withTags(VALID_TAG_FRONT).build(); + + EditInternshipDescriptor descriptor = new EditInternshipDescriptorBuilder() + .withCompanyName(VALID_COMPANY_NAME_GOOGLE).withRole(VALID_ROLE_GOOGLE) + .withTags(VALID_TAG_FRONT).build(); + EditCommand editCommand = new EditCommand(indexLastInternship, descriptor); + + String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_INTERNSHIP_SUCCESS, editedInternship); + + Model expectedModel = new ModelManager(new InternBuddy(model.getInternBuddy()), new UserPrefs()); + expectedModel.setInternship(lastInternship, editedInternship); + + assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_noFieldSpecifiedUnfilteredList_success() { + EditCommand editCommand = new EditCommand(INDEX_FIRST_INTERNSHIP, new EditInternshipDescriptor()); + Internship editedInternship = model.getFilteredInternshipList().get(INDEX_FIRST_INTERNSHIP.getZeroBased()); + + String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_INTERNSHIP_SUCCESS, editedInternship); + + Model expectedModel = new ModelManager(new InternBuddy(model.getInternBuddy()), new UserPrefs()); + + assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_filteredList_success() { + showInternshipAtIndex(model, INDEX_FIRST_INTERNSHIP); + + Internship internshipInFilteredList = model.getFilteredInternshipList() + .get(INDEX_FIRST_INTERNSHIP.getZeroBased()); + Internship editedInternship = new InternshipBuilder(internshipInFilteredList) + .withCompanyName(VALID_COMPANY_NAME_GOOGLE).build(); + EditCommand editCommand = new EditCommand(INDEX_FIRST_INTERNSHIP, + new EditInternshipDescriptorBuilder().withCompanyName(VALID_COMPANY_NAME_GOOGLE).build()); + + String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_INTERNSHIP_SUCCESS, editedInternship); + + Model expectedModel = new ModelManager(new InternBuddy(model.getInternBuddy()), new UserPrefs()); + expectedModel.setInternship(model.getFilteredInternshipList().get(0), editedInternship); + + assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_duplicateInternshipUnfilteredList_failure() { + Internship firstInternship = model.getFilteredInternshipList().get(INDEX_FIRST_INTERNSHIP.getZeroBased()); + EditInternshipDescriptor descriptor = new EditInternshipDescriptorBuilder(firstInternship).build(); + EditCommand editCommand = new EditCommand(INDEX_SECOND_INTERNSHIP, descriptor); + + assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_INTERNSHIP); + } + + @Test + public void execute_duplicateInternshipFilteredList_failure() { + showInternshipAtIndex(model, INDEX_FIRST_INTERNSHIP); + + // edit internship in filtered list into a duplicate in Internbuddy + Internship internshipInList = model.getInternBuddy().getInternshipList() + .get(INDEX_SECOND_INTERNSHIP.getZeroBased()); + EditCommand editCommand = new EditCommand(INDEX_FIRST_INTERNSHIP, + new EditInternshipDescriptorBuilder(internshipInList).build()); + + assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_INTERNSHIP); + } + + @Test + public void execute_invalidInternshipIndexUnfilteredList_failure() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredInternshipList().size() + 1); + EditInternshipDescriptor descriptor = new EditInternshipDescriptorBuilder() + .withCompanyName(VALID_COMPANY_NAME_GOOGLE).build(); + EditCommand editCommand = new EditCommand(outOfBoundIndex, descriptor); + + assertCommandFailure(editCommand, model, Messages.MESSAGE_OUT_OF_RANGE_INTERNSHIP_DISPLAYED_INDEX); + } + + /** + * Edit filtered list where index is larger than size of filtered list, + * but smaller than size of InternBuddy + */ + @Test + public void execute_invalidInternshipIndexFilteredList_failure() { + showInternshipAtIndex(model, INDEX_FIRST_INTERNSHIP); + Index outOfBoundIndex = INDEX_SECOND_INTERNSHIP; + // ensures that outOfBoundIndex is still in bounds of InternBuddy list + assertTrue(outOfBoundIndex.getZeroBased() < model.getInternBuddy().getInternshipList().size()); + + EditCommand editCommand = new EditCommand(outOfBoundIndex, + new EditInternshipDescriptorBuilder().withCompanyName(VALID_COMPANY_NAME_GOOGLE).build()); + + assertCommandFailure(editCommand, model, Messages.MESSAGE_OUT_OF_RANGE_INTERNSHIP_DISPLAYED_INDEX); + } + + @Test + public void equals() { + final EditCommand standardCommand = new EditCommand(INDEX_FIRST_INTERNSHIP, DESC_APPLE); + + // same values -> returns true + EditInternshipDescriptor copyDescriptor = new EditInternshipDescriptor(DESC_APPLE); + EditCommand commandWithSameValues = new EditCommand(INDEX_FIRST_INTERNSHIP, copyDescriptor); + assertTrue(standardCommand.equals(commandWithSameValues)); + + // same object -> returns true + assertTrue(standardCommand.equals(standardCommand)); + + // null -> returns false + assertFalse(standardCommand.equals(null)); + + // different types -> returns false + assertFalse(standardCommand.equals(new ClearCommand())); + + // different index -> returns false + assertFalse(standardCommand.equals(new EditCommand(INDEX_SECOND_INTERNSHIP, DESC_APPLE))); + + // different descriptor -> returns false + assertFalse(standardCommand.equals(new EditCommand(INDEX_FIRST_INTERNSHIP, DESC_GOOGLE))); + + // different comments -> return false + assertFalse(standardCommand.equals(new EditCommand(INDEX_FIRST_INTERNSHIP, DESC_APPLE_DIFFERENT_COMMENT))); + } + +} diff --git a/src/test/java/seedu/internship/logic/commands/EditInternshipDescriptorTest.java b/src/test/java/seedu/internship/logic/commands/EditInternshipDescriptorTest.java new file mode 100644 index 00000000000..6b3c246ce49 --- /dev/null +++ b/src/test/java/seedu/internship/logic/commands/EditInternshipDescriptorTest.java @@ -0,0 +1,59 @@ +package seedu.internship.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.internship.logic.commands.CommandTestUtil.DESC_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.DESC_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_DATE_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_ROLE_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_STATUS_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_TAG_BACK; + +import org.junit.jupiter.api.Test; + +import seedu.internship.logic.commands.EditCommand.EditInternshipDescriptor; +import seedu.internship.testutil.EditInternshipDescriptorBuilder; + +public class EditInternshipDescriptorTest { + + @Test + public void equals() { + // same values -> returns true + EditInternshipDescriptor descriptorWithSameValues = new EditInternshipDescriptor(DESC_APPLE); + assertTrue(DESC_APPLE.equals(descriptorWithSameValues)); + + // same object -> returns true + assertTrue(DESC_APPLE.equals(DESC_APPLE)); + + // null -> returns false + assertFalse(DESC_APPLE.equals(null)); + + // different types -> returns false + assertFalse(DESC_APPLE.equals(5)); + + // different values -> returns false + assertFalse(DESC_APPLE.equals(DESC_GOOGLE)); + + // different company name -> returns false + EditInternshipDescriptor editedApple = new EditInternshipDescriptorBuilder(DESC_APPLE) + .withCompanyName(VALID_COMPANY_NAME_GOOGLE).build(); + assertFalse(DESC_APPLE.equals(editedApple)); + + // different roles -> returns false + editedApple = new EditInternshipDescriptorBuilder(DESC_APPLE).withRole(VALID_ROLE_GOOGLE).build(); + assertFalse(DESC_APPLE.equals(editedApple)); + + // different status -> returns false + editedApple = new EditInternshipDescriptorBuilder(DESC_APPLE).withStatus(VALID_STATUS_GOOGLE).build(); + assertFalse(DESC_APPLE.equals(editedApple)); + + // different date -> returns false + editedApple = new EditInternshipDescriptorBuilder(DESC_APPLE).withDate(VALID_DATE_GOOGLE).build(); + assertFalse(DESC_APPLE.equals(editedApple)); + + // different tags -> returns false + editedApple = new EditInternshipDescriptorBuilder(DESC_APPLE).withTags(VALID_TAG_BACK).build(); + assertFalse(DESC_APPLE.equals(editedApple)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java b/src/test/java/seedu/internship/logic/commands/ExitCommandTest.java similarity index 59% rename from src/test/java/seedu/address/logic/commands/ExitCommandTest.java rename to src/test/java/seedu/internship/logic/commands/ExitCommandTest.java index 9533c473875..b274bc8392d 100644 --- a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java +++ b/src/test/java/seedu/internship/logic/commands/ExitCommandTest.java @@ -1,12 +1,12 @@ -package seedu.address.logic.commands; +package seedu.internship.logic.commands; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.ExitCommand.MESSAGE_EXIT_ACKNOWLEDGEMENT; +import static seedu.internship.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.internship.logic.commands.ExitCommand.MESSAGE_EXIT_ACKNOWLEDGEMENT; import org.junit.jupiter.api.Test; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; +import seedu.internship.model.Model; +import seedu.internship.model.ModelManager; public class ExitCommandTest { private Model model = new ModelManager(); diff --git a/src/test/java/seedu/internship/logic/commands/FindCommandTest.java b/src/test/java/seedu/internship/logic/commands/FindCommandTest.java new file mode 100644 index 00000000000..294003c3c79 --- /dev/null +++ b/src/test/java/seedu/internship/logic/commands/FindCommandTest.java @@ -0,0 +1,85 @@ +package seedu.internship.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.internship.commons.core.Messages.MESSAGE_INTERNSHIP_LISTED_OVERVIEW; +import static seedu.internship.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.internship.testutil.TypicalInternships.GOLDMAN; +import static seedu.internship.testutil.TypicalInternships.RIOTGAMES; +import static seedu.internship.testutil.TypicalInternships.SAMSUNG; +import static seedu.internship.testutil.TypicalInternships.getTypicalInternBuddy; + +import java.util.Arrays; +import java.util.Collections; + +import org.junit.jupiter.api.Test; + +import seedu.internship.model.Model; +import seedu.internship.model.ModelManager; +import seedu.internship.model.UserPrefs; +import seedu.internship.model.internship.InternshipContainsKeywordsPredicate; + +/** + * Contains integration tests (interaction with the Model) for {@code FindCommand}. + */ +public class FindCommandTest { + private Model model = new ModelManager(getTypicalInternBuddy(), new UserPrefs()); + private Model expectedModel = new ModelManager(getTypicalInternBuddy(), new UserPrefs()); + + @Test + public void equals() { + InternshipContainsKeywordsPredicate firstPredicate = + new InternshipContainsKeywordsPredicate(Collections.singletonList("first"), + Collections.singletonList("first"), Collections.singletonList("first"), + Collections.singletonList("first"), Collections.singletonList("first")); + InternshipContainsKeywordsPredicate secondPredicate = + new InternshipContainsKeywordsPredicate(Collections.singletonList("second"), + Collections.singletonList("second"), Collections.singletonList("second"), + Collections.singletonList("second"), Collections.singletonList("second")); + + FindCommand findFirstCommand = new FindCommand(firstPredicate); + FindCommand findSecondCommand = new FindCommand(secondPredicate); + + // same object -> returns true + assertTrue(findFirstCommand.equals(findFirstCommand)); + + // same values -> returns true + FindCommand findFirstCommandCopy = new FindCommand(firstPredicate); + assertTrue(findFirstCommand.equals(findFirstCommandCopy)); + + // different types -> returns false + assertFalse(findFirstCommand.equals(1)); + + // null -> returns false + assertFalse(findFirstCommand.equals(null)); + + // different internship -> returns false + assertFalse(findFirstCommand.equals(findSecondCommand)); + } + + @Test + public void execute_noKeywords_noInternshipFound() { + String expectedMessage = String.format(MESSAGE_INTERNSHIP_LISTED_OVERVIEW, 0); + InternshipContainsKeywordsPredicate predicate = + new InternshipContainsKeywordsPredicate(Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + FindCommand command = new FindCommand(predicate); + expectedModel.updateFilteredInternshipList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getFilteredInternshipList()); + } + + @Test + public void execute_multipleKeywords_multipleInternshipsFound() { + String expectedMessage = String.format(MESSAGE_INTERNSHIP_LISTED_OVERVIEW, 3); + InternshipContainsKeywordsPredicate predicate = + new InternshipContainsKeywordsPredicate(Arrays.asList("Goldman", "Riot Games", "Samsung"), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), + Collections.emptyList()); + FindCommand command = new FindCommand(predicate); + expectedModel.updateFilteredInternshipList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(GOLDMAN, RIOTGAMES, SAMSUNG), model.getFilteredInternshipList()); + } +} diff --git a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java b/src/test/java/seedu/internship/logic/commands/HelpCommandTest.java similarity index 59% rename from src/test/java/seedu/address/logic/commands/HelpCommandTest.java rename to src/test/java/seedu/internship/logic/commands/HelpCommandTest.java index 4904fc4352e..88d5bc7bdfe 100644 --- a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java +++ b/src/test/java/seedu/internship/logic/commands/HelpCommandTest.java @@ -1,12 +1,12 @@ -package seedu.address.logic.commands; +package seedu.internship.logic.commands; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.HelpCommand.SHOWING_HELP_MESSAGE; +import static seedu.internship.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.internship.logic.commands.HelpCommand.SHOWING_HELP_MESSAGE; import org.junit.jupiter.api.Test; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; +import seedu.internship.model.Model; +import seedu.internship.model.ModelManager; public class HelpCommandTest { private Model model = new ModelManager(); diff --git a/src/test/java/seedu/address/logic/commands/ListCommandTest.java b/src/test/java/seedu/internship/logic/commands/ListCommandTest.java similarity index 50% rename from src/test/java/seedu/address/logic/commands/ListCommandTest.java rename to src/test/java/seedu/internship/logic/commands/ListCommandTest.java index 435ff1f7275..0e21ae59a54 100644 --- a/src/test/java/seedu/address/logic/commands/ListCommandTest.java +++ b/src/test/java/seedu/internship/logic/commands/ListCommandTest.java @@ -1,16 +1,16 @@ -package seedu.address.logic.commands; +package seedu.internship.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.internship.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.internship.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.internship.testutil.TypicalIndexes.INDEX_FIRST_INTERNSHIP; +import static seedu.internship.testutil.TypicalInternships.getTypicalInternBuddy; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; +import seedu.internship.model.Model; +import seedu.internship.model.ModelManager; +import seedu.internship.model.UserPrefs; /** * Contains integration tests (interaction with the Model) and unit tests for ListCommand. @@ -22,8 +22,8 @@ public class ListCommandTest { @BeforeEach public void setUp() { - model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + model = new ModelManager(getTypicalInternBuddy(), new UserPrefs()); + expectedModel = new ModelManager(model.getInternBuddy(), new UserPrefs()); } @Test @@ -33,7 +33,7 @@ public void execute_listIsNotFiltered_showsSameList() { @Test public void execute_listIsFiltered_showsEverything() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); + showInternshipAtIndex(model, INDEX_FIRST_INTERNSHIP); assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel); } } diff --git a/src/test/java/seedu/internship/logic/commands/UpcomingCommandTest.java b/src/test/java/seedu/internship/logic/commands/UpcomingCommandTest.java new file mode 100644 index 00000000000..cb788b8d9de --- /dev/null +++ b/src/test/java/seedu/internship/logic/commands/UpcomingCommandTest.java @@ -0,0 +1,86 @@ +package seedu.internship.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.internship.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.internship.model.Model.PREDICATE_SHOW_UPCOMING_INTERNSHIPS; +import static seedu.internship.testutil.TypicalInternshipsVariableDates.FOODPANDA; +import static seedu.internship.testutil.TypicalInternshipsVariableDates.GOLDMAN; +import static seedu.internship.testutil.TypicalInternshipsVariableDates.SUPERCELLGAMES; +import static seedu.internship.testutil.TypicalInternshipsVariableDates.getTypicalInternBuddyVariableDates; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import seedu.internship.model.Model; +import seedu.internship.model.ModelManager; +import seedu.internship.model.UserPrefs; +import seedu.internship.model.internship.Internship; +import seedu.internship.testutil.InternshipBuilder; + + + + +/** + * Contains integration tests (interaction with the Model) for {@code FindCommand}. + */ +public class UpcomingCommandTest { + + private Model model = new ModelManager(getTypicalInternBuddyVariableDates(), new UserPrefs()); + private Model expectedModel = new ModelManager(getTypicalInternBuddyVariableDates(), new UserPrefs()); + private LocalDate currentDate = LocalDate.now(); + private DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + @Test + public void dateInternships() { + Internship today = new InternshipBuilder().withDate(currentDate.format(formatter)).build(); + Internship yesterday = new InternshipBuilder().withDate(currentDate.plusDays(-1) + .format(formatter)).build(); + Internship oneday = new InternshipBuilder().withDate(currentDate.plusDays(1) + .format(formatter)).build(); + Internship sixdays = new InternshipBuilder().withDate(currentDate.plusDays(6) + .format(formatter)).build(); + Internship sevendays = new InternshipBuilder().withDate(currentDate.plusDays(7) + .format(formatter)).build(); + + assertTrue(PREDICATE_SHOW_UPCOMING_INTERNSHIPS.test(today)); + assertTrue(PREDICATE_SHOW_UPCOMING_INTERNSHIPS.test(sixdays)); + assertTrue(PREDICATE_SHOW_UPCOMING_INTERNSHIPS.test(oneday)); + assertFalse(PREDICATE_SHOW_UPCOMING_INTERNSHIPS.test(yesterday)); + assertFalse(PREDICATE_SHOW_UPCOMING_INTERNSHIPS.test(sevendays)); + } + + @Test + public void statusInternships() { + String dateToday = currentDate.format(formatter).toString(); + Internship interview = new InternshipBuilder().withDate(dateToday).withStatus("interview").build(); + Internship offered = new InternshipBuilder().withDate(dateToday).withStatus("offered").build(); + Internship neww = new InternshipBuilder().withDate(dateToday).withStatus("new").build(); + Internship rejected = new InternshipBuilder().withDate(dateToday).withStatus("rejected").build(); + Internship assessment = new InternshipBuilder().withDate(dateToday).withStatus("assessment").build(); + Internship applied = new InternshipBuilder().withDate(dateToday).withStatus("applied").build(); + Internship accepted = new InternshipBuilder().withStatus("accepted").build(); + + assertTrue(PREDICATE_SHOW_UPCOMING_INTERNSHIPS.test(interview)); + assertTrue(PREDICATE_SHOW_UPCOMING_INTERNSHIPS.test(offered)); + assertTrue(PREDICATE_SHOW_UPCOMING_INTERNSHIPS.test(neww)); + assertTrue(PREDICATE_SHOW_UPCOMING_INTERNSHIPS.test(assessment)); + assertFalse(PREDICATE_SHOW_UPCOMING_INTERNSHIPS.test(rejected)); + assertFalse(PREDICATE_SHOW_UPCOMING_INTERNSHIPS.test(applied)); + assertFalse(PREDICATE_SHOW_UPCOMING_INTERNSHIPS.test(accepted)); + + + } + + @Test + public void multipleUpcomingInternships() { + UpcomingCommand command = new UpcomingCommand(); + expectedModel.updateFilteredInternshipList(PREDICATE_SHOW_UPCOMING_INTERNSHIPS); + assertEquals(Arrays.asList(FOODPANDA, GOLDMAN, SUPERCELLGAMES), expectedModel.getFilteredInternshipList()); + assertCommandSuccess(command, model, UpcomingCommand.MESSAGE_SUCCESS, expectedModel); + } +} diff --git a/src/test/java/seedu/internship/logic/commands/ViewCommandTest.java b/src/test/java/seedu/internship/logic/commands/ViewCommandTest.java new file mode 100644 index 00000000000..12986fdd84e --- /dev/null +++ b/src/test/java/seedu/internship/logic/commands/ViewCommandTest.java @@ -0,0 +1,104 @@ +package seedu.internship.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.internship.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.internship.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.internship.logic.commands.CommandTestUtil.showInternshipAtIndex; +import static seedu.internship.testutil.TypicalIndexes.INDEX_FIRST_INTERNSHIP; +import static seedu.internship.testutil.TypicalIndexes.INDEX_SECOND_INTERNSHIP; +import static seedu.internship.testutil.TypicalInternships.getTypicalInternBuddy; + +import org.junit.jupiter.api.Test; + +import seedu.internship.commons.core.Messages; +import seedu.internship.commons.core.index.Index; +import seedu.internship.model.Model; +import seedu.internship.model.ModelManager; +import seedu.internship.model.UserPrefs; +import seedu.internship.model.internship.Internship; + +/** + * Contains integration tests (interaction with the Model) and unit tests for + * {@code ViewCommand}. + */ +public class ViewCommandTest { + + + + private Model model = new ModelManager(getTypicalInternBuddy(), new UserPrefs()); + + @Test + public void execute_validIndexUnfilteredList_success() { + Internship internshipToView = model.getFilteredInternshipList().get(INDEX_FIRST_INTERNSHIP.getZeroBased()); + ViewCommand viewCommand = new ViewCommand(INDEX_FIRST_INTERNSHIP); + + String expectedMessage = String.format(ViewCommand.MESSAGE_VIEW_INTERNSHIP_SUCCESS, internshipToView); + + ModelManager expectedModel = new ModelManager(model.getInternBuddy(), new UserPrefs()); + expectedModel.viewInternship(internshipToView); + + assertCommandSuccess(viewCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredInternshipList().size() + 1); + ViewCommand viewCommand = new ViewCommand(outOfBoundIndex); + + assertCommandFailure(viewCommand, model, Messages.MESSAGE_OUT_OF_RANGE_INTERNSHIP_DISPLAYED_INDEX); + } + + @Test + public void execute_validIndexFilteredList_success() { + showInternshipAtIndex(model, INDEX_FIRST_INTERNSHIP); + + Internship internshipToView = model.getFilteredInternshipList().get(INDEX_FIRST_INTERNSHIP.getZeroBased()); + ViewCommand viewCommand = new ViewCommand(INDEX_FIRST_INTERNSHIP); + + String expectedMessage = String.format(ViewCommand.MESSAGE_VIEW_INTERNSHIP_SUCCESS, internshipToView); + + + Model expectedModel = new ModelManager(model.getInternBuddy(), new UserPrefs()); + showInternshipAtIndex(expectedModel, INDEX_FIRST_INTERNSHIP); + expectedModel.viewInternship(internshipToView); + + assertCommandSuccess(viewCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showInternshipAtIndex(model, INDEX_FIRST_INTERNSHIP); + + Index outOfBoundIndex = INDEX_SECOND_INTERNSHIP; + // ensures that outOfBoundIndex is still in bounds of internship list + assertTrue(outOfBoundIndex.getZeroBased() < model.getInternBuddy().getInternshipList().size()); + + ViewCommand viewCommand = new ViewCommand(outOfBoundIndex); + + assertCommandFailure(viewCommand, model, Messages.MESSAGE_OUT_OF_RANGE_INTERNSHIP_DISPLAYED_INDEX); + } + + @Test + public void equals() { + ViewCommand viewFirstCommand = new ViewCommand(INDEX_FIRST_INTERNSHIP); + ViewCommand viewSecondCommand = new ViewCommand(INDEX_SECOND_INTERNSHIP); + + // same object -> returns true + assertTrue(viewFirstCommand.equals(viewFirstCommand)); + + // same values -> returns true + ViewCommand viewFirstCommandCopy = new ViewCommand(INDEX_FIRST_INTERNSHIP); + assertTrue(viewFirstCommand.equals(viewFirstCommandCopy)); + + // different types -> returns false + assertFalse(viewFirstCommand.equals(1)); + + // null -> returns false + assertFalse(viewFirstCommand.equals(null)); + + // different internship -> returns false + assertFalse(viewFirstCommand.equals(viewSecondCommand)); + } + +} diff --git a/src/test/java/seedu/internship/logic/parser/AddCommandParserTest.java b/src/test/java/seedu/internship/logic/parser/AddCommandParserTest.java new file mode 100644 index 00000000000..97c54e49a6a --- /dev/null +++ b/src/test/java/seedu/internship/logic/parser/AddCommandParserTest.java @@ -0,0 +1,169 @@ +package seedu.internship.logic.parser; + +import static seedu.internship.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.internship.logic.commands.CommandTestUtil.COMMENT_DESC_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.COMMENT_DESC_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.COMPANY_NAME_DESC_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.COMPANY_NAME_DESC_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.DATE_DESC_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.DATE_DESC_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.INVALID_COMMENT_DESC; +import static seedu.internship.logic.commands.CommandTestUtil.INVALID_COMPANY_NAME_DESC; +import static seedu.internship.logic.commands.CommandTestUtil.INVALID_DATE_DESC; +import static seedu.internship.logic.commands.CommandTestUtil.INVALID_ROLE_DESC; +import static seedu.internship.logic.commands.CommandTestUtil.INVALID_STATUS_DESC; +import static seedu.internship.logic.commands.CommandTestUtil.INVALID_TAG_DESC; +import static seedu.internship.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY; +import static seedu.internship.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; +import static seedu.internship.logic.commands.CommandTestUtil.ROLE_DESC_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.ROLE_DESC_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.STATUS_DESC_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.STATUS_DESC_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.TAG_DESC_BACK; +import static seedu.internship.logic.commands.CommandTestUtil.TAG_DESC_FRONT; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_DATE_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_ROLE_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_STATUS_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_TAG_BACK; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_TAG_FRONT; +import static seedu.internship.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.internship.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.internship.testutil.TypicalInternships.APPLE; +import static seedu.internship.testutil.TypicalInternships.GOOGLE; + +import org.junit.jupiter.api.Test; + +import seedu.internship.logic.commands.AddCommand; +import seedu.internship.model.internship.Comment; +import seedu.internship.model.internship.CompanyName; +import seedu.internship.model.internship.Date; +import seedu.internship.model.internship.Internship; +import seedu.internship.model.internship.Role; +import seedu.internship.model.internship.Status; +import seedu.internship.model.tag.Tag; +import seedu.internship.testutil.InternshipBuilder; + +public class AddCommandParserTest { + private AddCommandParser parser = new AddCommandParser(); + + @Test + public void parse_allFieldsPresent_success() { + Internship expectedInternship = new InternshipBuilder(GOOGLE).withTags(VALID_TAG_FRONT).build(); + + // whitespace only preamble + assertParseSuccess(parser, PREAMBLE_WHITESPACE + COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_GOOGLE + + STATUS_DESC_GOOGLE + DATE_DESC_GOOGLE + COMMENT_DESC_GOOGLE + TAG_DESC_FRONT, + new AddCommand(expectedInternship)); + + // multiple company names - last company name accepted + assertParseSuccess(parser, COMPANY_NAME_DESC_APPLE + COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_GOOGLE + + STATUS_DESC_GOOGLE + DATE_DESC_GOOGLE + COMMENT_DESC_GOOGLE + TAG_DESC_FRONT, + new AddCommand(expectedInternship)); + + // multiple roles - last role accepted + assertParseSuccess(parser, COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_APPLE + ROLE_DESC_GOOGLE + + STATUS_DESC_GOOGLE + DATE_DESC_GOOGLE + COMMENT_DESC_GOOGLE + TAG_DESC_FRONT, + new AddCommand(expectedInternship)); + + // multiple statuses - last status accepted + assertParseSuccess(parser, COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_GOOGLE + STATUS_DESC_APPLE + + STATUS_DESC_GOOGLE + DATE_DESC_GOOGLE + COMMENT_DESC_GOOGLE + TAG_DESC_FRONT, + new AddCommand(expectedInternship)); + + // multiple dates - last date accepted + assertParseSuccess(parser, COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_GOOGLE + STATUS_DESC_GOOGLE + + DATE_DESC_APPLE + DATE_DESC_GOOGLE + COMMENT_DESC_GOOGLE + TAG_DESC_FRONT, + new AddCommand(expectedInternship)); + + // multiple comments - last comment accepted + assertParseSuccess(parser, COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_GOOGLE + STATUS_DESC_GOOGLE + + DATE_DESC_GOOGLE + COMMENT_DESC_APPLE + COMMENT_DESC_GOOGLE + TAG_DESC_FRONT, + new AddCommand(expectedInternship)); + + + // multiple tags - all accepted + Internship expectedInternshipMultipleTags = new InternshipBuilder(GOOGLE) + .withTags(VALID_TAG_FRONT, VALID_TAG_BACK).build(); + assertParseSuccess(parser, COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_GOOGLE + STATUS_DESC_GOOGLE + + DATE_DESC_GOOGLE + COMMENT_DESC_GOOGLE + TAG_DESC_BACK + TAG_DESC_FRONT, + new AddCommand(expectedInternshipMultipleTags)); + } + + @Test + public void parse_optionalFieldsMissing_success() { + //zero comments + Internship expectedInternship = new InternshipBuilder(APPLE).withComment("NA").build(); + assertParseSuccess(parser, COMPANY_NAME_DESC_APPLE + ROLE_DESC_APPLE + STATUS_DESC_APPLE + + DATE_DESC_APPLE + TAG_DESC_FRONT, new AddCommand(expectedInternship)); + + //zero tags + expectedInternship = new InternshipBuilder(APPLE).withTags().build(); + assertParseSuccess(parser, COMPANY_NAME_DESC_APPLE + ROLE_DESC_APPLE + STATUS_DESC_APPLE + + DATE_DESC_APPLE + COMMENT_DESC_APPLE, new AddCommand(expectedInternship)); + + } + + @Test + public void parse_compulsoryFieldMissing_failure() { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); + + // missing company name prefix + assertParseFailure(parser, VALID_COMPANY_NAME_GOOGLE + ROLE_DESC_GOOGLE + STATUS_DESC_GOOGLE + + DATE_DESC_GOOGLE, expectedMessage); + + // missing role prefix + assertParseFailure(parser, COMPANY_NAME_DESC_GOOGLE + VALID_ROLE_GOOGLE + STATUS_DESC_GOOGLE + + ROLE_DESC_GOOGLE, expectedMessage); + + // missing status prefix + assertParseFailure(parser, COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_GOOGLE + VALID_STATUS_GOOGLE + + DATE_DESC_GOOGLE, expectedMessage); + + // missing date prefix + assertParseFailure(parser, COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_GOOGLE + STATUS_DESC_GOOGLE + + VALID_DATE_GOOGLE, expectedMessage); + + // all prefixes missing + assertParseFailure(parser, VALID_COMPANY_NAME_GOOGLE + VALID_ROLE_GOOGLE + VALID_STATUS_GOOGLE + + VALID_DATE_GOOGLE, expectedMessage); + } + + @Test + public void parse_invalidValue_failure() { + // invalid company name + assertParseFailure(parser, INVALID_COMPANY_NAME_DESC + ROLE_DESC_GOOGLE + STATUS_DESC_GOOGLE + + DATE_DESC_GOOGLE + COMMENT_DESC_GOOGLE + TAG_DESC_BACK + TAG_DESC_FRONT, + CompanyName.MESSAGE_CONSTRAINTS); + + // invalid role + assertParseFailure(parser, COMPANY_NAME_DESC_GOOGLE + INVALID_ROLE_DESC + STATUS_DESC_GOOGLE + + DATE_DESC_GOOGLE + COMMENT_DESC_GOOGLE + TAG_DESC_BACK + TAG_DESC_FRONT, Role.MESSAGE_CONSTRAINTS); + + // invalid status + assertParseFailure(parser, COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_GOOGLE + INVALID_STATUS_DESC + + DATE_DESC_GOOGLE + COMMENT_DESC_GOOGLE + TAG_DESC_BACK + TAG_DESC_FRONT, Status.MESSAGE_CONSTRAINTS); + + // invalid date + assertParseFailure(parser, COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_GOOGLE + STATUS_DESC_GOOGLE + + INVALID_DATE_DESC + COMMENT_DESC_GOOGLE + TAG_DESC_BACK + TAG_DESC_FRONT, Date.MESSAGE_CONSTRAINTS); + + // invalid comment + assertParseFailure(parser, COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_GOOGLE + STATUS_DESC_GOOGLE + + DATE_DESC_GOOGLE + INVALID_COMMENT_DESC + TAG_DESC_BACK + TAG_DESC_FRONT, + Comment.MESSAGE_CONSTRAINTS); + + // invalid tag + assertParseFailure(parser, COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_GOOGLE + STATUS_DESC_GOOGLE + + DATE_DESC_GOOGLE + COMMENT_DESC_GOOGLE + INVALID_TAG_DESC, Tag.MESSAGE_CONSTRAINTS); + + // two invalid values, only first invalid value reported + assertParseFailure(parser, INVALID_COMPANY_NAME_DESC + ROLE_DESC_GOOGLE + STATUS_DESC_GOOGLE + + INVALID_DATE_DESC, CompanyName.MESSAGE_CONSTRAINTS); + + // non-empty preamble + assertParseFailure(parser, PREAMBLE_NON_EMPTY + COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_GOOGLE + + STATUS_DESC_GOOGLE + DATE_DESC_GOOGLE + COMMENT_DESC_GOOGLE + TAG_DESC_BACK + TAG_DESC_FRONT, + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java b/src/test/java/seedu/internship/logic/parser/ArgumentTokenizerTest.java similarity index 99% rename from src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java rename to src/test/java/seedu/internship/logic/parser/ArgumentTokenizerTest.java index c97308935f5..5f18ea4a819 100644 --- a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java +++ b/src/test/java/seedu/internship/logic/parser/ArgumentTokenizerTest.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.internship.logic.parser; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java b/src/test/java/seedu/internship/logic/parser/CommandParserTestUtil.java similarity index 89% rename from src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java rename to src/test/java/seedu/internship/logic/parser/CommandParserTestUtil.java index 9bf1ccf1cef..cead46fe4b6 100644 --- a/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java +++ b/src/test/java/seedu/internship/logic/parser/CommandParserTestUtil.java @@ -1,9 +1,9 @@ -package seedu.address.logic.parser; +package seedu.internship.logic.parser; import static org.junit.jupiter.api.Assertions.assertEquals; -import seedu.address.logic.commands.Command; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.internship.logic.commands.Command; +import seedu.internship.logic.parser.exceptions.ParseException; /** * Contains helper methods for testing command parsers. diff --git a/src/test/java/seedu/internship/logic/parser/CopyCommandParserTest.java b/src/test/java/seedu/internship/logic/parser/CopyCommandParserTest.java new file mode 100644 index 00000000000..d39bd79323b --- /dev/null +++ b/src/test/java/seedu/internship/logic/parser/CopyCommandParserTest.java @@ -0,0 +1,39 @@ +package seedu.internship.logic.parser; + +import static seedu.internship.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.internship.commons.core.Messages.MESSAGE_INVALID_INTERNSHIP_DISPLAYED_INDEX; +import static seedu.internship.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.internship.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.internship.testutil.TypicalIndexes.INDEX_FIRST_INTERNSHIP; + +import org.junit.jupiter.api.Test; + +import seedu.internship.logic.commands.CopyCommand; + +/** + * As we are only doing white-box testing, our test cases do not cover path variations + * outside from the CopyCommand code. For example, inputs "1" and "1 abc" take the + * same path through the CopyCommand, 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 CopyCommandParserTest { + + private CopyCommandParser parser = new CopyCommandParser(); + + @Test + public void parse_validArgs_returnsCopyCommand() { + assertParseSuccess(parser, "1", new CopyCommand(INDEX_FIRST_INTERNSHIP)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + CopyCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_invalidIndex_throwsParseException() { + assertParseFailure(parser, "-1", MESSAGE_INVALID_INTERNSHIP_DISPLAYED_INDEX); + } +} diff --git a/src/test/java/seedu/internship/logic/parser/DeleteFieldCommandParserTest.java b/src/test/java/seedu/internship/logic/parser/DeleteFieldCommandParserTest.java new file mode 100644 index 00000000000..6d4ceacf64b --- /dev/null +++ b/src/test/java/seedu/internship/logic/parser/DeleteFieldCommandParserTest.java @@ -0,0 +1,57 @@ +package seedu.internship.logic.parser; + +import static seedu.internship.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.internship.commons.core.Messages.MESSAGE_MISSING_ARGUMENTS; +import static seedu.internship.logic.commands.CommandTestUtil.ONE_FIELD_PREDICATE; +import static seedu.internship.logic.commands.CommandTestUtil.SIMPLE_PREDICATE; +import static seedu.internship.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.internship.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import seedu.internship.logic.commands.DeleteFieldCommand; + +/** + * As we are only doing white-box testing, our test cases do not cover path variations + * outside from the DeleteCommand code. For example, inputs "1" and "1 abc" take the + * same path through the DeleteCommand, and therefore we test only one of them. + * The path variation for those two cases occur inside the ParserUtil, and + * therefore should be covered by the ParserUtilTest. + */ +public class DeleteFieldCommandParserTest { + + private DeleteFieldCommandParser parser = new DeleteFieldCommandParser(); + + @Test + public void parse_oneFieldOneArgument_returnsDeleteCommand() { + assertParseSuccess(parser, " n/Amazon", new DeleteFieldCommand(SIMPLE_PREDICATE)); + } + + @Test + public void parse_oneFieldTwoArgument_returnsDeleteCommand() { + assertParseSuccess(parser, " n/Amazon n/Goldman", new DeleteFieldCommand(ONE_FIELD_PREDICATE)); + } + + @Test + public void parse_oneFieldTwoArgumentWithWhitespace_returnsDeleteCommand() { + assertParseSuccess(parser, " n/Amazon n/Goldman ", new DeleteFieldCommand(ONE_FIELD_PREDICATE)); + } + + @Test + public void parse_emptyArgument_throwsParseException() { + assertParseFailure(parser, "", String.format(MESSAGE_MISSING_ARGUMENTS, + DeleteFieldCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_indexArgument_throwsParseException() { + assertParseFailure(parser, "1 2", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteFieldCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteFieldCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/internship/logic/parser/DeleteIndexCommandParserTest.java b/src/test/java/seedu/internship/logic/parser/DeleteIndexCommandParserTest.java new file mode 100644 index 00000000000..88a11fb93b7 --- /dev/null +++ b/src/test/java/seedu/internship/logic/parser/DeleteIndexCommandParserTest.java @@ -0,0 +1,63 @@ +package seedu.internship.logic.parser; + +import static seedu.internship.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.internship.commons.core.Messages.MESSAGE_INVALID_INTERNSHIP_DISPLAYED_INDEX; +import static seedu.internship.commons.core.Messages.MESSAGE_MISSING_ARGUMENTS; +import static seedu.internship.logic.commands.CommandTestUtil.MULTIPLE_INDEX_LIST; +import static seedu.internship.logic.commands.CommandTestUtil.NON_EMPTY_INDEXLIST; +import static seedu.internship.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.internship.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import seedu.internship.logic.commands.DeleteIndexCommand; + +/** + * As we are only doing white-box testing, our test cases do not cover path variations + * outside from the DeleteCommand code. For example, inputs "1" and "1 abc" take the + * same path through the DeleteCommand, and therefore we test only one of them. + * The path variation for those two cases occur inside the ParserUtil, and + * therefore should be covered by the ParserUtilTest. + */ +public class DeleteIndexCommandParserTest { + + private DeleteIndexCommandParser parser = new DeleteIndexCommandParser(); + + @Test + public void parse_oneIndex_returnsDeleteCommand() { + assertParseSuccess(parser, "1", new DeleteIndexCommand(NON_EMPTY_INDEXLIST)); + } + + @Test + public void parse_multipleIndex_returnsDeleteCommand() { + assertParseSuccess(parser, "1 2", new DeleteIndexCommand(MULTIPLE_INDEX_LIST)); + } + + @Test + public void parse_multipleIndexWithWhitespace_returnsDeleteCommand() { + assertParseSuccess(parser, " 1 2 ", new DeleteIndexCommand(MULTIPLE_INDEX_LIST)); + } + + @Test + public void parse_emptyArgument_throwsParseException() { + assertParseFailure(parser, "", String.format(MESSAGE_MISSING_ARGUMENTS, + DeleteIndexCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_parameterArgument_throwsParseException() { + assertParseFailure(parser, "n/google", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteIndexCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteIndexCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_invalidIndex_throwsParseException() { + assertParseFailure(parser, "-1", MESSAGE_INVALID_INTERNSHIP_DISPLAYED_INDEX); + } +} diff --git a/src/test/java/seedu/internship/logic/parser/EditCommandParserTest.java b/src/test/java/seedu/internship/logic/parser/EditCommandParserTest.java new file mode 100644 index 00000000000..e16f136dc53 --- /dev/null +++ b/src/test/java/seedu/internship/logic/parser/EditCommandParserTest.java @@ -0,0 +1,238 @@ +package seedu.internship.logic.parser; + +import static seedu.internship.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.internship.logic.commands.CommandTestUtil.COMPANY_NAME_DESC_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.DATE_DESC_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.DATE_DESC_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.INVALID_COMPANY_NAME_DESC; +import static seedu.internship.logic.commands.CommandTestUtil.INVALID_DATE_DESC; +import static seedu.internship.logic.commands.CommandTestUtil.INVALID_ROLE_DESC; +import static seedu.internship.logic.commands.CommandTestUtil.INVALID_STATUS_DESC; +import static seedu.internship.logic.commands.CommandTestUtil.INVALID_TAG_DESC; +import static seedu.internship.logic.commands.CommandTestUtil.ROLE_DESC_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.ROLE_DESC_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.STATUS_DESC_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.STATUS_DESC_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.TAG_DESC_BACK; +import static seedu.internship.logic.commands.CommandTestUtil.TAG_DESC_FRONT; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_DATE_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_DATE_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_ROLE_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_ROLE_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_STATUS_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_STATUS_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_TAG_BACK; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_TAG_FRONT; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_COMMENT; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.internship.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.internship.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.internship.testutil.TypicalIndexes.INDEX_FIRST_INTERNSHIP; +import static seedu.internship.testutil.TypicalIndexes.INDEX_SECOND_INTERNSHIP; +import static seedu.internship.testutil.TypicalIndexes.INDEX_THIRD_INTERNSHIP; + +import org.junit.jupiter.api.Test; + +import seedu.internship.commons.core.Messages; +import seedu.internship.commons.core.index.Index; +import seedu.internship.logic.commands.EditCommand; +import seedu.internship.logic.commands.EditCommand.EditInternshipDescriptor; +import seedu.internship.model.internship.CompanyName; +import seedu.internship.model.internship.Date; +import seedu.internship.model.internship.Role; +import seedu.internship.model.internship.Status; +import seedu.internship.model.tag.Tag; +import seedu.internship.testutil.EditInternshipDescriptorBuilder; + +public class EditCommandParserTest { + + private static final String COMMENT_EMPTY = " " + PREFIX_COMMENT; + 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 static final String MESSAGE_INVALID_INDEX = Messages.MESSAGE_INVALID_INTERNSHIP_DISPLAYED_INDEX; + + private EditCommandParser parser = new EditCommandParser(); + + @Test + public void parse_missingParts_failure() { + // no index specified + assertParseFailure(parser, VALID_COMPANY_NAME_APPLE, 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" + COMPANY_NAME_DESC_APPLE, MESSAGE_INVALID_INDEX); + + // zero index + assertParseFailure(parser, "0" + COMPANY_NAME_DESC_APPLE, MESSAGE_INVALID_INDEX); + + // integer with positive sign + assertParseFailure(parser, "+10", 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() { + // invalid tag + assertParseFailure(parser, "1" + INVALID_TAG_DESC, Tag.MESSAGE_CONSTRAINTS); + // invalid company name + assertParseFailure(parser, "1" + INVALID_COMPANY_NAME_DESC, CompanyName.MESSAGE_CONSTRAINTS); + // invalid role + assertParseFailure(parser, "1" + INVALID_ROLE_DESC, Role.MESSAGE_CONSTRAINTS); + // invalid status + assertParseFailure(parser, "1" + INVALID_STATUS_DESC, Status.MESSAGE_CONSTRAINTS); + // invalid date + assertParseFailure(parser, "1" + INVALID_DATE_DESC, Date.MESSAGE_CONSTRAINTS); + // invalid role followed by valid status + assertParseFailure(parser, "1" + INVALID_ROLE_DESC + STATUS_DESC_APPLE, Role.MESSAGE_CONSTRAINTS); + // valid role followed by invalid role. The test case for invalid role followed by valid role + // is tested at {@code parse_invalidValueFollowedByValidValue_success()} + assertParseFailure(parser, "1" + ROLE_DESC_GOOGLE + INVALID_ROLE_DESC, Role.MESSAGE_CONSTRAINTS); + + + // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Internship} being edited, + // parsing it together with a valid tag results in error + assertParseFailure(parser, "1" + TAG_DESC_FRONT + TAG_DESC_BACK + TAG_EMPTY, Tag.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "1" + TAG_DESC_FRONT + TAG_EMPTY + TAG_DESC_BACK, Tag.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "1" + TAG_EMPTY + TAG_DESC_FRONT + TAG_DESC_BACK, Tag.MESSAGE_CONSTRAINTS); + + // multiple invalid values, but only the first invalid value is captured + assertParseFailure(parser, "1" + INVALID_COMPANY_NAME_DESC + INVALID_ROLE_DESC + VALID_STATUS_APPLE + + VALID_ROLE_APPLE, CompanyName.MESSAGE_CONSTRAINTS); + } + + @Test + public void parse_allFieldsSpecified_success() { + Index targetIndex = INDEX_SECOND_INTERNSHIP; + String userInput = targetIndex.getOneBased() + ROLE_DESC_GOOGLE + TAG_DESC_BACK + + STATUS_DESC_APPLE + DATE_DESC_APPLE + COMPANY_NAME_DESC_APPLE + TAG_DESC_FRONT; + + EditInternshipDescriptor descriptor = new EditInternshipDescriptorBuilder() + .withCompanyName(VALID_COMPANY_NAME_APPLE).withRole(VALID_ROLE_GOOGLE) + .withStatus(VALID_STATUS_APPLE).withDate(VALID_DATE_APPLE) + .withTags(VALID_TAG_FRONT, VALID_TAG_BACK).build(); + EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_someFieldsSpecified_success() { + Index targetIndex = INDEX_FIRST_INTERNSHIP; + String userInput = targetIndex.getOneBased() + ROLE_DESC_GOOGLE + STATUS_DESC_APPLE; + + EditInternshipDescriptor descriptor = new EditInternshipDescriptorBuilder().withRole(VALID_ROLE_GOOGLE) + .withStatus(VALID_STATUS_APPLE).build(); + EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_oneFieldSpecified_success() { + // company name + Index targetIndex = INDEX_THIRD_INTERNSHIP; + String userInput = targetIndex.getOneBased() + COMPANY_NAME_DESC_APPLE; + EditInternshipDescriptor descriptor = new EditInternshipDescriptorBuilder() + .withCompanyName(VALID_COMPANY_NAME_APPLE).build(); + EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // role + userInput = targetIndex.getOneBased() + ROLE_DESC_APPLE; + descriptor = new EditInternshipDescriptorBuilder().withRole(VALID_ROLE_APPLE).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // status + userInput = targetIndex.getOneBased() + STATUS_DESC_APPLE; + descriptor = new EditInternshipDescriptorBuilder().withStatus(VALID_STATUS_APPLE).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // date + userInput = targetIndex.getOneBased() + DATE_DESC_APPLE; + descriptor = new EditInternshipDescriptorBuilder().withDate(VALID_DATE_APPLE).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // tags + userInput = targetIndex.getOneBased() + TAG_DESC_FRONT; + descriptor = new EditInternshipDescriptorBuilder().withTags(VALID_TAG_FRONT).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_multipleRepeatedFields_acceptsLast() { + Index targetIndex = INDEX_FIRST_INTERNSHIP; + String userInput = targetIndex.getOneBased() + ROLE_DESC_APPLE + STATUS_DESC_APPLE + DATE_DESC_APPLE + + TAG_DESC_FRONT + ROLE_DESC_APPLE + DATE_DESC_APPLE + STATUS_DESC_APPLE + TAG_DESC_FRONT + + ROLE_DESC_GOOGLE + DATE_DESC_GOOGLE + STATUS_DESC_GOOGLE + TAG_DESC_BACK; + + EditInternshipDescriptor descriptor = new EditInternshipDescriptorBuilder().withRole(VALID_ROLE_GOOGLE) + .withStatus(VALID_STATUS_GOOGLE).withDate(VALID_DATE_GOOGLE).withTags(VALID_TAG_FRONT, VALID_TAG_BACK) + .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_INTERNSHIP; + String userInput = targetIndex.getOneBased() + INVALID_ROLE_DESC + ROLE_DESC_GOOGLE; + EditInternshipDescriptor descriptor = new EditInternshipDescriptorBuilder() + .withRole(VALID_ROLE_GOOGLE).build(); + EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // other valid values specified + userInput = targetIndex.getOneBased() + STATUS_DESC_GOOGLE + INVALID_ROLE_DESC + DATE_DESC_GOOGLE + + ROLE_DESC_GOOGLE; + descriptor = new EditInternshipDescriptorBuilder().withRole(VALID_ROLE_GOOGLE).withStatus(VALID_STATUS_GOOGLE) + .withDate(VALID_DATE_GOOGLE).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_resetTags_success() { + Index targetIndex = INDEX_THIRD_INTERNSHIP; + String userInput = targetIndex.getOneBased() + TAG_EMPTY; + + EditInternshipDescriptor descriptor = new EditInternshipDescriptorBuilder().withTags().build(); + EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_resetComment_success() { + Index targetIndex = INDEX_THIRD_INTERNSHIP; + String userInput = targetIndex.getOneBased() + COMMENT_EMPTY; + + EditInternshipDescriptor descriptor = new EditInternshipDescriptorBuilder().withComment("NA").build(); + EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + + } +} diff --git a/src/test/java/seedu/internship/logic/parser/FindCommandParserTest.java b/src/test/java/seedu/internship/logic/parser/FindCommandParserTest.java new file mode 100644 index 00000000000..b2e65233aa2 --- /dev/null +++ b/src/test/java/seedu/internship/logic/parser/FindCommandParserTest.java @@ -0,0 +1,177 @@ +package seedu.internship.logic.parser; + +import static seedu.internship.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.internship.logic.commands.CommandTestUtil.COMPANY_NAME_DESC_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.COMPANY_NAME_DESC_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.DATE_DESC_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.DATE_DESC_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.INVALID_COMPANY_NAME_DESC; +import static seedu.internship.logic.commands.CommandTestUtil.INVALID_DATE_DESC; +import static seedu.internship.logic.commands.CommandTestUtil.INVALID_ROLE_DESC; +import static seedu.internship.logic.commands.CommandTestUtil.INVALID_STATUS_DESC; +import static seedu.internship.logic.commands.CommandTestUtil.INVALID_TAG_DESC; +import static seedu.internship.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY; +import static seedu.internship.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; +import static seedu.internship.logic.commands.CommandTestUtil.ROLE_DESC_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.ROLE_DESC_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.STATUS_DESC_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.STATUS_DESC_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.TAG_DESC_BACK; +import static seedu.internship.logic.commands.CommandTestUtil.TAG_DESC_FRONT; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_TAG_FRONT; +import static seedu.internship.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.internship.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import java.util.Arrays; +import java.util.Collections; + +import org.junit.jupiter.api.Test; + +import seedu.internship.logic.commands.FindCommand; +import seedu.internship.model.internship.CompanyName; +import seedu.internship.model.internship.Date; +import seedu.internship.model.internship.InternshipContainsKeywordsPredicate; +import seedu.internship.model.internship.Role; +import seedu.internship.model.internship.Status; +import seedu.internship.model.tag.Tag; + +public class FindCommandParserTest { + + private FindCommandParser parser = new FindCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + FindCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_validArgs_returnsFindCommand() { + // no leading and trailing whitespaces + FindCommand expectedFindCommand = + new FindCommand(new InternshipContainsKeywordsPredicate(Arrays.asList("Google"), + Arrays.asList("Software Developer"), Arrays.asList("applied"), Arrays.asList("2023-02-03"), + Arrays.asList("frontend"))); + + assertParseSuccess(parser, COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_GOOGLE + STATUS_DESC_GOOGLE + + DATE_DESC_GOOGLE + TAG_DESC_FRONT, expectedFindCommand); + + // multiple whitespaces between keywords + assertParseSuccess(parser, " \n " + COMPANY_NAME_DESC_GOOGLE + " \n \t " + ROLE_DESC_GOOGLE + "\n " + + STATUS_DESC_GOOGLE + "\n " + DATE_DESC_GOOGLE + " \t" + TAG_DESC_FRONT + " \n\t", + expectedFindCommand); + } + + @Test + public void parse_allFieldsPresent_success() { + FindCommand expectedFindCommand = + new FindCommand(new InternshipContainsKeywordsPredicate(Arrays.asList("Google", "Apple"), + Arrays.asList("Software Developer", "Mobile Developer"), + Arrays.asList("applied", "interview"), Arrays.asList("2023-02-01", "2023-02-03"), + Arrays.asList("backend", "frontend"))); + + // whitespace only preamble (Note: List of strings in expectedFindCommand must be in this particular order. + // This is because the parse methods in FindCommandParser use HashSet objects which automatically orders + // everything. That's why assetParseSuccess still passes even though the sequence of fields may be different. + assertParseSuccess(parser, PREAMBLE_WHITESPACE + COMPANY_NAME_DESC_GOOGLE + COMPANY_NAME_DESC_APPLE + + ROLE_DESC_GOOGLE + ROLE_DESC_APPLE + STATUS_DESC_GOOGLE + STATUS_DESC_APPLE + DATE_DESC_GOOGLE + + DATE_DESC_APPLE + TAG_DESC_FRONT + TAG_DESC_BACK, expectedFindCommand); + + // multiple company names - all company names accepted + // multiple roles - all roles accepted + // multiple statuses - all statuses accepted + // multiple dates - all dates accepted + // multiple tags - all accepted + assertParseSuccess(parser, COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_GOOGLE + STATUS_DESC_GOOGLE + + COMPANY_NAME_DESC_APPLE + ROLE_DESC_APPLE + STATUS_DESC_APPLE + DATE_DESC_GOOGLE + DATE_DESC_APPLE + + TAG_DESC_FRONT + TAG_DESC_BACK, expectedFindCommand); + } + + @Test + public void parse_optionalFieldsMissing_success() { + FindCommand expectedFindCommand = + new FindCommand(new InternshipContainsKeywordsPredicate(Collections.emptyList(), + Arrays.asList("Mobile Developer"), Arrays.asList("interview"), Arrays.asList("2023-02-01"), + Arrays.asList("frontend"))); + + // no name + assertParseSuccess(parser, ROLE_DESC_APPLE + STATUS_DESC_APPLE + DATE_DESC_APPLE + TAG_DESC_FRONT, + expectedFindCommand); + + // no role + expectedFindCommand = + new FindCommand(new InternshipContainsKeywordsPredicate(Arrays.asList("Apple"), Collections.emptyList(), + Arrays.asList("interview"), Arrays.asList("2023-02-01"), Arrays.asList("frontend"))); + + assertParseSuccess(parser, COMPANY_NAME_DESC_APPLE + STATUS_DESC_APPLE + DATE_DESC_APPLE + TAG_DESC_FRONT, + expectedFindCommand); + + // no status + expectedFindCommand = + new FindCommand(new InternshipContainsKeywordsPredicate(Arrays.asList("Apple"), + Arrays.asList("Mobile Developer"), Collections.emptyList(), Arrays.asList("2023-02-01"), + Arrays.asList("frontend"))); + + assertParseSuccess(parser, COMPANY_NAME_DESC_APPLE + ROLE_DESC_APPLE + DATE_DESC_APPLE + TAG_DESC_FRONT, + expectedFindCommand); + + // no date + expectedFindCommand = + new FindCommand(new InternshipContainsKeywordsPredicate(Arrays.asList("Apple"), + Arrays.asList("Mobile Developer"), Arrays.asList("interview"), Collections.emptyList(), + Arrays.asList("frontend"))); + + assertParseSuccess(parser, COMPANY_NAME_DESC_APPLE + ROLE_DESC_APPLE + STATUS_DESC_APPLE + TAG_DESC_FRONT, + expectedFindCommand); + + // zero tags + expectedFindCommand = + new FindCommand(new InternshipContainsKeywordsPredicate(Arrays.asList("Apple"), + Arrays.asList("Mobile Developer"), Arrays.asList("interview"), Arrays.asList("2023-02-01"), + Collections.emptyList())); + + assertParseSuccess(parser, COMPANY_NAME_DESC_APPLE + ROLE_DESC_APPLE + STATUS_DESC_APPLE + DATE_DESC_APPLE, + expectedFindCommand); + } + + @Test + public void parse_compulsoryFieldMissing_failure() { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE); + + // no fields at all + assertParseFailure(parser, " ", expectedMessage); + } + + @Test + public void parse_invalidValue_failure() { + // invalid company name + assertParseFailure(parser, INVALID_COMPANY_NAME_DESC + ROLE_DESC_GOOGLE + STATUS_DESC_GOOGLE + + DATE_DESC_GOOGLE + TAG_DESC_BACK + TAG_DESC_FRONT, CompanyName.MESSAGE_CONSTRAINTS); + + // invalid role + assertParseFailure(parser, COMPANY_NAME_DESC_GOOGLE + INVALID_ROLE_DESC + ROLE_DESC_GOOGLE + DATE_DESC_GOOGLE + + TAG_DESC_BACK + TAG_DESC_FRONT, Role.MESSAGE_CONSTRAINTS); + + // invalid status + assertParseFailure(parser, COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_GOOGLE + INVALID_STATUS_DESC + + DATE_DESC_GOOGLE + TAG_DESC_BACK + TAG_DESC_FRONT, Status.MESSAGE_CONSTRAINTS); + + // invalid date + assertParseFailure(parser, COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_GOOGLE + STATUS_DESC_GOOGLE + + INVALID_DATE_DESC + DATE_DESC_GOOGLE + TAG_DESC_BACK + TAG_DESC_FRONT, Date.MESSAGE_CONSTRAINTS); + + // invalid tag + assertParseFailure(parser, COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_GOOGLE + STATUS_DESC_GOOGLE + DATE_DESC_GOOGLE + + INVALID_TAG_DESC + VALID_TAG_FRONT, Tag.MESSAGE_CONSTRAINTS); + + // two invalid values, only first invalid value reported + assertParseFailure(parser, INVALID_COMPANY_NAME_DESC + ROLE_DESC_GOOGLE + STATUS_DESC_GOOGLE + + DATE_DESC_GOOGLE + INVALID_TAG_DESC, CompanyName.MESSAGE_CONSTRAINTS); + + // non-empty preamble + assertParseFailure(parser, PREAMBLE_NON_EMPTY + COMPANY_NAME_DESC_GOOGLE + ROLE_DESC_GOOGLE + + STATUS_DESC_GOOGLE + DATE_DESC_GOOGLE + TAG_DESC_BACK + TAG_DESC_FRONT, + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + } + +} diff --git a/src/test/java/seedu/internship/logic/parser/InternBuddyParserTest.java b/src/test/java/seedu/internship/logic/parser/InternBuddyParserTest.java new file mode 100644 index 00000000000..a848d4dbb5c --- /dev/null +++ b/src/test/java/seedu/internship/logic/parser/InternBuddyParserTest.java @@ -0,0 +1,167 @@ +package seedu.internship.logic.parser; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.internship.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.internship.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.internship.logic.commands.CommandTestUtil.NON_EMPTY_INDEXLIST; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_ROLE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_STATUS; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.internship.testutil.Assert.assertThrows; +import static seedu.internship.testutil.TypicalIndexes.INDEX_FIRST_INTERNSHIP; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +import seedu.internship.logic.commands.AddCommand; +import seedu.internship.logic.commands.ClearCommand; +import seedu.internship.logic.commands.CopyCommand; +import seedu.internship.logic.commands.DeleteFieldCommand; +import seedu.internship.logic.commands.DeleteIndexCommand; +import seedu.internship.logic.commands.EditCommand; +import seedu.internship.logic.commands.EditCommand.EditInternshipDescriptor; +import seedu.internship.logic.commands.ExitCommand; +import seedu.internship.logic.commands.FindCommand; +import seedu.internship.logic.commands.HelpCommand; +import seedu.internship.logic.commands.ListCommand; +import seedu.internship.logic.parser.exceptions.ParseException; +import seedu.internship.model.internship.Internship; +import seedu.internship.model.internship.InternshipContainsKeywordsPredicate; +import seedu.internship.testutil.EditInternshipDescriptorBuilder; +import seedu.internship.testutil.InternshipBuilder; +import seedu.internship.testutil.InternshipUtil; + +public class InternBuddyParserTest { + + private final InternBuddyParser parser = new InternBuddyParser(); + + @Test + public void parseCommand_add() throws Exception { + Internship internship = new InternshipBuilder().build(); + AddCommand command = (AddCommand) parser.parseCommand(InternshipUtil.getAddCommand(internship)); + assertEquals(new AddCommand(internship), 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 { + DeleteIndexCommand command = (DeleteIndexCommand) parser.parseCommand( + DeleteIndexCommand.COMMAND_WORD + " " + INDEX_FIRST_INTERNSHIP.getOneBased()); + assertEquals(new DeleteIndexCommand(NON_EMPTY_INDEXLIST), command); + } + + @Test + public void parseCommand_deleteField() throws Exception { + List nameKeywords = Arrays.asList("bar bar", "baz", "foo foo"); + List roleKeywords = Arrays.asList("ha ha ha", "blah"); + List statusKeywords = Arrays.asList("new", "rejected"); + List keyDate = Arrays.asList("2023-02-02", "2023-02-01"); + // Note: Elements in the lists must be ordered this way as this is how HashSet orders them. + DeleteFieldCommand command = (DeleteFieldCommand) parser.parseCommand( + DeleteFieldCommand.COMMAND_WORD + + nameKeywords.stream() + .map(name -> " " + PREFIX_COMPANY_NAME + name) + .collect(Collectors.joining("")) + + roleKeywords.stream() + .map(role -> " " + PREFIX_ROLE + role) + .collect(Collectors.joining(" ")) + + statusKeywords.stream() + .map(status -> " " + PREFIX_STATUS + status) + .collect(Collectors.joining("")) + + keyDate.stream() + .map(date -> " " + PREFIX_DATE + date) + .collect(Collectors.joining(""))); + InternshipContainsKeywordsPredicate p = new InternshipContainsKeywordsPredicate(nameKeywords, roleKeywords, + statusKeywords, keyDate, new ArrayList<>()); + assertEquals(new DeleteFieldCommand(p), command); + } + + @Test + public void parseCommand_copy() throws Exception { + CopyCommand command = (CopyCommand) parser.parseCommand( + CopyCommand.COMMAND_WORD + " " + INDEX_FIRST_INTERNSHIP.getOneBased()); + assertEquals(new CopyCommand(INDEX_FIRST_INTERNSHIP), command); + } + + @Test + public void parseCommand_edit() throws Exception { + Internship internship = new InternshipBuilder().build(); + EditInternshipDescriptor descriptor = new EditInternshipDescriptorBuilder(internship).build(); + EditCommand command = (EditCommand) parser.parseCommand(EditCommand.COMMAND_WORD + " " + + INDEX_FIRST_INTERNSHIP.getOneBased() + " " + + InternshipUtil.getEditInternshipDescriptorDetails(descriptor)); + assertEquals(new EditCommand(INDEX_FIRST_INTERNSHIP, 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 nameKeywords = Arrays.asList("bar bar", "baz", "foo foo"); + List roleKeywords = Arrays.asList("ha ha ha", "blah"); + List statusKeywords = Arrays.asList("new", "rejected"); + List keyDate = Arrays.asList("2023-02-02", "2023-02-01"); + List tagKeywords = Arrays.asList("boo", "blah blah"); + // Note: Elements in the lists must be ordered this way as this is how HashSet orders them. + FindCommand command = (FindCommand) parser.parseCommand( + FindCommand.COMMAND_WORD + + nameKeywords.stream() + .map(name -> " " + PREFIX_COMPANY_NAME + name) + .collect(Collectors.joining("")) + + roleKeywords.stream() + .map(role -> " " + PREFIX_ROLE + role) + .collect(Collectors.joining(" ")) + + statusKeywords.stream() + .map(status -> " " + PREFIX_STATUS + status) + .collect(Collectors.joining("")) + + keyDate.stream() + .map(date -> " " + PREFIX_DATE + date) + .collect(Collectors.joining("")) + + tagKeywords.stream() + .map(tag -> " " + PREFIX_TAG + tag) + .collect(Collectors.joining(""))); + assertEquals(new FindCommand( + new InternshipContainsKeywordsPredicate(nameKeywords, roleKeywords, statusKeywords, keyDate, + tagKeywords)), + command); + } + + @Test + public void parseCommand_help() throws Exception { + assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD) instanceof HelpCommand); + assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD + " 3") instanceof HelpCommand); + } + + @Test + public void parseCommand_list() throws Exception { + assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD) instanceof ListCommand); + assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD + " 3") instanceof ListCommand); + } + + @Test + public void parseCommand_unrecognisedInput_throwsParseException() { + assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE), () + -> parser.parseCommand("")); + } + + @Test + public void parseCommand_unknownCommand_throwsParseException() { + assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, () -> parser.parseCommand("unknownCommand")); + } +} diff --git a/src/test/java/seedu/internship/logic/parser/ParserUtilTest.java b/src/test/java/seedu/internship/logic/parser/ParserUtilTest.java new file mode 100644 index 00000000000..3497bed25f0 --- /dev/null +++ b/src/test/java/seedu/internship/logic/parser/ParserUtilTest.java @@ -0,0 +1,222 @@ +package seedu.internship.logic.parser; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.internship.logic.parser.ParserUtil.MESSAGE_INVALID_INDEX; +import static seedu.internship.testutil.Assert.assertThrows; +import static seedu.internship.testutil.TypicalIndexes.INDEX_FIRST_INTERNSHIP; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import seedu.internship.logic.parser.exceptions.ParseException; +import seedu.internship.model.internship.Comment; +import seedu.internship.model.internship.CompanyName; +import seedu.internship.model.internship.Date; +import seedu.internship.model.internship.Role; +import seedu.internship.model.internship.Status; +import seedu.internship.model.tag.Tag; + +public class ParserUtilTest { + private static final String INVALID_COMPANY_NAME = "This Company name has more than fifty characters hmm"; + private static final String INVALID_ROLE = "This role has more than fifty characters so it is wrong"; + private static final String INVALID_STATUS = "pending"; + private static final String INVALID_DATE = "23-02-01"; + private static final String INVALID_COMMENT = ""; + private static final String INVALID_TAG = " "; + + private static final String VALID_COMPANY_NAME = "Apple"; + private static final String VALID_ROLE = "iOS Developer"; + private static final String VALID_STATUS = "applied"; + private static final String VALID_DATE = "2023-02-01"; + private static final String VALID_COMMENT = "I love Apple!"; + private static final String VALID_TAG_1 = "front"; + private static final String VALID_TAG_2 = "back"; + + 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_INTERNSHIP, ParserUtil.parseIndex("1")); + + // Leading and trailing whitespaces + assertEquals(INDEX_FIRST_INTERNSHIP, ParserUtil.parseIndex(" 1 ")); + } + + @Test + public void parseCompanyName_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseCompanyName((String) null)); + } + + @Test + public void parseCompanyName_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseCompanyName(INVALID_COMPANY_NAME)); + } + + @Test + public void parseCompanyName_validValueWithoutWhitespace_returnsCompanyName() throws Exception { + CompanyName expectedCompanyName = new CompanyName(VALID_COMPANY_NAME); + assertEquals(expectedCompanyName, ParserUtil.parseCompanyName(VALID_COMPANY_NAME)); + } + + @Test + public void parseCompanyName_validValueWithWhitespace_returnsTrimmedCompanyName() throws Exception { + String companyNameWithWhitespace = WHITESPACE + VALID_COMPANY_NAME + WHITESPACE; + CompanyName expectedCompanyName = new CompanyName(VALID_COMPANY_NAME); + assertEquals(expectedCompanyName, ParserUtil.parseCompanyName(companyNameWithWhitespace)); + } + + @Test + public void parseRole_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseRole((String) null)); + } + + @Test + public void parseRole_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseRole(INVALID_ROLE)); + } + + @Test + public void parseRole_validValueWithoutWhitespace_returnsRole() throws Exception { + Role expectedRole = new Role(VALID_ROLE); + assertEquals(expectedRole, ParserUtil.parseRole(VALID_ROLE)); + } + + @Test + public void parseRole_validValueWithWhitespace_returnsTrimmedRole() throws Exception { + String roleWithWhitespace = WHITESPACE + VALID_ROLE + WHITESPACE; + Role expectedRole = new Role(VALID_ROLE); + assertEquals(expectedRole, ParserUtil.parseRole(roleWithWhitespace)); + } + + @Test + public void parseStatus_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseStatus((String) null)); + } + + @Test + public void parseStatus_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseStatus(INVALID_STATUS)); + } + + @Test + public void parseStatus_validValueWithoutWhitespace_returnsStatus() throws Exception { + Status expectedStatus = new Status(VALID_STATUS); + assertEquals(expectedStatus, ParserUtil.parseStatus(VALID_STATUS)); + } + + @Test + public void parseStatus_validValueWithWhitespace_returnsTrimmedStatus() throws Exception { + String statusWithWhitespace = WHITESPACE + VALID_STATUS + WHITESPACE; + Status expectedStatus = new Status(VALID_STATUS); + assertEquals(expectedStatus, ParserUtil.parseStatus(statusWithWhitespace)); + } + + @Test + public void parseDate_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseDate((String) null)); + } + + @Test + public void parseDate_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseDate(INVALID_DATE)); + } + + @Test + public void parseDate_validValueWithoutWhitespace_returnsDate() throws Exception { + Date expectedDate = new Date(VALID_DATE); + assertEquals(expectedDate, ParserUtil.parseDate(VALID_DATE)); + } + + @Test + public void parseDate_validValueWithWhitespace_returnsTrimmedDate() throws Exception { + String dateWithWhitespace = WHITESPACE + VALID_DATE + WHITESPACE; + Date expectedDate = new Date(VALID_DATE); + assertEquals(expectedDate, ParserUtil.parseDate(dateWithWhitespace)); + } + + @Test + public void parseComment_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseComment((String) null)); + } + + @Test + public void parseComment_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseComment(INVALID_COMMENT)); + } + + @Test + public void parseComment_validValueWithoutWhitespace_returnsComment() throws Exception { + Comment expectedComment = new Comment(VALID_COMMENT); + assertEquals(expectedComment, ParserUtil.parseComment(VALID_COMMENT)); + } + + @Test + public void parseComment_validValueWithWhitespace_returnsTrimmedComment() throws Exception { + String commentWithWhitespace = WHITESPACE + VALID_COMMENT + WHITESPACE; + Comment expectedComment = new Comment(VALID_COMMENT); + assertEquals(expectedComment, ParserUtil.parseComment(commentWithWhitespace)); + } + + @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/internship/logic/parser/ViewCommandParserTest.java b/src/test/java/seedu/internship/logic/parser/ViewCommandParserTest.java new file mode 100644 index 00000000000..1334d709f2d --- /dev/null +++ b/src/test/java/seedu/internship/logic/parser/ViewCommandParserTest.java @@ -0,0 +1,37 @@ +package seedu.internship.logic.parser; + +import static seedu.internship.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.internship.commons.core.Messages.MESSAGE_INVALID_INTERNSHIP_DISPLAYED_INDEX; +import static seedu.internship.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.internship.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.internship.testutil.TypicalIndexes.INDEX_FIRST_INTERNSHIP; + +import org.junit.jupiter.api.Test; + +import seedu.internship.logic.commands.ViewCommand; + +/** + * As we are only doing white-box testing, our test cases do not cover path variations + * outside from the ViewCommand code. For example, inputs "1" and "1 abc" take the + * same path through the ViewCommand, 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 ViewCommandParserTest { + + private ViewCommandParser parser = new ViewCommandParser(); + + @Test + public void parse_validArgs_returnsViewCommand() { + assertParseSuccess(parser, "1", new ViewCommand(INDEX_FIRST_INTERNSHIP)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ViewCommand.MESSAGE_USAGE)); + + assertParseFailure(parser, "-1", String.format(MESSAGE_INVALID_INTERNSHIP_DISPLAYED_INDEX, + ViewCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/internship/model/InternBuddyTest.java b/src/test/java/seedu/internship/model/InternBuddyTest.java new file mode 100644 index 00000000000..b1cfb7d829a --- /dev/null +++ b/src/test/java/seedu/internship/model/InternBuddyTest.java @@ -0,0 +1,115 @@ +package seedu.internship.model; + +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 static seedu.internship.logic.commands.CommandTestUtil.VALID_DATE_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_TAG_BACK; +import static seedu.internship.testutil.Assert.assertThrows; +import static seedu.internship.testutil.TypicalInternships.APPLE; +import static seedu.internship.testutil.TypicalInternships.GOOGLE; +import static seedu.internship.testutil.TypicalInternships.getTypicalInternBuddy; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.internship.model.internship.Internship; +import seedu.internship.model.internship.exceptions.DuplicateInternshipException; +import seedu.internship.testutil.InternshipBuilder; + +public class InternBuddyTest { + + private final InternBuddy internBuddy = new InternBuddy(); + + @Test + public void constructor() { + assertEquals(Collections.emptyList(), internBuddy.getInternshipList()); + } + + @Test + public void resetData_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> internBuddy.resetData(null)); + } + + @Test + public void resetData_withValidReadOnlyInternBuddy_replacesData() { + InternBuddy newData = getTypicalInternBuddy(); + internBuddy.resetData(newData); + assertEquals(newData, internBuddy); + } + + @Test + public void resetData_withDuplicateInternships_throwsDuplicateInternshipException() { + // Two internships with the same fields + Internship editedApple = new InternshipBuilder(APPLE).withDate(VALID_DATE_APPLE).withTags(VALID_TAG_BACK) + .build(); + List newInternships = Arrays.asList(APPLE, editedApple); + InternBuddyStub newData = new InternBuddyStub(newInternships); + + assertThrows(DuplicateInternshipException.class, () -> internBuddy.resetData(newData)); + } + + @Test + public void hasInternship_nullInternship_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> internBuddy.hasInternship(null)); + } + + @Test + public void hasInternship_internshipNotInInternBuddy_returnsFalse() { + assertFalse(internBuddy.hasInternship(APPLE)); + } + + @Test + public void hasInternship_internshipInInternBuddy_returnsTrue() { + internBuddy.addInternship(APPLE); + assertTrue(internBuddy.hasInternship(APPLE)); + } + + @Test + public void hasInternship_internshipWithSameIdentityFieldsInInternBuddy_returnsTrue() { + internBuddy.addInternship(APPLE); + Internship editedApple = new InternshipBuilder(APPLE).withDate(VALID_DATE_APPLE).withTags(VALID_TAG_BACK) + .build(); + assertTrue(internBuddy.hasInternship(editedApple)); + } + + @Test + public void getInternshipList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> internBuddy.getInternshipList().remove(0)); + } + + @Test + public void hashcode() { + List oneInternships = Arrays.asList(APPLE); + InternBuddyStub internBuddyWithOneInternship = new InternBuddyStub(oneInternships); + List twoInternships = Arrays.asList(APPLE, GOOGLE); + InternBuddyStub internBuddyWithTwoInternships = new InternBuddyStub(twoInternships); + + // different internships -> returns different hashcode + assertNotEquals(internBuddyWithOneInternship.hashCode(), internBuddyWithTwoInternships.hashCode()); + } + + /** + * A stub ReadOnlyInternBuddy whose internships list can violate interface constraints. + */ + private static class InternBuddyStub implements ReadOnlyInternBuddy { + private final ObservableList internships = FXCollections.observableArrayList(); + + InternBuddyStub(Collection internships) { + this.internships.setAll(internships); + } + + @Override + public ObservableList getInternshipList() { + return internships; + } + } + +} diff --git a/src/test/java/seedu/internship/model/ModelManagerTest.java b/src/test/java/seedu/internship/model/ModelManagerTest.java new file mode 100644 index 00000000000..db1f6916fe3 --- /dev/null +++ b/src/test/java/seedu/internship/model/ModelManagerTest.java @@ -0,0 +1,146 @@ +package seedu.internship.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.internship.model.Model.PREDICATE_SHOW_ALL_INTERNSHIPS; +import static seedu.internship.testutil.Assert.assertThrows; +import static seedu.internship.testutil.TypicalInternships.APPLE; +import static seedu.internship.testutil.TypicalInternships.GOOGLE; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; + +import org.junit.jupiter.api.Test; + +import seedu.internship.commons.core.GuiSettings; +import seedu.internship.model.internship.InternshipContainsKeywordsPredicate; +import seedu.internship.testutil.InternBuddyBuilder; + +public class ModelManagerTest { + + private ModelManager modelManager = new ModelManager(); + + @Test + public void constructor() { + assertEquals(new UserPrefs(), modelManager.getUserPrefs()); + assertEquals(new GuiSettings(), modelManager.getGuiSettings()); + assertEquals(new InternBuddy(), new InternBuddy(modelManager.getInternBuddy())); + } + + @Test + public void setUserPrefs_nullUserPrefs_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> modelManager.setUserPrefs(null)); + } + + @Test + public void setUserPrefs_validUserPrefs_copiesUserPrefs() { + UserPrefs userPrefs = new UserPrefs(); + userPrefs.setInternBuddyFilePath(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.setInternBuddyFilePath(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 setInternBuddyFilePath_nullPath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> modelManager.setInternBuddyFilePath(null)); + } + + @Test + public void setInternBuddyFilePath_validPath_setsInternBuddyFilePath() { + Path path = Paths.get("address/book/file/path"); + modelManager.setInternBuddyFilePath(path); + assertEquals(path, modelManager.getInternBuddyFilePath()); + } + + @Test + public void hasInternship_nullInternship_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> modelManager.hasInternship(null)); + } + + @Test + public void hasInternship_internshipNotInInternBuddy_returnsFalse() { + assertFalse(modelManager.hasInternship(APPLE)); + } + + @Test + public void hasInternship_internshipInInternBuddy_returnsTrue() { + modelManager.addInternship(APPLE); + assertTrue(modelManager.hasInternship(APPLE)); + } + + @Test + public void getFilteredInternshipList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> modelManager + .getFilteredInternshipList().remove(0)); + } + + @Test + public void equals() { + InternBuddy internBuddy = new InternBuddyBuilder().withInternship(APPLE).withInternship(GOOGLE).build(); + InternBuddy differentInternBuddy = new InternBuddy(); + UserPrefs userPrefs = new UserPrefs(); + + // same values -> returns true + modelManager = new ModelManager(internBuddy, userPrefs); + ModelManager modelManagerCopy = new ModelManager(internBuddy, 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 internBuddy -> returns false + assertFalse(modelManager.equals(new ModelManager(differentInternBuddy, userPrefs))); + + // different filteredList -> returns false + String keyword = APPLE.getCompanyName().fullCompanyName; + modelManager.updateFilteredInternshipList(new InternshipContainsKeywordsPredicate(Arrays.asList(keyword), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList())); + assertFalse(modelManager.equals(new ModelManager(internBuddy, userPrefs))); + + // resets modelManager to initial state for upcoming tests + modelManager.updateFilteredInternshipList(PREDICATE_SHOW_ALL_INTERNSHIPS); + + // different userPrefs -> returns false + UserPrefs differentUserPrefs = new UserPrefs(); + differentUserPrefs.setInternBuddyFilePath(Paths.get("differentFilePath")); + assertFalse(modelManager.equals(new ModelManager(internBuddy, differentUserPrefs))); + } + + @Test + public void getSelectedInternship_null_success() { + assertEquals(modelManager.getSelectedInternship(), null); + } + + @Test + public void getUpdatedInternship_success() { + modelManager.updateSelectedInternship(APPLE); + assertEquals(modelManager.getSelectedInternship(), APPLE); + } +} diff --git a/src/test/java/seedu/address/model/UserPrefsTest.java b/src/test/java/seedu/internship/model/UserPrefsTest.java similarity index 70% rename from src/test/java/seedu/address/model/UserPrefsTest.java rename to src/test/java/seedu/internship/model/UserPrefsTest.java index b1307a70d52..6af9b9a0230 100644 --- a/src/test/java/seedu/address/model/UserPrefsTest.java +++ b/src/test/java/seedu/internship/model/UserPrefsTest.java @@ -1,6 +1,6 @@ -package seedu.address.model; +package seedu.internship.model; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.internship.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; @@ -13,9 +13,9 @@ public void setGuiSettings_nullGuiSettings_throwsNullPointerException() { } @Test - public void setAddressBookFilePath_nullPath_throwsNullPointerException() { + public void setInternBuddyFilePath_nullPath_throwsNullPointerException() { UserPrefs userPrefs = new UserPrefs(); - assertThrows(NullPointerException.class, () -> userPrefs.setAddressBookFilePath(null)); + assertThrows(NullPointerException.class, () -> userPrefs.setInternBuddyFilePath(null)); } } diff --git a/src/test/java/seedu/internship/model/internship/CommentTest.java b/src/test/java/seedu/internship/model/internship/CommentTest.java new file mode 100644 index 00000000000..854ccda5051 --- /dev/null +++ b/src/test/java/seedu/internship/model/internship/CommentTest.java @@ -0,0 +1,80 @@ +package seedu.internship.model.internship; + +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 static seedu.internship.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class CommentTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Comment(null)); + } + + @Test + public void constructor_invalidComment_throwsIllegalArgumentException() { + String invalidComment = ""; + assertThrows(IllegalArgumentException.class, () -> new Comment(invalidComment)); + } + + @Test + public void isValidComment() { + //Invalid Comments + assertThrows(NullPointerException.class, () -> Comment.isValidComment(null)); + assertFalse(Comment.isValidComment("")); // empty string + + //Valid Comments + assertTrue(Comment.isValidComment(" hello")); //with leading white space + assertTrue(Comment.isValidComment("hello ")); //with trailing white space + assertTrue(Comment.isValidComment("A1b2")); //alphanumeric + assertTrue(Comment.isValidComment("I love C#!_?")); //with symbols + } + + @Test + public void checkCommentEquality() { + Comment commentOne = new Comment("hello"); + Comment commentOneDuplicated = new Comment("hello"); + Comment commentTwo = new Comment("bye"); + + //Same object + assertEquals(commentOne, commentOne); + + //Different object but same content + assertEquals(commentOne, commentOneDuplicated); + + //One Comment and one non-Comment object + assertNotEquals(commentOne, "hello"); + + //Comment objects with different comment content + assertNotEquals(commentOne, commentTwo); + } + + @Test + public void checkStringRepresentation() { + Comment commentOne = new Comment("hello"); + assertEquals(commentOne.toString(), "[hello]"); + } + + @Test + public void hashcode() { + //Same string + Comment commentOne = new Comment("hello"); + Comment commentOneDuplicate = new Comment("hello"); + Comment commentTwo = new Comment("Hello"); + Comment commentThree = new Comment("helloThere!"); + + // same string -> equal + assertEquals(commentOne.hashCode(), commentOneDuplicate.hashCode()); + + // different capitalisation -> not equal + assertNotEquals(commentOne.hashCode(), commentTwo.hashCode()); + + // different strings -> not equal + assertNotEquals(commentOne.hashCode(), commentThree.hashCode()); + } + +} diff --git a/src/test/java/seedu/internship/model/internship/CompanyNameTest.java b/src/test/java/seedu/internship/model/internship/CompanyNameTest.java new file mode 100644 index 00000000000..2be922c9dd9 --- /dev/null +++ b/src/test/java/seedu/internship/model/internship/CompanyNameTest.java @@ -0,0 +1,42 @@ +package seedu.internship.model.internship; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.internship.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class CompanyNameTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new CompanyName(null)); + } + + @Test + public void constructor_invalidName_throwsIllegalArgumentException() { + String invalidName = ""; + assertThrows(IllegalArgumentException.class, () -> new CompanyName(invalidName)); + } + + @Test + public void isValidCompanyName() { + // null name + assertThrows(NullPointerException.class, () -> CompanyName.isValidCompanyName(null)); + + // invalid company name + assertFalse(CompanyName.isValidCompanyName("")); // empty string + assertFalse(CompanyName.isValidCompanyName("This company name has more than fifty characters hm")); + + // valid company name + assertTrue(CompanyName.isValidCompanyName("^")); + assertTrue(CompanyName.isValidCompanyName("gojek*")); + assertTrue(CompanyName.isValidCompanyName("big tech company")); // alphabets only + assertTrue(CompanyName.isValidCompanyName("12345")); // numbers only + assertTrue(CompanyName.isValidCompanyName("apple the 2nd")); // alphanumeric characters + assertTrue(CompanyName.isValidCompanyName("Capital Apple")); // with capital letters + assertTrue(CompanyName.isValidCompanyName("Apple which is 1st in Popularity")); // long names + assertTrue(CompanyName.isValidCompanyName("This company " + + "name has more than fifty characters h")); //50 characters + } +} diff --git a/src/test/java/seedu/internship/model/internship/DateTest.java b/src/test/java/seedu/internship/model/internship/DateTest.java new file mode 100644 index 00000000000..0e5250a6d8f --- /dev/null +++ b/src/test/java/seedu/internship/model/internship/DateTest.java @@ -0,0 +1,45 @@ +package seedu.internship.model.internship; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.internship.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class DateTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Date(null)); + } + + @Test + public void constructor_invalidDate_throwsIllegalArgumentException() { + String invalidDate = ""; + assertThrows(IllegalArgumentException.class, () -> new Date(invalidDate)); + } + + @Test + public void isValidDate() { + // null date + assertThrows(NullPointerException.class, () -> Date.isValidDate(null)); + + // invalid dates + assertFalse(Date.isValidDate("")); // empty string + assertFalse(Date.isValidDate(" ")); // spaces only + assertFalse(Date.isValidDate("1st March 2023")); //invalid date format + assertFalse(Date.isValidDate("March 1st 2023")); //invalid date format + assertFalse(Date.isValidDate("2023-0-02")); //invalid date format + assertFalse(Date.isValidDate("202-01-02")); //invalid date format + assertFalse(Date.isValidDate("2023-03-1")); //invalid date format + assertFalse(Date.isValidDate("2023-00-00")); //invalid date + assertFalse(Date.isValidDate("2023-04-31")); //invalid date + + // valid dates + assertTrue(Date.isValidDate("2023-03-01")); + assertTrue(Date.isValidDate("2023-02-05")); + assertTrue(Date.isValidDate("2023-12-31")); + assertTrue(Date.isValidDate("2023-02-28")); + assertTrue(Date.isValidDate("2020-02-29")); //leap year + } +} diff --git a/src/test/java/seedu/internship/model/internship/InternshipContainsKeywordsPredicateTest.java b/src/test/java/seedu/internship/model/internship/InternshipContainsKeywordsPredicateTest.java new file mode 100644 index 00000000000..66fe9c6e3bd --- /dev/null +++ b/src/test/java/seedu/internship/model/internship/InternshipContainsKeywordsPredicateTest.java @@ -0,0 +1,177 @@ +package seedu.internship.model.internship; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.internship.testutil.InternshipBuilder; + +public class InternshipContainsKeywordsPredicateTest { + + @Test + public void equals() { + List firstPredicateKeywordList = Collections.singletonList("first"); + List secondPredicateKeywordList = Arrays.asList("first", "second"); + + InternshipContainsKeywordsPredicate firstPredicate = + new InternshipContainsKeywordsPredicate(firstPredicateKeywordList, firstPredicateKeywordList, + firstPredicateKeywordList, firstPredicateKeywordList, firstPredicateKeywordList); + InternshipContainsKeywordsPredicate secondPredicate = + new InternshipContainsKeywordsPredicate(secondPredicateKeywordList, secondPredicateKeywordList, + secondPredicateKeywordList, secondPredicateKeywordList, secondPredicateKeywordList); + + // same object -> returns true + assertTrue(firstPredicate.equals(firstPredicate)); + + // same values -> returns true + InternshipContainsKeywordsPredicate firstPredicateCopy = + new InternshipContainsKeywordsPredicate(firstPredicateKeywordList, firstPredicateKeywordList, + firstPredicateKeywordList, firstPredicateKeywordList, firstPredicateKeywordList); + assertTrue(firstPredicate.equals(firstPredicateCopy)); + + // different types -> returns false + System.out.println("Check: " + firstPredicate.equals(1)); + assertFalse(firstPredicate.equals(1)); + + // null -> returns false + assertFalse(firstPredicate.equals(null)); + + // different internship -> returns false + assertFalse(firstPredicate.equals(secondPredicate)); + } + + @Test + public void test_nameContainsKeywords_returnsTrue() { + // One name keyword + InternshipContainsKeywordsPredicate predicate = + new InternshipContainsKeywordsPredicate(Collections.singletonList("Apple Ltd"), Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + assertTrue(predicate.test(new InternshipBuilder().withCompanyName("Apple Ltd").build())); + + // Only one matching name keyword + predicate = new InternshipContainsKeywordsPredicate(Arrays.asList("Google", "Grab"), Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + assertTrue(predicate.test(new InternshipBuilder().withCompanyName("Google").build())); + + // Mixed-case name keywords + predicate = new InternshipContainsKeywordsPredicate(Arrays.asList("aPPle", "gOOGLE"), Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + assertTrue(predicate.test(new InternshipBuilder().withCompanyName("Apple").build())); + + ///////////////////////////////////////////////////////////////////////////////////////////////////// + + // One role keyword + predicate = new InternshipContainsKeywordsPredicate(Collections.emptyList(), + Collections.singletonList("software developer"), Collections.emptyList(), Collections.emptyList(), + Collections.emptyList()); + assertTrue(predicate.test(new InternshipBuilder().withRole("software developer").build())); + + // Only one matching role keyword + predicate = new InternshipContainsKeywordsPredicate(Collections.emptyList(), + Arrays.asList("software developer", "software engineer"), Collections.emptyList(), + Collections.emptyList(), Collections.emptyList()); + assertTrue(predicate.test(new InternshipBuilder().withRole("software developer").build())); + + // Mixed-case status keyword + predicate = new InternshipContainsKeywordsPredicate(Collections.emptyList(), + Arrays.asList("soFTwaRE", "dEVELOPer"), Collections.emptyList(), Collections.emptyList(), + Collections.emptyList()); + assertTrue(predicate.test(new InternshipBuilder().withRole("developer").build())); + + ///////////////////////////////////////////////////////////////////////////////////////////////////// + + // One status keyword + predicate = new InternshipContainsKeywordsPredicate(Collections.emptyList(), Collections.emptyList(), + Collections.singletonList("applied"), Collections.emptyList(), Collections.emptyList()); + assertTrue(predicate.test(new InternshipBuilder().withStatus("applied").build())); + + // Only one matching status keyword + predicate = new InternshipContainsKeywordsPredicate(Collections.emptyList(), Collections.emptyList(), + Arrays.asList("new", "applied"), Collections.emptyList(), Collections.emptyList()); + assertTrue(predicate.test(new InternshipBuilder().withStatus("applied").build())); + + // Mixed-case status keyword + predicate = new InternshipContainsKeywordsPredicate(Collections.emptyList(), Collections.emptyList(), + Arrays.asList("oFFered", "reJECted"), Collections.emptyList(), Collections.emptyList()); + assertTrue(predicate.test(new InternshipBuilder().withStatus("offered").build())); + + ///////////////////////////////////////////////////////////////////////////////////////////////////// + + // One date + predicate = new InternshipContainsKeywordsPredicate(Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), Arrays.asList("2023-02-01"), Collections.emptyList()); + assertTrue(predicate.test(new InternshipBuilder().withDate("2023-02-01").build())); + + // Only one matching date + predicate = new InternshipContainsKeywordsPredicate(Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), Arrays.asList("2023-02-01", "2023-03-02"), Collections.emptyList()); + assertTrue(predicate.test(new InternshipBuilder().withDate("2023-02-01").build())); + + ///////////////////////////////////////////////////////////////////////////////////////////////////// + + // One tag keyword + predicate = new InternshipContainsKeywordsPredicate(Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), Collections.singletonList("python")); + assertTrue(predicate.test(new InternshipBuilder().withTags("python", "java").build())); + + // Multiple tag keywords + predicate = new InternshipContainsKeywordsPredicate(Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), Arrays.asList("python", "java")); + assertTrue(predicate.test(new InternshipBuilder().withTags("python", "java").build())); + + // Only one matching tag keyword + predicate = new InternshipContainsKeywordsPredicate(Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), Arrays.asList("python", "java")); + assertTrue(predicate.test(new InternshipBuilder().withTags("python", "rust").build())); + + // Mixed-case tag keywords + predicate = new InternshipContainsKeywordsPredicate(Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), Arrays.asList("pYTHon", "jAVA")); + assertTrue(predicate.test(new InternshipBuilder().withTags("python", "java").build())); + } + + @Test + public void test_nameDoesNotContainKeywords_returnsFalse() { + // Zero keywords + InternshipContainsKeywordsPredicate predicate = + new InternshipContainsKeywordsPredicate(Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + assertFalse(predicate.test(new InternshipBuilder().withCompanyName("Apple").build())); + + // Non-matching name keyword + predicate = new InternshipContainsKeywordsPredicate(Arrays.asList("Grab"), Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + assertFalse(predicate.test(new InternshipBuilder().withCompanyName("Apple Google").build())); + + // Non-matching role keyword + predicate = new InternshipContainsKeywordsPredicate(Collections.emptyList(), Arrays.asList("frontend"), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + assertFalse(predicate.test(new InternshipBuilder().withRole("backend").build())); + + // Non-matching status keyword + predicate = new InternshipContainsKeywordsPredicate(Collections.emptyList(), Collections.emptyList(), + Arrays.asList("new"), Collections.emptyList(), Collections.emptyList()); + assertFalse(predicate.test(new InternshipBuilder().withStatus("rejected").build())); + + // Non-matching date + predicate = new InternshipContainsKeywordsPredicate(Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), Arrays.asList("2023-02-02"), Collections.emptyList()); + assertFalse(predicate.test(new InternshipBuilder().withDate("2023-02-01").build())); + + // Non-matching tag keyword + predicate = new InternshipContainsKeywordsPredicate(Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), Arrays.asList("python")); + assertFalse(predicate.test(new InternshipBuilder().withTags("java").build())); + + // Keywords match role, status and date, but does not match name + predicate = new InternshipContainsKeywordsPredicate(Arrays.asList("Developer", "applied", "2023-02-01"), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + assertFalse(predicate.test(new InternshipBuilder().withCompanyName("Apple").withRole("Developer") + .withStatus("applied").withDate("2023-02-01").build())); + } +} diff --git a/src/test/java/seedu/internship/model/internship/InternshipTest.java b/src/test/java/seedu/internship/model/internship/InternshipTest.java new file mode 100644 index 00000000000..436d3e67589 --- /dev/null +++ b/src/test/java/seedu/internship/model/internship/InternshipTest.java @@ -0,0 +1,114 @@ +package seedu.internship.model.internship; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_COMMENT_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_DATE_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_ROLE_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_STATUS_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_TAG_BACK; +import static seedu.internship.testutil.Assert.assertThrows; +import static seedu.internship.testutil.TypicalInternships.APPLE; +import static seedu.internship.testutil.TypicalInternships.GOOGLE; + +import org.junit.jupiter.api.Test; + +import seedu.internship.testutil.InternshipBuilder; + +public class InternshipTest { + + @Test + public void asObservableList_modifyList_throwsUnsupportedOperationException() { + Internship internship = new InternshipBuilder().build(); + assertThrows(UnsupportedOperationException.class, () -> internship.getTags().remove(0)); + } + + @Test + public void isSameInternship() { + // same object -> returns true + assertTrue(APPLE.isSameInternship(APPLE)); + + // null -> returns false + assertFalse(APPLE.isSameInternship(null)); + + // same company name, all other attributes different -> returns false + Internship editedApple = new InternshipBuilder(APPLE).withRole(VALID_ROLE_GOOGLE) + .withStatus(VALID_STATUS_GOOGLE).withDate(VALID_DATE_GOOGLE).withTags(VALID_TAG_BACK).build(); + assertFalse(APPLE.isSameInternship(editedApple)); + + // different company name, all other attributes same -> returns false + editedApple = new InternshipBuilder(APPLE).withCompanyName(VALID_COMPANY_NAME_GOOGLE).build(); + assertFalse(APPLE.isSameInternship(editedApple)); + + // company name differs in case, all other attributes same -> returns true + Internship editedGoogle = new InternshipBuilder(GOOGLE) + .withCompanyName(VALID_COMPANY_NAME_GOOGLE.toLowerCase()).build(); + assertTrue(GOOGLE.isSameInternship(editedGoogle)); + + // role differs in case, all other attributes same -> returns true + editedGoogle = new InternshipBuilder(GOOGLE) + .withRole(VALID_ROLE_GOOGLE.toLowerCase()).build(); + assertTrue(GOOGLE.isSameInternship(editedGoogle)); + + // company name has trailing spaces, all other attributes same -> returns false + String nameWithTrailingSpaces = VALID_COMPANY_NAME_GOOGLE + " "; + editedGoogle = new InternshipBuilder(GOOGLE).withCompanyName(nameWithTrailingSpaces).build(); + assertFalse(GOOGLE.isSameInternship(editedGoogle)); + + // different date -> returns false + editedApple = new InternshipBuilder(APPLE).withDate(VALID_DATE_GOOGLE).build(); + assertFalse(APPLE.isSameInternship(editedApple)); + + // different comment -> returns true + editedApple = new InternshipBuilder(APPLE).withComment(VALID_COMMENT_GOOGLE).build(); + assertTrue(APPLE.isSameInternship(editedApple)); + + // different tags -> returns true + editedApple = new InternshipBuilder(APPLE).withTags(VALID_TAG_BACK).build(); + assertTrue(APPLE.isSameInternship(editedApple)); + } + + @Test + public void equals() { + // same values -> returns true + Internship appleCopy = new InternshipBuilder(APPLE).build(); + assertTrue(APPLE.equals(appleCopy)); + + // same object -> returns true + assertTrue(APPLE.equals(APPLE)); + + // null -> returns false + assertFalse(APPLE.equals(null)); + + // different type -> returns false + assertFalse(APPLE.equals(5)); + + // different internship -> returns false + assertFalse(APPLE.equals(GOOGLE)); + + // different name -> returns false + Internship editedApple = new InternshipBuilder(APPLE).withCompanyName(VALID_COMPANY_NAME_GOOGLE).build(); + assertFalse(APPLE.equals(editedApple)); + + // different role -> returns false + editedApple = new InternshipBuilder(APPLE).withRole(VALID_ROLE_GOOGLE).build(); + assertFalse(APPLE.equals(editedApple)); + + // different status -> returns false + editedApple = new InternshipBuilder(APPLE).withStatus(VALID_STATUS_GOOGLE).build(); + assertFalse(APPLE.equals(editedApple)); + + // different date -> returns false + editedApple = new InternshipBuilder(APPLE).withDate(VALID_DATE_GOOGLE).build(); + assertFalse(APPLE.equals(editedApple)); + + // different comment -> returns false + editedApple = new InternshipBuilder(APPLE).withComment(VALID_COMMENT_GOOGLE).build(); + assertFalse(APPLE.equals(editedApple)); + + // different tags -> returns false + editedApple = new InternshipBuilder(APPLE).withTags(VALID_TAG_BACK).build(); + assertFalse(APPLE.equals(editedApple)); + } +} diff --git a/src/test/java/seedu/internship/model/internship/RoleTest.java b/src/test/java/seedu/internship/model/internship/RoleTest.java new file mode 100644 index 00000000000..dd1cd2c31a8 --- /dev/null +++ b/src/test/java/seedu/internship/model/internship/RoleTest.java @@ -0,0 +1,43 @@ +package seedu.internship.model.internship; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.internship.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class RoleTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Role(null)); + } + + @Test + public void constructor_invalidRole_throwsIllegalArgumentException() { + String invalidRole = ""; + assertThrows(IllegalArgumentException.class, () -> new Role(invalidRole)); + } + + @Test + public void isValidRole() { + // null name + assertThrows(NullPointerException.class, () -> Role.isValidRole(null)); + + // invalid role + assertFalse(Role.isValidRole("")); // empty string + assertFalse(CompanyName.isValidCompanyName("This company name has more than fifty characters hm")); + + // valid role + + assertTrue(Role.isValidRole("^")); // only non-alphanumeric characters + assertTrue(Role.isValidRole("developer*")); // contains non-alphanumeric characters + assertTrue(Role.isValidRole("front end developer")); // alphabets only + assertTrue(Role.isValidRole("12345")); // numbers only + assertTrue(Role.isValidRole("developer 1st")); // alphanumeric characters + assertTrue(Role.isValidRole("Software Developer")); // with capital letters + assertTrue(Role.isValidRole("Full Stack Developer Specialising in Front end")); // long names + assertTrue(CompanyName.isValidCompanyName("This company " + + "name has more than fifty characters h")); //50 characters + } +} diff --git a/src/test/java/seedu/internship/model/internship/StatusTest.java b/src/test/java/seedu/internship/model/internship/StatusTest.java new file mode 100644 index 00000000000..49f51dad5a4 --- /dev/null +++ b/src/test/java/seedu/internship/model/internship/StatusTest.java @@ -0,0 +1,75 @@ +package seedu.internship.model.internship; + +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 static seedu.internship.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class StatusTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Status(null)); + } + + @Test + public void constructor_invalidStatus_throwsIllegalArgumentException() { + String invalidStatus = ""; + assertThrows(IllegalArgumentException.class, () -> new Status(invalidStatus)); + } + + @Test + public void isValidStatus() { + // null status + assertThrows(NullPointerException.class, () -> Status.isValidStatus(null)); + + // blank status + assertFalse(Status.isValidStatus("")); // empty string + assertFalse(Status.isValidStatus(" ")); // spaces only + + // non-default status values + assertFalse(Status.isValidStatus("waiting")); + assertFalse(Status.isValidStatus("interviewing")); + assertFalse(Status.isValidStatus("technical interview")); + assertFalse(Status.isValidStatus("have not applied")); + assertFalse(Status.isValidStatus("rejected.")); + + + // valid status for the default values only. Case-insensitive + assertTrue(Status.isValidStatus("new")); + assertTrue(Status.isValidStatus("applied")); + assertTrue(Status.isValidStatus("assessment")); + assertTrue(Status.isValidStatus("interview")); + assertTrue(Status.isValidStatus("offered")); + assertTrue(Status.isValidStatus("rejected")); + assertTrue(Status.isValidStatus("Rejected")); + assertTrue(Status.isValidStatus("INTERVIEW")); + assertTrue(Status.isValidStatus("APPlied")); + assertTrue(Status.isValidStatus("iNTerview")); + assertTrue(Status.isValidStatus("AcCepted")); + assertTrue(Status.isValidStatus("ACCEPTED")); + assertTrue(Status.isValidStatus("accepted")); + } + + @Test + public void equals() { + Status statusOne = new Status("Accepted"); + Status statusTwo = new Status("accepted"); + Status statusThree = new Status("Offered"); + + // Same object -> true + assertEquals(statusOne, statusOne); + + // Different captialisation -> true + assertEquals(statusOne, statusTwo); + + // Different status -> false + assertNotEquals(statusOne, statusThree); + + // Not a status -> false + assertNotEquals(statusOne, "Accepted"); + } +} diff --git a/src/test/java/seedu/internship/model/internship/UniqueInternshipListTest.java b/src/test/java/seedu/internship/model/internship/UniqueInternshipListTest.java new file mode 100644 index 00000000000..bcbc6f320cf --- /dev/null +++ b/src/test/java/seedu/internship/model/internship/UniqueInternshipListTest.java @@ -0,0 +1,192 @@ +package seedu.internship.model.internship; + +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.internship.logic.commands.CommandTestUtil.VALID_DATE_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_DATE_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_TAG_BACK; +import static seedu.internship.testutil.Assert.assertThrows; +import static seedu.internship.testutil.TypicalInternships.APPLE; +import static seedu.internship.testutil.TypicalInternships.GOOGLE; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.internship.model.internship.exceptions.DuplicateInternshipException; +import seedu.internship.model.internship.exceptions.InternshipNotFoundException; +import seedu.internship.testutil.InternshipBuilder; + +public class UniqueInternshipListTest { + + private final UniqueInternshipList uniqueInternshipList = new UniqueInternshipList(); + + @Test + public void contains_nullInternship_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueInternshipList.contains(null)); + } + + @Test + public void contains_internshipNotInList_returnsFalse() { + assertFalse(uniqueInternshipList.contains(APPLE)); + } + + @Test + public void contains_internshipInList_returnsTrue() { + uniqueInternshipList.add(APPLE); + assertTrue(uniqueInternshipList.contains(APPLE)); + } + + @Test + public void contains_internshipWithSameIdentityFieldsInList_returnsTrue() { + uniqueInternshipList.add(APPLE); + Internship editedAlice = new InternshipBuilder(APPLE).withDate(VALID_DATE_APPLE).withTags(VALID_TAG_BACK) + .build(); + assertTrue(uniqueInternshipList.contains(editedAlice)); + } + + @Test + public void add_nullInternship_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueInternshipList.add(null)); + } + + @Test + public void add_duplicateInternship_throwsDuplicateInternshipException() { + uniqueInternshipList.add(APPLE); + assertThrows(DuplicateInternshipException.class, () -> uniqueInternshipList.add(APPLE)); + } + + @Test + public void setInternship_nullTargetInternship_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueInternshipList.setInternship(null, APPLE)); + } + + @Test + public void setInternship_nullEditedInternship_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueInternshipList.setInternship(APPLE, null)); + } + + @Test + public void setInternship_targetInternshipNotInList_throwsInternshipNotFoundException() { + assertThrows(InternshipNotFoundException.class, () -> uniqueInternshipList.setInternship(APPLE, APPLE)); + } + + @Test + public void setInternship_editedInternshipIsSameInternship_success() { + uniqueInternshipList.add(APPLE); + uniqueInternshipList.setInternship(APPLE, APPLE); + UniqueInternshipList expectedUniqueInternshipList = new UniqueInternshipList(); + expectedUniqueInternshipList.add(APPLE); + assertEquals(expectedUniqueInternshipList, uniqueInternshipList); + } + + @Test + public void setInternship_editedInternshipHasSameIdentity_success() { + uniqueInternshipList.add(APPLE); + Internship editedApple = new InternshipBuilder(APPLE).withDate(VALID_DATE_GOOGLE).withTags(VALID_TAG_BACK) + .build(); + uniqueInternshipList.setInternship(APPLE, editedApple); + UniqueInternshipList expectedUniqueInternshipList = new UniqueInternshipList(); + expectedUniqueInternshipList.add(editedApple); + assertEquals(expectedUniqueInternshipList, uniqueInternshipList); + } + + @Test + public void setInternship_editedInternshipHasDifferentIdentity_success() { + uniqueInternshipList.add(APPLE); + uniqueInternshipList.setInternship(APPLE, GOOGLE); + UniqueInternshipList expectedUniqueInternshipList = new UniqueInternshipList(); + expectedUniqueInternshipList.add(GOOGLE); + assertEquals(expectedUniqueInternshipList, uniqueInternshipList); + } + + @Test + public void setInternship_editedInternshipHasNonUniqueIdentity_throwsDuplicateInternshipException() { + uniqueInternshipList.add(APPLE); + uniqueInternshipList.add(GOOGLE); + assertThrows(DuplicateInternshipException.class, () -> uniqueInternshipList.setInternship(APPLE, GOOGLE)); + } + + @Test + public void remove_nullInternship_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueInternshipList.remove(null)); + } + + @Test + public void remove_internshipDoesNotExist_throwsInternshipNotFoundException() { + assertThrows(InternshipNotFoundException.class, () -> uniqueInternshipList.remove(APPLE)); + } + + @Test + public void remove_existingInternship_removesInternship() { + uniqueInternshipList.add(APPLE); + uniqueInternshipList.remove(APPLE); + UniqueInternshipList expectedUniqueInternshipList = new UniqueInternshipList(); + assertEquals(expectedUniqueInternshipList, uniqueInternshipList); + } + + @Test + public void view_nullInternship_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueInternshipList.view(null)); + } + + @Test + public void view_internshipDoesNotExist_throwsInternshipNotFoundException() { + assertThrows(InternshipNotFoundException.class, () -> uniqueInternshipList.view(APPLE)); + } + + @Test + public void view_existingInternship_success() { + uniqueInternshipList.add(APPLE); + uniqueInternshipList.view(APPLE); + UniqueInternshipList expectedUniqueInternshipList = new UniqueInternshipList(); + expectedUniqueInternshipList.add(APPLE); + assertEquals(uniqueInternshipList, expectedUniqueInternshipList); + } + + @Test + public void setInternships_nullUniqueInternshipList_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueInternshipList + .setInternships((UniqueInternshipList) null)); + } + + @Test + public void setInternships_uniqueInternshipList_replacesOwnListWithProvidedUniqueInternshipList() { + uniqueInternshipList.add(APPLE); + UniqueInternshipList expectedUniqueInternshipList = new UniqueInternshipList(); + expectedUniqueInternshipList.add(GOOGLE); + uniqueInternshipList.setInternships(expectedUniqueInternshipList); + assertEquals(expectedUniqueInternshipList, uniqueInternshipList); + } + + @Test + public void setInternships_nullList_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueInternshipList.setInternships((List) null)); + } + + @Test + public void setInternships_list_replacesOwnListWithProvidedList() { + uniqueInternshipList.add(APPLE); + List internshipList = Collections.singletonList(GOOGLE); + uniqueInternshipList.setInternships(internshipList); + UniqueInternshipList expectedUniqueInternshipList = new UniqueInternshipList(); + expectedUniqueInternshipList.add(GOOGLE); + assertEquals(expectedUniqueInternshipList, uniqueInternshipList); + } + + @Test + public void setInternships_listWithDuplicateInternships_throwsDuplicateInternshipException() { + List listWithDuplicateInternships = Arrays.asList(APPLE, APPLE); + assertThrows(DuplicateInternshipException.class, () -> uniqueInternshipList + .setInternships(listWithDuplicateInternships)); + } + + @Test + public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () + -> uniqueInternshipList.asUnmodifiableObservableList().remove(0)); + } +} diff --git a/src/test/java/seedu/internship/model/tag/TagTest.java b/src/test/java/seedu/internship/model/tag/TagTest.java new file mode 100644 index 00000000000..1dd51e2aaa2 --- /dev/null +++ b/src/test/java/seedu/internship/model/tag/TagTest.java @@ -0,0 +1,51 @@ +package seedu.internship.model.tag; + +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 static seedu.internship.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() { + //Invalid Tag Names + assertThrows(NullPointerException.class, () -> Tag.isValidTagName(null)); + assertFalse(Tag.isValidTagName("")); // empty string + assertFalse(Tag.isValidTagName("Tag_That_Has_More_Than_20_Characters")); // more than 30 characters + assertFalse(Tag.isValidTagName("aaaaaaaaaaaaaaaaaaaa aaaaaaaaaa")); // 31 characters with spaces + assertFalse(Tag.isValidTagName("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); //31 characters + + //Valid Tag Names + assertTrue(Tag.isValidTagName("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); //exactly 30 characters + assertTrue(Tag.isValidTagName("Game Developer For Apple AAAAA")); + } + + @Test + public void equals() { + Tag originalTag = new Tag("tagOne"); + Tag originalTagDuplicate = new Tag("tagOne"); + Tag tagWithDifferentContent = new Tag("tagTwo"); + + assertEquals(originalTag, originalTag); + assertEquals(originalTag, originalTagDuplicate); + assertNotEquals(originalTag, tagWithDifferentContent); + assertNotEquals(originalTag, "tagThree"); + + } + +} diff --git a/src/test/java/seedu/internship/model/util/SampleDataUtilTest.java b/src/test/java/seedu/internship/model/util/SampleDataUtilTest.java new file mode 100644 index 00000000000..aae7fd215ae --- /dev/null +++ b/src/test/java/seedu/internship/model/util/SampleDataUtilTest.java @@ -0,0 +1,54 @@ +package seedu.internship.model.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.internship.model.util.SampleDataUtil.getTagSet; + +import org.junit.jupiter.api.Test; + +import seedu.internship.model.InternBuddy; +import seedu.internship.model.internship.Comment; +import seedu.internship.model.internship.CompanyName; +import seedu.internship.model.internship.Date; +import seedu.internship.model.internship.Internship; +import seedu.internship.model.internship.Role; +import seedu.internship.model.internship.Status; + +public class SampleDataUtilTest { + + @Test + public void getSampleInternships_success() { + Internship[] sampleInternships = SampleDataUtil.getSampleInternships(); + + //Check first internship + assertEquals(sampleInternships[0].getCompanyName().toString(), "Apple"); + + //Check second internship + assertEquals(sampleInternships[1].getCompanyName().toString(), "Amazon"); + + //Check third internship + assertEquals(sampleInternships[2].getCompanyName().toString(), "Google"); + + //Check fourth internship + assertEquals(sampleInternships[3].getCompanyName().toString(), "Samsung"); + + //Check fifth internship + assertEquals(sampleInternships[4].getCompanyName().toString(), "Grab"); + + //Check sixth internship + assertEquals(sampleInternships[5].getCompanyName().toString(), "Paypal"); + + //Check seventh internship + assertEquals(sampleInternships[6].getCompanyName().toString(), "Facebook"); + } + + @Test + public void getSampleInternBuddy_success() { + InternBuddy ib = (InternBuddy) SampleDataUtil.getSampleInternBuddy(); + Internship internshipToCheck = new Internship(new CompanyName("Apple"), new Role("iOS Developer"), + new Status("applied"), new Date("2023-03-20"), new Comment("My dream company!"), + getTagSet("iOS")); + assertTrue(ib.hasInternship(internshipToCheck)); + } + +} diff --git a/src/test/java/seedu/internship/storage/CommandHistoryTest.java b/src/test/java/seedu/internship/storage/CommandHistoryTest.java new file mode 100644 index 00000000000..f00426755e7 --- /dev/null +++ b/src/test/java/seedu/internship/storage/CommandHistoryTest.java @@ -0,0 +1,71 @@ +package seedu.internship.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class CommandHistoryTest { + private final String input1 = "add n/google r/backend s/new d/2023-02-01"; + private final String input2 = "edit 1 n/Google"; + private final String input3 = "blah"; + + @Test + public void emptyCommandHistory_getOlderInput_returnsEmptyString() { + CommandHistory commandHistory = new CommandHistory(); + + // List in commandHistory should be initialised with empty string + assertEquals("", commandHistory.getOlderInput()); + + // Once first element in commandHistory has been reached, further getOlderInput should still return the first + // element. + for (int i = 0; i < 3; i++) { + assertEquals("", commandHistory.getOlderInput()); + } + } + + @Test + public void emptyCommandHistory_getNewerInput_returnsEmptyString() { + CommandHistory commandHistory = new CommandHistory(); + + // List in commandHistory should be initialised with empty string + assertEquals("", commandHistory.getNewerInput()); + + // Once last element in commandHistory has been reached, further getNewerInput should still return the last + // element. + for (int i = 0; i < 3; i++) { + assertEquals("", commandHistory.getNewerInput()); + } + } + + @Test + public void commandHistory_withInputs_test() { + CommandHistory commandHistory = this.createSampleCommandHistory(); + + assertEquals(this.input3, commandHistory.getOlderInput()); + assertEquals(this.input2, commandHistory.getOlderInput()); + assertEquals(this.input1, commandHistory.getOlderInput()); + + // When index of list is at first element, getOlderInput will still return first element. + assertEquals(this.input1, commandHistory.getOlderInput()); + + assertEquals(this.input2, commandHistory.getNewerInput()); + assertEquals(this.input3, commandHistory.getNewerInput()); + + // When getNewerInput has returned all added inputs, it will return the empty string added when CommandHistory + // was made. + assertEquals("", commandHistory.getNewerInput()); + + // When index of list at last element, getNewerInput will still return last element. + assertEquals("", commandHistory.getNewerInput()); + } + + private CommandHistory createSampleCommandHistory() { + CommandHistory commandHistory = new CommandHistory(); + + commandHistory.addInput(this.input1); + commandHistory.addInput(this.input2); + commandHistory.addInput(this.input3); + + return commandHistory; + } +} diff --git a/src/test/java/seedu/internship/storage/JsonAdaptedInternshipTest.java b/src/test/java/seedu/internship/storage/JsonAdaptedInternshipTest.java new file mode 100644 index 00000000000..879358de29c --- /dev/null +++ b/src/test/java/seedu/internship/storage/JsonAdaptedInternshipTest.java @@ -0,0 +1,133 @@ +package seedu.internship.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.internship.storage.JsonAdaptedInternship.MISSING_FIELD_MESSAGE_FORMAT; +import static seedu.internship.testutil.Assert.assertThrows; +import static seedu.internship.testutil.TypicalInternships.GOOGLE; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +import seedu.internship.commons.exceptions.IllegalValueException; +import seedu.internship.model.internship.Comment; +import seedu.internship.model.internship.CompanyName; +import seedu.internship.model.internship.Date; +import seedu.internship.model.internship.Role; +import seedu.internship.model.internship.Status; + +public class JsonAdaptedInternshipTest { + private static final String INVALID_COMPANY_NAME = "This company name has more than fifty characters hmm"; + private static final String INVALID_ROLE = "This role has more than fifty characters so it is wrong"; + private static final String INVALID_STATUS = " "; + private static final String INVALID_DATE = "1st March 2023"; + private static final String INVALID_COMMENT = ""; + private static final String INVALID_TAG = ""; + + private static final String VALID_COMPANY_NAME = GOOGLE.getCompanyName().toString(); + private static final String VALID_ROLE = GOOGLE.getRole().toString(); + private static final String VALID_STATUS = GOOGLE.getStatus().toString(); + private static final String VALID_DATE = GOOGLE.getDate().toString(); + private static final String VALID_COMMENT = GOOGLE.getComment().toString(); + private static final List VALID_TAGS = GOOGLE.getTags().stream() + .map(JsonAdaptedTag::new) + .collect(Collectors.toList()); + + @Test + public void toModelType_validInternshipDetails_returnsInternship() throws Exception { + JsonAdaptedInternship internship = new JsonAdaptedInternship(GOOGLE); + assertEquals(GOOGLE, internship.toModelType()); + } + + @Test + public void toModelType_invalidCompanyName_throwsIllegalValueException() { + JsonAdaptedInternship internship = + new JsonAdaptedInternship(INVALID_COMPANY_NAME, VALID_ROLE, VALID_STATUS, VALID_DATE, VALID_COMMENT, + VALID_TAGS); + String expectedMessage = CompanyName.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, internship::toModelType); + } + + @Test + public void toModelType_nullCompanyName_throwsIllegalValueException() { + JsonAdaptedInternship internship = new + JsonAdaptedInternship(null, VALID_ROLE, VALID_STATUS, VALID_DATE, VALID_COMMENT, VALID_TAGS); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, CompanyName.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, internship::toModelType); + } + + @Test + public void toModelType_invalidRole_throwsIllegalValueException() { + JsonAdaptedInternship internship = + new JsonAdaptedInternship(VALID_COMPANY_NAME, INVALID_ROLE, VALID_STATUS, VALID_DATE, VALID_COMMENT, + VALID_TAGS); + String expectedMessage = Role.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, internship::toModelType); + } + + @Test + public void toModelType_nullRole_throwsIllegalValueException() { + JsonAdaptedInternship internship = + new JsonAdaptedInternship(VALID_COMPANY_NAME, null, VALID_STATUS, VALID_DATE, VALID_COMMENT, + VALID_TAGS); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Role.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, internship::toModelType); + } + + @Test + public void toModelType_invalidStatus_throwsIllegalValueException() { + JsonAdaptedInternship internship = + new JsonAdaptedInternship(VALID_COMPANY_NAME, VALID_ROLE, INVALID_STATUS, VALID_DATE, VALID_COMMENT, + VALID_TAGS); + String expectedMessage = Status.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, internship::toModelType); + } + + @Test + public void toModelType_nullStatus_throwsIllegalValueException() { + JsonAdaptedInternship internship = + new JsonAdaptedInternship(VALID_COMPANY_NAME, VALID_ROLE, null, VALID_DATE, VALID_COMMENT, + VALID_TAGS); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Status.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, internship::toModelType); + } + + @Test + public void toModelType_invalidDate_throwsIllegalValueException() { + JsonAdaptedInternship internship = + new JsonAdaptedInternship(VALID_COMPANY_NAME, VALID_ROLE, VALID_STATUS, INVALID_DATE, VALID_COMMENT, + VALID_TAGS); + String expectedMessage = Date.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, internship::toModelType); + } + + @Test + public void toModelType_nullDate_throwsIllegalValueException() { + JsonAdaptedInternship internship = + new JsonAdaptedInternship(VALID_COMPANY_NAME, VALID_ROLE, VALID_STATUS, null, VALID_COMMENT, + VALID_TAGS); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Date.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, internship::toModelType); + } + + @Test + public void toModelType_invalidComment_throwsIllegalValueException() { + JsonAdaptedInternship internship = + new JsonAdaptedInternship(VALID_COMPANY_NAME, VALID_ROLE, VALID_STATUS, VALID_DATE, INVALID_COMMENT, + VALID_TAGS); + String expectedMessage = Comment.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, internship::toModelType); + } + + @Test + public void toModelType_invalidTags_throwsIllegalValueException() { + List invalidTags = new ArrayList<>(VALID_TAGS); + invalidTags.add(new JsonAdaptedTag(INVALID_TAG)); + JsonAdaptedInternship internship = + new JsonAdaptedInternship(VALID_COMPANY_NAME, VALID_ROLE, VALID_STATUS, VALID_DATE, VALID_COMMENT, + invalidTags); + assertThrows(IllegalValueException.class, internship::toModelType); + } +} diff --git a/src/test/java/seedu/internship/storage/JsonInternBuddyStorageTest.java b/src/test/java/seedu/internship/storage/JsonInternBuddyStorageTest.java new file mode 100644 index 00000000000..b843426040c --- /dev/null +++ b/src/test/java/seedu/internship/storage/JsonInternBuddyStorageTest.java @@ -0,0 +1,110 @@ +package seedu.internship.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static seedu.internship.testutil.Assert.assertThrows; +import static seedu.internship.testutil.TypicalInternships.AMAZON; +import static seedu.internship.testutil.TypicalInternships.NITENDOGAMES; +import static seedu.internship.testutil.TypicalInternships.TESLA; +import static seedu.internship.testutil.TypicalInternships.getTypicalInternBuddy; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import seedu.internship.commons.exceptions.DataConversionException; +import seedu.internship.model.InternBuddy; +import seedu.internship.model.ReadOnlyInternBuddy; + +public class JsonInternBuddyStorageTest { + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonInternBuddyStorageTest"); + + @TempDir + public Path testFolder; + + @Test + public void readInternBuddy_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> readInternBuddy(null)); + } + + private java.util.Optional readInternBuddy(String filePath) throws Exception { + return new JsonInternBuddyStorage(Paths.get(filePath)).readInternBuddy(addToTestDataPathIfNotNull(filePath)); + } + + private Path addToTestDataPathIfNotNull(String prefsFileInTestDataFolder) { + return prefsFileInTestDataFolder != null + ? TEST_DATA_FOLDER.resolve(prefsFileInTestDataFolder) + : null; + } + + @Test + public void read_missingFile_emptyResult() throws Exception { + assertFalse(readInternBuddy("NonExistentFile.json").isPresent()); + } + + @Test + public void read_notJsonFormat_exceptionThrown() { + assertThrows(DataConversionException.class, () -> readInternBuddy("notJsonFormatInternBuddy.json")); + } + + @Test + public void readInternBuddy_invalidInternshipInternBuddy_throwDataConversionException() { + assertThrows(DataConversionException.class, () -> readInternBuddy("invalidInternshipInternBuddy.json")); + } + + @Test + public void readInternBuddy_invalidAndValidInternshipInternBuddy_throwDataConversionException() { + assertThrows(DataConversionException.class, () -> readInternBuddy("invalidAndValidInternshipInternBuddy.json")); + } + + @Test + public void readAndSaveInternBuddy_allInOrder_success() throws Exception { + Path filePath = testFolder.resolve("TempInternBuddy.json"); + InternBuddy original = getTypicalInternBuddy(); + JsonInternBuddyStorage jsonInternBuddyStorage = new JsonInternBuddyStorage(filePath); + + // Save in new file and read back + jsonInternBuddyStorage.saveInternBuddy(original, filePath); + ReadOnlyInternBuddy readBack = jsonInternBuddyStorage.readInternBuddy(filePath).get(); + assertEquals(original, new InternBuddy(readBack)); + + // Modify data, overwrite exiting file, and read back + original.addInternship(TESLA); + original.removeInternship(AMAZON); + jsonInternBuddyStorage.saveInternBuddy(original, filePath); + readBack = jsonInternBuddyStorage.readInternBuddy(filePath).get(); + assertEquals(original, new InternBuddy(readBack)); + + // Save and read without specifying file path + original.addInternship(NITENDOGAMES); + jsonInternBuddyStorage.saveInternBuddy(original); // file path not specified + readBack = jsonInternBuddyStorage.readInternBuddy().get(); // file path not specified + assertEquals(original, new InternBuddy(readBack)); + + } + + @Test + public void saveInternBuddy_nullInternBuddy_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveInternBuddy(null, "SomeFile.json")); + } + + /** + * Saves {@code internBuddy} at the specified {@code filePath}. + */ + private void saveInternBuddy(ReadOnlyInternBuddy internBuddy, String filePath) { + try { + new JsonInternBuddyStorage(Paths.get(filePath)) + .saveInternBuddy(internBuddy, addToTestDataPathIfNotNull(filePath)); + } catch (IOException ioe) { + throw new AssertionError("There should not be an error writing to the file.", ioe); + } + } + + @Test + public void saveInternBuddy_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveInternBuddy(new InternBuddy(), null)); + } +} diff --git a/src/test/java/seedu/internship/storage/JsonSerializableInternBuddyTest.java b/src/test/java/seedu/internship/storage/JsonSerializableInternBuddyTest.java new file mode 100644 index 00000000000..0b1bc12572d --- /dev/null +++ b/src/test/java/seedu/internship/storage/JsonSerializableInternBuddyTest.java @@ -0,0 +1,51 @@ +package seedu.internship.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.internship.testutil.Assert.assertThrows; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; + +import seedu.internship.commons.exceptions.IllegalValueException; +import seedu.internship.commons.util.JsonUtil; +import seedu.internship.model.InternBuddy; +import seedu.internship.testutil.TypicalInternships; + +public class JsonSerializableInternBuddyTest { + + private static final Path TEST_DATA_FOLDER = + Paths.get("src", "test", "data", "JsonSerializableInternBuddyTest"); + private static final Path TYPICAL_INTERNSHIPS_FILE = + TEST_DATA_FOLDER.resolve("typicalInternshipInternBuddy.json"); + private static final Path INVALID_INTERNSHIP_FILE = + TEST_DATA_FOLDER.resolve("invalidInternshipInternBuddy.json"); + private static final Path DUPLICATE_INTERNSHIP_FILE = + TEST_DATA_FOLDER.resolve("duplicateInternshipInternBuddy.json"); + + @Test + public void toModelType_typicalInternshipsFile_success() throws Exception { + JsonSerializableInternBuddy dataFromFile = JsonUtil.readJsonFile(TYPICAL_INTERNSHIPS_FILE, + JsonSerializableInternBuddy.class).get(); + InternBuddy internBuddyFromFile = dataFromFile.toModelType(); + InternBuddy typicalInternshipsInternBuddy = TypicalInternships.getTypicalInternBuddy(); + assertEquals(internBuddyFromFile, typicalInternshipsInternBuddy); + } + + @Test + public void toModelType_invalidInternshipFile_throwsIllegalValueException() throws Exception { + JsonSerializableInternBuddy dataFromFile = JsonUtil.readJsonFile(INVALID_INTERNSHIP_FILE, + JsonSerializableInternBuddy.class).get(); + assertThrows(IllegalValueException.class, dataFromFile::toModelType); + } + + @Test + public void toModelType_duplicateInternships_throwsIllegalValueException() throws Exception { + JsonSerializableInternBuddy dataFromFile = JsonUtil.readJsonFile(DUPLICATE_INTERNSHIP_FILE, + JsonSerializableInternBuddy.class).get(); + assertThrows(IllegalValueException.class, JsonSerializableInternBuddy.MESSAGE_DUPLICATE_INTERNSHIP, + dataFromFile::toModelType); + } + +} diff --git a/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java b/src/test/java/seedu/internship/storage/JsonUserPrefsStorageTest.java similarity index 93% rename from src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java rename to src/test/java/seedu/internship/storage/JsonUserPrefsStorageTest.java index 16f33f4a6bb..861eb285612 100644 --- a/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java +++ b/src/test/java/seedu/internship/storage/JsonUserPrefsStorageTest.java @@ -1,8 +1,8 @@ -package seedu.address.storage; +package seedu.internship.storage; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.internship.testutil.Assert.assertThrows; import java.io.IOException; import java.nio.file.Path; @@ -12,9 +12,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.UserPrefs; +import seedu.internship.commons.core.GuiSettings; +import seedu.internship.commons.exceptions.DataConversionException; +import seedu.internship.model.UserPrefs; public class JsonUserPrefsStorageTest { @@ -73,7 +73,7 @@ public void readUserPrefs_extraValuesInFile_extraValuesIgnored() throws DataConv private UserPrefs getTypicalUserPrefs() { UserPrefs userPrefs = new UserPrefs(); userPrefs.setGuiSettings(new GuiSettings(1000, 500, 300, 100)); - userPrefs.setAddressBookFilePath(Paths.get("addressbook.json")); + userPrefs.setInternBuddyFilePath(Paths.get("internbuddy.json")); return userPrefs; } diff --git a/src/test/java/seedu/address/storage/StorageManagerTest.java b/src/test/java/seedu/internship/storage/StorageManagerTest.java similarity index 60% rename from src/test/java/seedu/address/storage/StorageManagerTest.java rename to src/test/java/seedu/internship/storage/StorageManagerTest.java index 99a16548970..8088cb62e66 100644 --- a/src/test/java/seedu/address/storage/StorageManagerTest.java +++ b/src/test/java/seedu/internship/storage/StorageManagerTest.java @@ -1,8 +1,8 @@ -package seedu.address.storage; +package seedu.internship.storage; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.internship.testutil.TypicalInternships.getTypicalInternBuddy; import java.nio.file.Path; @@ -10,10 +10,10 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.UserPrefs; +import seedu.internship.commons.core.GuiSettings; +import seedu.internship.model.InternBuddy; +import seedu.internship.model.ReadOnlyInternBuddy; +import seedu.internship.model.UserPrefs; public class StorageManagerTest { @@ -24,9 +24,9 @@ public class StorageManagerTest { @BeforeEach public void setUp() { - JsonAddressBookStorage addressBookStorage = new JsonAddressBookStorage(getTempFilePath("ab")); + JsonInternBuddyStorage internBuddyStorage = new JsonInternBuddyStorage(getTempFilePath("ab")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(getTempFilePath("prefs")); - storageManager = new StorageManager(addressBookStorage, userPrefsStorage); + storageManager = new StorageManager(internBuddyStorage, userPrefsStorage); } private Path getTempFilePath(String fileName) { @@ -48,21 +48,21 @@ public void prefsReadSave() throws Exception { } @Test - public void addressBookReadSave() throws Exception { + public void internBuddyReadSave() throws Exception { /* * Note: This is an integration test that verifies the StorageManager is properly wired to the - * {@link JsonAddressBookStorage} class. - * More extensive testing of UserPref saving/reading is done in {@link JsonAddressBookStorageTest} class. + * {@link JsonInternBuddyStorage} class. + * More extensive testing of UserPref saving/reading is done in {@link JsonInternBuddyStorageTest} class. */ - AddressBook original = getTypicalAddressBook(); - storageManager.saveAddressBook(original); - ReadOnlyAddressBook retrieved = storageManager.readAddressBook().get(); - assertEquals(original, new AddressBook(retrieved)); + InternBuddy original = getTypicalInternBuddy(); + storageManager.saveInternBuddy(original); + ReadOnlyInternBuddy retrieved = storageManager.readInternBuddy().get(); + assertEquals(original, new InternBuddy(retrieved)); } @Test - public void getAddressBookFilePath() { - assertNotNull(storageManager.getAddressBookFilePath()); + public void getInternBuddyFilePath() { + assertNotNull(storageManager.getInternBuddyFilePath()); } } diff --git a/src/test/java/seedu/address/testutil/Assert.java b/src/test/java/seedu/internship/testutil/Assert.java similarity index 97% rename from src/test/java/seedu/address/testutil/Assert.java rename to src/test/java/seedu/internship/testutil/Assert.java index 9863093bd6e..a959646c3f7 100644 --- a/src/test/java/seedu/address/testutil/Assert.java +++ b/src/test/java/seedu/internship/testutil/Assert.java @@ -1,4 +1,4 @@ -package seedu.address.testutil; +package seedu.internship.testutil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.function.Executable; diff --git a/src/test/java/seedu/internship/testutil/EditInternshipDescriptorBuilder.java b/src/test/java/seedu/internship/testutil/EditInternshipDescriptorBuilder.java new file mode 100644 index 00000000000..adba5858e0e --- /dev/null +++ b/src/test/java/seedu/internship/testutil/EditInternshipDescriptorBuilder.java @@ -0,0 +1,99 @@ +package seedu.internship.testutil; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import seedu.internship.logic.commands.EditCommand.EditInternshipDescriptor; +import seedu.internship.model.internship.Comment; +import seedu.internship.model.internship.CompanyName; +import seedu.internship.model.internship.Date; +import seedu.internship.model.internship.Internship; +import seedu.internship.model.internship.Role; +import seedu.internship.model.internship.Status; +import seedu.internship.model.tag.Tag; + +/** + * A utility class to help with building EditInternshipDescriptor 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(Internship internship) { + descriptor = new EditInternshipDescriptor(); + descriptor.setCompanyName(internship.getCompanyName()); + descriptor.setRole(internship.getRole()); + descriptor.setStatus(internship.getStatus()); + descriptor.setDate(internship.getDate()); + descriptor.setComment(internship.getComment()); + descriptor.setTags(internship.getTags()); + } + + /** + * Sets the {@code CompanyName} of the {@code EditInternshipDescriptor} that we are building. + */ + public EditInternshipDescriptorBuilder withCompanyName(String companyName) { + descriptor.setCompanyName(new CompanyName(companyName)); + return this; + } + + /** + * Sets the {@code Role} of the {@code EditInternshipDescriptor} that we are building. + */ + public EditInternshipDescriptorBuilder withRole(String role) { + descriptor.setRole(new Role(role)); + return this; + } + + /** + * Sets the {@code Status} of the {@code EditInternshipDescriptor} that we are building. + */ + public EditInternshipDescriptorBuilder withStatus(String status) { + descriptor.setStatus(new Status(status)); + return this; + } + + /** + * Sets the {@code Date} of the {@code EditInternshipDescriptor} that we are building. + */ + public EditInternshipDescriptorBuilder withDate(String date) { + descriptor.setDate(new Date(date)); + return this; + } + + /** + * Sets the {@code Comment} of the {@code EditInternshipDescriptor} that we are building. + */ + public EditInternshipDescriptorBuilder withComment(String comment) { + descriptor.setComment(new Comment(comment)); + return this; + } + + + + /** + * Parses the {@code tags} into a {@code Set} and set it to the {@code EditInternshipDescriptor} + * that we are building. + */ + public EditInternshipDescriptorBuilder withTags(String... tags) { + Set tagSet = Stream.of(tags).map(Tag::new).collect(Collectors.toSet()); + descriptor.setTags(tagSet); + return this; + } + + public EditInternshipDescriptor build() { + return descriptor; + } +} diff --git a/src/test/java/seedu/internship/testutil/InternBuddyBuilder.java b/src/test/java/seedu/internship/testutil/InternBuddyBuilder.java new file mode 100644 index 00000000000..488978685e2 --- /dev/null +++ b/src/test/java/seedu/internship/testutil/InternBuddyBuilder.java @@ -0,0 +1,34 @@ +package seedu.internship.testutil; + +import seedu.internship.model.InternBuddy; +import seedu.internship.model.internship.Internship; + +/** + * A utility class to help with building InternBuddy objects. + * Example usage:
+ * {@code InternBuddy ab = new InternBuddyBuilder().withInternship("Apple", "Google").build();} + */ +public class InternBuddyBuilder { + + private InternBuddy internBuddy; + + public InternBuddyBuilder() { + internBuddy = new InternBuddy(); + } + + public InternBuddyBuilder(InternBuddy internBuddy) { + this.internBuddy = internBuddy; + } + + /** + * Adds a new {@code Internship} to the {@code InternBuddy} that we are building. + */ + public InternBuddyBuilder withInternship(Internship internship) { + internBuddy.addInternship(internship); + return this; + } + + public InternBuddy build() { + return internBuddy; + } +} diff --git a/src/test/java/seedu/internship/testutil/InternshipBuilder.java b/src/test/java/seedu/internship/testutil/InternshipBuilder.java new file mode 100644 index 00000000000..3b0744d866a --- /dev/null +++ b/src/test/java/seedu/internship/testutil/InternshipBuilder.java @@ -0,0 +1,115 @@ +package seedu.internship.testutil; + +import java.util.HashSet; +import java.util.Set; + +import seedu.internship.model.internship.Comment; +import seedu.internship.model.internship.CompanyName; +import seedu.internship.model.internship.Date; +import seedu.internship.model.internship.Internship; +import seedu.internship.model.internship.Role; +import seedu.internship.model.internship.Status; +import seedu.internship.model.tag.Tag; +import seedu.internship.model.util.SampleDataUtil; + +/** + * A utility class to help with building Internship objects. + */ +public class InternshipBuilder { + + public static final String DEFAULT_COMPANY_NAME = "Apple"; + public static final String DEFAULT_ROLE = "Mobile Developer"; + public static final String DEFAULT_STATUS = "interview"; + public static final String DEFAULT_DATE = "2023-02-01"; + public static final String DEFAULT_COMMENT = "I love Apple!"; + + private CompanyName companyName; + private Role role; + private Status status; + private Date date; + private Comment comment; + private Set tags; + + /** + * Creates a {@code InternshipBuilder} with the default details. + */ + public InternshipBuilder() { + companyName = new CompanyName(DEFAULT_COMPANY_NAME); + role = new Role(DEFAULT_ROLE); + status = new Status(DEFAULT_STATUS); + date = new Date(DEFAULT_DATE); + comment = new Comment(DEFAULT_COMMENT); + tags = new HashSet<>(); + } + + /** + * Initializes the InternshipBuilder with the data of {@code internshipToCopy}. + */ + public InternshipBuilder(Internship internshipToCopy) { + companyName = internshipToCopy.getCompanyName(); + role = internshipToCopy.getRole(); + status = internshipToCopy.getStatus(); + date = internshipToCopy.getDate(); + comment = internshipToCopy.getComment(); + tags = new HashSet<>(internshipToCopy.getTags()); + } + + /** + * Sets the {@code Name} of the {@code Internship} that we are building. + */ + public InternshipBuilder withCompanyName(String companyName) { + this.companyName = new CompanyName(companyName); + return this; + } + + /** + * Parses the {@code tags} into a {@code Set} and set it to the {@code Internship} that we are building. + */ + public InternshipBuilder withTags(String ... tags) { + this.tags = SampleDataUtil.getTagSet(tags); + return this; + } + + /** + * Sets the {@code Date} of the {@code Internship} that we are building. + */ + public InternshipBuilder withDate(String date) { + this.date = new Date(date); + return this; + } + + /** + * Sets the {@code Comment} of the {@code Internship} that we are building. + */ + public InternshipBuilder withComment(String comment) { + if (comment != null) { + this.comment = new Comment(comment); + } else { + this.comment = new Comment("NA"); + } + return this; + } + + + + /** + * Sets the {@code Role} of the {@code Internship} that we are building. + */ + public InternshipBuilder withRole(String role) { + this.role = new Role(role); + return this; + } + + /** + * Sets the {@code Status} of the {@code Status} that we are building. + */ + public InternshipBuilder withStatus(String status) { + this.status = new Status(status); + return this; + } + + public Internship build() { + return new Internship(companyName, role, status, date, comment, tags); + } + +} diff --git a/src/test/java/seedu/internship/testutil/InternshipUtil.java b/src/test/java/seedu/internship/testutil/InternshipUtil.java new file mode 100644 index 00000000000..24fc933dc54 --- /dev/null +++ b/src/test/java/seedu/internship/testutil/InternshipUtil.java @@ -0,0 +1,67 @@ +package seedu.internship.testutil; + +import static seedu.internship.logic.parser.CliSyntax.PREFIX_COMMENT; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_COMPANY_NAME; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_ROLE; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_STATUS; +import static seedu.internship.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Set; + +import seedu.internship.logic.commands.AddCommand; +import seedu.internship.logic.commands.EditCommand.EditInternshipDescriptor; +import seedu.internship.model.internship.Internship; +import seedu.internship.model.tag.Tag; + +/** + * A utility class for Internship + */ +public class InternshipUtil { + + /** + * Returns an add command string for adding the {@code internship}. + */ + public static String getAddCommand(Internship internship) { + return AddCommand.COMMAND_WORD + " " + getInternshipDetails(internship); + } + + /** + * Returns the part of command string for the given {@code internship}'s details. + */ + public static String getInternshipDetails(Internship internship) { + StringBuilder sb = new StringBuilder(); + sb.append(PREFIX_COMPANY_NAME + internship.getCompanyName().fullCompanyName + " "); + sb.append(PREFIX_ROLE + internship.getRole().fullRole + " "); + sb.append(PREFIX_STATUS + internship.getStatus().toString() + " "); + sb.append(PREFIX_DATE + internship.getDate().fullDate + " "); + sb.append(PREFIX_COMMENT + internship.getComment().commentContent + " "); + internship.getTags().stream().forEach( + s -> sb.append(PREFIX_TAG + s.tagName + " ") + ); + return sb.toString(); + } + + /** + * Returns the part of command string for the given {@code EditInternshipDescriptor}'s details. + */ + public static String getEditInternshipDescriptorDetails(EditInternshipDescriptor descriptor) { + StringBuilder sb = new StringBuilder(); + descriptor.getCompanyName().ifPresent(companyName -> sb.append(PREFIX_COMPANY_NAME).append( + companyName.fullCompanyName).append(" ")); + descriptor.getRole().ifPresent(role -> sb.append(PREFIX_ROLE).append(role.fullRole).append(" ")); + descriptor.getStatus().ifPresent(status -> sb.append(PREFIX_STATUS).append(status.toString()).append(" ")); + descriptor.getDate().ifPresent(date -> sb.append(PREFIX_DATE).append(date.fullDate).append(" ")); + descriptor.getComment().ifPresent(comment -> sb.append(PREFIX_COMMENT).append(comment.commentContent) + .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/SerializableTestClass.java b/src/test/java/seedu/internship/testutil/SerializableTestClass.java similarity index 98% rename from src/test/java/seedu/address/testutil/SerializableTestClass.java rename to src/test/java/seedu/internship/testutil/SerializableTestClass.java index f5a66340489..4c41dbc9934 100644 --- a/src/test/java/seedu/address/testutil/SerializableTestClass.java +++ b/src/test/java/seedu/internship/testutil/SerializableTestClass.java @@ -1,4 +1,4 @@ -package seedu.address.testutil; +package seedu.internship.testutil; import java.time.LocalDateTime; import java.util.ArrayList; diff --git a/src/test/java/seedu/address/testutil/TestUtil.java b/src/test/java/seedu/internship/testutil/TestUtil.java similarity index 56% rename from src/test/java/seedu/address/testutil/TestUtil.java rename to src/test/java/seedu/internship/testutil/TestUtil.java index 896d103eb0b..a68e8aa5d67 100644 --- a/src/test/java/seedu/address/testutil/TestUtil.java +++ b/src/test/java/seedu/internship/testutil/TestUtil.java @@ -1,13 +1,13 @@ -package seedu.address.testutil; +package seedu.internship.testutil; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import seedu.address.commons.core.index.Index; -import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.internship.commons.core.index.Index; +import seedu.internship.model.Model; +import seedu.internship.model.internship.Internship; /** * A utility class for test cases. @@ -33,23 +33,23 @@ public static Path getFilePathInSandboxFolder(String fileName) { } /** - * Returns the middle index of the person in the {@code model}'s person list. + * Returns the middle index of the internship in the {@code model}'s internship list. */ public static Index getMidIndex(Model model) { - return Index.fromOneBased(model.getFilteredPersonList().size() / 2); + return Index.fromOneBased(model.getFilteredInternshipList().size() / 2); } /** - * Returns the last index of the person in the {@code model}'s person list. + * Returns the last index of the internship in the {@code model}'s internship list. */ public static Index getLastIndex(Model model) { - return Index.fromOneBased(model.getFilteredPersonList().size()); + return Index.fromOneBased(model.getFilteredInternshipList().size()); } /** - * Returns the person in the {@code model}'s person list at {@code index}. + * Returns the internship in the {@code model}'s internship list at {@code index}. */ - public static Person getPerson(Model model, Index index) { - return model.getFilteredPersonList().get(index.getZeroBased()); + public static Internship getInternship(Model model, Index index) { + return model.getFilteredInternshipList().get(index.getZeroBased()); } } diff --git a/src/test/java/seedu/internship/testutil/TypicalIndexes.java b/src/test/java/seedu/internship/testutil/TypicalIndexes.java new file mode 100644 index 00000000000..b99ead104c1 --- /dev/null +++ b/src/test/java/seedu/internship/testutil/TypicalIndexes.java @@ -0,0 +1,12 @@ +package seedu.internship.testutil; + +import seedu.internship.commons.core.index.Index; + +/** + * A utility class containing a list of {@code Index} objects to be used in tests. + */ +public class TypicalIndexes { + public static final Index INDEX_FIRST_INTERNSHIP = Index.fromOneBased(1); + public static final Index INDEX_SECOND_INTERNSHIP = Index.fromOneBased(2); + public static final Index INDEX_THIRD_INTERNSHIP = Index.fromOneBased(3); +} diff --git a/src/test/java/seedu/internship/testutil/TypicalInternships.java b/src/test/java/seedu/internship/testutil/TypicalInternships.java new file mode 100644 index 00000000000..086d1694ab4 --- /dev/null +++ b/src/test/java/seedu/internship/testutil/TypicalInternships.java @@ -0,0 +1,113 @@ +package seedu.internship.testutil; + +import static seedu.internship.logic.commands.CommandTestUtil.VALID_COMMENT_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_COMMENT_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_COMPANY_NAME_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_DATE_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_DATE_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_ROLE_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_ROLE_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_STATUS_APPLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_STATUS_GOOGLE; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_TAG_BACK; +import static seedu.internship.logic.commands.CommandTestUtil.VALID_TAG_FRONT; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import seedu.internship.model.InternBuddy; +import seedu.internship.model.internship.Internship; + +/** + * A utility class containing a list of {@code Internship} objects to be used in tests. + */ +public class TypicalInternships { + + public static final Internship AMAZON = new InternshipBuilder().withCompanyName("Amazon") + .withRole("Cloud Architect") + .withStatus("assessment") + .withDate("2023-02-01") + .withComment("I love Amazon!") + .withTags("aws", "back").build(); + public static final Internship FOODPANDA = new InternshipBuilder().withCompanyName("Food Panda") + .withRole("Back end Developer") + .withStatus("assessment") + .withComment("I love Food Panda!") + .withDate("2023-02-02").build(); + public static final Internship GOLDMAN = new InternshipBuilder().withCompanyName("Goldman") + .withRole("Cyber Security Analyst") + .withStatus("offered") + .withComment("I love Goldman!") + .withDate("2023-02-03").build(); + public static final Internship GRAB = new InternshipBuilder().withCompanyName("Grab") + .withRole("Front end Engineer") + .withStatus("rejected") + .withComment("I love Grab!") + .withDate("2023-02-04").build(); + public static final Internship RIOTGAMES = new InternshipBuilder().withCompanyName("Riot Games") + .withRole("Game Client Developer") + .withStatus("interview") + .withDate("2023-02-05") + .withComment("I love Riot Games!") + .withTags("game", "developer").build(); + + public static final Internship SAMSUNG = new InternshipBuilder().withCompanyName("Samsung") + .withRole("Android Developer") + .withStatus("applied") + .withDate("2023-02-06") + .withComment("I love Samsung!").build(); + + public static final Internship SUPERCELLGAMES = new InternshipBuilder().withCompanyName("Supercell Games") + .withRole("Game Designer") + .withStatus("new") + .withDate("2023-02-07") + .withComment("I love Supercell Games!") + .withTags("design", "game").build(); + + + // Manually added + public static final Internship TESLA = new InternshipBuilder().withCompanyName("Tesla") + .withRole("App Developer") + .withStatus("offered") + .withDate("2023-02-08").build(); + public static final Internship NITENDOGAMES = new InternshipBuilder().withCompanyName("Nitendo Games") + .withRole("Game Developer") + .withStatus("interview") + .withDate("2023-02-09").build(); + + // Manually added - Internship's details found in {@code CommandTestUtil} + public static final Internship APPLE = new InternshipBuilder().withCompanyName(VALID_COMPANY_NAME_APPLE) + .withRole(VALID_ROLE_APPLE) + .withStatus(VALID_STATUS_APPLE) + .withDate(VALID_DATE_APPLE) + .withComment(VALID_COMMENT_APPLE) + .withTags(VALID_TAG_FRONT).build(); + public static final Internship GOOGLE = new InternshipBuilder().withCompanyName(VALID_COMPANY_NAME_GOOGLE) + .withRole(VALID_ROLE_GOOGLE) + .withStatus(VALID_STATUS_GOOGLE) + .withDate(VALID_DATE_GOOGLE) + .withComment(VALID_COMMENT_GOOGLE) + .withTags(VALID_TAG_FRONT, VALID_TAG_BACK) + .build(); + + public static final String KEYWORD_MATCHING_GAMES = "Games"; // A keyword that matches GAMES + + private TypicalInternships() {} // prevents instantiation + + /** + * Returns an {@code InternBuddy} with all the typical internships. + */ + public static InternBuddy getTypicalInternBuddy() { + InternBuddy ab = new InternBuddy(); + for (Internship internship : getTypicalInternships()) { + ab.addInternship(internship); + } + return ab; + } + + public static List getTypicalInternships() { + return new ArrayList<>(Arrays.asList(AMAZON, FOODPANDA, GOLDMAN, GRAB, RIOTGAMES, SAMSUNG, SUPERCELLGAMES)); + } +} diff --git a/src/test/java/seedu/internship/testutil/TypicalInternshipsVariableDates.java b/src/test/java/seedu/internship/testutil/TypicalInternshipsVariableDates.java new file mode 100644 index 00000000000..f8caa768ae3 --- /dev/null +++ b/src/test/java/seedu/internship/testutil/TypicalInternshipsVariableDates.java @@ -0,0 +1,77 @@ +package seedu.internship.testutil; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import seedu.internship.model.InternBuddy; +import seedu.internship.model.internship.Internship; + +/** + * A utility class containing a list of {@code Internship} objects to be used in tests. + */ +public class TypicalInternshipsVariableDates { + private static LocalDate currentDate = LocalDate.now(); + private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + public static final Internship AMAZON = new InternshipBuilder().withCompanyName("Amazon") + .withRole("Cloud Architect") + .withStatus("assessment") + .withDate(currentDate.plusDays(-1).format(formatter)) + .withComment("I love Amazon!") + .withTags("aws", "back").build(); + public static final Internship FOODPANDA = new InternshipBuilder().withCompanyName("Food Panda") + .withRole("Back end Developer") + .withStatus("assessment") + .withComment("I love Food Panda!") + .withDate(currentDate.plusDays(0).format(formatter)).build(); + public static final Internship GOLDMAN = new InternshipBuilder().withCompanyName("Goldman") + .withRole("Cyber Security Analyst") + .withStatus("offered") + .withComment("I love Goldman!") + .withDate(currentDate.plusDays(1).format(formatter)).build(); + public static final Internship GRAB = new InternshipBuilder().withCompanyName("Grab") + .withRole("Front end Engineer") + .withStatus("rejected") + .withComment("I love Grab!") + .withDate(currentDate.plusDays(0).format(formatter)).build(); + public static final Internship RIOTGAMES = new InternshipBuilder().withCompanyName("Riot Games") + .withRole("Game Client Developer") + .withStatus("interview") + .withDate(currentDate.plusDays(7).format(formatter)) + .withComment("I love Riot Games!") + .withTags("game", "developer").build(); + + public static final Internship SAMSUNG = new InternshipBuilder().withCompanyName("Samsung") + .withRole("Android Developer") + .withStatus("applied") + .withDate(currentDate.plusDays(-100).format(formatter)) + .withComment("I love Samsung!").build(); + + public static final Internship SUPERCELLGAMES = new InternshipBuilder().withCompanyName("Supercell Games") + .withRole("Game Designer") + .withStatus("new") + .withDate(currentDate.plusDays(6).format(formatter)) + .withComment("I love Supercell Games!") + .withTags("design", "game").build(); + + + private TypicalInternshipsVariableDates() {} // prevents instantiation + + /** + * Returns an {@code InternBuddy} with all the typical internships. + */ + public static InternBuddy getTypicalInternBuddyVariableDates() { + InternBuddy ab = new InternBuddy(); + for (Internship internship : getTypicalInternships()) { + ab.addInternship(internship); + } + return ab; + } + + public static List getTypicalInternships() { + return new ArrayList<>(Arrays.asList(AMAZON, FOODPANDA, GOLDMAN, GRAB, RIOTGAMES, SAMSUNG, SUPERCELLGAMES)); + } +} diff --git a/src/test/java/seedu/internship/ui/CommandBoxTest.java b/src/test/java/seedu/internship/ui/CommandBoxTest.java new file mode 100644 index 00000000000..9eb4a308e48 --- /dev/null +++ b/src/test/java/seedu/internship/ui/CommandBoxTest.java @@ -0,0 +1,83 @@ +package seedu.internship.ui; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import javafx.scene.control.TextField; +import javafx.scene.input.KeyCode; +import seedu.internship.logic.commands.CommandResult; +import seedu.internship.logic.commands.ListCommand; +import seedu.internship.logic.commands.exceptions.CommandException; + +// @@author potty10-reused +// Adapted and reused from +// https://github.com/se-edu/addressbook-level4/blob/master/src/test/java/seedu/address/ui/CommandBoxTest.java +// with modifications. +/** + * A test class for the command box of InternBuddy + * + */ +public class CommandBoxTest extends GuiUnitTest { + private static final String COMMAND_THAT_SUCCEEDS = ListCommand.COMMAND_WORD; + private static final String COMMAND_THAT_FAILS = "hello there"; + private static final String COMMAND_INPUT_FIELD_ID = "#commandTextField"; + + private TextField commandTextField; + + private void run(String command) { + fxRobot.clickOn(commandTextField); + fxRobot.interact(() -> commandTextField.setText(command)); + fxRobot.type(KeyCode.ENTER); + } + + @BeforeEach + public void setUp() { + CommandBox commandBox = new CommandBox(commandText -> { + if (commandText.equals(COMMAND_THAT_SUCCEEDS)) { + return new CommandResult("Command successful"); + } + throw new CommandException("Command failed"); + }); + commandTextField = getChildNode(commandBox.getRoot(), COMMAND_INPUT_FIELD_ID); + uiPartExtension.setUiPart(commandBox); + } + + @Test + public void commandBox_multipleCommands_success() { + assertBehaviorForSuccessfulCommand(); + assertBehaviorForSuccessfulCommand(); + assertBehaviorForFailedCommand(); + assertBehaviorForFailedCommand(); + assertBehaviorForSuccessfulCommand(); + } + + @Test + public void commandBox_handleKeyPress_success() { + run("CHEESE"); + run("HELLO"); + fxRobot.push(KeyCode.UP); + fxRobot.push(KeyCode.UP); + assertEquals("CHEESE", commandTextField.getText()); + fxRobot.push(KeyCode.DOWN); + assertEquals("HELLO", commandTextField.getText()); + } + + + /** + * Runs a command that fails, then verifies that the command box is not cleared. + */ + private void assertBehaviorForFailedCommand() { + run(COMMAND_THAT_FAILS); + assertEquals(COMMAND_THAT_FAILS, commandTextField.getText()); + } + + /** + * Runs a command that succeeds, then verifies that the command box is cleared. + */ + private void assertBehaviorForSuccessfulCommand() { + run(COMMAND_THAT_SUCCEEDS); + assertEquals("", commandTextField.getText()); + } +} diff --git a/src/test/java/seedu/internship/ui/GuiUnitTest.java b/src/test/java/seedu/internship/ui/GuiUnitTest.java new file mode 100644 index 00000000000..71906ffb880 --- /dev/null +++ b/src/test/java/seedu/internship/ui/GuiUnitTest.java @@ -0,0 +1,58 @@ +package seedu.internship.ui; + +import java.util.Optional; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.testfx.api.FxRobot; + +import javafx.scene.Node; + +/** + * A GUI unit test class for InternBuddy + * + * @@author eugenetangkj-reused + * Adapted and reused from + * https://github.com/se-edu/addressbook-level4/blob/master/src/test/java/seedu/address/ui/GuiUnitTest.java, + * https://github.com/AY2223S1-CS2103T-W17-4/tp/blob/master/src/test/java/seedu/phu/ui/GuiUnitTest.java and + * https://github.com/TestFX/TestFX/blob/master/subprojects/testfx-core/src/main/java/org/testfx/api/FxRobot.java + * with minor modifications. + */ +public abstract class GuiUnitTest { + // TODO: Remove this workaround after using JavaFX version 13 or above + // This is a workaround to solve headless test failure on Windows OS + // Refer to https://github.com/javafxports/openjdk-jfx/issues/66 for more details. + static { + if (System.getProperty("os.name").toLowerCase().startsWith("win")) { + System.loadLibrary("WindowsCodecs"); + } + setHeadlessTestModeToTrue(); + + } + + @RegisterExtension + public final UiPartExtension uiPartExtension = new UiPartExtension(); + + protected final FxRobot fxRobot = new FxRobot(); + + /** + * Retrieves the {@code query} node owned by the {@code rootNode}. + * + * @param query name of the CSS selector of the node to retrieve. + */ + protected T getChildNode(Node rootNode, String query) { + Optional node = fxRobot.from(rootNode).lookup(query).tryQuery(); + return node.orElse(null); + } + + //@@author eugenetangkj-reused + //Reused from https://github.com/AY2223S1-CS2103T-W17-4/tp/blob/master/src/test/java/seedu/phu/ui/GuiUnitTest.java + private static void setHeadlessTestModeToTrue() { + System.setProperty("testfx.robot", "glass"); + System.setProperty("testfx.headless", "true"); + System.setProperty("prism.order", "sw"); + System.setProperty("prism.text", "t2k"); + System.setProperty("java.awt.headless", "true"); + } + + +} diff --git a/src/test/java/seedu/internship/ui/InternshipCardTest.java b/src/test/java/seedu/internship/ui/InternshipCardTest.java new file mode 100644 index 00000000000..46fa1be1c3b --- /dev/null +++ b/src/test/java/seedu/internship/ui/InternshipCardTest.java @@ -0,0 +1,147 @@ +package seedu.internship.ui; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertIterableEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.internship.model.internship.Status.ACCEPTED; +import static seedu.internship.model.internship.Status.APPLIED; +import static seedu.internship.model.internship.Status.ASSESSMENT; +import static seedu.internship.model.internship.Status.INTERVIEW; +import static seedu.internship.model.internship.Status.NEW; +import static seedu.internship.model.internship.Status.OFFERED; +import static seedu.internship.model.internship.Status.REJECTED; +import static seedu.internship.ui.InternshipCard.ROLE_LABEL; + +import java.util.HashMap; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +import javafx.collections.ObservableList; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.Region; +import javafx.scene.paint.Color; +import seedu.internship.model.internship.Internship; +import seedu.internship.testutil.InternshipBuilder; +import seedu.internship.testutil.TypicalInternships; + +public class InternshipCardTest extends GuiUnitTest { + @Test + public void create_internshipCard_success() { + //@@author eugenetangkj-reused + //Reused with modifications from the following link for testing of creation of internship card in GUI testing + // https://github.com/AY2223S1-CS2103T-W17-4/tp/blob/master/src/test/java/seedu/phu/ui/InternshipCardTest.java + Internship internship = new InternshipBuilder().build(); + InternshipCard internshipCard = new InternshipCard(internship, 1); + new UiPartExtension().setUiPart(internshipCard); + } + + @Test + public void display_internshipCard_success() { + Internship internship = new InternshipBuilder().build(); + InternshipCard internshipCard = new InternshipCard(internship, 1); + uiPartExtension.setUiPart(internshipCard); + assertInternshipCardEqual(internshipCard, internship, 1); + } + + @Test + public void equals() { + InternshipCard internshipCardAmazonOne = new InternshipCard(TypicalInternships.AMAZON, 1); + InternshipCard internshipCardAmazonTwo = new InternshipCard(TypicalInternships.AMAZON, 2); + InternshipCard internshipCardAmazonOneDuplicate = new InternshipCard(TypicalInternships.AMAZON, 1); + InternshipCard internshipCardTesla = new InternshipCard(TypicalInternships.TESLA, 1); + //Same object + assertTrue(internshipCardAmazonOne.equals(internshipCardAmazonOne)); + + //Not an instance of internship card + assertFalse(internshipCardAmazonOne.equals(TypicalInternships.AMAZON)); + + //Same internship but different index should not be equal + assertFalse(internshipCardAmazonOne.equals(internshipCardAmazonTwo)); + + //Different objects but same internship and index + assertTrue(internshipCardAmazonOne.equals(internshipCardAmazonOneDuplicate)); + + ////Same index but different internships + assertFalse(internshipCardAmazonOne.equals(internshipCardTesla)); + } + + @Test + public void create_colorMap_success() { + HashMap colorMap = new InternshipCard(TypicalInternships.AMAZON, 1).setupColours(); + //Check "New" color + assertTrue(colorMap.get(NEW).equals(Color.rgb(250, 155, 68, 1.0))); + //Check "Applied" color + assertTrue(colorMap.get(APPLIED).equals(Color.rgb(68, 170, 250, 1.0))); + //Check "Assessment" color + assertTrue(colorMap.get(ASSESSMENT).equals(Color.rgb(250, 68, 155, 1.0))); + //Check "Interview" color + assertTrue(colorMap.get(INTERVIEW).equals(Color.rgb(126, 68, 250, 1.0))); + //Check "Rejected" color + assertTrue(colorMap.get(REJECTED).equals(Color.rgb(250, 68, 68, 1.0))); + //Check "Offered" color + assertTrue(colorMap.get(OFFERED).equals(Color.rgb(42, 174, 166, 1.0))); + //Check "Accepted" color + assertTrue(colorMap.get(ACCEPTED).equals(Color.rgb(42, 174, 79, 1.0))); + } + + @ Test + public void create_dateLabel_success() { + // Test new + Internship internship = new InternshipBuilder().withStatus(NEW).build(); + InternshipCard internshipCard = new InternshipCard(internship, 1); + assertEquals(internshipCard.getDateLabel(internship.getStatus().toString()), "Deadline of Application: "); + // Test applied + internship = new InternshipBuilder().withStatus(APPLIED).build(); + internshipCard = new InternshipCard(internship, 1); + assertEquals(internshipCard.getDateLabel(internship.getStatus().toString()), "Date Applied: "); + // Test Assessment + internship = new InternshipBuilder().withStatus(ASSESSMENT).build(); + internshipCard = new InternshipCard(internship, 1); + assertEquals(internshipCard.getDateLabel(internship.getStatus().toString()), "Date of Assessment: "); + // Test Interview + internship = new InternshipBuilder().withStatus(INTERVIEW).build(); + internshipCard = new InternshipCard(internship, 1); + assertEquals(internshipCard.getDateLabel(internship.getStatus().toString()), "Date of Interview: "); + // Test Offered + internship = new InternshipBuilder().withStatus(OFFERED).build(); + internshipCard = new InternshipCard(internship, 1); + assertEquals(internshipCard.getDateLabel(internship.getStatus().toString()), + "Deadline of Offer Acceptance: "); + // Test rejected + internship = new InternshipBuilder().withStatus(REJECTED).build(); + internshipCard = new InternshipCard(internship, 1); + assertEquals(internshipCard.getDateLabel(internship.getStatus().toString()), + "Date of Rejection: "); + // Test accepted + internship = new InternshipBuilder().withStatus(ACCEPTED).build(); + internshipCard = new InternshipCard(internship, 1); + assertEquals(internshipCard.getDateLabel(internship.getStatus().toString()), + "Date of Acceptance: "); + } + + + private void assertInternshipCardEqual(InternshipCard internshipCard, Internship internship, int expectedIndex) { + //@@author potty10-reused + //Reused with some modification from + //https://github.com/AY2223S1-CS2103T-W17-4/tp/blob/master/src/test/java/seedu/phu/ui/InternshipCardTest.java + Region internshipRegion = internshipCard.getRoot(); + Label companyName = (Label) internshipRegion.lookup("#companyName"); + Label internshipRoleLabel = (Label) internshipRegion.lookup("#role"); + Label internshipDate = (Label) internshipRegion.lookup("#date"); + Label internshipStatus = (Label) internshipRegion.lookup("#statusLabel"); + String expectedDateLabel = internshipCard.getDateLabel(internship.getStatus().toString()); + ObservableList internshipNodeTags = ((FlowPane) internshipRegion.lookup("#tags")).getChildren(); + + assertEquals(companyName.getText(), internship.getCompanyName().toString()); + assertEquals(internshipRoleLabel.getText(), ROLE_LABEL + internship.getRole().toString()); + assertEquals(internshipDate.getText(), expectedDateLabel + internship.getDate().toString()); + assertEquals(internshipStatus.getText(), internship.getStatus().toString().toUpperCase()); + assertIterableEquals(internshipNodeTags.stream().map(node -> ((Label) node).getText()).sorted() + .collect(Collectors.toList()), internship.getTags().stream().sorted().collect(Collectors.toList())); + } + +} diff --git a/src/test/java/seedu/internship/ui/InternshipDetailsCardTest.java b/src/test/java/seedu/internship/ui/InternshipDetailsCardTest.java new file mode 100644 index 00000000000..8936c319533 --- /dev/null +++ b/src/test/java/seedu/internship/ui/InternshipDetailsCardTest.java @@ -0,0 +1,181 @@ +package seedu.internship.ui; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertIterableEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.internship.model.internship.Status.ACCEPTED; +import static seedu.internship.model.internship.Status.APPLIED; +import static seedu.internship.model.internship.Status.ASSESSMENT; +import static seedu.internship.model.internship.Status.INTERVIEW; +import static seedu.internship.model.internship.Status.NEW; +import static seedu.internship.model.internship.Status.OFFERED; +import static seedu.internship.model.internship.Status.REJECTED; +import static seedu.internship.ui.InternshipCard.ROLE_LABEL; + +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +import javafx.collections.ObservableList; +import javafx.scene.Group; +import javafx.scene.Node; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.Region; +import javafx.scene.paint.Color; +import javafx.scene.text.Text; +import seedu.internship.model.internship.Internship; +import seedu.internship.testutil.InternshipBuilder; +import seedu.internship.testutil.TypicalInternships; + +public class InternshipDetailsCardTest extends GuiUnitTest { + @Test + public void create_internshipCard_success() { + //@@author eugenetangkj-reused + //Reused with modifications from the following link for testing of creation of internship card in GUI testing + // https://github.com/AY2223S1-CS2103T-W17-4/tp/blob/master/src/test/java/seedu/phu/ui/InternshipCardTest.java + Internship internship = new InternshipBuilder().build(); + // @@author eugenetangkj-reused + // Sample scene reused from https://docs.oracle.com/javase/8/javafx/api/javafx/scene/Scene.html + Group rootGroup = new Group(); + Scene sceneNew = new Scene(rootGroup, 300, 300, Color.BLACK); + InternshipDetailsCard internshipDetailsCard = new InternshipDetailsCard(internship, sceneNew); + new UiPartExtension().setUiPart(internshipDetailsCard); + } + + //@@author eugenetangkj-reused + //Reused from teammate potty10's code in InternshipCard.java + @Test + public void display_internshipDetailsCard_success() { + Internship internship = new InternshipBuilder().build(); + + // @@author eugenetangkj-reused + // Sample scene reused from https://docs.oracle.com/javase/8/javafx/api/javafx/scene/Scene.html + Group rootGroup = new Group(); + Scene sceneNew = new Scene(rootGroup, 300, 300, Color.BLACK); + + InternshipDetailsCard internshipDetailsCard = new InternshipDetailsCard(internship, sceneNew); + uiPartExtension.setUiPart(internshipDetailsCard); + assertInternshipDetailsCardEqual(internshipDetailsCard, internship); + } + + @Test + public void equals() { + // @@author eugenetangkj-reused + // Sample scene reused from https://docs.oracle.com/javase/8/javafx/api/javafx/scene/Scene.html + Group rootGroup = new Group(); + Scene sceneNew = new Scene(rootGroup, 300, 300, Color.BLACK); + + InternshipDetailsCard internshipDetailsCardAmazonOne = + new InternshipDetailsCard(TypicalInternships.AMAZON, sceneNew); + + InternshipDetailsCard internshipDetailsCardAmazonOneDuplicate = + new InternshipDetailsCard(TypicalInternships.AMAZON, sceneNew); + + InternshipDetailsCard internshipDetailsCardTesla = + new InternshipDetailsCard(TypicalInternships.TESLA, sceneNew); + + // Same object + assertTrue(internshipDetailsCardAmazonOne.equals(internshipDetailsCardAmazonOne)); + + // Not an instance of internship card + assertFalse(internshipDetailsCardAmazonOne.equals(TypicalInternships.AMAZON)); + + // Different objects but same internship + assertTrue(internshipDetailsCardAmazonOne.equals(internshipDetailsCardAmazonOneDuplicate)); + + // Different internships + assertFalse(internshipDetailsCardAmazonOne.equals(internshipDetailsCardTesla)); + } + + + @ Test + public void getTips_success() { + // @@author eugenetangkj-reused + // Sample scene reused from https://docs.oracle.com/javase/8/javafx/api/javafx/scene/Scene.html + Group rootGroup = new Group(); + Scene sceneNew = new Scene(rootGroup, 300, 300, Color.BLACK); + + // Test New + Internship internship = new InternshipBuilder().withStatus(NEW).build(); + InternshipDetailsCard internshipDetailsCard = new InternshipDetailsCard(internship, sceneNew); + assertEquals(internshipDetailsCard.getTips(), + "If possible, try to apply early because once companies receive applications, they would start" + + " screening for potential candidates. Also, remember to do a thorough check of your resume" + + " before sending out your application."); + + // Test Applied + internship = new InternshipBuilder().withStatus(APPLIED).build(); + internshipDetailsCard = new InternshipDetailsCard(internship, sceneNew); + assertEquals(internshipDetailsCard.getTips(), + "While waiting for the company's response, you can try applying to other companies as well" + + " to have a higher chance of landing an internship."); + + // Test Assessment + internship = new InternshipBuilder().withStatus(ASSESSMENT).build(); + internshipDetailsCard = new InternshipDetailsCard(internship, sceneNew); + assertEquals(internshipDetailsCard.getTips(), + "Practice makes perfect! Visit sites such as HackerRank and LeetCode to practice your" + + " algorithms and problem-solving skills. You could also attempt the practices under a time" + + " trial to give you a better sense of the actual coding assignment."); + + // Test Interview + internship = new InternshipBuilder().withStatus(INTERVIEW).build(); + internshipDetailsCard = new InternshipDetailsCard(internship, sceneNew); + assertEquals(internshipDetailsCard.getTips(), + "Be natural! The role of the interviewer is not to put you in a tight position, but rather to" + + " learn more about who you are as a person. It's good if you could share what makes you" + + " special and about your personalised experience that makes you suitable for the job."); + + // Test Offered + internship = new InternshipBuilder().withStatus(OFFERED).build(); + internshipDetailsCard = new InternshipDetailsCard(internship, sceneNew); + assertEquals(internshipDetailsCard.getTips(), + "Congratulations! Your hard work has paid off. Remember to read through the details of the" + + " letter of offer such as job scope and working hours before committing to the offer."); + + // Test Rejected + internship = new InternshipBuilder().withStatus(REJECTED).build(); + internshipDetailsCard = new InternshipDetailsCard(internship, sceneNew); + assertEquals(internshipDetailsCard.getTips(), + "Fret not! The process of landing an internship is not a smooth-sailing one, and failures are" + + " part of the journey. Continue your search and you will eventually a suitable internship." + + " Fighting!"); + + // Test Accepted + internship = new InternshipBuilder().withStatus(ACCEPTED).build(); + internshipDetailsCard = new InternshipDetailsCard(internship, sceneNew); + assertEquals(internshipDetailsCard.getTips(), + "Congratulations! This is a chance to build new skills, make connections, and explore your " + + "interests in a real-world setting. Embrace every moment of this journey and " + + "don't be afraid to ask questions, seek guidance, and take risks."); + + } + + private void assertInternshipDetailsCardEqual(InternshipDetailsCard internshipDetailCard, Internship internship) { + //@@author eugenetangkj-reused + //Reused from teammate potty10 where he modified the code from + //https://github.com/AY2223S1-CS2103T-W17-4/tp/blob/master/src/test/java/seedu/phu/ui/InternshipCardTest.java + Region internshipRegion = internshipDetailCard.getRoot(); + Text companyName = (Text) internshipRegion.lookup("#companyName"); + Text internshipRoleText = (Text) internshipRegion.lookup("#role"); + Label internshipDate = (Label) internshipRegion.lookup("#date"); + Label internshipStatus = (Label) internshipRegion.lookup("#statusLabel"); + String expectedDateLabel = InternshipCard.getDateLabel(internship.getStatus().toString()); + Text comment = (Text) internshipRegion.lookup("#comment"); + ObservableList internshipNodeTags = ((FlowPane) internshipRegion.lookup("#tags")).getChildren(); + assertEquals(companyName.getText(), internship.getCompanyName().toString()); + assertEquals(internshipRoleText.getText(), ROLE_LABEL + internship.getRole().toString()); + + assertEquals(internshipDate.getText(), expectedDateLabel + internship.getDate().toString()); + assertEquals("[" + comment.getText() + "]", internship.getComment().toString()); + assertEquals(internshipStatus.getText(), internship.getStatus().toString().toUpperCase()); + assertIterableEquals(internshipNodeTags.stream().map(node -> ((Label) node).getText()).sorted() + .collect(Collectors.toList()), internship.getTags().stream().sorted().collect(Collectors.toList())); + + } + +} + diff --git a/src/test/java/seedu/internship/ui/StageExtension.java b/src/test/java/seedu/internship/ui/StageExtension.java new file mode 100644 index 00000000000..9aece46f0a8 --- /dev/null +++ b/src/test/java/seedu/internship/ui/StageExtension.java @@ -0,0 +1,28 @@ +package seedu.internship.ui; + +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.testfx.api.FxToolkit; + +/** + * Properly sets up and tears down a JavaFx stage for our testing purposes. + * + * @@author eugenetangkj-reused + * Reused with slight modifications from + * https://github.com/se-edu/addressbook-level4/blob/master/src/test/java/seedu/address/ui/testutil/StageExtension.java + */ +public class StageExtension implements BeforeEachCallback, AfterEachCallback { + + //Sets up the JavaFx stage for testing + @Override + public void beforeEach(ExtensionContext context) throws Exception { + FxToolkit.registerPrimaryStage(); + } + + //Closes and cleans up the JavaFx stage after testing + @Override + public void afterEach(ExtensionContext context) throws Exception { + FxToolkit.cleanupStages(); + } +} diff --git a/src/test/java/seedu/address/ui/TestFxmlObject.java b/src/test/java/seedu/internship/ui/TestFxmlObject.java similarity index 96% rename from src/test/java/seedu/address/ui/TestFxmlObject.java rename to src/test/java/seedu/internship/ui/TestFxmlObject.java index 5ecd82656f2..80c3699d3c3 100644 --- a/src/test/java/seedu/address/ui/TestFxmlObject.java +++ b/src/test/java/seedu/internship/ui/TestFxmlObject.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.internship.ui; import javafx.beans.DefaultProperty; diff --git a/src/test/java/seedu/internship/ui/UiPartExtension.java b/src/test/java/seedu/internship/ui/UiPartExtension.java new file mode 100644 index 00000000000..8afcceb1565 --- /dev/null +++ b/src/test/java/seedu/internship/ui/UiPartExtension.java @@ -0,0 +1,34 @@ +package seedu.internship.ui; + +import java.util.concurrent.TimeoutException; + +import org.testfx.api.FxToolkit; + +import javafx.scene.Parent; +import javafx.scene.Scene; + +/** + * Provides an isolated stage to test an individual {@code UiPart}. + * + * @@author eugenetangkj-reused + * Reused with slight modifications from + * https://github.com/se-edu/addressbook-level4/blob/master/src/test/java/seedu/address/ui/testutil/UiPartExtension.java + */ +public class UiPartExtension extends StageExtension { + //CSS files related to the stage + private static final String[] CSS_FILES = {"view/DarkTheme.css", "view/Extensions.css"}; + + //Set up the related UI component for testing + public void setUiPart(final UiPart uiPart) { + try { + FxToolkit.setupScene(() -> { + Scene scene = new Scene(uiPart.getRoot()); + scene.getStylesheets().setAll(CSS_FILES); + return scene; + }); + FxToolkit.showStage(); + } catch (TimeoutException te) { + throw new AssertionError("Timeout should not take place.", te); + } + } +} diff --git a/src/test/java/seedu/address/ui/UiPartTest.java b/src/test/java/seedu/internship/ui/UiPartTest.java similarity index 97% rename from src/test/java/seedu/address/ui/UiPartTest.java rename to src/test/java/seedu/internship/ui/UiPartTest.java index 33d82d911b8..a07b34714eb 100644 --- a/src/test/java/seedu/address/ui/UiPartTest.java +++ b/src/test/java/seedu/internship/ui/UiPartTest.java @@ -1,8 +1,8 @@ -package seedu.address.ui; +package seedu.internship.ui; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.internship.testutil.Assert.assertThrows; import java.net.URL; import java.nio.file.Path; @@ -11,7 +11,7 @@ import org.junit.jupiter.api.io.TempDir; import javafx.fxml.FXML; -import seedu.address.MainApp; +import seedu.internship.MainApp; public class UiPartTest { diff --git a/src/test/resources/view/UiPartTest/validFile.fxml b/src/test/resources/view/UiPartTest/validFile.fxml index bab836af0db..6b1e6bc406f 100644 --- a/src/test/resources/view/UiPartTest/validFile.fxml +++ b/src/test/resources/view/UiPartTest/validFile.fxml @@ -1,4 +1,4 @@ - + Hello World! diff --git a/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml b/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml index 151e09ce926..21c6990208e 100644 --- a/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml +++ b/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml @@ -1,6 +1,6 @@ - + Hello World!