diff --git a/README.md b/README.md index 13f5c77403f..e88ccdc1cc7 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,33 @@ -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) +[![CI Status](https://github.com/AY2122S1-CS2103T-W12-2/tp/actions/workflows/gradle.yml/badge.svg)](https://github.com/AY2122S1-CS2103T-W12-2/tp/actions/workflows/gradle.yml) +[![codecov](https://codecov.io/gh/AY2122S1-CS2103T-W12-2/tp/branch/master/graph/badge.svg?token=MORDANUUIH)](https://codecov.io/gh/AY2122S1-CS2103T-W12-2/tp) +# SWEe-book +**SWEe-book** is a desktop app that makes it easier for CS2103T/CS2101 students to +**keep track of their tasks and group mates in different groups**. + +### Features +1. Adding a contact +1. Editing a contact +1. Deleting a contact +1. Filtering contacts by name +1. Filtering contacts by group +1. Listing all contacts +1. Adding a task +1. Editing a task +1. Deleting a task +1. Marking a task as done +1. Sorting tasks +1. Filtering tasks by modules +1. Listing all tasks + +### SWEe-book GUI ![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. +### User Guide +The User Guide can be accessed [here](https://github.com/AY2122S1-CS2103T-W12-2/tp/blob/master/docs/UserGuide.md). + +### Developer Guide +The Developer Guide can be accessed [here](https://github.com/AY2122S1-CS2103T-W12-2/tp/blob/master/docs/DeveloperGuide.md). + +#### Acknowledgements +This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org). diff --git a/build.gradle b/build.gradle index be2d2905dde..ca2c9ac4fde 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,11 @@ plugins { id 'jacoco' } -mainClassName = 'seedu.address.Main' +run { + enableAssertions = true +} + +mainClassName = 'sweebook.Main' sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 @@ -66,7 +70,7 @@ dependencies { } shadowJar { - archiveName = 'addressbook.jar' + archiveName = 'sweebook.jar' } defaultTasks 'clean', 'test' diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..77fbd9542be 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -9,51 +9,47 @@ You can reach us at the email `seer[at]comp.nus.edu.sg` ## Project team -### John Doe +### Alina Lee - + -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/alinaleehx)] [[portfolio](team/alinaleehx.md)] -* Role: Project Advisor +* Role: Documentation +* Responsibilities: Responsible for the quality of various project documents. -### Jane Doe +### Joseph Nathanael - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/JosephN37)] [[portfolio](team/JosephN37.md)] -* Role: Team Lead -* Responsibilities: UI +* Role: Deliverables and deadlines +* Responsibilities: Ensure project deliverables are done on time and in the right format. -### Johnny Doe +### Yeu Chen Yuan - + -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] +[[github](https://github.com/cyyeu)] [[portfolio](team/cyyeu.md)] -* Role: Developer -* Responsibilities: Data +* Role: Code quality +* Responsibilities: Ensure overall adherence to coding standards. -### Jean Doe +### Ambrose Boo - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/ambroseboo)] [[portfolio](team/ambroseboo.md)] -* Role: Developer -* Responsibilities: Dev Ops + Threading +* Role: Scheduling and Tracking +* Responsibilities: In charge of defining, assigning and tracking project tasks. -### James Doe +### Tan Zhen Xuan - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/zhenxuantan)] [[portfolio](team/zhenxuantan.md)] -* Role: Developer -* Responsibilities: UI +* Role: UI/UX +* Responsibilities: Responsible for the UI/UX component of the SWEe-book app. diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 46eae8ee565..dfd7d7801b2 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -4,44 +4,27 @@ title: Developer Guide --- * Table of Contents {:toc} +--- --------------------------------------------------------------------------------------------------------------------- - -## **Acknowledgements** - -* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} - --------------------------------------------------------------------------------------------------------------------- - -## **Setting up, getting started** - -Refer to the guide [_Setting up and getting started_](SettingUp.md). - --------------------------------------------------------------------------------------------------------------------- +
## **Design** -
- -:bulb: **Tip:** The `.puml` files used to create diagrams in this document 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. -
- ### Architecture - - - -The ***Architecture Diagram*** given above explains the high-level design of the App. +![Architecture diagram](images/ArchitectureDiagram.png) Given below is a quick overview of 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/AY2122S1-CS2103T-W12-2/tp/blob/master/src/main/java/sweebook/Main.java) and [`MainApp`](https://github.com/AY2122S1-CS2103T-W12-2/tp/blob/master/src/main/java/sweebook/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. [**`Commons`**](#common-classes) represents a collection of classes used by multiple other components. +
+ The rest of the App consists of four components. * [**`UI`**](#ui-component): The UI of the App. @@ -49,12 +32,13 @@ The rest of the App consists of four components. * [**`Model`**](#model-component): Holds the data of the App in memory. * [**`Storage`**](#storage-component): Reads data from, and writes data to, the hard disk. - **How the architecture components interact with each other** -The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete 1`. +The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the +command `addTask d/project meeting g/CS2101 type/event date/2021-10-10`. + +![addTask Sequence diagram](images/ArchitectureSequenceDiagramAddTask1.png) - Each of the four main components (also shown in the diagram above), @@ -63,193 +47,248 @@ Each of the four main components (also shown in the diagram above), For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below. - +![Component managers](images/ComponentManagers.png) The sections below give more details of each component. -### UI component - -The **API** of this component is specified in [`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java) - -![Structure of the UI Component](images/UiClassDiagram.png) - -The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. - -The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml) - -The `UI` component, - -* 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`. +
### Logic component - -**API** : [`Logic.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/logic/Logic.java) - Here's a (partial) class diagram of the `Logic` component: - +![Logic Class diagram](images/LogicClassDiagram.png) 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 `SweeBookParser` class to parse the user command. +2. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddTaskCommand`) which is executed by the `LogicManager`. +3. The command can communicate with the `Model` when it is executed (e.g. to add a task). +4. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. -The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("delete 1")` API call. +The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("addTask d/project meeting g/CS2101 type/event date/2021-10-10")` 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. -
+![Interactions Inside the Logic Component for the `addTask d/project meeting g/CS2101 type/event date/2021-10-10` Command](images/AddTaskSequenceDiagram1.png) Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command: - +![Parser class](images/ParserClasses.png) 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 `SweeBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddTaskCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `SweeBookParser` returns back as a `Command` object. + +
### Model component -**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java) +Here is an overview of the model component. - +![Model class](images/SweebookModelClassDiagram.png) +
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 list of contacts and list of tasks (contained in UniquePersonList and TaskList respectively) +* stores the currently 'selected' `Person` and `Task` objects (e.g., results of a search query) as separate _filtered_ lists which is exposed to outsiders as an unmodifiable `ObservableList` or `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.
+Taking a closer look at the person and tasks models, here is the class diagram. +![Person and tasks class](images/PersonAndTasksClassDiagram.png) - +`Person` and `Task` models are similar such that, +* they share a `Group` class, which can be either `CS2103T` or `CS2101` -
+Specifically for `Person`, +* the model stores the phone number, email and socials +* socials refer to their Telegram and Github usernames +Lastly, specfically for `Task`, +* a task has a date (to specify a deadline or time of event), description, priority, and recurring frequency +* recurring frequency can be in terms of weekly, monthly and yearly +* priority can be low, medium or high +* a task can be instantiated as a `Todo`, `Deadlne` or `Event` -### Storage component +
+ +### UI component +The **API** of this component is specified in [`Ui.java`](https://github.com/AY2122S1-CS2103T-W12-2/tp/blob/master/src/main/java/seedu/address/ui/Ui.java) -**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java) +![Ui Class Diagram](images/UiClassDiagram.png) - +The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. -The `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). -* depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`) +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/AY2122S1-CS2103T-W12-2/tp/blob/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/AY2122S1-CS2103T-W12-2/tp/blob/master/src/main/resources/view/MainWindow.fxml) -### Common classes +
-Classes used by multiple components are in the `seedu.addressbook.commons` package. +### Storage component +![Storage Class Diagram](images/StorageClassDiagram1.png) --------------------------------------------------------------------------------------------------------------------- +The Storage component, -## **Implementation** +* can save contact list/task records/user preference data in json format, and read them back into corresponding objects. +* inherits from `ContactListStorage`, `TaskRecordsStorage`, 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) -This section describes some noteworthy details on how certain features are implemented. -### \[Proposed\] Undo/redo feature +--- -#### 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: +## **Implementation** -* `VersionedAddressBook#commit()` — Saves the current address book state in its history. -* `VersionedAddressBook#undo()` — Restores the previous address book state from its history. -* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history. +This section describes some noteworthy details on how certain features are implemented. -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. +### Edit feature ( `edit` and `editTask` commands) +#### Implementation +The edit feature allows users to edit specific fields in tasks or contacts. The implementation for both contacts (`edit`) and +tasks (`editTask`) are similar. Therefore we can generalize the implementation of the edit feature by exploring how the `editTask` command works for tasks. +Given below is a sequence diagram of the execution of an edit command: [Click here for better resolution](https://ay2122s1-cs2103t-w12-2.github.io/tp/images/EditTaskSequenceDiagram.png) -Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. +![Seq-diagram for the parsing of command for `editTask 1 d/OP2 rehearsal g/CS2101 type/Event date/2021-11-11`](images/EditTaskSequenceDiagram.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. +The general logic of the `editTask` command is similar to `addTask` (which can be found above), +with the following differences: +1. editTask uses a `EditTaskDescriptor` to store the specified values that the user want to change +1. when EditTaskCommand is executed, we create a new task, where the value of each field is given more priority to the `EditTaskDescriptor` than the non-edited task. (i.e if a field is non-null in EditTaskDescriptor, the value of that field in the new task will be equal to that field in the `EditTaskDescriptor`. Else, it will remain unchanged from the old task) +1. we then replace this new task with the current task in the model -![UndoRedoState0](images/UndoRedoState0.png) +Given below is an example usage scenario of how a contact is edited: +1. The user enters the edit command with the specified fields to be edited. + (e.g edit 1 n/Johm Doe tg/johndoeee)) +1. SWEe-book updates the contact with the new updated fields, with non-updated fields left unchanged. -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. +
:information_source: **Note:** For `editTask` command, a date **must** be specified +for **recurring** tasks and **deadline/event** tasks. Else, an error message will be shown to the user. (It does not make sense for a task that is recurring, or that is a deadline/event, to have no date!) +
-![UndoRedoState1](images/UndoRedoState1.png) +#### Alternative considerations +* Alternative 1: Update the fields of the old task, without creating a new task. + * Pros: Less logic needed, less complexity. (No need for `EditTaskDescriptor` class) + * Cons: Hard to debug, and more prone to errors, as we are mutating the object in the list -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) +### Recurring Tasks feature +#### Implementation +The recurring task feature allows users to add tasks that can be repeated by week, month, or year. It is facilitated +by `RecurringFrequency`, which is a optional component of `Task`. Additionally, the following operations are implemented +in `Task`, `TaskList`, `TaskRecords` and `Date`: +* `Task#updateRecurringTaskDate()` - Task updates its Date to the current week/month/year, based on the + recurringFrequency of the Task. +* `Date#isLastWeek()`, `Date#isLastMonth()`, `Date#isLastYear()` - Checks current Date of Task against real-time date. +* `Date#getDateForThisWeek()`, `Date#getDateForThisMonth()`, `Date#getDateForThisYear()` - Updates Date to be within + current week/month/year. +* `TaskList#updateRecurringTasksDates()` - Iterates through list of Tasks and updates its Date if Task is recurring. +* `TaskRecords#updateRecurringTasks()` - Calls for TaskList to update recurring Tasks. -
: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`. +`TaskRecords#updateRecurringTasks()` is used in the `ModelManager` on boot-up of the application to update all Tasks, if +required. Below is a sequence diagram after the initialisation of the `ModelManager`: -
+![Seq-diagram for updating dates of recurring Tasks after start up](images/RecurringFrequencySequenceDiagram.png) -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. +Do note that `Date` is required for a `Task` to be recurring. Notably, `Date` is optional for `Todo`. -![UndoRedoState3](images/UndoRedoState3.png) +
-
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather -than attempting to perform the undo. +Given below is an example usage scenario of how a recurring task is added and how it behaves upon re-launching of +the SWEe-book application. -
+* Step 1. The user launches the application. A recurring `Task` is added, where the user specifies the + `recurringFrequency` to be weekly, and the `Date` to be from the previous week. The `Task` is added, but the `Date` is + not updated yet, even if it is not of the current week. The `recurringFrequency` of the task is marked as `week`. +* Step 2. The user re-launches the application. `ModelManager` calls `TaskRecords#updateRecurringTasks()`, which then + calls `TaskList#updateRecurringTasksDates()`, which then calls `Task#updateRecurringTaskDate()` on the task added. + Since the `Task` added was recurring (its `recurringFrequency` is marked as `week`, its `Date` is updated to the + current week, with the same day. +* Step 3. The user then launches the application a week after. The `Task` is updated similarly to Step 2, and since it + is checked against real-time, it is updated to the current week. + +Given below is an activity diagram when a user adds a recurring task and restarts his SWEe-book application. -The following sequence diagram shows how the undo operation works: +![Activity-diagram for user adding recurring Task](images/RecurringTaskActivityDiagram.png) -![UndoSequenceDiagram](images/UndoSequenceDiagram.png) +
-
: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. +#### Alternative considerations +* Alternative 1: Let the user choose when to refresh his tasks to their new dates, rather than on start-up of the application. + * Pros: Allows user more control over their recurring tasks + * Cons: Less intuitive since tasks are not updated to real-time, having a refresh command just for recurring tasks + is not ideal. -
+
-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. +### Sort Tasks Feature -
:information_source: **Note:** If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone AddressBook states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo. +The sort tasks feature allows users to sort the task list according to their needs. They can sort the tasks by their descriptions, groups, priorities and date. They can also choose whether they want the sorting be in ascending or descending order. This gives users 8 different ways to sort their tasks. -
+The following is the Activity Diagram for the sort tasks command. -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. +![Activity Diagram for sortTasksCommand](images/SortTasksActivityDiagram.png) -![UndoRedoState4](images/UndoRedoState4.png) +
-Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …​` command. This is the behavior that most modern desktop applications follow. +#### Implementation +The Sequence Diagram below illustrates the interactions between the `Logic` component and `Model` component for the `execute("sortTasks param/desc o/a")` API call. It also shows how it interacts with the `UI` Component through the illustration of the command's interaction with JavaFx's `ObservableLists` (`FilteredList` and `SortedList`). -![UndoRedoState5](images/UndoRedoState5.png) +![Interactions between logic and model component for `sortTasks param/desc o/a`](images/SortTasksExecutionSequenceDiagram.png) -The following activity diagram summarizes what happens when a user executes a new command: +![Sub-diagram for the parsing of command for `sortTasks param/desc o/a`](images/SortTasksParserSequenceDiagram.png) - +The JavaFx package automatically detects any changes to the task list, implemented with JavaFX's `ObservableList`. This includes detecting changes in the comparators and filters applied on it. When the `SortTasksCommand` is executed, it removes any existing filters applied on the task list to reset the task list back to its original state before setting a comparator to it. The `FilterTasksCommand` works the same way as well by setting the `comparator` to `null` while setting the `predicate` to the appropriate predicate (filter). -#### Design considerations: +
-**Aspect: How undo & redo executes:** +### Done Tasks Feature +#### Implementation +The doneTask feature allows users to mark a specific task as done. +Given below is a sequence diagram of the execution of a doneTask command: +[Click here for better resolution](https://ay2122s1-cs2103t-w12-2.github.io/tp/images/DoneTaskSequenceDiagram.png) -* **Alternative 1 (current choice):** Saves the entire address book. - * Pros: Easy to implement. - * Cons: May have performance issues in terms of memory usage. +![Seq-diagram for the parsing of command for `doneTask 1`](images/DoneTaskSequenceDiagram.png) -* **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. +Given below is an example usage scenario of how a task is marked as done: +Preconditions: Valid task index provided. +1. User keys in doneTask command with the specific index of task. + (e.g. doneTask 1) +2. The first task in task list is marked as done. -_{more aspects and alternatives to be added}_ +
:information_source: **Note:** If any of the guard clauses fail, +i.e. the index given is negative or otherwise invalid, or the task has already been marked as done, an appropriate +exception will be thrown and an appropriate error message will be shown to the user. +(It does not make sense to mark a task that has been done as done!) +
-### \[Proposed\] Data archiving +
-_{Explain here how the data archiving feature will be implemented}_ +The following activity diagram summarizes the actions taken when DoneTaskCommand is executed:
+![DoneTaskActivityDiagram](images/DoneTaskCommandActivityDiagram.png) --------------------------------------------------------------------------------------------------------------------- +#### Alternative considerations +* Alternative 1 (current choice): Separate updating of a task's status into its own command + * Pros: More streamlined command for ease of use + * Our target user (CS2103T students) are likely to update task statuses a with must higher frequency than + information like task description or task priority etc. + Hence, having a dedicated command to update status of task streamlines the user workflow. + * Cons: Additional command increases complexity, harder to maintain. +* Alternative 2: Integrate updating of task status into ‘editTask’ command + * Pros: Fewer commands for user to remember as well as fewer commands for developers to maintain + * Cons: More troublesome to when user just wants to update task status. + +
+--- ## **Documentation, logging, testing, configuration, dev-ops** -* [Documentation guide](Documentation.md) -* [Testing guide](Testing.md) -* [Logging guide](Logging.md) -* [Configuration guide](Configuration.md) -* [DevOps guide](DevOps.md) +* [Documentation Guide](https://ay2122s1-cs2103t-w12-2.github.io/tp/Documentation.html) +* [Testing Guide](https://ay2122s1-cs2103t-w12-2.github.io/tp/Testing.html) +* [Logging Guide](https://ay2122s1-cs2103t-w12-2.github.io/tp/Logging.html) +* [Configuration Guide](https://ay2122s1-cs2103t-w12-2.github.io/tp/Configuration.html) +* [DevOps Guide](https://ay2122s1-cs2103t-w12-2.github.io/tp/DevOps.html) --------------------------------------------------------------------------------------------------------------------- +--- + +
## **Appendix: Requirements** @@ -257,42 +296,68 @@ _{Explain here how the data archiving feature will be implemented}_ **Target user profile**: -* has a need to manage a significant number of contacts +* is a CS2103T/CS2101 student +* has a need to keep track of tasks pertaining to CS2103T/CS2101 module +* has a need to keep track his/her CS2103T/CS2101 group mates' contact details * prefer desktop apps over other types * can type fast * prefers typing to mouse interactions * is reasonably comfortable using CLI apps -**Value proposition**: manage contacts faster than a typical mouse/GUI driven app +**Value proposition**: Manage contacts of group mates and daily tasks faster than a typical mouse/GUI driven app ### User stories Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` -| Priority | As a …​ | I want to …​ | So that I can…​ | +| 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}* +| `*` | user with many persons in the contact list | sort persons by name | locate a person easily | +| `*` | user | Add a new task to the list | keep track of the tasks that needs to be done | +| `* * *` | new user | Have an overview of my groupmate details like telegram, email and name. | So I can easily contact them | +| `* * *` | forgetful user | have a list of tasks | follow up on it and not miss out tasks | +| `*` | user | delete a task in the list | delete the tasks that are no longer needed | +| `*` | user | mark a task in the list as done | keep track of which tasks are done or not yet done | +| `* * *` | long term user | quickly check deadlines in order of priority (sort) | clear the tasks due one at a time | +| `* * *` | user | filter the task according to the different modules | know what I can do for each module | +| `* *` | user | edit specific fields in a task | conveniently change specific fields without needing to delete and add back a task + +
### Use cases -(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise) +(For all use cases below, the **System** is the `SWEe-book`, **Actor** is the `user` while **person** can be used interchangeably with contact (as in contact list), unless specified otherwise) -**Use case: Delete a person** +**Use case (UC01): Add a person** **MSS** -1. User requests to list persons -2. AddressBook shows a list of persons -3. User requests to delete a specific person in the list -4. AddressBook deletes the person +1. User adds a person +2. System shows the details of the person + + Use case ends. + +**Extensions** + +* 2a. The details are invalid or incomplete. + * 2a1. System shows an error message. + + Use case resumes at step 2. + +**Use case (UC02): Delete a person** + +**MSS** + +1. User requests to list persons +2. System shows a list of persons +3. User requests to delete a specific person in the list +4. System deletes the person Use case ends. @@ -303,75 +368,406 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli Use case ends. * 3a. The given index is invalid. - - * 3a1. AddressBook shows an error message. + * 3a1. System shows an error message. Use case resumes at step 2. -*{More to be added}* +
+ +**Use case (UC03): Edit a person's particulars** + +**MSS** + +1. User requests to list persons +2. System shows a list of persons +3. User requests to edit a specific person in the list +4. System shows the details of the updated person + + Use case ends. + +**Extensions** + +* 3a. The details are invalid or incomplete. + * 3a1. System shows an error message. + + Use case resumes at step 3. + +**Use case (UC04): Find a person / a group of people** + +**MSS** + +1. User requests to find a person / a group of people using some keywords +2. System shows a list of persons pertaining to the keywords + + Use case ends. + +**Use case (UC05): Add a task** + +**MSS** + +1. User keys in a task. +2. System shows the details of the task added to task list. + + Use case ends. + +**Extensions** + +* 2a. The task details are invalid or incomplete. + * 2a1. System shows an error message about the incorrect or missing details. + + Use case ends. + +
+ +**Use case (UC06): Edit a task** + +**MSS** + +1. User requests to edit a specific person in the list +1. System shows the details of the updated person + + Use case ends. + +**Extensions** + +* 1a. The details are invalid or incomplete. + * 1a1. System shows an error message. + + Use case resumes at step 3. + +**Use case (UC07): Delete a task** + +1. User requests to list tasks. +2. System shows a list of tasks. +3. User keys in an index. +4. The task of specified index in task list is removed. + + Use case ends. + +**Extensions** + +* 1a. User keys in an invalid index. + * 1a1. System displays an error message about invalid index. + + Use case ends. + +
+ +**Use case (UC08): Have an overview of group mates' contact details** + +1. User keys in a group of which its members' contact details are needed. +2. System displays the contact information of the group members of specified group + + Use case ends. + +**Extensions** + +* 1a. User keys in an invalid group. + * 1a1. System displays an error message about invalid group. + + Use case ends. + +**Use case (UC09): Have a list of tasks** + +1. User keys in the command to list the tasks. +2. System displays the list of tasks. + + Use case ends. + +**Extensions** + +* 1a. User keys in an invalid command. + * 1a1. System displays an error message about invalid command. + + Use case ends. + +**Use case (UC10): Sort tasks** + +1. User keys the command, the parameter and order which he wants the tasks to be sorted by. +2. System displays the tasks in the parameter and order specified. + + Use case ends. + +**Extensions** + +* 1a. User keys in an invalid parameter or order. + * 1a1. System displays an error message about invalid parameter or order. + + Use case ends. + +
+ +**Use case (UC11): Filter tasks** + +1. User keys in a filter criterion. +2. System displays the tasks pertaining to the criterion specified. + + Use case ends. + +**Extensions** + +* 1a. User keys in an invalid criterion. + * 1a1. System displays an error message about invalid criterion. Use case ends. + +**Use case (UC12): Mark a task as done** + +1. User requests to list tasks. +2. System shows a list of tasks. +3. User keys in an index. +4. The task of specified index in task list is marked as done. + + Use case ends. + +**Extensions** + +* 1a. User keys in an invalid index. + * 1a1. System displays an error message about invalid index. + + Use case ends. + +
### Non-Functional Requirements -1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed. -2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage. -3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. +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. +4. System should respond within three seconds. +5. System still works even if the data file is missing. +6. If the data file is corrupted, the corrupted file is overwritten with an empty data file. +7. System will not collect any information from the user to abide by the Personal Data Protection Act. -*{More to be added}* +
### Glossary -* **Mainstream OS**: Windows, Linux, Unix, OS-X -* **Private contact detail**: A contact detail that is not meant to be shared with others +| **Term** | **Meaning** | +| ---- | --------| +|**Mainstream OS**| Windows, Linux, Unix, OS-X | +|**Private contact detail**| A contact detail that is not meant to be shared with others| +|**CLI**|Command-Line Interface| +|**Group**| a group of either CS2103T or CS2101 students| +|**Index**| the ordering of task in the task list| --------------------------------------------------------------------------------------------------------------------- +
## **Appendix: Instructions for manual testing** Given below are instructions to test the app manually. +* Note: These instructions only provide a starting point for testers to work on; testers are expected to do more + exploratory testing. +* Note that only refactored or new features from AB3 are shown here. -
:information_source: **Note:** These instructions only provide a starting point for testers to work on; -testers are expected to do more *exploratory* testing. +### Adding a Contact -
+1. Prerequisites: No existing contact with the same... + 1. Name of `John Doe`, ignoring case-sensitivity and whitespaces between first/middle/last names (i.e "john doe" and "john    doe" are the same as "John Doe") + 1. Email of `johnd@example.com` + 1. Telegram and GitHub usernames of `johndoe` + +1. Testcase 1: `add n/John Doe g/CS2103T g/CS2101 p/98765432 e/johnd@example.com tg/johndoe gh/johndoe` + + Expected: Command result box shows the person is added with the correct fields specified. The added person should appear as the last person of the contact list in the GUI. + +1. Testcase 2: `add n/Jane Doe g/CS2103T g/CS2101 p/98765432 e/janed@example.com tg/janedoe gh/janedoe` + + Expected: Unable to add person due to duplicate phone number. Command result box shows `A person with this phone number (98765432) already exists in the contact list.` + +1. Other similar testcases: Trying to add any person which does not meet the prerequisites + + Expected: Unable to add person due to the duplicated field. Command result box shows `A person with this () already exists in the contact list.` + +
+ +### Editing a contact + +1. Prerequisites: + 1. There are at least 1 contact in the list + +1. Testcase 1: `edit 1 tg/@newusername gh/newusername` + + Expected: Edits the usernames of first contact in the list to the new usernames. + +1. Testcase 2: `edit 1 p/91234567` + + Expected: Edits the phone number of first contact in the list to the new phone number. + +### Filtering contacts by group +1. Testcase 1: `group CS2101` + + Expected: Contact list returns the list of people in the group `CS2101`. +1. Testcase 2: `group CS2103T` + + Expected: Contact list returns the list of people in the group `CS2103T`. +1. Testcase 3: `group CS1111` + + Expected: Error message shown due to invalid group name. + +
+ +### Editing a task +1. Prerequisites: + 1. For each testcase, please use the following task by using the addTask command: `addTask d/finish coding g/CS2101 type/todo` + 1. Assume that the task list only contains the above task. + +1. Testcase 1: `editTask 1 type/event` + + Expected: Error message shown as an event needs to have an associated date. + +1. Testcase 2: `editTask 1 type/event date/2021-11-11` + + Expected: Edits the first task in the list with the correct updated fields. Command result shows edited task fields, and GUI updates the corresponding fields of the task. + +1. Testcase 3: `editTask 1 recurring/week` + + Expected: Edits the first task in the list with the correct updated fields. Command result shows edited task fields, and GUI updates the corresponding fields of the task. + +
+ +### Sorting task lists + +1. Sorting task list with valid parameter and order + 1. Prerequisites: The task list is from the sample task list data that comes with the jar file is first run. + 2. Test case: `sortTasks param/desc o/a`
Expected task list (sorted by description in ascending order lexicographically): + 1. Create slides + 2. OP1 Presentation + 3. OP1 script + 4. Project Meeting + 5. Update User Guide + + 3. Test case: `sortTasks param/date o/d`
Expected task list (sorted by date in reverse chronological order) + 1. Project Meeting (Dec 21 2021) + 2. Update User Guide (Dec 11 2021) + 3. OP1 Presentation (Nov 21 2021) + 4. Create slides (Nov 17 2021) + 5. OP1 script (Nov 11 2021) + + +2. Sorting task list with invalid parameter, order or both + 1. Prerequisites: The task list is from the default task list data that comes with the jar file. + 2. Test case: `sortTasks param/tasktype o/a`
Task list does not change and SWEe-book returns an error message with its correct usage. + 3. Test case: `sortTasks param/date o/c`
Task list does not change and SWEe-book returns an error message with its correct usage. + +
+ +### Filtering task lists + +1. Filtering task list with a valid criterion + 1. Prerequisites: The task list is from the sample task list data that comes with the jar file is first run. + 2. Test case: `filterTasks date/2021-11-11`
Expected task list (filtered by date of 11 Nov 2021): + 1. OP1 script + 3. Test case: `filterTasks g/CS2103T`
Expected task list (filtered by group of CS2103T): + 1. Update User Guide + 2. Project Meeting + + +2. Filtering task list with an invalid criterion + 1. Prerequisites: The task list is from the sample task list data that comes with the jar file is first run. + 2. Test case: `filterTasks pty/1`
Task list does not change and SWEe-book returns an error message with its correct usage. + 3. Test case: `filterTasks g/CS2107`
Task list does not change and SWEe-book returns an error message with its correct usage. + +
+ +### Marking a task as done + +1. Marking a task's status as done successfully + 1. Prerequisites: There is at least one task in task list. List all tasks using the `listTasks` command. + Assume there is a task `Project Meeting` that is not done at index 1 of task list. + + 2. Test case: `doneTask 1` + Expected: Task `Project Meeting` status is updated to done. + A success message, with details of task that is marked as done, is shown. + +2. Attempting to mark a task as done with invalid index + 1. Prerequisites: List all tasks using the `listTasks`. Assume there are a total of 4 tasks. + + 2. Test case: `doneTask 6` + Expected: An error message is shown, indicating that given index exceeded total number of tasks. + + 3. Test case: `doneTask -1` + Expected: An error message is shown, indicating that index should not be negative. + +3. Attempting to mark a task, that is already done, as done + 1. Prerequisites: List all tasks using the `listTasks`. Assume there is a task `Project Meeting` + that has been marked as done at index 1 of task list. + + 2. Test case: `doneTask 1` + Expected: An error message is shown, indicating that task has already been marked as done. + +
+ +### Adding a task + +1. Adding a task with all fields + 1. Test case: `addTask d/Project Meeting g/CS2101 type/Todo date/2021-11-11 pty/low recurring/week` + Expected: Task with all the above fields added to the task list. Task list in GUI is updated with this new task, + added to the bottom of the list + 2. For incorrect addTask commands, try: `addTask d/ g/ `, + `addTask type/ `, + `addTask pty/ `, + `addTask recurring/` -### Launch and shutdown + 2. Adding a task without optional fields + 1. Test case: `addTask d/Assignment g/CS2103T type/Todo` + Expected: Task with all the above fields added to the task list. Task list in GUI is updated with this new task, + added to the bottom of the list + 2. Test case: `addTask d/Assignment g/CS2103T type/Event` + Expected: No task is added and error is shown. Event type task must have a date attached, date is not optional for + events. + 3. Test case: `addTask d/Assignment g/CS2103T type/Event` + Expected: No task is added and error is shown. Deadline type task must have a date attached, date is not optional + for deadlines. + 4. Test case: `addTask d/Rehearsal g/CS2101 type/Todo recurring/month` + Expected: No task is added and error is shown. Task with recurring field must have a date as well. -1. Initial launch +
- 1. Download the jar file and copy into an empty folder +### Listing all tasks - 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. +1. List all tasks + 1. Prerequisite: Have some tasks saved in `[JAR file location]/data/taskrecords.json` + 2. Test case: `listTasks` + Expected: Task list in GUI is updated to show all saved tasks. -1. Saving window preferences +### Deleting a task +1. Deleting a task from SWEe-book + 1. Prerequisites: There are 2 tasks in task list. List all tasks using the `listTasks` command. + At index `1` we have `Project Meeting`, and at index `2` we have `Presentation` - 1. Resize the window to an optimum size. Move the window to a different location. Close the window. + 2. Test case: `deleteTask 1`
+ Expected: Task `Project Meeting` is deleted and removed from task list. + A success message with details of the deleted task is shown. - 1. Re-launch the app by double-clicking the jar file.
- Expected: The most recent window size and location is retained. + 3. Test case: `deleteTask 3`
+ Expected: An error message is shown, indicating that given index exceeded total number of tasks. -1. _{ more test cases …​ }_ + 4. Test case: `deleteTask -3`
+ Expected: An error message is shown, indicating that index should not be negative. -### Deleting a person +
-1. Deleting a person while all persons are being shown +## **Appendix: Effort** - 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. +### Adding new UI components +* After V1.2, our team had yet to modify the existing AB3 UI , despite already implementing one of the main components of SWEe-book, namely task management, and made some adjustments to the existing contact list component. We encountered some difficulties in reflecting task list in the UI, and also modifying it with features such as FilterTasks and SortTasks, and thus at that time most of the task management is still reflected on the CLI only. Fortunately, our group managed to make use of Java SortedList and FilteredList to help us implement the task management UI component. - 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. +### Implementing dynamic features +* SWEe-book features implemented require the user to type in commands in the CLI to be executed. As our group implemented more features which allow for more variations and contextualization, such as task priority, we realized that this may be too troublesome for a part of our target audience as they may have to type relatively long commands, especially for task management. Thus, to minimize this, we decided to make some feature fields optional. This means that when the user does not specify the field, it will be set to a default value. For example, when task priority is not specified, it will be automatically set to a default value of “medium”. We believe that this will make SWEe-book more convenient to use as our target audience do not need to type too long of a command, while still being given the option to specify more details. - 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. +### Showing warnings when the data file is invalid or empty +* When the app starts up, it will load the data from the respective data files (.json files). When an error occurs, it is difficult to display an error message onto the Ui as it would break the current design pattern where a message will only appear through a user-input command from the class CommandResult. Loading from the storage files should not go through a series command. However, It is also not good to not inform the users about the error and just simply give an empty contact list or task list. To counter that, we just simply make the Ui pop-up a new window to inform the users about the error, whether the data files are missing or corrupted. - 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
- Expected: Similar to previous. +
-1. _{ more test cases …​ }_ +## Acknowledgement -### Saving data +### Code reuse -1. Dealing with missing/corrupted data files +1. The following code is reused from [here](https://github.com/regexhq/regex-username/blob/master/index.js). The code is used to check if usernames are valid via regex. + ![code snippet of regex](images/codeReuseRegex.png) +1. The following code is reused from [here](https://stackoverflow.com/a/5226244). The code is used to enable users to launch hyperlinks in their default browser by clicking on them in the GUI. + ![code snippet of hyperlink](images/codeReuseHyperlink.png) - 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ +### Documentation and diagrams -1. _{ more test cases …​ }_ +A large part of the documentation and diagrams are reused from the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org). diff --git a/docs/SettingUp.md b/docs/SettingUp.md index 275445bd551..e71a50491fc 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 `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..efdd1db3a7e 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. `sweebook.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. `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. `LogicManagerTest` diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 3716f3ca8a4..b95e1ea50a4 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -3,120 +3,211 @@ 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. +SWEe-book is a **desktop app for CS2103T/CS2101 Computer Science students to manage contacts and tasks +pertaining to CS2103T/CS2101 module, optimized for use via a Command Line Interface** (CLI). This means that you can operate +the application by typing commands into a Command Box. If you can type fast, SWEe-book can get your contact management and +task management done faster than traditional Graphical User Interface (GUI) applications, +which allows users to interact with the application through graphical icons such as buttons.
+ +SWEe-book offers **one-stop integration solution to managing tasks and contacts in your CS2103T or CS2101 group.** + +This SWEe-book User Guide will help you to: + +* Manage your contacts + * Add/Delete/Edit your list of contacts + * Search through your contacts + * Group your contacts together by module + + +* Manage your tasks + * Add/Delete tasks + * Classify tasks as todos, deadlines, or events + * Mark your tasks as completed + * Filter/Sort tasks + +Look into the first few sections to get started on using SWEe-book + +Browse through the table of contents below to navigate through SWEe-book's features and how to use them for a more +efficient and convenient task and contact management! * Table of Contents {:toc} -------------------------------------------------------------------------------------------------------------------- +## How to use SWEe-book User Guide + +* The Quick Start section [below](#quick-start) will help you set up your SWEe-book +* Click [here](#features) to dive straight to SWEe-book's list of features +* Alternatively, you can click [here](#command-summary) to refer to the Command Summary Table +* You can also checkout a list of frequently asked questions (FAQs) [here](#faq), and the SWEe-book's glossary [here](#glossary)! + + +Throughout SWEe-book User Guide, + +
+**:information_source: Notes** are there to give you extra information about SWEe-book's features. +
+
:bulb: **Tips** provide you quick and useful information. +
+
:exclamation: **Cautions** are to warn you of possible pitfalls to avoid! +
+ +We hope that this user guide will be useful to you in using SWEe-book! + +-------------------------------------------------------------------------------------------------------------------- +
+ ## Quick start 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). +1. Download the latest `SWEe-book.jar` from [here](https://github.com/AY2122S1-CS2103T-W12-2/tp/releases). -1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook. +1. Copy the file to the folder you want to use as the _home folder_ for your SWEe-book. 1. Double-click the file to start the app. The GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
- ![Ui](images/Ui.png) + ![Ui](images/QuickStartSS.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. + * **`list`** : Lists all contacts. + + * **`add`**`n/John Doe g/CS2103T p/98765432 e/johnd@example.com tg/johndoe gh/johndoe` : Adds a contact named `John Doe` to the SWEe-book. - * **`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. + * **`listTasks`** : Lists all tasks. - * **`delete`**`3` : Deletes the 3rd contact shown in the current list. + * **`deleteTask`**`1` : Deletes the first task shown in the current task list. - * **`clear`** : Deletes all contacts. + * **`exit`** : Exits the app. - * **`exit`** : Exits the app. +6. Refer to the [Features](#features) below for details of each command. -1. Refer to the [Features](#features) below for details of each command. +
+ +**:bulb: Tip:** +
+* If you are unsure about where the contact list, task list or command input box are, check out the [Ui Design](#ui-design) section. + +
-------------------------------------------------------------------------------------------------------------------- +
-## Features +## UI Design -
+![UiExplanation](images/UIDesign.png) +
+* **Command Input Box**: Type your commands here! + +* **Message Box**: Displays the details of the result of your commands or error messages. -**:information_source: Notes about the command format:**
+* **Contact List**: Displays your list of group mates and their relevant information. -* 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`. +* **Task List**: Displays your tasks. -* Items in square brackets are optional.
- e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`. +-------------------------------------------------------------------------------------------------------------------- +
-* Items with `…`​ after them can be used multiple times including zero times.
- e.g. `[t/TAG]…​` can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc. +## Glossary -* 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. +* **GUI** is short-form for Graphical User Interface, which refers to what the user sees as a graphic. In SWEe-book, the GUI refers to the contact and task lists as shown in the red box below.
+ +![GUI](images/UserGuideGUIRedBox.png) -* 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. +
-* 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`. +* **CLI** is short-form for Command Line Interface, it is what the user types commands into to query or add information into SWEe-book. It refers to the box at the top which users type into (also known as the command box) and the box directly below it which shows feedback based on what command was typed. They are shown in the coloured boxes below.
+ +![CLI and Command Box](images/UserGuideCLIRedBox.png) -
+
+ +* **Tasks** are items which you would like to keep track of with a description attached to them. The task list is displayed on the right side of the GUI. There are three different types of tasks: a todo, deadline, or an event. A todo is a task that can be date-less (e.g. buy stationary) while a deadline and an event must be accompanied by a date. + +* **Contacts** are items which signify persons who you would like to keep a contact of. The contact list displayed on the left side of the GUI. + +* **Commands** are what the user types into the command box. They consist of the command keyword (e.g. `add`, `find`, `addTask`)and the parameters to the command. Explanation of parameters is shown below. + +* **Parameters** are the terms that the user types after the command keyword to narrow the scope of their query. There can any number of parameters to a command, even 0, depending on the command. + An example of parameters is in the command format `add n/NAME g/GROUP1 [g/GROUP2] p/PHONE_NUMBER e/EMAIL tg/TELEGRAM_USERNAME gh/GITHUB_USERNAME`,(`n/NAME`, `g/GROUP1`, `[g/GROUP2]`, `p/PHONE_NUMBER`, `e/EMAIL`, `tg/TELEGRAM_USERNAME`, `gh/GITHUB_USERNAME`) are all parameters. Do note parameters in square brackets (`[g/GROUP2]`) are optional. + +* Do note that any highlighted words (e.g. `add`) in this User Guide refers to words that can be typed into the command box, or items that will show up in the GUI as a result.
+ +* **INDEX** refers to a number which corresponds to a task's/contact's position in the task list/contact list. (e.g. INDEX 1 means it's the first task on the task list) + +* **Lexicographical order** is an order similar to alphabetical order, but more relevant to programming and is more comprehensive. Just like how "a, b, c" is in alphabetical order, "0, 1, A, B, a, b" is in lexicographical order. Notice how the numbers come before upper case letters, which in turn come before lower case letters. + +-------------------------------------------------------------------------------------------------------------------- +
+ +## Features + +### General Features -### Viewing help : `help` +#### Viewing help : `help` -Shows a message explaning how to access the help page. +Shows a window that allows you to copy the website link to access the user guide in cases when error messages are still insufficient. ![help message](images/helpMessage.png) Format: `help` +
-### Adding a person: `add` +#### Exiting the program : `exit` -Adds a person to the address book. +Exits the program. -Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` +Format: `exit` -
:bulb: **Tip:** -A person can have any number of tags (including 0) -
+#### Editing the data file -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` +SWEe-book contacts data are saved as a JSON file `[JAR file location]/data/contactlist.json`, whereas tasks management data are saved as `[JAR file location]/data/taskrecords.json`. Advanced users are welcome to update data directly by editing the data files. -### Listing all persons : `list` +
:exclamation: **Caution:** +If your changes to the data file makes its format invalid, SWEe-book will discard all data and start with an empty data file at the next run. +
-Shows a list of all persons in the address book. +#### Saving the data -Format: `list` +SWEe-book data are saved in the hard disk automatically after any command that changes the data. You do not need to save manually. -### Editing a person : `edit` +### Contact Management Features -Edits an existing person in the address book. +#### Adding a person: `add` -Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` +Adds a person to SWEe-book. -* 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. +Format: `add n/NAME g/GROUP1 [g/GROUP2] p/PHONE_NUMBER e/EMAIL tg/TELEGRAM_USERNAME gh/GITHUB_USERNAME` + +
:bulb: **Tip:** +You can optionally prepend usernames with '@'. SWEe-book takes care of it! +(i.e. `@johndoe` and `johndoe` are equivalent as they will both be parsed into the username `johndoe`) +
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. +* `add n/John Doe g/CS2103T p/98765432 e/johnd@example.com tg/@johndoe gh/johndoe` + * Adds a contact with name `John Doe`, group `CS2103T`, phone number `98765432`, email `johnd@example.com`, telegram username `johndoe`, github username `johndoe` +* `add n/Betsy Crowe p/92221234 g/CS2103T g/CS2101 e/betsycrowe@example.com tg/betsyyy gh/crowebetsy` + * Adds a contact with name `Betsy Crowe`, group `CS2103T` **and** `CS2101`, phone number `92221234 `, email `betsycrowe@example.com`, telegram username `betsyyy`, github username `crowebetsy` + +#### Listing all persons : `list` -### Locating persons by name: `find` +Shows a list of all persons in SWEe-book. + +Format: `list` + +
+ +#### Locating persons by name: `find` Finds persons whose names contain any of the given keywords. Format: `find KEYWORD [MORE_KEYWORDS]` -* The search is case-insensitive. e.g `hans` will match `Hans` +* The search is case-**in**sensitive. 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` @@ -124,13 +215,46 @@ Format: `find KEYWORD [MORE_KEYWORDS]` e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang` Examples: -* `find John` returns `john` and `John Doe` -* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png) +* `find alex` + * Returns `Alex Yeoh` + +![result for 'find alex'](images/findAlexResult.png) + +
+ +* `find alex david` + * Returns `Alex Yeoh`, `David Li`
+ +![result for 'find alex david'](images/findAlexDavidResult.png) + +#### Editing a person : `edit` + +Edits an existing person in SWEe-book. + +Format: `edit INDEX [n/NAME] [g/GROUP1] [g/GROUP2] [p/PHONE] [e/EMAIL] [tg/TELEGRAM] [gh/GITHUB]` + +* 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. + +
+ +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. + +![result for edit 1 p/91234567 e/johndoe@example.com'](images/EditPersonExample1.png) + +* `find Bernice` followed by `edit 1 tg/bernyu gh/bernyu` + * Edits the telegram and github usernames of the 1st person in the results of the `find` command. -### Deleting a person : `delete` +![result for find and edit'](images/EditPersonExample2.png) -Deletes the specified person from the address book. +
+ +#### Deleting a person : `delete` + +Deletes the specified person from SWEe-book. Format: `delete INDEX` @@ -139,54 +263,236 @@ Format: `delete INDEX` * The index **must be a positive integer** 1, 2, 3, …​ Examples: -* `list` followed by `delete 2` deletes the 2nd person in the address book. -* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command. +* `list` followed by `delete 2` + * Deletes the 2nd person in SWEe-book. +* `find Betsy` followed by `delete 1` + * Deletes the 1st person in the results of the `find` command. + +
+ +#### Filtering persons by a specified group: `group` +Retrieves the list of people that are in the specified group. + +Format: `group GROUP_NAME` +* `GROUP_NAME` refers to one of the 2 groups: CS2101 or CS2103T. +* `GROUP_NAME` is case-**in**sensitive. +* Note that this is not a strict filter (i.e `group CS2103T` can return a person who is from both CS2103T **and** CS2101 groups) + +Examples: +* `group CS2101` + * Returns people in CS2101 + +![result for 'group CS2101'](images/groupCs2101.png) + +
-### Clearing all entries : `clear` +* `group CS2103T` + * Returns people in CS2103T -Clears all entries from the address book. +![result for 'group CS2103T'](images/groupCs2103t.png) + +#### Clearing all contact entries : `clear` + +Clears all contact entries from SWEe-book. Format: `clear` -### Exiting the program : `exit` +
-Exits the program. +### Task Management Features -Format: `exit` +#### Adding a Task : `addTask` -### Saving the data +Adds a new task to SWEe-book. -AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. +Format: `addTask d/DESCRIPTION g/GROUP type/TYPE [date/DATE] [pty/PRIORITY] [recurring/RECURRING_FREQUENCY]` +* `GROUP` refers to one of the 2 groups: `CS2101` or `CS2103T` +* `TYPE` refers to one of the 3 types of tasks: `todo`, `event` or `deadline` +* `DATE` is in YYYY-MM-DD format and is only needed for events or deadlines (i.e. `DATE` is optional for Todo tasks) +* `PRIORITY` refers to one of the 3 levels of priorities / importance of the task: `low`, `med` (default) or `high` +* `RECURRING_FREQUENCY` refers to one of the 3 different frequencies that the task could occur: `week`, `month` or `year` (where `week` means that the task is recurring weekly) + * Any Task that has a recurring frequency must have a date as well, for example a Todo with recurring frequency must have a date. + * If a Task has a recurring frequency and its date is initialised to be in the last week/month/year, it will be set to the current week/month/year in the next boot up of the application. +Examples: +* `addTask d/Project meeting g/CS2103T type/todo pty/low` + * Add a non-recurring `todo` with no date and `low` priority and + task description `Project meeting` to the group `CS2103T` -### Editing the data file +![result for 'addTask d/Project meeting g/CS2103T type/todo pty/low'](images/addTaskExample1.PNG) -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. +* `addTask d/Presentation 1 g/CS2101 type/deadline date/2020-11-02 pty/high` + * Add a non-recurring `deadline` due on `2020-11-02` + with `high` priority and task description `Presentation 1`to the group `CS2101` + +![result for 'addTask d/Presentation 1 g/CS2101 type/deadline date/2020-11-02 pty/high'](images/addTaskExample2.PNG) + +* `addTask d/Mock QnA 1 g/CS2101 type/event date/2020-10-02 recurring/month` + * Add a `event` that recurs every `month` with + date `2020-10-02` and default `med` priority and task description `Mock QnA` to the group `CS2101` + +![result for 'addTask d/Mock QnA 1 g/CS2101 type/event date/2020-10-02 recurring/month'](images/addTaskExample3.PNG) + +
+ +#### Editing a task: `editTask` + +Edits an existing task in the task list. + +Format: `editTask INDEX [d/DESCRIPTION] [g/GROUP] [type/TYPE] [date/DATE] [pty/PRIORITY] [recurring/RECURRING_FREQUENCY]` +* Edits the task at the specified `INDEX`. The index refers to the index number shown in the displayed task 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. +* To set a recurring task to be non-recurring, you can specify `recurring/none` as one of the arguments. + +
:bulb: **Tip:** +Please specify a date if you are changing a todo with no associated date into a deadline/event or recurring task!
-### Archiving data files `[coming in v2.0]` +Examples: +* `editTask 1 d/OP2 rehearsal g/CS2101 type/Event date/2021-11-11` + * Edits the description, group, type of task, date of the first task in the task list to be + `OP2 rehearsal`, `CS2101`, `Event` and`2021-11-11` respectively. + ![result for 'editTask 1 d/OP2 rehearsal g/CS2101 type/Event date/2021-11-11'](images/EditTaskExample1.PNG) + +
+ +#### Deleting a task: `deleteTask` + +Format: `deleteTask INDEX` + +Deletes the task at the specified `INDEX`. +* The index refers to the index number shown in the displayed task list. +* The index **must be a positive integer** 1, 2, 3, … + +Examples: +* `deleteTask 1` + * Deletes the 1st task in the task list. + +#### Marking a task as done: `doneTask` -_Details coming soon ..._ +Format: `doneTask INDEX` + +Marks the task at the specified `INDEX` as done. +* The index refers to the index number shown in the displayed task list. +* The index **must be a positive integer** 1, 2, 3, … +* The task must not have been marked as done before. + +Examples: +* `doneTask 1` + * Marks the 1st task in the task list as done. + +![result for 'doneTask 1'](images/doneTaskCommandExample.png) + +
+ +#### Sorting tasks: `sortTasks` +Sort tasks based on their descriptions, groups, priorities or deadlines / event dates. + +Format: `sortTasks param/PARAMETER o/ORDER` +* The sort is case-**in**sensitive. e.g. CS2103T will be lexicographically identical to cs2103t, but cs2101 still comes before CS2103T. +* `PARAMETER` includes `desc` (for description), `date` (for a deadline / date of event), `pty` (for priority) and `group`. +* `ORDER` includes `a` for ascending order (0-9 and A-Z, oldest to newest, lowest to highest priority) and `d` for descending order(Z-A and 9-0, newest to oldest, highest to lowest priority) +* When the tasks are sorted by `date`, date-less `Todo` tasks will always be at the bottom of the list. + +Examples: +* `sortTasks param/pty o/a` + * Sorts the task list by priority, with the low-priority tasks at the top and high-priority tasks at the bottom +* `sortTasks param/group o/a` + * Sorts the task list by group, with CS2101 tasks at the top and CS2103T tasks at the bottom +* `sortTasks param/desc o/d` + * Sorts the task list by description, with the tasks that has description which starts with "Z" at the top, then "Y", and so on. +* `sortTasks param/date o/a` + * Sorts the task list in chronological order (in terms of their deadlines / event dates) +* `sortTasks param/date o/d` + * Sorts the task list in reverse chronological order (in terms of their deadlines / event dates) + +![result for 'sortTasks param/date o/a' and 'sortTasks param/date o/a'](images/SortTasksScreenshot.png) + +
+ +#### Filtering tasks by modules: `filterTasks` +Filter tasks based on a criterion + +Format: `filterTasks FILTER_CRITERION` +* Filters the task by the specified `FILTER_CRITERION` +* `FILTER_CRITERION` refers to either `g/GROUP`, `date/DATE`, `type/TASKTYPE`, `d/DESCRIPTION` or `pty/PRIORITY` +* FilterTasks only accepts 1 `FILTER_CRITERION` and not more. +* Tasks corresponding to the criterion specified will be shown + +Examples: +* `filterTasks g/CS2101` + * Shows all the tasks related to CS2101 group +* `filterTasks date/2021-11-21` + * Shows all the tasks with the date of 21 Nov 2021 + +![result for 'filterTasks g/CS2101' and 'filterTasks date/2021-11-21'](images/FilterTasksScreenshot.png) + +
+ +#### Listing all tasks: `listTasks` +Shows a list of all tasks in SWEe-book. + +Format: `listTasks` +* Lists all tasks, resetting any sorting and filtering done by you previously + +Examples: +* `listTasks` + * Displays all tasks in right half of the GUI -------------------------------------------------------------------------------------------------------------------- ## FAQ **Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder. +**A**: You can 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 SWEe-book home folder. + +**Q**: How do I install Java 11 on my computer?
+**A**: You can install Java 11 by downloading it [here](https://www.oracle.com/sg/java/technologies/javase/jdk11-archive-downloads.html). + +**Q**: How can I ensure that Java 11 has been installed on my computer?
+**A**: Open up your terminal and key in `java --version`. You can then see the java version that has been installed. An output of "java 11" means that java 11 has been successfully installed. + +**Q**: Can I sync my contacts and tasks with other devices?
+**A**: Unfortunately, you cannot sync them at the moment. However, please look out for future updates. -------------------------------------------------------------------------------------------------------------------- ## Command summary +Here, you can view the summary of general, contact management, and task management commands. + +### General Commands + +Action | Format, Examples +--------|------------------ +**Help** | `help` +**Exit** | `exit` + +
+ +### Contact Management Commands 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` +**Add** | `add n/NAME g/GROUP1 [g/GROUP2] p/PHONE_NUMBER e/EMAIL tg/TELEGRAM_USERNAME gh/GITHUB_USERNAME`
e.g., `add n/John Doe g/CS2103T p/98765432 e/johnd@example.com tg/@johndoe gh/johndoe` **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` +**Edit** | `edit INDEX [n/NAME] [g/GROUP1] [g/GROUP2] [p/PHONE] [e/EMAIL] [tg/TELEGRAM] [gh/GITHUB]`
e.g.,`edit 2 n/James Lee e/jameslee@example.com` **Find** | `find KEYWORD [MORE_KEYWORDS]`
e.g., `find James Jake` +**Group** | `group GROUP`
e.g., `group CS2103T` **List** | `list` -**Help** | `help` + +
+ +### Task Management Commands + +Action | Format, Examples +--------|------------------ +**Add Task** | `addTask d/DESCRIPTION g/GROUP type/TYPE [date/DATE] [pty/PRIORITY] [recurring/RECURRING_FREQUENCY]`
(do note that `DATE` is only optional for `Todo` tasks) +**Edit Task** | `editTask INDEX [d/DESCRIPTION] [g/GROUP] [type/TYPE] [date/DATE] [pty/PRIORITY] [recurring/RECURRING_FREQUENCY]` +**Delete Task** | `deleteTask INDEX` +**Done Task** | `doneTask INDEX` +**Sort Tasks** | `sortTasks p/PARAMETER o/ORDER`
e.g., `sortTasks p/desc o/1` +**Filter Tasks** | `filterTasks FILTER_CRITERION`
e.g., `filterTasks g/CS2101` +**List Tasks** | `listTasks` diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..b65d5a9b0cc 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,5 +1,5 @@ -title: "AB-3" -theme: minima +title: "SWEe-book" +theme: jekyll-theme-slate header_pages: - UserGuide.md @@ -8,7 +8,7 @@ header_pages: markdown: kramdown -repository: "se-edu/addressbook-level3" +repository: "AY2122S1-CS2103T-W12-2/tp" github_icon: "images/github-icon.png" plugins: diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss index 0d3f6e80ced..9feaf8f3fb3 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: "SWEe-book"; font-size: 32px; } } diff --git a/docs/diagrams/AddTaskSequenceDiagram.puml b/docs/diagrams/AddTaskSequenceDiagram.puml new file mode 100644 index 00000000000..4adb48dce13 --- /dev/null +++ b/docs/diagrams/AddTaskSequenceDiagram.puml @@ -0,0 +1,69 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":SweeBookParser" as SweeBookParser LOGIC_COLOR +participant ":AddTaskCommandParser" as AddTaskCommandParser LOGIC_COLOR +participant "a:AddTaskCommand" as AddTaskCommand 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("addTask d/project meeting \ng/CS2101 type/event date/2021-10-10") +activate LogicManager + +LogicManager -> SweeBookParser : parseCommand("addTask d/project meeting \ng/CS2101 type/event date/2021-10-10") +activate SweeBookParser + +create AddTaskCommandParser +SweeBookParser -> AddTaskCommandParser +activate AddTaskCommandParser + +AddTaskCommandParser --> SweeBookParser +deactivate AddTaskCommandParser + +SweeBookParser -> AddTaskCommandParser : parse("d/project meeting g/CS2101 \ntype/event date/2021-10-10") +activate AddTaskCommandParser + +create AddTaskCommand +AddTaskCommandParser -> AddTaskCommand +activate AddTaskCommand + +AddTaskCommand --> AddTaskCommandParser : a +deactivate AddTaskCommand + +AddTaskCommandParser --> SweeBookParser : a +deactivate AddTaskCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +AddTaskCommandParser -[hidden]-> SweeBookParser +destroy AddTaskCommandParser + +SweeBookParser --> LogicManager : a +deactivate SweeBookParser + +LogicManager -> AddTaskCommand : execute() +activate AddTaskCommand + +AddTaskCommand -> Model : addTask(task) +activate Model + +Model --> AddTaskCommand +deactivate Model + +create CommandResult +AddTaskCommand -> CommandResult +activate CommandResult + +CommandResult --> AddTaskCommand +deactivate CommandResult + +AddTaskCommand --> LogicManager : result +deactivate AddTaskCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ArchitectureSequenceDiagramAddTask.puml b/docs/diagrams/ArchitectureSequenceDiagramAddTask.puml new file mode 100644 index 00000000000..7bf5aa54819 --- /dev/null +++ b/docs/diagrams/ArchitectureSequenceDiagramAddTask.puml @@ -0,0 +1,38 @@ +@startuml +!include style.puml + +Actor User as user USER_COLOR +Participant ":UI" as ui UI_COLOR +Participant ":Logic" as logic LOGIC_COLOR +Participant ":Model" as model MODEL_COLOR +Participant ":Storage" as storage STORAGE_COLOR + +user -[USER_COLOR]> ui : "addTask d/project meeting g/CS2101 \ntype/event date/2021-10-10" +activate ui UI_COLOR + +ui -[UI_COLOR]> logic : execute("addTask d/project meeting g/CS2101 \ntype/event date/2021-10-10") +activate logic LOGIC_COLOR + +logic -[LOGIC_COLOR]> model : addTask(task) +activate model MODEL_COLOR + +model -[MODEL_COLOR]-> logic +deactivate model + +logic -[LOGIC_COLOR]> storage : saveTaskRecords(taskRecords) +activate storage STORAGE_COLOR + +storage -[STORAGE_COLOR]> storage : Save to file +activate storage STORAGE_COLOR_T1 +storage --[STORAGE_COLOR]> storage +deactivate storage + +storage --[STORAGE_COLOR]> logic +deactivate storage + +logic --[LOGIC_COLOR]> ui +deactivate logic + +ui--[UI_COLOR]> user +deactivate ui +@enduml diff --git a/docs/diagrams/DoneTaskCommandActivityDiagram.puml b/docs/diagrams/DoneTaskCommandActivityDiagram.puml new file mode 100644 index 00000000000..7ac23a615fc --- /dev/null +++ b/docs/diagrams/DoneTaskCommandActivityDiagram.puml @@ -0,0 +1,14 @@ +@startuml +start +if () then ([else]) + :Retrieve task at given index; + if () then ([else]) + :Update task status to done; + else ([task already marked as done]) + :CommandException; + endif +else ([index is invalid]) + :CommandException; +endif +stop +@enduml diff --git a/docs/diagrams/DoneTaskSequenceDiagram.puml b/docs/diagrams/DoneTaskSequenceDiagram.puml new file mode 100644 index 00000000000..057702aa8a6 --- /dev/null +++ b/docs/diagrams/DoneTaskSequenceDiagram.puml @@ -0,0 +1,74 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":SweeBookParser" as SweeBookParser LOGIC_COLOR +participant ":DoneTaskCommandParser" as DoneTaskCommandParser LOGIC_COLOR +participant ":DoneTaskCommand" as DoneTaskCommand 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("doneTask 1") +activate LogicManager + +LogicManager -> SweeBookParser : parseCommand("doneTask 1") +activate SweeBookParser + +create DoneTaskCommandParser +SweeBookParser -> DoneTaskCommandParser +activate DoneTaskCommandParser + +DoneTaskCommandParser --> SweeBookParser +deactivate DoneTaskCommandParser + +SweeBookParser -> DoneTaskCommandParser : parse("1") +activate DoneTaskCommandParser + +create DoneTaskCommand +DoneTaskCommandParser -> DoneTaskCommand : new DoneTaskCommand(1) +activate DoneTaskCommand + +DoneTaskCommand --> DoneTaskCommandParser : +deactivate DoneTaskCommand + +DoneTaskCommandParser --> SweeBookParser : +deactivate DoneTaskCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +DoneTaskCommandParser -[hidden]-> SweeBookParser +destroy DoneTaskCommandParser + +SweeBookParser --> LogicManager : +deactivate SweeBookParser + +LogicManager -> DoneTaskCommand : execute() +activate DoneTaskCommand + +DoneTaskCommand -> Model : getTasks() +activate Model + +Model --> DoneTaskCommand + +DoneTaskCommand -> Model : doneTask(task) + +Model --> DoneTaskCommand + +deactivate Model + +create CommandResult +DoneTaskCommand -> CommandResult +activate CommandResult + +CommandResult --> DoneTaskCommand +deactivate CommandResult + +DoneTaskCommand --> LogicManager : +deactivate DoneTaskCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/EditTaskSequenceDiagram.puml b/docs/diagrams/EditTaskSequenceDiagram.puml new file mode 100644 index 00000000000..5be65f51443 --- /dev/null +++ b/docs/diagrams/EditTaskSequenceDiagram.puml @@ -0,0 +1,88 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":SweeBookParser" as SweeBookParser LOGIC_COLOR +participant ":EditTaskCommandParser" as EditTaskCommandParser LOGIC_COLOR +participant ":EditTaskDescriptor" as EditTaskDescriptor LOGIC_COLOR +participant "command:EditTaskCommand" as EditTaskCommand 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("editTask 1 d/OP2\nrehearsal g/CS2101\ntype/Event date/2021-11-11") +activate LogicManager + +LogicManager -> SweeBookParser : parseCommand("editTask 1 d/OP2\nrehearsal g/CS2101\ntype/Event date/2021-11-11") +activate SweeBookParser + +create EditTaskCommandParser +SweeBookParser -> EditTaskCommandParser +activate EditTaskCommandParser + +EditTaskCommandParser --> SweeBookParser +deactivate EditTaskCommandParser + +SweeBookParser -> EditTaskCommandParser : parse("1 d/OP2 rehearsal g/CS2101\ntype/Event date/2021-11-11") +activate EditTaskCommandParser + +create EditTaskDescriptor +EditTaskCommandParser -> EditTaskDescriptor +activate EditTaskDescriptor + +EditTaskDescriptor --> EditTaskCommandParser : descriptor +deactivate EditTaskDescriptor + +create EditTaskCommand +EditTaskCommandParser -> EditTaskCommand : new EditTaskCommand(index, descriptor) +activate EditTaskCommand + +EditTaskCommand --> EditTaskCommandParser : command +deactivate EditTaskCommand + +EditTaskCommandParser --> SweeBookParser : command +deactivate EditTaskCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +EditTaskCommandParser -[hidden]-> SweeBookParser +destroy EditTaskCommandParser + +SweeBookParser --> LogicManager : command +deactivate SweeBookParser + +LogicManager -> EditTaskCommand : execute() +activate EditTaskCommand + + +EditTaskCommand -> EditTaskCommand : createEditedTask() +activate EditTaskCommand +EditTaskCommand -> EditTaskDescriptor +activate EditTaskDescriptor +EditTaskDescriptor --> EditTaskCommand +deactivate EditTaskDescriptor + +EditTaskCommand --> EditTaskCommand : editedTask +deactivate EditTaskCommand + +EditTaskCommand -> Model : setTask(taskToEdit, editedTask) +activate Model + +Model --> EditTaskCommand +deactivate Model + +create CommandResult +EditTaskCommand -> CommandResult +activate CommandResult + +CommandResult --> EditTaskCommand +deactivate CommandResult + +EditTaskCommand --> LogicManager : result +deactivate EditTaskCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml index 6d14b17b361..2bb883ac7b6 100644 --- a/docs/diagrams/LogicClassDiagram.puml +++ b/docs/diagrams/LogicClassDiagram.puml @@ -6,7 +6,7 @@ skinparam classBackgroundColor LOGIC_COLOR package Logic { -Class AddressBookParser +Class SweeBookParser Class XYZCommand Class CommandResult Class "{abstract}\nCommand" as Command @@ -27,8 +27,8 @@ Class HiddenOutside #FFFFFF HiddenOutside ..> Logic LogicManager .right.|> Logic -LogicManager -right->"1" AddressBookParser -AddressBookParser ..> XYZCommand : creates > +LogicManager -right->"1" SweeBookParser +SweeBookParser ..> XYZCommand : creates > XYZCommand -up-|> Command LogicManager .left.> Command : executes > diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 1122257bd9a..859097964b0 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -5,50 +5,47 @@ skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR Package Model <>{ -Interface ReadOnlyAddressBook <> -Interface ReadOnlyUserPrefs <> Interface Model <> -Class AddressBook -Class ReadOnlyAddressBook -Class Model +Interface ReadOnlyUserPrefs <> +Interface ReadOnlyContactList <> +Interface ReadOnlyTaskRecords <> + Class ModelManager Class UserPrefs -Class ReadOnlyUserPrefs - +Class ContactList +Class TaskRecords Class UniquePersonList Class Person -Class Address -Class Email -Class Name -Class Phone -Class Tag +Class TaskList +Class Task } Class HiddenOutside #FFFFFF HiddenOutside ..> Model +Model .left......> ReadOnlyUserPrefs +Model ..> ReadOnlyContactList +Model .right.> ReadOnlyTaskRecords -AddressBook .up.|> ReadOnlyAddressBook - -ModelManager .up.|> Model -Model .right.> ReadOnlyUserPrefs -Model .left.> ReadOnlyAddressBook -ModelManager -left-> "1" AddressBook -ModelManager -right-> "1" UserPrefs +ContactList .up.|> ReadOnlyContactList +TaskRecords .up.|> ReadOnlyTaskRecords UserPrefs .up.|> ReadOnlyUserPrefs -AddressBook *--> "1" UniquePersonList -UniquePersonList --> "~* all" Person -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address -Person *--> "*" Tag +ModelManager .up...|> Model +ModelManager --> "1" ContactList +ModelManager --> "1" UserPrefs +ModelManager --> "1" TaskRecords + + +ContactList *--> "1" UniquePersonList +UniquePersonList ----> "~* all" Person +TaskRecords -> "1" TaskList +TaskList --> "~* all" Task -Name -[hidden]right-> Phone -Phone -[hidden]right-> Address -Address -[hidden]right-> Email +ModelManager ----> "~* filtered" Person +ModelManager -----> "~* filtered" Task -ModelManager -->"~* filtered" Person +ReadOnlyUserPrefs -right[hidden]- ReadOnlyContactList +ReadOnlyContactList -right[hidden]- ReadOnlyTaskRecords @enduml diff --git a/docs/diagrams/ParserClasses.puml b/docs/diagrams/ParserClasses.puml index 6ba585cba01..6f4c2ce490f 100644 --- a/docs/diagrams/ParserClasses.puml +++ b/docs/diagrams/ParserClasses.puml @@ -9,7 +9,7 @@ Class XYZCommand package "Parser classes"{ Interface Parser <> -Class AddressBookParser +Class SweeBookParser Class XYZCommandParser Class CliSyntax Class ParserUtil @@ -19,12 +19,12 @@ Class Prefix } Class HiddenOutside #FFFFFF -HiddenOutside ..> AddressBookParser +HiddenOutside ..> SweeBookParser -AddressBookParser .down.> XYZCommandParser: creates > +SweeBookParser .down.> XYZCommandParser: creates > XYZCommandParser ..> XYZCommand : creates > -AddressBookParser ..> Command : returns > +SweeBookParser ..> Command : returns > XYZCommandParser .up.|> Parser XYZCommandParser ..> ArgumentMultimap XYZCommandParser ..> ArgumentTokenizer diff --git a/docs/diagrams/PersonAndTasksClassDiagram.puml b/docs/diagrams/PersonAndTasksClassDiagram.puml new file mode 100644 index 00000000000..e87bd023f5f --- /dev/null +++ b/docs/diagrams/PersonAndTasksClassDiagram.puml @@ -0,0 +1,54 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor MODEL_COLOR +skinparam classBackgroundColor MODEL_COLOR + +Package PersonAndTasks <>{ +Class Person +Class Phone +Class Email +Class Github +Class Telegram +Class "{abstract}\nSocial" as Social + +Class Group + +Class Task +Class Todo +Class Deadline +Class Event +Class Priority +Class Date +Class Description +class RecurringFrequency +} + +Class HiddenOutside #FFFFFF +HiddenOutside --> Person +HiddenOutside -[hidden]- Group +HiddenOutside --> Task + +Task -right-> "1" Group : is for > +Person -left-> "1..2" Group : is in > + +Task --> "0..1" Date +Task --> "1" Description +Task --> " 1" RecurringFrequency +Task --> "1" Priority + +Todo --|> Task +Deadline --|> Task +Event --|> Task + +Person --> "1" Phone +Person --> "1" Email +Person --> "1" Telegram +Person --> "1" Github +Telegram --|> Social +Github --|> Social + +Phone -right[hidden]- Email +Email -right[hidden]- Telegram +Telegram -right[hidden]- Github +@enduml diff --git a/docs/diagrams/RecurringFrequencySequenceDiagram.puml b/docs/diagrams/RecurringFrequencySequenceDiagram.puml new file mode 100644 index 00000000000..e9daab75744 --- /dev/null +++ b/docs/diagrams/RecurringFrequencySequenceDiagram.puml @@ -0,0 +1,52 @@ +@startuml +!include style.puml + +participant ":MainApp" as MainApp LOGIC_COLOR +box Model MODEL_COLOR_T1 +participant ":ModelManager" as ModelManager MODEL_COLOR +participant ":TaskRecords" as TaskRecords MODEL_COLOR +participant ":TaskList" as TaskList MODEL_COLOR +participant "task:Task" as Task MODEL_COLOR +participant "date:Date" as Date MODEL_COLOR +end box + +create MainApp +activate MainApp + +create ModelManager +MainApp-> ModelManager : new ModelManager(initialData,\nuserPrefs, initialTasks) +activate ModelManager + +ModelManager -> TaskRecords : updateRecurringTasks() +activate TaskRecords + +TaskRecords -> TaskList : updateRecurringTaskDates() +activate TaskList + +loop all tasks +opt is a recurring task +TaskList -> Task : updateRecurringTaskDate() +activate Task + +opt is from last week +Task -> Date : getDateForThisWeek() +activate Date +Date --> Task : newDate +deactivate Date +end + +Task -->TaskList +deactivate Task +end +end +TaskList --> TaskRecords +deactivate TaskList + +TaskRecords -->ModelManager +deactivate TaskRecords + +ModelManager --> MainApp +deactivate ModelManager + +deactivate MainApp +@enduml diff --git a/docs/diagrams/RecurringTaskActivityDiagram.puml b/docs/diagrams/RecurringTaskActivityDiagram.puml new file mode 100644 index 00000000000..7d8883eff21 --- /dev/null +++ b/docs/diagrams/RecurringTaskActivityDiagram.puml @@ -0,0 +1,22 @@ +@startuml +start +:User adds Task with recurring frequency; + +:Parse command arguments; + +if () then ([input is valid]) + :Create new task which will recur; + :User restarts application; + if () then ([date of task needs + to be recurred]) + :Date of task is updated to current week/month/year; + stop + else ([else]) + :Task is not updated; + stop + endif +else ([else]) +: Throw ParseException; +stop +endif +@enduml diff --git a/docs/diagrams/SortTaskActivityDiagram.puml b/docs/diagrams/SortTaskActivityDiagram.puml new file mode 100644 index 00000000000..c99111f76ee --- /dev/null +++ b/docs/diagrams/SortTaskActivityDiagram.puml @@ -0,0 +1,22 @@ +@startuml +start +:User enters sortTasks command; +:Parse command word and arguments; + +if () then ([user input is valid]) + :Create sortTasksComparator; + :Create sortTasksCommand + using the comparator; + :Execute sortTasksCommand; + :Reset any filters + set on the task list; + :Set the comparator of + the task list with + the sortTaskComparator, + and sorting it; + :Display sorted task list; +else ([else]) + :Throw \nParseException; + +stop +@enduml diff --git a/docs/diagrams/SortTasksExecutionSequenceDiagram.puml b/docs/diagrams/SortTasksExecutionSequenceDiagram.puml new file mode 100644 index 00000000000..80ba132b70f --- /dev/null +++ b/docs/diagrams/SortTasksExecutionSequenceDiagram.puml @@ -0,0 +1,58 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant "s:SortTasksCommand" as SortTasksCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +box ObservableList UI_COLOR_T2 +participant ":FilteredList" as FilteredList UI_COLOR_T3 +participant ":SortedList" as SortedList UI_COLOR_T3 +end box + + +[-> LogicManager : execute("sortTasks param/desc o/a") +activate LogicManager + +ref over LogicManager, SortTasksCommand : Parses and return s:SortTasksCommand\nwith c:SortTaskComparator + +LogicManager -> SortTasksCommand : execute() +activate SortTasksCommand + +SortTasksCommand -> Model : updateSortedTaskList(c) +activate Model + +Model -> FilteredList : setPredicate(null) +activate FilteredList + +FilteredList --> Model +deactivate FilteredList + +Model -> SortedList : setComparator(c) +activate SortedList + +SortedList --> Model +deactivate SortedList + +Model --> SortTasksCommand +deactivate Model + +create CommandResult +SortTasksCommand -> CommandResult +activate CommandResult + +CommandResult --> SortTasksCommand +deactivate CommandResult + +SortTasksCommand --> LogicManager : result +deactivate SortTasksCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/SortTasksParserSequenceDiagram.puml b/docs/diagrams/SortTasksParserSequenceDiagram.puml new file mode 100644 index 00000000000..96e93c78eb7 --- /dev/null +++ b/docs/diagrams/SortTasksParserSequenceDiagram.puml @@ -0,0 +1,54 @@ +@startuml + +!include style.puml + +mainframe **sd** Parses and return s:SortTasksCommand with c:SortTaskComparator + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":SortTasksCommandParser" as SortTasksCommandParser LOGIC_COLOR +participant "s:SortTasksCommand" as SortTasksCommand LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant "c:SortTaskComparator" as SortTaskComparator MODEL_COLOR +end box + +LogicManager -> AddressBookParser : parseCommand("sortTasks \nparam/desc o/a") +activate AddressBookParser + +create SortTasksCommandParser +AddressBookParser -> SortTasksCommandParser +activate SortTasksCommandParser + +SortTasksCommandParser --> AddressBookParser +deactivate SortTasksCommandParser + +AddressBookParser -> SortTasksCommandParser : parse("param/desc\no/a") +activate SortTasksCommandParser + +create SortTaskComparator +SortTasksCommandParser -> SortTaskComparator : parse("desc", "a") +activate SortTaskComparator + +SortTaskComparator --> SortTasksCommandParser : c +deactivate SortTaskComparator + +create SortTasksCommand +SortTasksCommandParser -> SortTasksCommand : SortTasksCommand(c) +activate SortTasksCommand + +SortTasksCommand --> SortTasksCommandParser : s +deactivate SortTasksCommand + +SortTasksCommandParser --> AddressBookParser : s +deactivate SortTasksCommand +'Hidden arrow to position the destroy marker below the end of the activation bar. +SortTasksCommandParser -[hidden]-> AddressBookParser +destroy SortTasksCommandParser + +AddressBookParser --> LogicManager : s +deactivate AddressBookParser + +@enduml diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml index 85ac3ea2dee..744d20b3a0d 100644 --- a/docs/diagrams/StorageClassDiagram.puml +++ b/docs/diagrams/StorageClassDiagram.puml @@ -14,12 +14,20 @@ Class JsonUserPrefsStorage Interface Storage <> Class StorageManager -package "AddressBook Storage" #F4F6F6{ -Interface AddressBookStorage <> -Class JsonAddressBookStorage -Class JsonSerializableAddressBook + +package "TaskRecordsStorage" #F4F6F6{ +Interface TaskRecordsStorage <> +Class JsonTaskRecordsStorage +Class JsonSerializableTaskRecords +Class JsonAdaptedTask +} + +package "ContactListStorage" #F4F6F6{ +Interface ContactListStorage <> +Class JsonContactListStorage +Class JsonSerializableContactList Class JsonAdaptedPerson -Class JsonAdaptedTag +Class JsonAdaptedGroup } } @@ -29,15 +37,21 @@ HiddenOutside ..> Storage StorageManager .up.|> Storage StorageManager -up-> "1" UserPrefsStorage -StorageManager -up-> "1" AddressBookStorage +StorageManager -up-> "1" ContactListStorage +StorageManager -up-> "1" TaskRecordsStorage Storage -left-|> UserPrefsStorage -Storage -right-|> AddressBookStorage +Storage -right-|> ContactListStorage +Storage -down-|> TaskRecordsStorage JsonUserPrefsStorage .up.|> UserPrefsStorage -JsonAddressBookStorage .up.|> AddressBookStorage -JsonAddressBookStorage ..> JsonSerializableAddressBook -JsonSerializableAddressBook --> "*" JsonAdaptedPerson -JsonAdaptedPerson --> "*" JsonAdaptedTag +JsonContactListStorage .up.|> ContactListStorage +JsonContactListStorage ..> JsonSerializableContactList +JsonSerializableContactList --> "*" JsonAdaptedPerson +JsonAdaptedPerson --> JsonAdaptedGroup + +JsonTaskRecordsStorage .up.|> TaskRecordsStorage +JsonTaskRecordsStorage ..> JsonSerializableTaskRecords +JsonSerializableTaskRecords --> "*" JsonAdaptedTask @enduml diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml index ecae4876432..b5c13c10da3 100644 --- a/docs/diagrams/UiClassDiagram.puml +++ b/docs/diagrams/UiClassDiagram.puml @@ -15,6 +15,8 @@ Class PersonListPanel Class PersonCard Class StatusBarFooter Class CommandBox +Class TaskListPanel +Class TaskCard } package Model <> { @@ -35,8 +37,10 @@ MainWindow *-down-> "1" ResultDisplay MainWindow *-down-> "1" PersonListPanel MainWindow *-down-> "1" StatusBarFooter MainWindow --> "0..1" HelpWindow +MainWindow *-down-> "1" TaskListPanel PersonListPanel -down-> "*" PersonCard +TaskListPanel -down-> "*" TaskCard MainWindow -left-|> UiPart @@ -46,8 +50,10 @@ PersonListPanel --|> UiPart PersonCard --|> UiPart StatusBarFooter --|> UiPart HelpWindow --|> UiPart +TaskListPanel --|> UiPart -PersonCard ..> Model +PersonCard ...> Model +TaskCard ...> Model UiManager -right-> Logic MainWindow -left-> Logic diff --git a/docs/images/AddTaskSequenceDiagram.png b/docs/images/AddTaskSequenceDiagram.png new file mode 100644 index 00000000000..974a4049f18 Binary files /dev/null and b/docs/images/AddTaskSequenceDiagram.png differ diff --git a/docs/images/AddTaskSequenceDiagram1.png b/docs/images/AddTaskSequenceDiagram1.png new file mode 100644 index 00000000000..37d09608219 Binary files /dev/null and b/docs/images/AddTaskSequenceDiagram1.png differ diff --git a/docs/images/ArchitectureSequenceDiagramAddTask.png b/docs/images/ArchitectureSequenceDiagramAddTask.png new file mode 100644 index 00000000000..f01510d1560 Binary files /dev/null and b/docs/images/ArchitectureSequenceDiagramAddTask.png differ diff --git a/docs/images/ArchitectureSequenceDiagramAddTask1.png b/docs/images/ArchitectureSequenceDiagramAddTask1.png new file mode 100644 index 00000000000..f01510d1560 Binary files /dev/null and b/docs/images/ArchitectureSequenceDiagramAddTask1.png differ diff --git a/docs/images/DoneTaskCommandActivityDiagram.png b/docs/images/DoneTaskCommandActivityDiagram.png new file mode 100644 index 00000000000..17d9a2129fa Binary files /dev/null and b/docs/images/DoneTaskCommandActivityDiagram.png differ diff --git a/docs/images/DoneTaskSequenceDiagram.png b/docs/images/DoneTaskSequenceDiagram.png new file mode 100644 index 00000000000..e1ada184ef2 Binary files /dev/null and b/docs/images/DoneTaskSequenceDiagram.png differ diff --git a/docs/images/EditPersonExample1.png b/docs/images/EditPersonExample1.png new file mode 100644 index 00000000000..2e39529d4b3 Binary files /dev/null and b/docs/images/EditPersonExample1.png differ diff --git a/docs/images/EditPersonExample2.png b/docs/images/EditPersonExample2.png new file mode 100644 index 00000000000..61d8816893a Binary files /dev/null and b/docs/images/EditPersonExample2.png differ diff --git a/docs/images/EditTaskExample1.PNG b/docs/images/EditTaskExample1.PNG new file mode 100644 index 00000000000..3d13bf69c18 Binary files /dev/null and b/docs/images/EditTaskExample1.PNG differ diff --git a/docs/images/EditTaskSequenceDiagram.png b/docs/images/EditTaskSequenceDiagram.png new file mode 100644 index 00000000000..7a861ead5f9 Binary files /dev/null and b/docs/images/EditTaskSequenceDiagram.png differ diff --git a/docs/images/FilterTasksScreenshot.png b/docs/images/FilterTasksScreenshot.png new file mode 100644 index 00000000000..0c3e7c61f55 Binary files /dev/null and b/docs/images/FilterTasksScreenshot.png differ diff --git a/docs/images/GroupCommandExample1.png b/docs/images/GroupCommandExample1.png new file mode 100644 index 00000000000..e4497cf8975 Binary files /dev/null and b/docs/images/GroupCommandExample1.png differ diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png index c3028aa1cda..5faa4769bad 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 39d7aec4b33..ea8536d6b0d 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 58ad22ce16a..31a0e38403b 100644 Binary files a/docs/images/ParserClasses.png and b/docs/images/ParserClasses.png differ diff --git a/docs/images/PersonAndTasksClassDiagram.png b/docs/images/PersonAndTasksClassDiagram.png new file mode 100644 index 00000000000..499336cfe22 Binary files /dev/null and b/docs/images/PersonAndTasksClassDiagram.png differ diff --git a/docs/images/QuickStartSS.PNG b/docs/images/QuickStartSS.PNG new file mode 100644 index 00000000000..f3c476d6601 Binary files /dev/null and b/docs/images/QuickStartSS.PNG differ diff --git a/docs/images/RecurringFrequencySequenceDiagram.png b/docs/images/RecurringFrequencySequenceDiagram.png new file mode 100644 index 00000000000..b2508d6610c Binary files /dev/null and b/docs/images/RecurringFrequencySequenceDiagram.png differ diff --git a/docs/images/RecurringTaskActivityDiagram.png b/docs/images/RecurringTaskActivityDiagram.png new file mode 100644 index 00000000000..6451e29972b Binary files /dev/null and b/docs/images/RecurringTaskActivityDiagram.png differ diff --git a/docs/images/SortTasksActivityDiagram.png b/docs/images/SortTasksActivityDiagram.png new file mode 100644 index 00000000000..4dcda76304c Binary files /dev/null and b/docs/images/SortTasksActivityDiagram.png differ diff --git a/docs/images/SortTasksExecutionSequenceDiagram.png b/docs/images/SortTasksExecutionSequenceDiagram.png new file mode 100644 index 00000000000..3b71780ec1a Binary files /dev/null and b/docs/images/SortTasksExecutionSequenceDiagram.png differ diff --git a/docs/images/SortTasksParserSequenceDiagram.png b/docs/images/SortTasksParserSequenceDiagram.png new file mode 100644 index 00000000000..2260844516e Binary files /dev/null and b/docs/images/SortTasksParserSequenceDiagram.png differ diff --git a/docs/images/SortTasksScreenshot.png b/docs/images/SortTasksScreenshot.png new file mode 100644 index 00000000000..f97ba1cc600 Binary files /dev/null and b/docs/images/SortTasksScreenshot.png differ diff --git a/docs/images/StorageClassDiagram1.png b/docs/images/StorageClassDiagram1.png new file mode 100644 index 00000000000..0401de48dd9 Binary files /dev/null and b/docs/images/StorageClassDiagram1.png differ diff --git a/docs/images/SweebookModelClassDiagram.png b/docs/images/SweebookModelClassDiagram.png new file mode 100644 index 00000000000..1b0a07146e9 Binary files /dev/null and b/docs/images/SweebookModelClassDiagram.png differ diff --git a/docs/images/UIDesign.png b/docs/images/UIDesign.png new file mode 100644 index 00000000000..fd9de0e887b Binary files /dev/null and b/docs/images/UIDesign.png differ diff --git a/docs/images/UISS.PNG b/docs/images/UISS.PNG new file mode 100644 index 00000000000..2ab9ddd5df5 Binary files /dev/null and b/docs/images/UISS.PNG differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 91488fd1a0f..6b268c508f1 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 4bb8b2ce591..28a6262463a 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/UserGuideCLIRedBox.png b/docs/images/UserGuideCLIRedBox.png new file mode 100644 index 00000000000..b61c0308114 Binary files /dev/null and b/docs/images/UserGuideCLIRedBox.png differ diff --git a/docs/images/UserGuideGUIRedBox.png b/docs/images/UserGuideGUIRedBox.png new file mode 100644 index 00000000000..5f61efd4198 Binary files /dev/null and b/docs/images/UserGuideGUIRedBox.png differ diff --git a/docs/images/addTaskExample1.PNG b/docs/images/addTaskExample1.PNG new file mode 100644 index 00000000000..748c0d82723 Binary files /dev/null and b/docs/images/addTaskExample1.PNG differ diff --git a/docs/images/addTaskExample2.PNG b/docs/images/addTaskExample2.PNG new file mode 100644 index 00000000000..1477c6683c3 Binary files /dev/null and b/docs/images/addTaskExample2.PNG differ diff --git a/docs/images/addTaskExample3.PNG b/docs/images/addTaskExample3.PNG new file mode 100644 index 00000000000..b37d0f3ca33 Binary files /dev/null and b/docs/images/addTaskExample3.PNG differ diff --git a/docs/images/alinaleehx.png b/docs/images/alinaleehx.png new file mode 100644 index 00000000000..10232522765 Binary files /dev/null and b/docs/images/alinaleehx.png differ diff --git a/docs/images/ambroseboo.png b/docs/images/ambroseboo.png new file mode 100644 index 00000000000..c77dce4817f Binary files /dev/null and b/docs/images/ambroseboo.png differ diff --git a/docs/images/codeReuseHyperlink.png b/docs/images/codeReuseHyperlink.png new file mode 100644 index 00000000000..b63fe0eaf74 Binary files /dev/null and b/docs/images/codeReuseHyperlink.png differ diff --git a/docs/images/codeReuseRegex.png b/docs/images/codeReuseRegex.png new file mode 100644 index 00000000000..d18ffb62d5c Binary files /dev/null and b/docs/images/codeReuseRegex.png differ diff --git a/docs/images/cyyeu.png b/docs/images/cyyeu.png new file mode 100644 index 00000000000..dc75126ebe3 Binary files /dev/null and b/docs/images/cyyeu.png differ diff --git a/docs/images/deleteTaskCommandExample.png b/docs/images/deleteTaskCommandExample.png new file mode 100644 index 00000000000..8b35e53335f Binary files /dev/null and b/docs/images/deleteTaskCommandExample.png differ diff --git a/docs/images/doneTaskCommandExample.png b/docs/images/doneTaskCommandExample.png new file mode 100644 index 00000000000..104267c5de4 Binary files /dev/null and b/docs/images/doneTaskCommandExample.png differ diff --git a/docs/images/findAlexDavidResult.png b/docs/images/findAlexDavidResult.png index 235da1c273e..91fe624f3bd 100644 Binary files a/docs/images/findAlexDavidResult.png and b/docs/images/findAlexDavidResult.png differ diff --git a/docs/images/findAlexResult.png b/docs/images/findAlexResult.png new file mode 100644 index 00000000000..676a51aa513 Binary files /dev/null and b/docs/images/findAlexResult.png differ diff --git a/docs/images/github.png b/docs/images/github.png new file mode 100644 index 00000000000..4d639db6e2c Binary files /dev/null and b/docs/images/github.png differ diff --git a/docs/images/groupCs2101.png b/docs/images/groupCs2101.png new file mode 100644 index 00000000000..b997129831c Binary files /dev/null and b/docs/images/groupCs2101.png differ diff --git a/docs/images/groupCs2103t.png b/docs/images/groupCs2103t.png new file mode 100644 index 00000000000..7a5617b9f0e Binary files /dev/null and b/docs/images/groupCs2103t.png differ diff --git a/docs/images/helpMessage.png b/docs/images/helpMessage.png index b1f70470137..f3103122471 100644 Binary files a/docs/images/helpMessage.png and b/docs/images/helpMessage.png differ diff --git a/docs/images/josephn37.png b/docs/images/josephn37.png new file mode 100644 index 00000000000..fbfb9abba8d Binary files /dev/null and b/docs/images/josephn37.png differ diff --git a/docs/images/mail.png b/docs/images/mail.png new file mode 100644 index 00000000000..e2bf04afdf1 Binary files /dev/null and b/docs/images/mail.png differ diff --git a/docs/images/tele.jpeg b/docs/images/tele.jpeg new file mode 100644 index 00000000000..d26742fde15 Binary files /dev/null and b/docs/images/tele.jpeg differ diff --git a/docs/images/zhenxuantan.png b/docs/images/zhenxuantan.png new file mode 100644 index 00000000000..0a2dd68cffb Binary files /dev/null and b/docs/images/zhenxuantan.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..9f0bf644ffe 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,19 +1,20 @@ --- layout: page -title: AddressBook Level-3 +title: SWEe-book --- -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) -[![codecov](https://codecov.io/gh/se-edu/addressbook-level3/branch/master/graph/badge.svg)](https://codecov.io/gh/se-edu/addressbook-level3) +[![CI Status](https://github.com/AY2122S1-CS2103T-W12-2/tp/actions/workflows/gradle.yml/badge.svg)](https://github.com/AY2122S1-CS2103T-W12-2/tp/actions/workflows/gradle.yml) +[![codecov](https://codecov.io/gh/AY2122S1-CS2103T-W12-2/tp/branch/master/graph/badge.svg?token=MORDANUUIH)](https://codecov.io/gh/AY2122S1-CS2103T-W12-2/tp) ![Ui](images/Ui.png) -**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). +**SWEe-book is a project management tool targeted for CS2103T students.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). -* If you are interested in using AddressBook, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). -* If you are interested about developing AddressBook, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. +* If you are interested in using SWEe-book, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). +* If you are interested about developing SWEe-book, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. **Acknowledgements** * Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5) +* This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org). diff --git a/docs/team/JosephN37.md b/docs/team/JosephN37.md new file mode 100644 index 00000000000..d374b5bbb96 --- /dev/null +++ b/docs/team/JosephN37.md @@ -0,0 +1,36 @@ +--- +layout: page +title: Joseph's Project Portfolio Page +--- + +# Project: SWEe-book + +SWEe-book is a desktop application used for contact and task management pertaining to CS2103T and CS2101 module. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java. + +Given below are my contributions to the project. + +* **New Feature**: Added FilterTasks command to filter tasks according to specified criterion. + * What it does: Filters the task list according to the argument input (date, tasktype, priority, description, group) + * Justification: This feature improves the product significantly because a user can now filter and look through the tasks that they desire, for instance the list of tasks for a specific date, or one which has specific keywords. + * Highlights: Since this enhancement allows user to filter tasks according to specified word / phrase, FilterTasks essentially encompasses a search function too + * Credits: + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/?search=w12-2&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-17&tabOpen=true&tabType=authorship&tabAuthor=simonjulianl&tabRepo=AY2122S1-CS2103T-T15-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false&zFR=false) + +* **Project management**: + * Managed releases `v1.3` (1 release) on GitHub + +* **Enhancements to existing features**: + + +* **Documentation**: + * User Guide: + * Added documentation for the feature `FilterTasks`. + * Cleared Address book remnants in the User Guide. + * Developer Guide: + * Added UML diagram to illustrate AddTask command, and for storage component. + * Modified existing UML diagrams from Address book to suit SWEe-book. + +* **Community**: + * PRs reviewed (with non-trivial review comments): [\#123](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/123) + diff --git a/docs/team/alinaleehx.md b/docs/team/alinaleehx.md new file mode 100644 index 00000000000..7df1d60fa82 --- /dev/null +++ b/docs/team/alinaleehx.md @@ -0,0 +1,58 @@ +--- +layout: page +title: Alina Lee's Project Portfolio Page +--- +### Project: SWEe-book + +SWEe-book is a desktop application used for contact and task management pertaining to CS2103T and CS2101 module. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java. + +Given below are my contributions to the project. + +* **Enhancements to existing features**: + * Update test data for tasks. [#47](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/47) + * Add additional tests. [#66](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/66) + +* **New Feature**: Added internal features like tasks, group, taskList, storage for tasks and more to support task management in the app SWEe-book. [#5](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/5) + * What it does: Created the internal features necessary for all task commands and task management in SWEe-book. + * Justification: This allows for development of features related to task management and is hence essential. + * Highlights: All future commands related to task management relies on this. + +* **New Feature**: Add the ability to add 3 different types of tasks. [#5](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/5) [#42](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/42) + * What it does: allows the user to add 3 different types of tasks, namely todo, event and deadline specifying date, group and task description. + * Justification: This feature allows the user to add and keep track of the tasks that needs to be done for each group by a certain date. + * Highlights: This allows 3 different types of tasks which likely covers most user's needs for task types, and + provides the base for other future features like sorting and filtering tasks and is a basic but integral part of task management in SWEe-book. + +* **New Feature**: Added the ability to delete a task. [#39](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/39) + * What it does: allows the user to delete a task already present in the task list, namely by keying the index of that task to be deleted. + * Justification: This feature improves the product significantly because a user can make mistakes when using addTask + command and this feature provides a convenient way to rectify these mistakes by allowing the user to delete the tasks that are added by mistake or no longer needed. + * Highlights: This allows users to delete tasks and is a key feature of task management in SWEe-book. + +* **New Feature**: Added the ability to mark a task as done. [#69](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/69) [#123](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/123) + * What it does: allows the user to mark a task already present in the task list as done, namely by keying the index of that task to be marked as done. + * Justification: This feature allows the user to keep track of which tasks are done or not yet done. + * Highlights: Task List UI is updated synchronously when task is marked as done. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-17&tabOpen=true&tabType=authorship&tabAuthor=alinaleehx&tabRepo=AY2122S1-CS2103T-W12-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false) + +* **Project management**: + * Reviewed and successfully 3 pull requests into the team's repository. [#122](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/122) + [#73](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/73) [#67](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/67) + +* **Documentation**: + * User Guide: + * Added documentation for `addTask` command [#47](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/47) + * Added documentation for `deleteTasks` command [#47](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/47) + * Added documentation for `doneTasks` command [#69](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/69) + * Did cosmetic tweaks to existing documentation of features `help` [#125](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/125) + * Added UI mockup for initial user guide [#20](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/20) + + * Developer Guide: + * Added documentation for `addTasks` command and `deleteTasks` command [#47](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/47) + * Added documentation for `doneTasks` command [#69](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/69) + [#149](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/149) + [#151](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/151) + +* **Community**: + * Helped to spot bugs in Practical Examinations - Dry Run (PE-D) [Issues](https://github.com/alinaleehx/ped/issues) diff --git a/docs/team/ambroseboo.md b/docs/team/ambroseboo.md new file mode 100644 index 00000000000..78a4507ae89 --- /dev/null +++ b/docs/team/ambroseboo.md @@ -0,0 +1,138 @@ +--- +layout: page +title: Ambrose's Project Portfolio Page +--- + +### Project: SWEe-book + +SWEe-book is a desktop application used for contact and task management pertaining to CS2103T and CS2101 module. The +user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java. + +Given below are my contributions to the project. + +* **Enhancements to existing features**: + * Implement listTasks to reflect in GUI, so that both contacts and tasks are simultaneously displayed [#49](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/49) + +* **New Feature**: Added the ability to list all tasks added. [#37](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/37), [#46](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/46), [#50](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/50) + * What it does: Allows the user to list all tasks that were previously added to the project. + * Justification: This feature allows users to view their added tasks, and serves as a start point fetch data for + filterTasks and also the tasks appearing on the UI on startup. + * Highlights: Upon the editing of the UI to include the task section, the tasks are reflected in the GUI rather than + the CLI. + +* **New Feature**: Added the ability to add recurring frequencies to tasks. [#59](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/59) + * What it does: Allows the user to add recurring frequencies to their tasks which can be weekly, monthly, or yearly + frequencies. For weekly, + the tasks are repeated on the same day of the week, for monthly the tasks are repeated by the date (DD) every + month, for yearly the tasks are repeated by the DD/MM every year. + * Justification: This feature allows the user to add tasks that can repeat themselves, so that the tasks can update + themselves automatically. + * Highlights: The tasks are updated on the startup of the application, ensuring they are always updated whenever the + user opens the application. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/?search=CS2103T-W12-2&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-17&tabOpen=true&tabType=zoom&tabAuthor=zhenxuantan&tabRepo=AY2122S1-CS2103T-W12-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false&zA=ambroseboo&zR=AY2122S1-CS2103T-W12-2%2Ftp%5Bmaster%5D&zACS=198.05172413793105&zS=2021-09-17&zFS=CS2103T-W12-2&zU=2021-11-07&zMG=false&zFTF=commit&zFGS=groupByRepos&zFR=false&until=2021-11-07) + +* **Project management**: + * Merged and fixed style errors on pull request #5 and #139 into the team's repository + +* **Documentation**: + * User Guide: + * Added initial documentation for `addTask`, `deleteTask`, `listTasks`, `filterTasks`, `sortTasks` + * Added full documentation for `listTasks` + * Updated command summary + * Updated documentation for `addTask` (explain recurring field), add screenshots + * Developer Guide: + * Added documentation for implementation of `recurring` feature in `Task` + * Including sequence diagram and activity diagram for recurring frequency + +* **Community**: + * Helped to spot bugs in Practical Examinations - Dry Run (PE-D) [Issues](https://github.com/ambroseboo/ped/issues) + +
+ +## **Contributions to the Developer Guide (Extracts)**: +### Recurring Tasks feature +#### Implementation +The recurring task feature allows users to add tasks that can be repeated by week, month, or year. It is facilitated +by `RecurringFrequency`, which is a optional component of `Task`. Additionally, the following operations are implemented +in `Task`, `TaskList`, `TaskRecords` and `Date`: +* `Task#updateRecurringTaskDate()` - Task updates its Date to the current week/month/year, based on the + recurringFrequency of the Task. +* `Date#isLastWeek()`, `Date#isLastMonth()`, `Date#isLastYear()` - Checks current Date of Task against real-time date. +* `Date#getDateForThisWeek()`, `Date#getDateForThisMonth()`, `Date#getDateForThisYear()` - Updates Date to be within + current week/month/year. +* `TaskList#updateRecurringTasksDates()` - Iterates through list of Tasks and updates its Date if Task is recurring. +* `TaskRecords#updateRecurringTasks()` - Calls for TaskList to update recurring Tasks. + +`TaskRecords#updateRecurringTasks()` is used in the `ModelManager` on boot-up of the application to update all Tasks, if +required. Below is a sequence diagram after the initialisation of the `ModelManager`: + +![Seq-diagram for updating dates of recurring Tasks after start up](../images/RecurringFrequencySequenceDiagram.png) + +Do note that `Date` is required for a `Task` to be recurring. Notably, `Date` is optional for `Todo`. + +
+ +Given below is an example usage scenario of how a recurring task is added and how it behaves upon re-launching of +the SWEe-book application. + +* Step 1. The user launches the application. A recurring `Task` is added, where the user specifies the + `recurringFrequency` to be weekly, and the `Date` to be from the previous week. The `Task` is added, but the `Date` is + not updated yet, even if it is not of the current week. The `recurringFrequency` of the task is marked as `week`. +* Step 2. The user re-launches the application. `ModelManager` calls `TaskRecords#updateRecurringTasks()`, which then + calls `TaskList#updateRecurringTasksDates()`, which then calls `Task#updateRecurringTaskDate()` on the task added. + Since the `Task` added was recurring (its `recurringFrequency` is marked as `week`, its `Date` is updated to the + current week, with the same day. +* Step 3. The user then launches the application a week after. The `Task` is updated similarly to Step 2, and since it + is checked against real-time, it is updated to the current week. + +Given below is an activity diagram when a user adds a recurring task and restarts his SWEe-book application. + +![Activity-diagram for user adding recurring Task](../images/RecurringTaskActivityDiagram.png) + +
+ +#### Alternative considerations +* Alternative 1: Let the user choose when to refresh his tasks to their new dates, rather than on start-up of the + application. + * Pros: Allows user more control over their recurring tasks + * Cons: Less intuitive since tasks are not updated to real-time, having a refresh command just for recurring tasks + is not ideal. + +
+ +## **Contributions to the User Guide (Extracts)**: +#### Adding a Task : `addTask` + +Adds a new task to SWEe-book. + +Format: `addTask d/DESCRIPTION g/GROUP type/TYPE [date/DATE] [pty/PRIORITY] [recurring/RECURRING_FREQUENCY]` +* `GROUP` refers to one of the 2 groups: `CS2101` or `CS2103T` +* `TYPE` refers to one of the 3 types of tasks: `todo`, `event` or `deadline` +* `DATE` is in YYYY-MM-DD format and is only needed for events or deadlines (i.e. `DATE` is optional for Todo tasks) +* `PRIORITY` refers to one of the 3 levels of priorities / importance of the task: `low`, `med` (default) or `high` +* `RECURRING_FREQUENCY` refers to one of the 3 different frequencies that the task could occur: `week`, `month` or + `year` (where `week` means that the task is recurring weekly) + * Any Task that has a recurring frequency must have a date as well, for example a Todo with recurring frequency must + have a date. + +
+ +Examples: +* `addTask d/Project meeting g/CS2103T type/todo pty/low` + * Add a non-recurring `todo` with no date and `low` priority and + task description `Project meeting` to the group `CS2103T` + ![result for 'addTask d/Project meeting g/CS2103T type/todo pty/low'](../images/addTaskExample1.PNG) + +* `addTask d/Presentation 1 g/CS2101 type/deadline date/2020-11-02 pty/high` + * Add a non-recurring `deadline` due on `2020-11-02` + with `high` priority and task description `Presentation 1`to the group `CS2101` + ![result for 'addTask d/Presentation 1 g/CS2101 type/deadline date/2020-11-02 pty/high'](../images/addTaskExample2.PNG) + +
+ +* `addTask d/Mock QnA 1 g/CS2101 type/event date/2020-10-02 recurring/month` + * Add a `event` that recurs every `month` with + date `2020-10-02` and default `med` priority and task description `Mock QnA` to the group `CS2101` + ![result for 'addTask d/Mock QnA 1 g/CS2101 type/event date/2020-10-02 recurring/month'](../images/addTaskExample3.PNG) + diff --git a/docs/team/cyyeu.md b/docs/team/cyyeu.md new file mode 100644 index 00000000000..2bf41746d17 --- /dev/null +++ b/docs/team/cyyeu.md @@ -0,0 +1,46 @@ +--- +layout: page +title: Chen Yuan's Project Portfolio Page +--- + +### Project: SWEe-book + +SWEe-book is a desktop application used for contact and task management pertaining to CS2103T and CS2101 module. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java. + +Given below are my contributions to the project. + + +* **Enhancements to existing features**: + * Removed address and tag fields from Person as they were irrelevant to our product. + * Added Telegram And Github, Group fields into Person. + * Refactored the `add` and `edit` command to reflect both of the above changes + +* **New Feature**: Added the ability to edit tasks. + * What it does: allows the user to choose a task from the list in the UI, and edit any number of fields of this task. + * Justification: This feature removes the hassle of the need to delete and add a new task, just to change one field of a task. + * Highlights: Wrote tests to cover the new feature + +* **New Feature**: Added the ability to filter people by group. + * What it does: allows the user to filter the list of people by group (CS2103T or CS2101) + * Justification: This feature allows the user to have an overview of their groupmates from a specific group, should there be a need to declutter the contact list. + * Highlights: Refactored and add new tests to cover the deleted (address and tag) and newly added fields (github and telegram and group). + + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-17&tabOpen=true&tabType=authorship&tabAuthor=cyyeu&tabRepo=AY2122S1-CS2103T-W12-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false) + +* **Project management**: + * Managed releases `v1.1` - `v1.2` (2 releases) on GitHub + * Merged 21/75 pull requests + * Set up GitHub repo and Codecov + +* **Documentation**: + * User Guide: + * Added documentation for the features `editTask` command + * Update documentation for the refactored `add` and `edit` commands + * Developer Guide: + * Added implementation details of the `edit` feature. + * Updated documentation for the model component to reflect latest changes + * Add use case for `editTask` feature + +* **Community**: + * Helped to spot bugs in Practical Examinations - Dry Run (PE-D) [Issues](https://github.com/cyyeu/ped/issues) diff --git a/docs/team/zhenxuantan.md b/docs/team/zhenxuantan.md new file mode 100644 index 00000000000..99ecc027d56 --- /dev/null +++ b/docs/team/zhenxuantan.md @@ -0,0 +1,64 @@ +--- +layout: page +title: Zhen Xuan's Project Portfolio Page +--- + +## **Project: SWEe-book** + +### **Overview** + +SWEe-book is a desktop application used for contact and task management pertaining to CS2103T and CS2101 module. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java. + +Given below are my contributions to the project. + +**Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/?search=CS2103T-W12-2&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-17&tabOpen=true&tabType=zoom&tabAuthor=zhenxuantan&tabRepo=AY2122S1-CS2103T-W12-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false&zA=zhenxuantan&zR=AY2122S1-CS2103T-W12-2%2Ftp%5Bmaster%5D&zACS=198.05172413793105&zS=2021-09-17&zFS=CS2103T-W12-2&zU=2021-11-05&zMG=false&zFTF=commit&zFGS=groupByRepos&zFR=false&until=2021-11-05) + +### **Enhancements to existing features** + +* Improved Ui for contact list and added Ui for task list [#48](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/48) +* Removed all references to pre-existing project ([AB-3](https://se-education.org/addressbook-level3/)) [#127](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/127) +* Wrote tests for the internal features like Task, Group, TaskList and Date [#132](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/132) + +### **New Features added** + +#### **Added the ability to sort the tasks in 8 different ways.** [#38](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/38), [#46](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/46), [$50](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/50) + +* What it does: allows the user to sort the tasks based on their description, group, priority, and (due) date in two different orders: ascending order and descending order. +* Justification: This feature allows the user to navigate through their tasks easily, giving them a broad overview of what is important or urgent instead of having to scroll through the whole task list. +* Highlights: Reflected changes in Ui and allowed users to perform other commands like delete task while having the task list sorted, it also allows potential future commands like sorting and filtering simultaneously or sorting via multiple variables to be implemented easily. + +
+ +#### **Added the ability to add priorities to tasks.** [#64](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/64) + +* What it does: allows the user to add priorities to their tasks which includes high, medium or low priorities. +* Justification: This feature allows the user to recognise the importance of their tasks without having to read the whole task list. +* Highlights: Allowed the level of priority to change the colour of the alert in the Ui, utilising code to change the Ui based on the type of priority of the task. + +#### **Allowed date, priority and recurring frequency to be optional.** [#64](https://github.com/AY2122S1-CS2103T-W12-2/tp/pull/64) + +* What it does: allows users to add tasks without having to type date (todo task only), priority or recurring frequency. +* Justification: This feature allows the user to add tasks without the hassle of typing too many parameters by making tasks having the default priority of medium and default recurring frequency of none. +* Highlights: This enhancement allows future parameters or fields to be optional as well. + +### **Project Management** + +* Merged 28 out of 79 successfully merged pull requests into the team's repository + +### **Documentation** + +#### **User Guide** + +* Added documentation for `sortTasks` command +* Added documentation for `priority` feature +* Added screenshots for `sortTasks` and `filterTasks` command + +#### **Developer Guide** + +* Added documentation for `sortTasks` feature and how it links between different components: Logic, Model and Ui +* Updated the screenshots to suit SWEe-book +* Added use cases + +### **Community** + +* Helped to spot bugs in Practical Examinations - Dry Run (PE-D) See [here](https://github.com/zhenxuantan/ped/issues) diff --git a/docs/tutorials/AddRemark.md b/docs/tutorials/AddRemark.md index 8919d8eaa17..df9506ef264 100644 --- a/docs/tutorials/AddRemark.md +++ b/docs/tutorials/AddRemark.md @@ -25,7 +25,7 @@ For now, let’s keep `RemarkCommand` as simple as possible and print some outpu ``` java package seedu.address.logic.commands; -import seedu.address.model.Model; +import 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 CollectionUtil.requireAllNonNull; //... public class RemarkCommand extends Command { //... @@ -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-0c6b6abcfac8c205e075294f25e851fe). +Simply add the following to [`PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-0c6b6abcfac8c205e075294f25e851fe). **`PersonCard.java`:** diff --git a/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md index 4fb62a83ef6..28e57fdb7f8 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 `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 `Logic` (an extract given below) confirms that this indeed might be what we’re looking for. ```java public interface Logic { diff --git a/img.png b/img.png new file mode 100644 index 00000000000..ea84bfae3bb Binary files /dev/null and b/img.png differ 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/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/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/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 0650c954f5c..00000000000 --- a/src/main/java/seedu/address/model/ModelManager.java +++ /dev/null @@ -1,151 +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) { - super(); - 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/person/Address.java b/src/main/java/seedu/address/model/person/Address.java deleted file mode 100644 index 60472ca22a0..00000000000 --- a/src/main/java/seedu/address/model/person/Address.java +++ /dev/null @@ -1,57 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's address in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)} - */ -public class Address { - - public static final String MESSAGE_CONSTRAINTS = "Addresses can take any values, and it should not be blank"; - - /* - * The first character of the address must not be a whitespace, - * otherwise " " (a blank string) becomes a valid input. - */ - public static final String VALIDATION_REGEX = "[^\\s].*"; - - public final String value; - - /** - * Constructs an {@code Address}. - * - * @param address A valid address. - */ - public Address(String address) { - requireNonNull(address); - checkArgument(isValidAddress(address), MESSAGE_CONSTRAINTS); - value = address; - } - - /** - * Returns true if a given string is a valid email. - */ - public static boolean isValidAddress(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Address // instanceof handles nulls - && value.equals(((Address) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/person/Name.java deleted file mode 100644 index 79244d71cf7..00000000000 --- a/src/main/java/seedu/address/model/person/Name.java +++ /dev/null @@ -1,59 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's name in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} - */ -public class Name { - - public static final String MESSAGE_CONSTRAINTS = - "Names should only contain alphanumeric characters and spaces, and it should not be blank"; - - /* - * The first character of the address must not be a whitespace, - * otherwise " " (a blank string) becomes a valid input. - */ - public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; - - public final String fullName; - - /** - * Constructs a {@code Name}. - * - * @param name A valid name. - */ - public Name(String name) { - requireNonNull(name); - checkArgument(isValidName(name), MESSAGE_CONSTRAINTS); - fullName = name; - } - - /** - * Returns true if a given string is a valid name. - */ - public static boolean isValidName(String test) { - return test.matches(VALIDATION_REGEX); - } - - - @Override - public String toString() { - return fullName; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Name // instanceof handles nulls - && fullName.equals(((Name) other).fullName)); // state check - } - - @Override - public int hashCode() { - return fullName.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java deleted file mode 100644 index 8ff1d83fe89..00000000000 --- a/src/main/java/seedu/address/model/person/Person.java +++ /dev/null @@ -1,123 +0,0 @@ -package seedu.address.model.person; - -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import seedu.address.model.tag.Tag; - -/** - * Represents a Person in the address book. - * Guarantees: details are present and not null, field values are validated, immutable. - */ -public class Person { - - // Identity fields - private final Name name; - private final Phone phone; - private final Email email; - - // Data fields - private final Address address; - private final Set tags = new HashSet<>(); - - /** - * Every field must be present and not null. - */ - public Person(Name name, Phone phone, Email email, Address address, Set tags) { - requireAllNonNull(name, phone, email, address, tags); - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - this.tags.addAll(tags); - } - - public Name getName() { - return name; - } - - public Phone getPhone() { - return phone; - } - - public Email getEmail() { - return email; - } - - public Address getAddress() { - return address; - } - - /** - * Returns an immutable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - */ - public Set getTags() { - return Collections.unmodifiableSet(tags); - } - - /** - * Returns true if both persons have the same name. - * This defines a weaker notion of equality between two persons. - */ - public boolean isSamePerson(Person otherPerson) { - if (otherPerson == this) { - return true; - } - - return otherPerson != null - && otherPerson.getName().equals(getName()); - } - - /** - * Returns true if both persons have the same identity and data fields. - * This defines a stronger notion of equality between two persons. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof Person)) { - return false; - } - - Person otherPerson = (Person) other; - return otherPerson.getName().equals(getName()) - && otherPerson.getPhone().equals(getPhone()) - && otherPerson.getEmail().equals(getEmail()) - && otherPerson.getAddress().equals(getAddress()) - && otherPerson.getTags().equals(getTags()); - } - - @Override - public int hashCode() { - // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append(getName()) - .append("; Phone: ") - .append(getPhone()) - .append("; Email: ") - .append(getEmail()) - .append("; Address: ") - .append(getAddress()); - - Set tags = getTags(); - if (!tags.isEmpty()) { - builder.append("; Tags: "); - tags.forEach(builder::append); - } - return builder.toString(); - } - -} diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java deleted file mode 100644 index b0ea7e7dad7..00000000000 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ /dev/null @@ -1,54 +0,0 @@ -package seedu.address.model.tag; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Tag in the address book. - * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)} - */ -public class Tag { - - public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric"; - public static final String VALIDATION_REGEX = "\\p{Alnum}+"; - - public final String tagName; - - /** - * Constructs a {@code Tag}. - * - * @param tagName A valid tag name. - */ - public Tag(String tagName) { - requireNonNull(tagName); - checkArgument(isValidTagName(tagName), MESSAGE_CONSTRAINTS); - this.tagName = tagName; - } - - /** - * Returns true if a given string is a valid tag name. - */ - public static boolean isValidTagName(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Tag // instanceof handles nulls - && tagName.equals(((Tag) other).tagName)); // state check - } - - @Override - public int hashCode() { - return tagName.hashCode(); - } - - /** - * Format state as text for viewing. - */ - public String toString() { - return '[' + tagName + ']'; - } - -} diff --git a/src/main/java/seedu/address/model/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/JsonAdaptedTag.java b/src/main/java/seedu/address/storage/JsonAdaptedTag.java deleted file mode 100644 index 0df22bdb754..00000000000 --- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java +++ /dev/null @@ -1,48 +0,0 @@ -package seedu.address.storage; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.tag.Tag; - -/** - * Jackson-friendly version of {@link Tag}. - */ -class JsonAdaptedTag { - - private final String tagName; - - /** - * Constructs a {@code JsonAdaptedTag} with the given {@code tagName}. - */ - @JsonCreator - public JsonAdaptedTag(String tagName) { - this.tagName = tagName; - } - - /** - * Converts a given {@code Tag} into this class for Jackson use. - */ - public JsonAdaptedTag(Tag source) { - tagName = source.tagName; - } - - @JsonValue - public String getTagName() { - return tagName; - } - - /** - * Converts this Jackson-friendly adapted tag object into the model's {@code Tag} object. - * - * @throws IllegalValueException if there were any data constraints violated in the adapted tag. - */ - public Tag toModelType() throws IllegalValueException { - if (!Tag.isValidTagName(tagName)) { - throw new IllegalValueException(Tag.MESSAGE_CONSTRAINTS); - } - return new Tag(tagName); - } - -} diff --git a/src/main/java/seedu/address/storage/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/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/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java deleted file mode 100644 index 79868290974..00000000000 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ /dev/null @@ -1,79 +0,0 @@ -package seedu.address.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; - -/** - * Manages storage of AddressBook data in local storage. - */ -public class StorageManager implements Storage { - - private static final Logger logger = LogsCenter.getLogger(StorageManager.class); - private AddressBookStorage addressBookStorage; - private UserPrefsStorage userPrefsStorage; - - /** - * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}. - */ - public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) { - super(); - this.addressBookStorage = addressBookStorage; - this.userPrefsStorage = userPrefsStorage; - } - - // ================ UserPrefs methods ============================== - - @Override - public Path getUserPrefsFilePath() { - return userPrefsStorage.getUserPrefsFilePath(); - } - - @Override - public Optional readUserPrefs() throws DataConversionException, IOException { - return userPrefsStorage.readUserPrefs(); - } - - @Override - public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { - userPrefsStorage.saveUserPrefs(userPrefs); - } - - - // ================ AddressBook methods ============================== - - @Override - public Path getAddressBookFilePath() { - return addressBookStorage.getAddressBookFilePath(); - } - - @Override - public Optional readAddressBook() throws DataConversionException, IOException { - return readAddressBook(addressBookStorage.getAddressBookFilePath()); - } - - @Override - public Optional readAddressBook(Path filePath) throws DataConversionException, IOException { - logger.fine("Attempting to read data from file: " + filePath); - return addressBookStorage.readAddressBook(filePath); - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath()); - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { - logger.fine("Attempting to write to data file: " + filePath); - addressBookStorage.saveAddressBook(addressBook, filePath); - } - -} diff --git a/src/main/java/seedu/address/AppParameters.java b/src/main/java/sweebook/AppParameters.java similarity index 93% rename from src/main/java/seedu/address/AppParameters.java rename to src/main/java/sweebook/AppParameters.java index ab552c398f3..197acb9f5da 100644 --- a/src/main/java/seedu/address/AppParameters.java +++ b/src/main/java/sweebook/AppParameters.java @@ -1,4 +1,4 @@ -package seedu.address; +package sweebook; 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 sweebook.commons.core.LogsCenter; +import sweebook.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/sweebook/Main.java similarity index 97% rename from src/main/java/seedu/address/Main.java rename to src/main/java/sweebook/Main.java index 052a5068631..50bea1f9cf8 100644 --- a/src/main/java/seedu/address/Main.java +++ b/src/main/java/sweebook/Main.java @@ -1,4 +1,4 @@ -package seedu.address; +package sweebook; import javafx.application.Application; diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/sweebook/MainApp.java similarity index 50% rename from src/main/java/seedu/address/MainApp.java rename to src/main/java/sweebook/MainApp.java index 4133aaa0151..11d7cf3e49c 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/sweebook/MainApp.java @@ -1,35 +1,40 @@ -package seedu.address; +package sweebook; import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Optional; import java.util.logging.Logger; 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 sweebook.commons.core.Config; +import sweebook.commons.core.LogsCenter; +import sweebook.commons.core.Version; +import sweebook.commons.exceptions.DataConversionException; +import sweebook.commons.util.ConfigUtil; +import sweebook.commons.util.StringUtil; +import sweebook.logic.Logic; +import sweebook.logic.LogicManager; +import sweebook.model.ContactList; +import sweebook.model.Model; +import sweebook.model.ModelManager; +import sweebook.model.ReadOnlyContactList; +import sweebook.model.ReadOnlyTaskRecords; +import sweebook.model.ReadOnlyUserPrefs; +import sweebook.model.TaskRecords; +import sweebook.model.UserPrefs; +import sweebook.model.util.SampleDataUtil; +import sweebook.storage.ContactListStorage; +import sweebook.storage.JsonContactListStorage; +import sweebook.storage.JsonTaskRecordsStorage; +import sweebook.storage.JsonUserPrefsStorage; +import sweebook.storage.Storage; +import sweebook.storage.StorageManager; +import sweebook.storage.TaskRecordsStorage; +import sweebook.storage.UserPrefsStorage; +import sweebook.ui.Ui; +import sweebook.ui.UiManager; /** * Runs the application. @@ -48,7 +53,7 @@ public class MainApp extends Application { @Override public void init() throws Exception { - logger.info("=============================[ Initializing AddressBook ]==========================="); + logger.info("=============================[ Initializing SWEe-Book ]==========================="); super.init(); AppParameters appParameters = AppParameters.parse(getParameters()); @@ -56,41 +61,79 @@ 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); + ContactListStorage contactListStorage = new JsonContactListStorage(userPrefs.getContactListFilePath()); + TaskRecordsStorage taskRecordsStorage = new JsonTaskRecordsStorage(userPrefs.getTaskRecordsFilePath()); + storage = new StorageManager(contactListStorage, userPrefsStorage, taskRecordsStorage); initLogging(config); - model = initModelManager(storage, userPrefs); + ArrayList warnings = new ArrayList<>(); + + model = initModelManager(storage, userPrefs, warnings); logic = new LogicManager(model, storage); - ui = new UiManager(logic); + ui = new UiManager(logic, warnings); + } /** - * 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 contact list, task records, + * and {@code userPrefs}.
+ * The data from the sample contact list or task records will be used instead if {@code storage}'s + * contact list or task records are not found, or an empty contact list or task records will be used + * instead if errors occur when reading {@code storage}'s contact list or task records. */ - private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { - Optional addressBookOptional; - ReadOnlyAddressBook initialData; + private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs, ArrayList warnings) { + Optional contactListOptional; + Optional taskRecordsOptional; + ReadOnlyContactList initialData; + ReadOnlyTaskRecords initialTasks; + try { + contactListOptional = storage.readContactList(); + if (!contactListOptional.isPresent()) { + String message = "Contact list data file not found. Will be starting with a sample contact list"; + warnings.add(message); + logger.info(message); + } + initialData = contactListOptional.orElseGet(SampleDataUtil::getSampleContactList); + } catch (DataConversionException e) { + String message = "Contact list data file is not in the correct format. " + + "Will be starting with an empty contact list"; + warnings.add(message); + logger.warning(message); + initialData = new ContactList(); + } catch (IOException e) { + String message = "Problem while reading from the contact list data file. " + + "Will be starting with an empty contact list"; + warnings.add(message); + logger.warning(message); + initialData = new ContactList(); + } + try { - addressBookOptional = storage.readAddressBook(); - if (!addressBookOptional.isPresent()) { - logger.info("Data file not found. Will be starting with a sample AddressBook"); + taskRecordsOptional = storage.readTaskRecords(); + if (!taskRecordsOptional.isPresent()) { + String message = "Task list data file not found. Will be starting with a sample task list"; + warnings.add(message); + logger.info(message); } - initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); + initialTasks = taskRecordsOptional.orElseGet(SampleDataUtil::getSampleTaskRecords); } catch (DataConversionException e) { - logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + String message = "Task list data file is not in the correct format. " + + "Will be starting with an empty task list"; + warnings.add(message); + logger.warning(message); + initialTasks = new TaskRecords(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + String message = "Problem while reading from the task list data file. " + + "Will be starting with an empty task list"; + warnings.add(message); + logger.warning(message); + initialTasks = new TaskRecords(); } - return new ModelManager(initialData, userPrefs); + return new ModelManager(initialData, userPrefs, initialTasks); } private void initLogging(Config config) { @@ -151,7 +194,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 SWEe-book"); initializedPrefs = new UserPrefs(); } @@ -167,13 +210,13 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { @Override public void start(Stage primaryStage) { - logger.info("Starting AddressBook " + MainApp.VERSION); + logger.info("Starting SWEe-Book " + MainApp.VERSION); ui.start(primaryStage); } @Override public void stop() { - logger.info("============================ [ Stopping Address Book ] ============================="); + logger.info("============================ [ Stopping SWEe-Book ] ============================="); try { storage.saveUserPrefs(model.getUserPrefs()); } catch (IOException e) { diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/sweebook/commons/core/Config.java similarity index 97% rename from src/main/java/seedu/address/commons/core/Config.java rename to src/main/java/sweebook/commons/core/Config.java index 91145745521..7fed200956a 100644 --- a/src/main/java/seedu/address/commons/core/Config.java +++ b/src/main/java/sweebook/commons/core/Config.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package sweebook.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/sweebook/commons/core/GuiSettings.java similarity index 98% rename from src/main/java/seedu/address/commons/core/GuiSettings.java rename to src/main/java/sweebook/commons/core/GuiSettings.java index ba33653be67..cd2f2ac2638 100644 --- a/src/main/java/seedu/address/commons/core/GuiSettings.java +++ b/src/main/java/sweebook/commons/core/GuiSettings.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package sweebook.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/sweebook/commons/core/LogsCenter.java similarity index 97% rename from src/main/java/seedu/address/commons/core/LogsCenter.java rename to src/main/java/sweebook/commons/core/LogsCenter.java index 431e7185e76..4e1436c18c0 100644 --- a/src/main/java/seedu/address/commons/core/LogsCenter.java +++ b/src/main/java/sweebook/commons/core/LogsCenter.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package sweebook.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 = "sweebook.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/address/commons/core/Messages.java b/src/main/java/sweebook/commons/core/Messages.java similarity index 59% rename from src/main/java/seedu/address/commons/core/Messages.java rename to src/main/java/sweebook/commons/core/Messages.java index 1deb3a1e469..a5a88e17852 100644 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ b/src/main/java/sweebook/commons/core/Messages.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package sweebook.commons.core; /** * Container for user visible messages. @@ -7,7 +7,10 @@ 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_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is too high " + + "and exceeds total number of people"; + public static final String MESSAGE_INVALID_TASK_INDEX = "The task index provided is too high " + + "and exceeds total number of tasks"; public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; } diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/sweebook/commons/core/Version.java similarity index 98% rename from src/main/java/seedu/address/commons/core/Version.java rename to src/main/java/sweebook/commons/core/Version.java index 12142ec1e32..0271f0aad29 100644 --- a/src/main/java/seedu/address/commons/core/Version.java +++ b/src/main/java/sweebook/commons/core/Version.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package sweebook.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/sweebook/commons/core/index/Index.java similarity index 97% rename from src/main/java/seedu/address/commons/core/index/Index.java rename to src/main/java/sweebook/commons/core/index/Index.java index 19536439c09..e89978e7e41 100644 --- a/src/main/java/seedu/address/commons/core/index/Index.java +++ b/src/main/java/sweebook/commons/core/index/Index.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core.index; +package sweebook.commons.core.index; /** * Represents a zero-based or one-based index. diff --git a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java b/src/main/java/sweebook/commons/exceptions/DataConversionException.java similarity index 84% rename from src/main/java/seedu/address/commons/exceptions/DataConversionException.java rename to src/main/java/sweebook/commons/exceptions/DataConversionException.java index 1f689bd8e3f..1567c715a8b 100644 --- a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java +++ b/src/main/java/sweebook/commons/exceptions/DataConversionException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package sweebook.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/sweebook/commons/exceptions/IllegalValueException.java similarity index 93% rename from src/main/java/seedu/address/commons/exceptions/IllegalValueException.java rename to src/main/java/sweebook/commons/exceptions/IllegalValueException.java index 19124db485c..6f8d921ae6c 100644 --- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java +++ b/src/main/java/sweebook/commons/exceptions/IllegalValueException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package sweebook.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/sweebook/commons/util/AppUtil.java similarity index 94% rename from src/main/java/seedu/address/commons/util/AppUtil.java rename to src/main/java/sweebook/commons/util/AppUtil.java index 87aa89c0326..afb12979120 100644 --- a/src/main/java/seedu/address/commons/util/AppUtil.java +++ b/src/main/java/sweebook/commons/util/AppUtil.java @@ -1,9 +1,9 @@ -package seedu.address.commons.util; +package sweebook.commons.util; import static java.util.Objects.requireNonNull; import javafx.scene.image.Image; -import seedu.address.MainApp; +import sweebook.MainApp; /** * A container for App specific utility functions diff --git a/src/main/java/seedu/address/commons/util/CollectionUtil.java b/src/main/java/sweebook/commons/util/CollectionUtil.java similarity index 96% rename from src/main/java/seedu/address/commons/util/CollectionUtil.java rename to src/main/java/sweebook/commons/util/CollectionUtil.java index eafe4dfd681..a782a7016ff 100644 --- a/src/main/java/seedu/address/commons/util/CollectionUtil.java +++ b/src/main/java/sweebook/commons/util/CollectionUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package sweebook.commons.util; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/commons/util/ConfigUtil.java b/src/main/java/sweebook/commons/util/ConfigUtil.java similarity index 77% rename from src/main/java/seedu/address/commons/util/ConfigUtil.java rename to src/main/java/sweebook/commons/util/ConfigUtil.java index f7f8a2bd44c..bd32c37923e 100644 --- a/src/main/java/seedu/address/commons/util/ConfigUtil.java +++ b/src/main/java/sweebook/commons/util/ConfigUtil.java @@ -1,11 +1,11 @@ -package seedu.address.commons.util; +package sweebook.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 sweebook.commons.core.Config; +import sweebook.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/sweebook/commons/util/FileUtil.java similarity index 96% rename from src/main/java/seedu/address/commons/util/FileUtil.java rename to src/main/java/sweebook/commons/util/FileUtil.java index b1e2767cdd9..a2346397733 100644 --- a/src/main/java/seedu/address/commons/util/FileUtil.java +++ b/src/main/java/sweebook/commons/util/FileUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package sweebook.commons.util; import java.io.IOException; import java.nio.file.Files; @@ -18,7 +18,7 @@ public static boolean isFileExists(Path file) { } /** - * Returns true if {@code path} can be converted into a {@code Path} via {@link Paths#get(String)}, + * Returns true if {@code path} can be converted into a {@code Path} via {@link Paths#get(String, String...)}, * otherwise returns false. * @param path A string representing the file path. Cannot be null. */ diff --git a/src/main/java/seedu/address/commons/util/JsonUtil.java b/src/main/java/sweebook/commons/util/JsonUtil.java similarity index 97% rename from src/main/java/seedu/address/commons/util/JsonUtil.java rename to src/main/java/sweebook/commons/util/JsonUtil.java index 8ef609f055d..6049918e150 100644 --- a/src/main/java/seedu/address/commons/util/JsonUtil.java +++ b/src/main/java/sweebook/commons/util/JsonUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package sweebook.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 sweebook.commons.core.LogsCenter; +import sweebook.commons.exceptions.DataConversionException; /** * Converts a Java object instance to JSON and vice versa diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/sweebook/commons/util/StringUtil.java similarity index 95% rename from src/main/java/seedu/address/commons/util/StringUtil.java rename to src/main/java/sweebook/commons/util/StringUtil.java index 61cc8c9a1cb..e783d5141ef 100644 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ b/src/main/java/sweebook/commons/util/StringUtil.java @@ -1,7 +1,7 @@ -package seedu.address.commons.util; +package sweebook.commons.util; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static sweebook.commons.util.AppUtil.checkArgument; import java.io.PrintWriter; import java.io.StringWriter; diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/sweebook/logic/Logic.java similarity index 60% rename from src/main/java/seedu/address/logic/Logic.java rename to src/main/java/sweebook/logic/Logic.java index 92cd8fa605a..2c817b1458b 100644 --- a/src/main/java/seedu/address/logic/Logic.java +++ b/src/main/java/sweebook/logic/Logic.java @@ -1,14 +1,16 @@ -package seedu.address.logic; +package sweebook.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; +import sweebook.commons.core.GuiSettings; +import sweebook.logic.commands.CommandResult; +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.logic.parser.exceptions.ParseException; +import sweebook.model.Model; +import sweebook.model.ReadOnlyContactList; +import sweebook.model.person.Person; +import sweebook.model.task.Task; /** * API of the Logic component @@ -24,19 +26,19 @@ public interface Logic { CommandResult execute(String commandText) throws CommandException, ParseException; /** - * Returns the AddressBook. + * Returns the ContactList. * - * @see seedu.address.model.Model#getAddressBook() + * @see Model#getContactList() */ - ReadOnlyAddressBook getAddressBook(); + ReadOnlyContactList getContactList(); /** Returns an unmodifiable view of the filtered list of persons */ ObservableList getFilteredPersonList(); /** - * Returns the user prefs' address book file path. + * Returns the contact list file path. */ - Path getAddressBookFilePath(); + Path getContactListFilePath(); /** * Returns the user prefs' GUI settings. @@ -47,4 +49,6 @@ public interface Logic { * Set the user prefs' GUI settings. */ void setGuiSettings(GuiSettings guiSettings); + + ObservableList getFilteredTaskList(); } diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/sweebook/logic/LogicManager.java similarity index 55% rename from src/main/java/seedu/address/logic/LogicManager.java rename to src/main/java/sweebook/logic/LogicManager.java index 9d9c6d15bdc..c5e355ae0c0 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/sweebook/logic/LogicManager.java @@ -1,21 +1,23 @@ -package seedu.address.logic; +package sweebook.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 sweebook.commons.core.GuiSettings; +import sweebook.commons.core.LogsCenter; +import sweebook.logic.commands.Command; +import sweebook.logic.commands.CommandResult; +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.logic.parser.SweeBookParser; +import sweebook.logic.parser.exceptions.ParseException; +import sweebook.model.Model; +import sweebook.model.ReadOnlyContactList; +import sweebook.model.ReadOnlyTaskRecords; +import sweebook.model.person.Person; +import sweebook.model.task.Task; +import sweebook.storage.Storage; /** * The main LogicManager of the app. @@ -26,7 +28,7 @@ public class LogicManager implements Logic { private final Model model; private final Storage storage; - private final AddressBookParser addressBookParser; + private final SweeBookParser sweeBookParser; /** * Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}. @@ -34,7 +36,7 @@ public class LogicManager implements Logic { public LogicManager(Model model, Storage storage) { this.model = model; this.storage = storage; - addressBookParser = new AddressBookParser(); + sweeBookParser = new SweeBookParser(); } @Override @@ -42,11 +44,12 @@ public CommandResult execute(String commandText) throws CommandException, ParseE logger.info("----------------[USER COMMAND][" + commandText + "]"); CommandResult commandResult; - Command command = addressBookParser.parseCommand(commandText); + Command command = sweeBookParser.parseCommand(commandText); commandResult = command.execute(model); try { - storage.saveAddressBook(model.getAddressBook()); + storage.saveContactList(model.getContactList()); + storage.saveTaskRecords(model.getTaskList()); } catch (IOException ioe) { throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe); } @@ -55,8 +58,8 @@ public CommandResult execute(String commandText) throws CommandException, ParseE } @Override - public ReadOnlyAddressBook getAddressBook() { - return model.getAddressBook(); + public ReadOnlyContactList getContactList() { + return model.getContactList(); } @Override @@ -64,9 +67,17 @@ public ObservableList getFilteredPersonList() { return model.getFilteredPersonList(); } + public ReadOnlyTaskRecords getTaskRecords() { + return model.getTaskList(); + } + + public ObservableList getFilteredTaskList() { + return model.getTasks(); + } + @Override - public Path getAddressBookFilePath() { - return model.getAddressBookFilePath(); + public Path getContactListFilePath() { + return model.getContactListFilePath(); } @Override diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/sweebook/logic/commands/AddCommand.java similarity index 57% rename from src/main/java/seedu/address/logic/commands/AddCommand.java rename to src/main/java/sweebook/logic/commands/AddCommand.java index 71656d7c5c8..be8bab01605 100644 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ b/src/main/java/sweebook/logic/commands/AddCommand.java @@ -1,40 +1,43 @@ -package seedu.address.logic.commands; +package sweebook.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 sweebook.logic.parser.CliSyntax.PREFIX_EMAIL; +import static sweebook.logic.parser.CliSyntax.PREFIX_GITHUB; +import static sweebook.logic.parser.CliSyntax.PREFIX_GROUP; +import static sweebook.logic.parser.CliSyntax.PREFIX_NAME; +import static sweebook.logic.parser.CliSyntax.PREFIX_PHONE; +import static sweebook.logic.parser.CliSyntax.PREFIX_TELEGRAM; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.model.Model; +import sweebook.model.person.Person; /** - * Adds a person to the address book. + * Adds a person to the contact list. */ public class AddCommand extends Command { public static final String COMMAND_WORD = "add"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. " + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the contact list. " + "Parameters: " + PREFIX_NAME + "NAME " + + PREFIX_GROUP + "GROUP1 " + + "[" + PREFIX_GROUP + "GROUP2] " + PREFIX_PHONE + "PHONE " + PREFIX_EMAIL + "EMAIL " - + PREFIX_ADDRESS + "ADDRESS " - + "[" + PREFIX_TAG + "TAG]...\n" + + PREFIX_TELEGRAM + "TELEGRAM " + + PREFIX_GITHUB + "GITHUB\n" + "Example: " + COMMAND_WORD + " " + PREFIX_NAME + "John Doe " + + PREFIX_GROUP + "CS2103T " + + PREFIX_GROUP + "CS2101 " + PREFIX_PHONE + "98765432 " + PREFIX_EMAIL + "johnd@example.com " - + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " - + PREFIX_TAG + "friends " - + PREFIX_TAG + "owesMoney"; + + PREFIX_TELEGRAM + "johndoe " + + PREFIX_GITHUB + "johndoe "; 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; @@ -51,7 +54,7 @@ public CommandResult execute(Model model) throws CommandException { requireNonNull(model); if (model.hasPerson(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); + throw new CommandException(model.getSamePersonConstraintMessage(toAdd)); } model.addPerson(toAdd); diff --git a/src/main/java/sweebook/logic/commands/AddTaskCommand.java b/src/main/java/sweebook/logic/commands/AddTaskCommand.java new file mode 100644 index 00000000000..562d7d1bc53 --- /dev/null +++ b/src/main/java/sweebook/logic/commands/AddTaskCommand.java @@ -0,0 +1,53 @@ +package sweebook.logic.commands; + +import static java.util.Objects.requireNonNull; +import static sweebook.logic.parser.CliSyntax.PREFIX_DATE; +import static sweebook.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static sweebook.logic.parser.CliSyntax.PREFIX_GROUP; +import static sweebook.logic.parser.CliSyntax.PREFIX_PRIORITY; +import static sweebook.logic.parser.CliSyntax.PREFIX_RECURRING_FREQUENCY; +import static sweebook.logic.parser.CliSyntax.PREFIX_TASKTYPE; + +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.model.Model; +import sweebook.model.task.Task; + +/** + * Adds a task to the task list. + */ +public class AddTaskCommand extends Command { + public static final String COMMAND_WORD = "addTask"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a task to specified group. " + + "Parameters: " + + PREFIX_DESCRIPTION + "DESCRIPTION " + + PREFIX_GROUP + "GROUP " + + PREFIX_TASKTYPE + "TASKTYPE " + + "[" + PREFIX_DATE + "DATE] " + + "[" + PREFIX_RECURRING_FREQUENCY + "RECURRING_FREQUENCY] " + + "[" + PREFIX_PRIORITY + "PRIORITY]\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_DESCRIPTION + "Project Meeting " + + PREFIX_GROUP + "CS2101 " + + PREFIX_TASKTYPE + "Event " + + PREFIX_DATE + "2021-11-11 "; + public static final String MESSAGE_RECURRING_FREQ_NO_DATE = + "Note that any Task with recurring frequency must have a date as well!\n" + MESSAGE_USAGE; + public static final String MESSAGE_SUCCESS = "Got it. I've added this task:\n %1$s \n\t"; + private final Task toAdd; + + /** + * Creates an AddTaskCommand to add the specified {@code Task} + */ + public AddTaskCommand(Task task) { + requireNonNull(task); + toAdd = task; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + model.addTask(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd.toString())); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/sweebook/logic/commands/ClearCommand.java similarity index 56% rename from src/main/java/seedu/address/logic/commands/ClearCommand.java rename to src/main/java/sweebook/logic/commands/ClearCommand.java index 9c86b1fa6e4..392f01f583a 100644 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ b/src/main/java/sweebook/logic/commands/ClearCommand.java @@ -1,23 +1,22 @@ -package seedu.address.logic.commands; +package sweebook.logic.commands; import static java.util.Objects.requireNonNull; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; +import sweebook.model.ContactList; +import sweebook.model.Model; /** - * Clears the address book. + * Clears the contact list. */ public class ClearCommand extends Command { public static final String COMMAND_WORD = "clear"; - public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; - + public static final String MESSAGE_SUCCESS = "Contact list has been cleared!"; @Override public CommandResult execute(Model model) { requireNonNull(model); - model.setAddressBook(new AddressBook()); + model.setContactList(new ContactList()); return new CommandResult(MESSAGE_SUCCESS); } } diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/sweebook/logic/commands/Command.java similarity index 78% rename from src/main/java/seedu/address/logic/commands/Command.java rename to src/main/java/sweebook/logic/commands/Command.java index 64f18992160..b871d286c17 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/sweebook/logic/commands/Command.java @@ -1,7 +1,7 @@ -package seedu.address.logic.commands; +package sweebook.logic.commands; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.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/sweebook/logic/commands/CommandResult.java similarity index 97% rename from src/main/java/seedu/address/logic/commands/CommandResult.java rename to src/main/java/sweebook/logic/commands/CommandResult.java index 92f900b7916..87672ec254c 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/sweebook/logic/commands/CommandResult.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package sweebook.logic.commands; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/sweebook/logic/commands/DeleteCommand.java similarity index 81% rename from src/main/java/seedu/address/logic/commands/DeleteCommand.java rename to src/main/java/sweebook/logic/commands/DeleteCommand.java index 02fd256acba..420fd671f45 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ b/src/main/java/sweebook/logic/commands/DeleteCommand.java @@ -1,17 +1,17 @@ -package seedu.address.logic.commands; +package sweebook.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; +import sweebook.commons.core.Messages; +import sweebook.commons.core.index.Index; +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.model.Model; +import sweebook.model.person.Person; /** - * Deletes a person identified using it's displayed index from the address book. + * Deletes a person identified using it's displayed index from the contact list. */ public class DeleteCommand extends Command { @@ -19,7 +19,7 @@ public class DeleteCommand extends Command { 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" + + "Parameters: INDEX (must be a positive integer and have a value lower than 2,147,483,648)\n" + "Example: " + COMMAND_WORD + " 1"; public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s"; diff --git a/src/main/java/sweebook/logic/commands/DeleteTaskCommand.java b/src/main/java/sweebook/logic/commands/DeleteTaskCommand.java new file mode 100644 index 00000000000..0420a06d510 --- /dev/null +++ b/src/main/java/sweebook/logic/commands/DeleteTaskCommand.java @@ -0,0 +1,49 @@ +package sweebook.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import sweebook.commons.core.Messages; +import sweebook.commons.core.index.Index; +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.model.Model; +import sweebook.model.task.Task; + +/** + * Deletes a task identified using it's displayed index from the task list. + */ +public class DeleteTaskCommand extends Command { + public static final String COMMAND_WORD = "deleteTask"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deletes task at specified index. " + + "Parameters: " + + "INDEX (must be a positive integer and have a value lower than 2,147,483,648)\n" + + "Example: " + COMMAND_WORD + " " + + "1"; + + public static final String MESSAGE_SUCCESS = "Noted! I've removed this task:\n\t %1$s\n\t"; + + private final Index targetIndex; + + /** + * Creates an DeleteTaskCommand to delete the specified {@code Task} + */ + public DeleteTaskCommand(Index index) { + this.targetIndex = index; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List taskList = model.getTasks(); + + if (targetIndex.getZeroBased() >= taskList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_TASK_INDEX); + } + + Task toDelete = taskList.get(targetIndex.getZeroBased()); + model.deleteTask(toDelete); + return new CommandResult(String.format(MESSAGE_SUCCESS, toDelete.toString())); + } +} diff --git a/src/main/java/sweebook/logic/commands/DoneTaskCommand.java b/src/main/java/sweebook/logic/commands/DoneTaskCommand.java new file mode 100644 index 00000000000..17d9d35931f --- /dev/null +++ b/src/main/java/sweebook/logic/commands/DoneTaskCommand.java @@ -0,0 +1,51 @@ +package sweebook.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import sweebook.commons.core.Messages; +import sweebook.commons.core.index.Index; +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.model.Model; +import sweebook.model.task.Task; + +/** + * Marks a task identified using it's displayed index from the task list as done. + */ +public class DoneTaskCommand extends Command { + public static final String COMMAND_WORD = "doneTask"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Marks task at specified index as done. " + + "Parameters: " + + "INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " " + + "1"; + + public static final String MESSAGE_SUCCESS = "Noted! I've marked this task as done:\n\t %1$s\n\t"; + + public static final String MESSAGE_ALREADY_DONE = "This task has already been marked as done!\n\t %1$s\n\t"; + + private final Index targetIndex; + + public DoneTaskCommand(Index index) { + this.targetIndex = index; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List taskList = model.getTasks(); + + if (targetIndex.getZeroBased() >= taskList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_TASK_INDEX); + } + + Task doneTask = taskList.get(targetIndex.getZeroBased()); + if (doneTask.isDone()) { + throw new CommandException(String.format(MESSAGE_ALREADY_DONE, doneTask.toString())); + } + model.doneTask(doneTask); + return new CommandResult(String.format(MESSAGE_SUCCESS, doneTask.toString())); + } +} diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/sweebook/logic/commands/EditCommand.java similarity index 59% rename from src/main/java/seedu/address/logic/commands/EditCommand.java rename to src/main/java/sweebook/logic/commands/EditCommand.java index 7e36114902f..89cf7f7136d 100644 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ b/src/main/java/sweebook/logic/commands/EditCommand.java @@ -1,12 +1,12 @@ -package seedu.address.logic.commands; +package sweebook.logic.commands; import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import static sweebook.logic.parser.CliSyntax.PREFIX_EMAIL; +import static sweebook.logic.parser.CliSyntax.PREFIX_GITHUB; +import static sweebook.logic.parser.CliSyntax.PREFIX_GROUP; +import static sweebook.logic.parser.CliSyntax.PREFIX_NAME; +import static sweebook.logic.parser.CliSyntax.PREFIX_PHONE; +import static sweebook.logic.parser.CliSyntax.PREFIX_TELEGRAM; import java.util.Collections; import java.util.HashSet; @@ -14,20 +14,21 @@ 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; +import sweebook.commons.core.Messages; +import sweebook.commons.core.index.Index; +import sweebook.commons.util.CollectionUtil; +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.model.Model; +import sweebook.model.group.Group; +import sweebook.model.person.Email; +import sweebook.model.person.Name; +import sweebook.model.person.Person; +import sweebook.model.person.Phone; +import sweebook.model.person.social.GitHub; +import sweebook.model.person.social.Telegram; /** - * Edits the details of an existing person in the address book. + * Edits the details of an existing person in the contact list. */ public class EditCommand extends Command { @@ -36,19 +37,23 @@ public class EditCommand extends Command { 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) " + + "Parameters: INDEX (must be a positive integer and have a value lower than 2,147,483,648) " + "[" + PREFIX_NAME + "NAME] " + + "[" + PREFIX_GROUP + "GROUP1] " + + "[" + PREFIX_GROUP + "GROUP2] " + "[" + PREFIX_PHONE + "PHONE] " + "[" + PREFIX_EMAIL + "EMAIL] " - + "[" + PREFIX_ADDRESS + "ADDRESS] " - + "[" + PREFIX_TAG + "TAG]...\n" + + "[" + PREFIX_TELEGRAM + "TELEGRAM] " + + "[" + PREFIX_GITHUB + "GITHUB]\n" + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_GROUP + "CS2101 " + PREFIX_PHONE + "91234567 " - + PREFIX_EMAIL + "johndoe@example.com"; + + PREFIX_EMAIL + "johndoe@example.com " + + PREFIX_TELEGRAM + "johndoe1 " + + PREFIX_GITHUB + "johndoe2"; 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; @@ -65,6 +70,23 @@ public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) { this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor); } + /** + * 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()); + Set updatedGroups = editPersonDescriptor.getGroups().orElse(personToEdit.getGroups()); + Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); + Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); + Telegram updatedTele = editPersonDescriptor.getTelegram().orElse(personToEdit.getTelegram()); + GitHub updatedGit = editPersonDescriptor.getGitHub().orElse(personToEdit.getGitHub()); + + return new Person(updatedName, updatedGroups, updatedPhone, updatedEmail, updatedTele, updatedGit); + } + @Override public CommandResult execute(Model model) throws CommandException { requireNonNull(model); @@ -77,31 +99,20 @@ public CommandResult execute(Model model) throws CommandException { Person personToEdit = lastShownList.get(index.getZeroBased()); Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor); - if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); + for (int i = 0; i < model.getContactList().getPersonList().size(); i++) { + Person curr = model.getContactList().getPersonList().get(i); + if (lastShownList.contains(curr) && lastShownList.indexOf(curr) == index.getZeroBased()) { + continue; + } else if (curr.isSamePerson(editedPerson)) { + throw new CommandException(curr.getSamePersonConstraintMessage(editedPerson)); + } } model.setPerson(personToEdit, editedPerson); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + model.updateFilteredPersonList(Model.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 @@ -126,79 +137,80 @@ public boolean equals(Object other) { */ public static class EditPersonDescriptor { private Name name; + private Set groups; private Phone phone; private Email email; - private Address address; - private Set tags; + private Telegram tg; + private GitHub gh; - public EditPersonDescriptor() {} + public EditPersonDescriptor() { + } /** * Copy constructor. - * A defensive copy of {@code tags} is used internally. */ public EditPersonDescriptor(EditPersonDescriptor toCopy) { setName(toCopy.name); + setGroups(toCopy.groups); setPhone(toCopy.phone); setEmail(toCopy.email); - setAddress(toCopy.address); - setTags(toCopy.tags); + setTelegram(toCopy.tg); + setGitHub(toCopy.gh); } /** * Returns true if at least one field is edited. */ public boolean isAnyFieldEdited() { - return CollectionUtil.isAnyNonNull(name, phone, email, address, tags); + return CollectionUtil.isAnyNonNull(name, groups, phone, email, tg, gh); + } + + public Optional getName() { + return Optional.ofNullable(name); } public void setName(Name name) { this.name = name; } - public Optional getName() { - return Optional.ofNullable(name); + public Optional getPhone() { + return Optional.ofNullable(phone); } public void setPhone(Phone phone) { this.phone = phone; } - public Optional getPhone() { - return Optional.ofNullable(phone); + public Optional getEmail() { + return Optional.ofNullable(email); } public void setEmail(Email email) { this.email = email; } - public Optional getEmail() { - return Optional.ofNullable(email); + public Optional> getGroups() { + return (groups != null) ? Optional.of(Collections.unmodifiableSet(groups)) : Optional.empty(); } - public void setAddress(Address address) { - this.address = address; + public void setGroups(Set groups) { + this.groups = (groups != null) ? new HashSet<>(groups) : null; } - public Optional
getAddress() { - return Optional.ofNullable(address); + public Optional getTelegram() { + return Optional.ofNullable(tg); } - /** - * 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; + public void setTelegram(Telegram tg) { + this.tg = tg; } - /** - * 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(); + public Optional getGitHub() { + return Optional.ofNullable(gh); + } + + public void setGitHub(GitHub gh) { + this.gh = gh; } @Override @@ -217,10 +229,11 @@ public boolean equals(Object other) { EditPersonDescriptor e = (EditPersonDescriptor) other; return getName().equals(e.getName()) + && getGroups().equals(e.getGroups()) && getPhone().equals(e.getPhone()) && getEmail().equals(e.getEmail()) - && getAddress().equals(e.getAddress()) - && getTags().equals(e.getTags()); + && getTelegram().equals(e.getTelegram()) + && getGitHub().equals(e.getGitHub()); } } } diff --git a/src/main/java/sweebook/logic/commands/EditTaskCommand.java b/src/main/java/sweebook/logic/commands/EditTaskCommand.java new file mode 100644 index 00000000000..2b563de6ba9 --- /dev/null +++ b/src/main/java/sweebook/logic/commands/EditTaskCommand.java @@ -0,0 +1,247 @@ +package sweebook.logic.commands; + +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; +import static sweebook.logic.parser.CliSyntax.PREFIX_DATE; +import static sweebook.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static sweebook.logic.parser.CliSyntax.PREFIX_GROUP; +import static sweebook.logic.parser.CliSyntax.PREFIX_PRIORITY; +import static sweebook.logic.parser.CliSyntax.PREFIX_RECURRING_FREQUENCY; +import static sweebook.logic.parser.CliSyntax.PREFIX_TASKTYPE; + +import java.util.List; +import java.util.Optional; + +import sweebook.commons.core.Messages; +import sweebook.commons.core.index.Index; +import sweebook.commons.util.CollectionUtil; +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.model.Model; +import sweebook.model.group.Group; +import sweebook.model.task.Date; +import sweebook.model.task.Description; +import sweebook.model.task.Priority; +import sweebook.model.task.RecurringFrequency; +import sweebook.model.task.Task; +import sweebook.model.task.TaskType; + +/** + * Edits the details of an existing task in the task list. + */ +public class EditTaskCommand extends Command { + public static final String COMMAND_WORD = "editTask"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the specified task " + + "by the index number used in the displayed task list. " + + "Existing values will be overwritten by the input values. To remove recurring frequencies," + + " specify \"" + PREFIX_RECURRING_FREQUENCY + "none\".\n" + + "Parameters: INDEX (must be a positive integer and have a value lower than 2,147,483,648) " + + "[" + PREFIX_DESCRIPTION + "DESCRIPTION] " + + "[" + PREFIX_GROUP + "GROUP] " + + "[" + PREFIX_TASKTYPE + "TASKTYPE] " + + "[" + PREFIX_DATE + "DATE] " + + "[" + PREFIX_RECURRING_FREQUENCY + "RECURRING_FREQUENCY] " + + "[" + PREFIX_PRIORITY + "PRIORITY]\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_DESCRIPTION + "OP2 rehearsal " + + PREFIX_GROUP + "CS2101 " + + PREFIX_TASKTYPE + "Event " + + PREFIX_DATE + "2021-11-11 "; + + public static final String MESSAGE_EDIT_TASK_SUCCESS = "Edited Task: %1$s"; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + public static final String MESSAGE_MISSING_DATE = "Date is compulsory for deadlines and events, " + + "or if the task is recurring."; + + private final Index index; + private final EditTaskDescriptor editTaskDescriptor; + + /** + * @param index of the person in the filtered task list to edit + * @param editTaskDescriptor details to edit the task with + */ + public EditTaskCommand(Index index, EditTaskDescriptor editTaskDescriptor) { + requireNonNull(index); + requireNonNull(editTaskDescriptor); + + this.index = index; + this.editTaskDescriptor = new EditTaskDescriptor(editTaskDescriptor); + } + + /** + * Creates and returns a {@code Task} with the details of {@code taskToEdit} + * edited with {@code editTaskDescriptor}. + */ + private static Task createEditedTask(Task taskToEdit, + EditTaskDescriptor editTaskDescriptor) throws CommandException { + assert taskToEdit != null; + + Description updatedDescription = editTaskDescriptor.getDescription().orElse(taskToEdit.getDescription()); + Group updatedGroup = editTaskDescriptor.getGroup().orElse(taskToEdit.getGroup()); + TaskType updatedTaskType = editTaskDescriptor.getTaskType().orElse(taskToEdit.getTaskType()); + RecurringFrequency updatedRecurringFrequency = editTaskDescriptor.getRecurringFrequency() + .orElse(taskToEdit.getRecurringFrequency()); + Date updatedDate = editTaskDescriptor.getDate().orElse(taskToEdit.getDate()); + Priority updatedPriority = editTaskDescriptor.getPriority().orElse(taskToEdit.getPriority()); + + // date is compulsory if the task type is deadline or event, OR if the task is recurring + if ((updatedTaskType.taskType.equals("deadline") + || updatedTaskType.taskType.equals("event") + || updatedRecurringFrequency.isRecurring()) + && isNull(updatedDate)) { + throw new CommandException(MESSAGE_MISSING_DATE); + } + + Task updatedTask = new Task(updatedDescription, updatedGroup, updatedDate, + updatedTaskType, updatedRecurringFrequency, updatedPriority); + + if (taskToEdit.isDone()) { + updatedTask.markAsDone(); + } + + return updatedTask; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getTasks(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_TASK_INDEX); + } + + Task taskToEdit = lastShownList.get(index.getZeroBased()); + Task editedTask = createEditedTask(taskToEdit, editTaskDescriptor); + + model.setTask(taskToEdit, editedTask); + model.updateFilteredTaskList(Model.PREDICATE_SHOW_ALL_TASKS); + return new CommandResult(String.format(MESSAGE_EDIT_TASK_SUCCESS, editedTask)); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditTaskCommand)) { + return false; + } + + // state check + EditTaskCommand e = (EditTaskCommand) other; + return index.equals(e.index) + && editTaskDescriptor.equals(e.editTaskDescriptor); + } + + /** + * Stores the details to edit the task with. Each non-empty field value will replace the + * corresponding field value of the task. + */ + public static class EditTaskDescriptor { + private Description description; + private Group group; + private TaskType taskType; + private Date date; + private RecurringFrequency recurringFrequency; + private Priority priority; + + public EditTaskDescriptor() { + } + + /** + * Copy constructor. + */ + public EditTaskDescriptor(EditTaskDescriptor toCopy) { + setDescription(toCopy.description); + setGroup(toCopy.group); + setTaskType(toCopy.taskType); + setDate(toCopy.date); + setRecurringFrequency(toCopy.recurringFrequency); + setPriority(toCopy.priority); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(description, + group, taskType, date, recurringFrequency, priority); + } + + public Optional getDescription() { + return Optional.ofNullable(description); + } + + public void setDescription(Description description) { + this.description = description; + } + + public Optional getGroup() { + return Optional.ofNullable(group); + } + + public void setGroup(Group group) { + this.group = group; + } + + public Optional getTaskType() { + return Optional.ofNullable(taskType); + } + + public void setTaskType(TaskType type) { + this.taskType = type; + } + + public Optional getDate() { + return Optional.ofNullable(date); + } + + public void setDate(Date date) { + this.date = date; + } + + public Optional getRecurringFrequency() { + return Optional.ofNullable(recurringFrequency); + } + + public void setRecurringFrequency(RecurringFrequency recurringFrequency) { + this.recurringFrequency = recurringFrequency; + } + + public Optional getPriority() { + return Optional.ofNullable(priority); + } + + public void setPriority(Priority priority) { + this.priority = priority; + } + + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditTaskDescriptor)) { + return false; + } + + // state check + EditTaskDescriptor e = (EditTaskDescriptor) other; + + return getDescription().equals(e.getDescription()) + && getGroup().equals(e.getGroup()) + && getTaskType().equals(e.getTaskType()) + && getDate().equals(e.getDate()) + && getRecurringFrequency().equals(e.getRecurringFrequency()) + && getPriority().equals(e.getPriority()); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/sweebook/logic/commands/ExitCommand.java similarity index 75% rename from src/main/java/seedu/address/logic/commands/ExitCommand.java rename to src/main/java/sweebook/logic/commands/ExitCommand.java index 3dd85a8ba90..c119185a59f 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/sweebook/logic/commands/ExitCommand.java @@ -1,6 +1,6 @@ -package seedu.address.logic.commands; +package sweebook.logic.commands; -import seedu.address.model.Model; +import sweebook.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 SWEe-book as requested ..."; @Override public CommandResult execute(Model model) { diff --git a/src/main/java/sweebook/logic/commands/FilterTasksCommand.java b/src/main/java/sweebook/logic/commands/FilterTasksCommand.java new file mode 100644 index 00000000000..d4cbeb7f7e1 --- /dev/null +++ b/src/main/java/sweebook/logic/commands/FilterTasksCommand.java @@ -0,0 +1,47 @@ +package sweebook.logic.commands; + +import static java.util.Objects.requireNonNull; +import static sweebook.logic.parser.CliSyntax.PREFIX_GROUP; + +import sweebook.commons.core.Messages; +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.model.Model; +import sweebook.model.task.FilterTaskPredicate; + +/** + * Filters the tasks in the task list with a specified criterion. + */ +public class FilterTasksCommand extends Command { + public static final String COMMAND_WORD = "filterTasks"; + + public static final String MULTIPLE_FIELD_ERROR_MESSAGE = "filterTasks can only accept 1 field at a time."; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": filters tasks based on specified criterion.\n" + + "Parameters: " + "FILTER_CRITERION\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_GROUP + "CS2101 "; + + public static final String MESSAGE_SUCCESS = "Filtered tasks by %1$s"; + + private final FilterTaskPredicate predicate; + + /** + * Creates FilterTasksCommand to filter according to the specified {@code filterTaskCriterion} + */ + public FilterTasksCommand(FilterTaskPredicate predicate) { + requireNonNull(predicate); + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + if (!predicate.isValidPredicate()) { + throw new CommandException(Messages.MESSAGE_INVALID_COMMAND_FORMAT); + } + String returnMessage = String.format(MESSAGE_SUCCESS, predicate.toString()) + "\n"; + model.updateFilteredTaskList(predicate); + return new CommandResult(returnMessage); + } + +} diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/sweebook/logic/commands/FindCommand.java similarity index 82% rename from src/main/java/seedu/address/logic/commands/FindCommand.java rename to src/main/java/sweebook/logic/commands/FindCommand.java index d6b19b0a0de..6a1e2f3e907 100644 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ b/src/main/java/sweebook/logic/commands/FindCommand.java @@ -1,13 +1,13 @@ -package seedu.address.logic.commands; +package sweebook.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; +import sweebook.commons.core.Messages; +import sweebook.model.Model; +import sweebook.model.person.NameContainsKeywordsPredicate; /** - * Finds and lists all persons in address book whose name contains any of the argument keywords. + * Finds and lists all persons in the contact list whose name contains any of the argument keywords. * Keyword matching is case insensitive. */ public class FindCommand extends Command { diff --git a/src/main/java/sweebook/logic/commands/GroupCommand.java b/src/main/java/sweebook/logic/commands/GroupCommand.java new file mode 100644 index 00000000000..7cafdddcf21 --- /dev/null +++ b/src/main/java/sweebook/logic/commands/GroupCommand.java @@ -0,0 +1,46 @@ +package sweebook.logic.commands; + +import static java.util.Objects.requireNonNull; + +import sweebook.commons.core.Messages; +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.model.Model; +import sweebook.model.group.GroupPredicate; + +/** + * Retrieves people from the same specified group. + */ +public class GroupCommand extends Command { + + public static final String COMMAND_WORD = "group"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons who are in the specified group.\n" + + "Parameters: GROUP_NAME\n" + + "Example: " + COMMAND_WORD + " CS2103T"; + + private final GroupPredicate predicate; + + /** + * Constructor of the GroupCommand class. + * + * @param predicate The predicate to filter the list of people. + */ + public GroupCommand(GroupPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + 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 GroupCommand // instanceof handles nulls + && predicate.equals(((GroupCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/sweebook/logic/commands/HelpCommand.java similarity index 88% rename from src/main/java/seedu/address/logic/commands/HelpCommand.java rename to src/main/java/sweebook/logic/commands/HelpCommand.java index bf824f91bd0..4ab7ddb8e8c 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/sweebook/logic/commands/HelpCommand.java @@ -1,6 +1,6 @@ -package seedu.address.logic.commands; +package sweebook.logic.commands; -import seedu.address.model.Model; +import sweebook.model.Model; /** * Format full help instructions for every command for display. diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/sweebook/logic/commands/ListCommand.java similarity index 59% rename from src/main/java/seedu/address/logic/commands/ListCommand.java rename to src/main/java/sweebook/logic/commands/ListCommand.java index 84be6ad2596..978c8b4e47f 100644 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ b/src/main/java/sweebook/logic/commands/ListCommand.java @@ -1,12 +1,11 @@ -package seedu.address.logic.commands; +package sweebook.logic.commands; import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; -import seedu.address.model.Model; +import sweebook.model.Model; /** - * Lists all persons in the address book to the user. + * Lists all persons in the contact list to the user. */ public class ListCommand extends Command { @@ -18,7 +17,7 @@ public class ListCommand extends Command { @Override public CommandResult execute(Model model) { requireNonNull(model); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + model.updateFilteredPersonList(Model.PREDICATE_SHOW_ALL_PERSONS); return new CommandResult(MESSAGE_SUCCESS); } } diff --git a/src/main/java/sweebook/logic/commands/ListTasksCommand.java b/src/main/java/sweebook/logic/commands/ListTasksCommand.java new file mode 100644 index 00000000000..f4828664895 --- /dev/null +++ b/src/main/java/sweebook/logic/commands/ListTasksCommand.java @@ -0,0 +1,26 @@ +package sweebook.logic.commands; + +import static java.util.Objects.requireNonNull; + +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.model.Model; + +/** + * Queries the task list for the saved tasks of an existing person. + */ +public class ListTasksCommand extends Command { + + public static final String COMMAND_WORD = "listTasks"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lists all tasks saved for the current user.\n" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "Listed all tasks"; + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + model.updateFilteredTaskList(Model.PREDICATE_SHOW_ALL_TASKS); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/sweebook/logic/commands/SortTasksCommand.java b/src/main/java/sweebook/logic/commands/SortTasksCommand.java new file mode 100644 index 00000000000..1dbd9ad48bb --- /dev/null +++ b/src/main/java/sweebook/logic/commands/SortTasksCommand.java @@ -0,0 +1,55 @@ +package sweebook.logic.commands; + +import static java.util.Objects.requireNonNull; +import static sweebook.logic.parser.CliSyntax.PREFIX_ORDER; +import static sweebook.logic.parser.CliSyntax.PREFIX_PARAMETER; + +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.model.Model; +import sweebook.model.task.SortTaskComparator; + +/** + * Sorts the tasks in the task list with a specified parameter and order. + */ +public class SortTasksCommand extends Command { + public static final String COMMAND_WORD = "sortTasks"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Sorts tasks according to a given parameter " + + "('desc' (for description), 'date' (for a deadline / time of event),\n" + + "'pty' (for priority) and 'group') and order ('a' (for ascending), 'd' (for descending)).\n" + + "Parameters: " + + PREFIX_PARAMETER + "PARAMETER " + + PREFIX_ORDER + "ORDER\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_PARAMETER + "desc " + + PREFIX_ORDER + "a\n" + + "Sorts the tasks by description in ascending order (lexicographical order)"; + + public static final String MESSAGE_SUCCESS = "Sorted tasks by %1$s"; + + private final SortTaskComparator comparator; + + /** + * Constructor for SortTasksCommand + * @param comparator the comparator to compare tasks. + */ + public SortTasksCommand(SortTaskComparator comparator) { + requireNonNull(comparator); + this.comparator = comparator; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + String returnMessage = String.format(MESSAGE_SUCCESS, comparator.toString()) + "\n"; + requireNonNull(model); + model.updateSortedTaskList(comparator); + return new CommandResult(returnMessage); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof SortTasksCommand // instanceof handles nulls + && comparator.equals(((SortTasksCommand) other).comparator)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/sweebook/logic/commands/exceptions/CommandException.java similarity index 83% rename from src/main/java/seedu/address/logic/commands/exceptions/CommandException.java rename to src/main/java/sweebook/logic/commands/exceptions/CommandException.java index a16bd14f2cd..0da33b852ea 100644 --- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java +++ b/src/main/java/sweebook/logic/commands/exceptions/CommandException.java @@ -1,4 +1,6 @@ -package seedu.address.logic.commands.exceptions; +package sweebook.logic.commands.exceptions; + +import sweebook.logic.commands.Command; /** * Represents an error which occurs during execution of a {@link Command}. diff --git a/src/main/java/sweebook/logic/parser/AddCommandParser.java b/src/main/java/sweebook/logic/parser/AddCommandParser.java new file mode 100644 index 00000000000..05c385a30c7 --- /dev/null +++ b/src/main/java/sweebook/logic/parser/AddCommandParser.java @@ -0,0 +1,65 @@ +package sweebook.logic.parser; + +import static sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.logic.commands.AddCommand.MESSAGE_USAGE; +import static sweebook.logic.parser.CliSyntax.PREFIX_EMAIL; +import static sweebook.logic.parser.CliSyntax.PREFIX_GITHUB; +import static sweebook.logic.parser.CliSyntax.PREFIX_GROUP; +import static sweebook.logic.parser.CliSyntax.PREFIX_NAME; +import static sweebook.logic.parser.CliSyntax.PREFIX_PHONE; +import static sweebook.logic.parser.CliSyntax.PREFIX_TELEGRAM; + +import java.util.Set; +import java.util.stream.Stream; + +import sweebook.logic.commands.AddCommand; +import sweebook.logic.parser.exceptions.ParseException; +import sweebook.model.group.Group; +import sweebook.model.person.Email; +import sweebook.model.person.Name; +import sweebook.model.person.Person; +import sweebook.model.person.Phone; +import sweebook.model.person.social.GitHub; +import sweebook.model.person.social.Telegram; + +/** + * 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_GROUP, + PREFIX_PHONE, PREFIX_EMAIL, PREFIX_TELEGRAM, PREFIX_GITHUB); + + if (!arePrefixesPresent + (argMultimap, PREFIX_NAME, PREFIX_GROUP, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_TELEGRAM, PREFIX_GITHUB) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE)); + } + + Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); + Set groups = ParserUtil.parseGroups(argMultimap.getAllValues(PREFIX_GROUP)); + Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()); + Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()); + Telegram tele = ParserUtil.parseTelegram(argMultimap.getValue(PREFIX_TELEGRAM).get()); + GitHub git = ParserUtil.parseGitHub(argMultimap.getValue(PREFIX_GITHUB).get()); + + Person person = new Person(name, groups, phone, email, tele, git); + + 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/sweebook/logic/parser/AddTaskCommandParser.java b/src/main/java/sweebook/logic/parser/AddTaskCommandParser.java new file mode 100644 index 00000000000..1d27b717f8f --- /dev/null +++ b/src/main/java/sweebook/logic/parser/AddTaskCommandParser.java @@ -0,0 +1,97 @@ +package sweebook.logic.parser; + +import static java.util.Objects.requireNonNull; +import static sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.logic.commands.AddTaskCommand.MESSAGE_USAGE; +import static sweebook.logic.parser.CliSyntax.PREFIX_DATE; +import static sweebook.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static sweebook.logic.parser.CliSyntax.PREFIX_GROUP; +import static sweebook.logic.parser.CliSyntax.PREFIX_PRIORITY; +import static sweebook.logic.parser.CliSyntax.PREFIX_RECURRING_FREQUENCY; +import static sweebook.logic.parser.CliSyntax.PREFIX_TASKTYPE; + +import java.util.stream.Stream; + +import sweebook.logic.commands.AddTaskCommand; +import sweebook.logic.parser.exceptions.ParseException; +import sweebook.model.group.Group; +import sweebook.model.task.Date; +import sweebook.model.task.Deadline; +import sweebook.model.task.Description; +import sweebook.model.task.Event; +import sweebook.model.task.Priority; +import sweebook.model.task.RecurringFrequency; +import sweebook.model.task.Task; +import sweebook.model.task.TaskType; +import sweebook.model.task.Todo; + +/** + * Parses input arguments and creates a new AddTaskCommand object + */ +public class AddTaskCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddTaskCommand + * and returns an AddTaskCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddTaskCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_DESCRIPTION, PREFIX_GROUP, PREFIX_TASKTYPE, + PREFIX_DATE, PREFIX_RECURRING_FREQUENCY, PREFIX_PRIORITY); + + if (!arePrefixesPresent(argMultimap, PREFIX_DESCRIPTION, PREFIX_GROUP, PREFIX_TASKTYPE) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE)); + } + + Description description = ParserUtil.parseDescription(argMultimap.getValue(PREFIX_DESCRIPTION).get()); + Group group = ParserUtil.parseGroup(argMultimap.getValue(PREFIX_GROUP).get()); + TaskType taskType = ParserUtil.parseTaskType(argMultimap.getValue(PREFIX_TASKTYPE).get()); + + if (taskType.taskType.equals("deadline") || taskType.taskType.equals("event")) { + if (!arePrefixesPresent(argMultimap, PREFIX_DATE)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE)); + } + } + + Date date = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).orElse(null)); + Priority priority = ParserUtil.parsePriority(argMultimap.getValue(PREFIX_PRIORITY).orElse("med")); + + if (taskType.taskType.equals("todo") + && !arePrefixesPresent(argMultimap, PREFIX_DATE) + && arePrefixesPresent(argMultimap, PREFIX_RECURRING_FREQUENCY)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AddTaskCommand.MESSAGE_RECURRING_FREQ_NO_DATE)); + } + + RecurringFrequency recurringFrequency = + ParserUtil.parseRecurringFrequency(argMultimap.getValue(PREFIX_RECURRING_FREQUENCY).orElse("none")); + + Task toAdd; + switch (taskType.toString()) { + case "todo": + toAdd = new Todo(description, group, date, taskType, recurringFrequency, priority); + break; + case "event": + toAdd = new Event(description, group, date, taskType, recurringFrequency, priority); + break; + case "deadline": + requireNonNull(date); + toAdd = new Deadline(description, group, date, taskType, recurringFrequency, priority); + break; + default: + throw new ParseException(TaskType.MESSAGE_CONSTRAINTS); + } + return new AddTaskCommand(toAdd); + } + + /** + * 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/sweebook/logic/parser/ArgumentMultimap.java similarity index 98% rename from src/main/java/seedu/address/logic/parser/ArgumentMultimap.java rename to src/main/java/sweebook/logic/parser/ArgumentMultimap.java index 954c8e18f8e..a055e747981 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java +++ b/src/main/java/sweebook/logic/parser/ArgumentMultimap.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package sweebook.logic.parser; import java.util.ArrayList; import java.util.HashMap; @@ -57,4 +57,5 @@ public List getAllValues(Prefix prefix) { public String getPreamble() { return getValue(new Prefix("")).orElse(""); } + } diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/sweebook/logic/parser/ArgumentTokenizer.java similarity index 99% rename from src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java rename to src/main/java/sweebook/logic/parser/ArgumentTokenizer.java index 5c9aebfa488..539820a09ec 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java +++ b/src/main/java/sweebook/logic/parser/ArgumentTokenizer.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package sweebook.logic.parser; import java.util.ArrayList; import java.util.Arrays; diff --git a/src/main/java/sweebook/logic/parser/CliSyntax.java b/src/main/java/sweebook/logic/parser/CliSyntax.java new file mode 100644 index 00000000000..9f3acc86f62 --- /dev/null +++ b/src/main/java/sweebook/logic/parser/CliSyntax.java @@ -0,0 +1,23 @@ +package sweebook.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_TELEGRAM = new Prefix("tg/"); + public static final Prefix PREFIX_GITHUB = new Prefix("gh/"); + public static final Prefix PREFIX_DESCRIPTION = new Prefix("d/"); + public static final Prefix PREFIX_DATE = new Prefix("date/"); + public static final Prefix PREFIX_GROUP = new Prefix("g/"); + public static final Prefix PREFIX_TASKTYPE = new Prefix("type/"); + public static final Prefix PREFIX_PRIORITY = new Prefix("pty/"); + public static final Prefix PREFIX_PARAMETER = new Prefix("param/"); + public static final Prefix PREFIX_ORDER = new Prefix("o/"); + public static final Prefix PREFIX_RECURRING_FREQUENCY = new Prefix("recurring/"); + +} diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/sweebook/logic/parser/DeleteCommandParser.java similarity index 68% rename from src/main/java/seedu/address/logic/parser/DeleteCommandParser.java rename to src/main/java/sweebook/logic/parser/DeleteCommandParser.java index 522b93081cc..a80a8a8cea5 100644 --- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java +++ b/src/main/java/sweebook/logic/parser/DeleteCommandParser.java @@ -1,10 +1,11 @@ -package seedu.address.logic.parser; +package sweebook.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.logic.commands.DeleteCommand.MESSAGE_USAGE; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.DeleteCommand; -import seedu.address.logic.parser.exceptions.ParseException; +import sweebook.commons.core.index.Index; +import sweebook.logic.commands.DeleteCommand; +import sweebook.logic.parser.exceptions.ParseException; /** * Parses input arguments and creates a new DeleteCommand object @@ -22,7 +23,7 @@ public DeleteCommand parse(String args) throws ParseException { return new DeleteCommand(index); } catch (ParseException pe) { throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe); + String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE), pe); } } diff --git a/src/main/java/sweebook/logic/parser/DeleteTaskCommandParser.java b/src/main/java/sweebook/logic/parser/DeleteTaskCommandParser.java new file mode 100644 index 00000000000..5cdc7697024 --- /dev/null +++ b/src/main/java/sweebook/logic/parser/DeleteTaskCommandParser.java @@ -0,0 +1,29 @@ +package sweebook.logic.parser; + +import static sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.logic.commands.DeleteTaskCommand.MESSAGE_USAGE; + +import sweebook.commons.core.index.Index; +import sweebook.logic.commands.DeleteTaskCommand; +import sweebook.logic.parser.exceptions.ParseException; + +public class DeleteTaskCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteTaskCommand + * and returns an DeleteTaskCommand object for execution. + * @param args + * @return DeleteTaskCommand + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteTaskCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteTaskCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/sweebook/logic/parser/DoneTaskCommandParser.java b/src/main/java/sweebook/logic/parser/DoneTaskCommandParser.java new file mode 100644 index 00000000000..712499dd6e7 --- /dev/null +++ b/src/main/java/sweebook/logic/parser/DoneTaskCommandParser.java @@ -0,0 +1,28 @@ +package sweebook.logic.parser; + +import static sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.logic.commands.DoneTaskCommand.MESSAGE_USAGE; + +import sweebook.commons.core.index.Index; +import sweebook.logic.commands.DoneTaskCommand; +import sweebook.logic.parser.exceptions.ParseException; + +public class DoneTaskCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DoneTaskCommand + * and returns an DoneTaskCommand object for execution. + * @param args + * @return DoneTaskCommand + * @throws ParseException if the user input does not conform the expected format + */ + public DoneTaskCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DoneTaskCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/sweebook/logic/parser/EditCommandParser.java b/src/main/java/sweebook/logic/parser/EditCommandParser.java new file mode 100644 index 00000000000..929d724c011 --- /dev/null +++ b/src/main/java/sweebook/logic/parser/EditCommandParser.java @@ -0,0 +1,69 @@ +package sweebook.logic.parser; + +import static java.util.Objects.requireNonNull; +import static sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.logic.commands.EditCommand.EditPersonDescriptor; +import static sweebook.logic.commands.EditCommand.MESSAGE_NOT_EDITED; +import static sweebook.logic.commands.EditCommand.MESSAGE_USAGE; +import static sweebook.logic.parser.CliSyntax.PREFIX_EMAIL; +import static sweebook.logic.parser.CliSyntax.PREFIX_GITHUB; +import static sweebook.logic.parser.CliSyntax.PREFIX_GROUP; +import static sweebook.logic.parser.CliSyntax.PREFIX_NAME; +import static sweebook.logic.parser.CliSyntax.PREFIX_PHONE; +import static sweebook.logic.parser.CliSyntax.PREFIX_TELEGRAM; + +import sweebook.commons.core.index.Index; +import sweebook.logic.commands.EditCommand; +import sweebook.logic.parser.exceptions.ParseException; + +/** + * 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_GROUP, + PREFIX_PHONE, PREFIX_EMAIL, PREFIX_TELEGRAM, PREFIX_GITHUB); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE), pe); + } + + EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); + } + if (!argMultimap.getAllValues(PREFIX_GROUP).isEmpty()) { + editPersonDescriptor.setGroups(ParserUtil.parseGroups(argMultimap.getAllValues(PREFIX_GROUP))); + } + 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_TELEGRAM).isPresent()) { + editPersonDescriptor.setTelegram(ParserUtil.parseTelegram(argMultimap.getValue(PREFIX_TELEGRAM).get())); + } + if (argMultimap.getValue(PREFIX_GITHUB).isPresent()) { + editPersonDescriptor.setGitHub(ParserUtil.parseGitHub(argMultimap.getValue(PREFIX_GITHUB).get())); + } + + if (!editPersonDescriptor.isAnyFieldEdited()) { + throw new ParseException(MESSAGE_NOT_EDITED); + } + + return new EditCommand(index, editPersonDescriptor); + } +} diff --git a/src/main/java/sweebook/logic/parser/EditTaskCommandParser.java b/src/main/java/sweebook/logic/parser/EditTaskCommandParser.java new file mode 100644 index 00000000000..4faf4f3e702 --- /dev/null +++ b/src/main/java/sweebook/logic/parser/EditTaskCommandParser.java @@ -0,0 +1,67 @@ +package sweebook.logic.parser; + +import static java.util.Objects.requireNonNull; +import static sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.logic.commands.EditTaskCommand.MESSAGE_USAGE; +import static sweebook.logic.parser.CliSyntax.PREFIX_DATE; +import static sweebook.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static sweebook.logic.parser.CliSyntax.PREFIX_GROUP; +import static sweebook.logic.parser.CliSyntax.PREFIX_PRIORITY; +import static sweebook.logic.parser.CliSyntax.PREFIX_RECURRING_FREQUENCY; +import static sweebook.logic.parser.CliSyntax.PREFIX_TASKTYPE; + +import sweebook.commons.core.index.Index; +import sweebook.logic.commands.EditTaskCommand; +import sweebook.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new EditCommand object + */ +public class EditTaskCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditTaskCommand + * and returns an EditTaskCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EditTaskCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_DESCRIPTION, PREFIX_GROUP, + PREFIX_TASKTYPE, PREFIX_DATE, PREFIX_RECURRING_FREQUENCY, PREFIX_PRIORITY); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE), pe); + } + + EditTaskCommand.EditTaskDescriptor editPersonDescriptor = new EditTaskCommand.EditTaskDescriptor(); + if (argMultimap.getValue(PREFIX_DESCRIPTION).isPresent()) { + editPersonDescriptor.setDescription( + ParserUtil.parseDescription(argMultimap.getValue(PREFIX_DESCRIPTION).get())); + } + if (!argMultimap.getAllValues(PREFIX_GROUP).isEmpty()) { + editPersonDescriptor.setGroup(ParserUtil.parseGroup(argMultimap.getValue(PREFIX_GROUP).get())); + } + if (argMultimap.getValue(PREFIX_TASKTYPE).isPresent()) { + editPersonDescriptor.setTaskType(ParserUtil.parseTaskType(argMultimap.getValue(PREFIX_TASKTYPE).get())); + } + if (argMultimap.getValue(PREFIX_DATE).isPresent()) { + editPersonDescriptor.setDate(ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get())); + } + if (argMultimap.getValue(PREFIX_RECURRING_FREQUENCY).isPresent()) { + editPersonDescriptor.setRecurringFrequency( + ParserUtil.parseRecurringFrequency(argMultimap.getValue(PREFIX_RECURRING_FREQUENCY).get())); + } + if (argMultimap.getValue(PREFIX_PRIORITY).isPresent()) { + editPersonDescriptor.setPriority(ParserUtil.parsePriority(argMultimap.getValue(PREFIX_PRIORITY).get())); + } + if (!editPersonDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditTaskCommand.MESSAGE_NOT_EDITED); + } + + return new EditTaskCommand(index, editPersonDescriptor); + } +} diff --git a/src/main/java/sweebook/logic/parser/FilterTaskCommandParser.java b/src/main/java/sweebook/logic/parser/FilterTaskCommandParser.java new file mode 100644 index 00000000000..9f017b2f8fd --- /dev/null +++ b/src/main/java/sweebook/logic/parser/FilterTaskCommandParser.java @@ -0,0 +1,89 @@ +package sweebook.logic.parser; + +import static sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.logic.commands.FilterTasksCommand.MESSAGE_USAGE; +import static sweebook.logic.commands.FilterTasksCommand.MULTIPLE_FIELD_ERROR_MESSAGE; +import static sweebook.logic.parser.CliSyntax.PREFIX_DATE; +import static sweebook.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static sweebook.logic.parser.CliSyntax.PREFIX_GROUP; +import static sweebook.logic.parser.CliSyntax.PREFIX_PRIORITY; +import static sweebook.logic.parser.CliSyntax.PREFIX_TASKTYPE; + +import java.util.stream.Stream; + +import sweebook.logic.commands.FilterTasksCommand; +import sweebook.logic.parser.exceptions.ParseException; +import sweebook.model.group.Group; +import sweebook.model.task.Date; +import sweebook.model.task.Description; +import sweebook.model.task.FilterTaskPredicate; +import sweebook.model.task.Priority; +import sweebook.model.task.TaskType; + +/** + * Parses input arguments and creates a new FilterTasksCommand object + */ +public class FilterTaskCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FilterTasksCommand + * and returns a FilterTasksCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FilterTasksCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_GROUP, PREFIX_TASKTYPE, PREFIX_DATE, PREFIX_PRIORITY, + PREFIX_DESCRIPTION); + + if (!argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE)); + } + + if (numberOfPrefixesPresent(argMultimap, PREFIX_GROUP, PREFIX_TASKTYPE, PREFIX_DATE, PREFIX_PRIORITY, + PREFIX_DESCRIPTION) > 1) { + throw new ParseException(String.format(MULTIPLE_FIELD_ERROR_MESSAGE, + MESSAGE_USAGE)); + } + + if (arePrefixesPresent(argMultimap, PREFIX_DATE)) { + Date date = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get()); + return new FilterTasksCommand(new FilterTaskPredicate(PREFIX_DATE + date.getString())); + } + if (arePrefixesPresent(argMultimap, PREFIX_TASKTYPE)) { + TaskType taskType = ParserUtil.parseTaskType(argMultimap.getValue(PREFIX_TASKTYPE).get()); + return new FilterTasksCommand(new FilterTaskPredicate(PREFIX_TASKTYPE + taskType.toString())); + } + if (arePrefixesPresent(argMultimap, PREFIX_GROUP)) { + Group group = ParserUtil.parseGroup(argMultimap.getValue(PREFIX_GROUP).get()); + return new FilterTasksCommand(new FilterTaskPredicate(PREFIX_GROUP + group.toString())); + } + if (arePrefixesPresent(argMultimap, PREFIX_PRIORITY)) { + Priority priority = ParserUtil.parsePriority(argMultimap.getValue(PREFIX_PRIORITY).get()); + return new FilterTasksCommand(new FilterTaskPredicate(PREFIX_PRIORITY + priority.toString())); + } + if (arePrefixesPresent(argMultimap, PREFIX_DESCRIPTION)) { + Description desc = ParserUtil.parseDescription(argMultimap.getValue(PREFIX_DESCRIPTION).get()); + return new FilterTasksCommand(new FilterTaskPredicate(PREFIX_DESCRIPTION + desc.toString())); + } + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE)); + + } + + /** + * 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()); + } + + private static int numberOfPrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + int count = 0; + for (Prefix prefix : prefixes) { + if (arePrefixesPresent(argumentMultimap, prefix)) { + count++; + } + } + return count; + } +} diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/sweebook/logic/parser/FindCommandParser.java similarity index 70% rename from src/main/java/seedu/address/logic/parser/FindCommandParser.java rename to src/main/java/sweebook/logic/parser/FindCommandParser.java index 4fb71f23103..8e9349cfcf2 100644 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ b/src/main/java/sweebook/logic/parser/FindCommandParser.java @@ -1,12 +1,13 @@ -package seedu.address.logic.parser; +package sweebook.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.logic.commands.FindCommand.MESSAGE_USAGE; import java.util.Arrays; -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import sweebook.logic.commands.FindCommand; +import sweebook.logic.parser.exceptions.ParseException; +import sweebook.model.person.NameContainsKeywordsPredicate; /** * Parses input arguments and creates a new FindCommand object @@ -22,7 +23,7 @@ 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.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE)); } String[] nameKeywords = trimmedArgs.split("\\s+"); diff --git a/src/main/java/sweebook/logic/parser/GroupCommandParser.java b/src/main/java/sweebook/logic/parser/GroupCommandParser.java new file mode 100644 index 00000000000..59101e3d668 --- /dev/null +++ b/src/main/java/sweebook/logic/parser/GroupCommandParser.java @@ -0,0 +1,32 @@ +package sweebook.logic.parser; + +import static sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.logic.commands.GroupCommand.MESSAGE_USAGE; + +import sweebook.logic.commands.GroupCommand; +import sweebook.logic.parser.exceptions.ParseException; +import sweebook.model.group.Group; +import sweebook.model.group.GroupPredicate; + +/** + * Parses input arguments and creates a new GroupCommand object + */ +public class GroupCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the GroupCommand + * and returns a GroupCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public GroupCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE)); + } + + Group group = ParserUtil.parseGroup(args); + + return new GroupCommand(new GroupPredicate(group)); + } +} diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/sweebook/logic/parser/Parser.java similarity index 72% rename from src/main/java/seedu/address/logic/parser/Parser.java rename to src/main/java/sweebook/logic/parser/Parser.java index d6551ad8e3f..f8e57c05f65 100644 --- a/src/main/java/seedu/address/logic/parser/Parser.java +++ b/src/main/java/sweebook/logic/parser/Parser.java @@ -1,7 +1,7 @@ -package seedu.address.logic.parser; +package sweebook.logic.parser; -import seedu.address.logic.commands.Command; -import seedu.address.logic.parser.exceptions.ParseException; +import sweebook.logic.commands.Command; +import sweebook.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/sweebook/logic/parser/ParserUtil.java b/src/main/java/sweebook/logic/parser/ParserUtil.java new file mode 100644 index 00000000000..cad124e4e8f --- /dev/null +++ b/src/main/java/sweebook/logic/parser/ParserUtil.java @@ -0,0 +1,252 @@ +package sweebook.logic.parser; + +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import sweebook.commons.core.index.Index; +import sweebook.commons.util.StringUtil; +import sweebook.logic.parser.exceptions.ParseException; +import sweebook.model.group.Group; +import sweebook.model.person.Email; +import sweebook.model.person.Name; +import sweebook.model.person.Phone; +import sweebook.model.person.social.GitHub; +import sweebook.model.person.social.Social; +import sweebook.model.person.social.Telegram; +import sweebook.model.task.Date; +import sweebook.model.task.Description; +import sweebook.model.task.Priority; +import sweebook.model.task.RecurringFrequency; +import sweebook.model.task.TaskType; + +/** + * 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 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 description} into an {@code Description}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code description} is invalid. + */ + public static Description parseDescription(String description) throws ParseException { + requireNonNull(description); + String trimmedDescription = description.trim(); + if (!Description.isValidDescription(trimmedDescription)) { + throw new ParseException(Description.MESSAGE_CONSTRAINTS); + } + return new Description(trimmedDescription); + } + + /** + * Parses a {@code String group} into an {@code Group}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code group} is invalid. + */ + public static Group parseGroup(String group) throws ParseException { + requireNonNull(group); + String trimmedGroup = group.trim(); + if (!Group.isValidGroup(trimmedGroup)) { + throw new ParseException(Group.MESSAGE_CONSTRAINTS); + } + return new Group(trimmedGroup); + } + + /** + * Parses {@code Collection groups} into a {@code Set}. + * Used for SWEe-Book. + */ + public static Set parseGroups(Collection groups) throws ParseException { + requireNonNull(groups); + if (groups.size() == 0) { + throw new ParseException(Group.MESSAGE_CONSTRAINTS); + } + final Set groupSet = new HashSet<>(); + List groupList = new ArrayList<>(groups); + if (groupList.size() > 2) { // we only take the last 2 group arguments if there are more than 2 + groupList = groupList.subList(groupList.size() - 2, groupList.size()); + } + for (String groupName : groupList) { + groupSet.add(parseGroup(groupName)); + } + + assert groupSet.size() <= Group.VALID_GROUPS.length; + return groupSet; + } + + /** + * Parses a {@code String taskType} into an {@code TaskType}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code taskType} is invalid. + */ + public static TaskType parseTaskType(String taskType) throws ParseException { + requireNonNull(taskType); + String trimmedTaskType = taskType.trim(); + if (!TaskType.isValidTaskType(trimmedTaskType)) { + throw new ParseException(TaskType.MESSAGE_CONSTRAINTS); + } + return new TaskType(trimmedTaskType); + } + + /** + * Parses a {@code String date} into an {@code Date}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code date} is invalid. + */ + public static Date parseDate(String date) throws ParseException { + if (isNull(date)) { + return null; + } + String trimmedDate = date.trim(); + if (!Date.isValidDate(trimmedDate)) { + throw new ParseException(Date.MESSAGE_CONSTRAINTS); + } + return new Date(trimmedDate); + } + + /** + * Parses a username of the form "username" or "@username". + * Leading and trailing whitespaces will be trimmed. + * + * @param username The username. + * @return The cleaned username. + * @throws ParseException if the given {@code username} is invalid. + */ + public static String parseUsername(String username) throws ParseException { + requireNonNull(username); + String trimmedUsername = username.trim(); + if (trimmedUsername.isEmpty()) { + throw new ParseException(Social.MESSAGE_CONSTRAINTS); + } + if (trimmedUsername.charAt(0) == '@') { // removes any prepended '@' if it is present + trimmedUsername = trimmedUsername.substring(1); + } + + if (trimmedUsername.isEmpty() || !Social.isValidUsername(trimmedUsername)) { + throw new ParseException(Social.MESSAGE_CONSTRAINTS); + } + return trimmedUsername; + } + + /** + * Returns a Telegram object that corresponds to the username. + * + * @param username The Telegram username. + * @return The Telegram object. + */ + public static Telegram parseTelegram(String username) throws ParseException { + return new Telegram(parseUsername(username)); + } + + /** + * Returns a GitHub object that corresponds to the username. + * + * @param username The GitHub username. + * @return The GitHub object. + */ + public static GitHub parseGitHub(String username) throws ParseException { + return new GitHub(parseUsername(username)); + } + + /** + * Parses a {@code String recurringFrequency} into an {@code recurringFrequency}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code recurringFrequency} is invalid. + */ + public static RecurringFrequency parseRecurringFrequency(String recurringFrequency) throws ParseException { + requireNonNull(recurringFrequency); + String trimmedRecurringFrequency = recurringFrequency.trim(); + if (!RecurringFrequency.isValidRecurringFrequency(trimmedRecurringFrequency)) { + throw new ParseException(RecurringFrequency.MESSAGE_CONSTRAINTS); + } + return new RecurringFrequency(trimmedRecurringFrequency); + } + + /** + * Parses a {@code String priority} into a {@code Priority}. + * + * @throws ParseException if the given {@code priority} is invalid. + */ + public static Priority parsePriority(String priority) throws ParseException { + if (priority == null) { + return new Priority("med"); + } + + if (!Priority.isValidPriority(priority)) { + throw new ParseException(Priority.MESSAGE_CONSTRAINTS); + } + + return new Priority(priority); + } +} diff --git a/src/main/java/seedu/address/logic/parser/Prefix.java b/src/main/java/sweebook/logic/parser/Prefix.java similarity index 95% rename from src/main/java/seedu/address/logic/parser/Prefix.java rename to src/main/java/sweebook/logic/parser/Prefix.java index c859d5fa5db..2ada177a6a0 100644 --- a/src/main/java/seedu/address/logic/parser/Prefix.java +++ b/src/main/java/sweebook/logic/parser/Prefix.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package sweebook.logic.parser; /** * A prefix that marks the beginning of an argument in an arguments string. diff --git a/src/main/java/sweebook/logic/parser/SortTasksCommandParser.java b/src/main/java/sweebook/logic/parser/SortTasksCommandParser.java new file mode 100644 index 00000000000..f21d57d3aa0 --- /dev/null +++ b/src/main/java/sweebook/logic/parser/SortTasksCommandParser.java @@ -0,0 +1,51 @@ +package sweebook.logic.parser; + +import static sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.logic.commands.SortTasksCommand.MESSAGE_USAGE; +import static sweebook.logic.parser.CliSyntax.PREFIX_ORDER; +import static sweebook.logic.parser.CliSyntax.PREFIX_PARAMETER; + +import java.util.stream.Stream; + +import sweebook.logic.commands.SortTasksCommand; +import sweebook.logic.parser.exceptions.ParseException; +import sweebook.model.task.SortTaskComparator; + +/** + * Parses input arguments and creates a new SortTaskCommand object + */ +public class SortTasksCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the SortTasksCommand + * and returns a SortTasksCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public SortTasksCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_PARAMETER, PREFIX_ORDER); + + if (!arePrefixesPresent(argMultimap, PREFIX_PARAMETER, PREFIX_ORDER) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE)); + } + + String param = argMultimap.getValue(PREFIX_PARAMETER).get().toLowerCase(); + String order = argMultimap.getValue(PREFIX_ORDER).get().toLowerCase(); + + if (!SortTaskComparator.isValidComparator(param, order)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE)); + } + + return new SortTasksCommand(new SortTaskComparator(param, order)); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/sweebook/logic/parser/SweeBookParser.java similarity index 50% rename from src/main/java/seedu/address/logic/parser/AddressBookParser.java rename to src/main/java/sweebook/logic/parser/SweeBookParser.java index 1e466792b46..d3890da1b5f 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/sweebook/logic/parser/SweeBookParser.java @@ -1,26 +1,34 @@ -package seedu.address.logic.parser; +package sweebook.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 sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.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 sweebook.logic.commands.AddCommand; +import sweebook.logic.commands.AddTaskCommand; +import sweebook.logic.commands.ClearCommand; +import sweebook.logic.commands.Command; +import sweebook.logic.commands.DeleteCommand; +import sweebook.logic.commands.DeleteTaskCommand; +import sweebook.logic.commands.DoneTaskCommand; +import sweebook.logic.commands.EditCommand; +import sweebook.logic.commands.EditTaskCommand; +import sweebook.logic.commands.ExitCommand; +import sweebook.logic.commands.FilterTasksCommand; +import sweebook.logic.commands.FindCommand; +import sweebook.logic.commands.GroupCommand; +import sweebook.logic.commands.HelpCommand; +import sweebook.logic.commands.ListCommand; +import sweebook.logic.commands.ListTasksCommand; +import sweebook.logic.commands.SortTasksCommand; +import sweebook.logic.parser.exceptions.ParseException; /** * Parses user input. */ -public class AddressBookParser { +public class SweeBookParser { /** * Used for initial separation of command word and args. @@ -59,6 +67,9 @@ public Command parseCommand(String userInput) throws ParseException { case FindCommand.COMMAND_WORD: return new FindCommandParser().parse(arguments); + case GroupCommand.COMMAND_WORD: + return new GroupCommandParser().parse(arguments); + case ListCommand.COMMAND_WORD: return new ListCommand(); @@ -68,6 +79,27 @@ public Command parseCommand(String userInput) throws ParseException { case HelpCommand.COMMAND_WORD: return new HelpCommand(); + case AddTaskCommand.COMMAND_WORD: + return new AddTaskCommandParser().parse(arguments); + + case ListTasksCommand.COMMAND_WORD: + return new ListTasksCommand(); + + case SortTasksCommand.COMMAND_WORD: + return new SortTasksCommandParser().parse(arguments); + + case FilterTasksCommand.COMMAND_WORD: + return new FilterTaskCommandParser().parse(arguments); + + case DeleteTaskCommand.COMMAND_WORD: + return new DeleteTaskCommandParser().parse(arguments); + + case DoneTaskCommand.COMMAND_WORD: + return new DoneTaskCommandParser().parse(arguments); + + case EditTaskCommand.COMMAND_WORD: + return new EditTaskCommandParser().parse(arguments); + default: throw new ParseException(MESSAGE_UNKNOWN_COMMAND); } diff --git a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java b/src/main/java/sweebook/logic/parser/exceptions/ParseException.java similarity index 73% rename from src/main/java/seedu/address/logic/parser/exceptions/ParseException.java rename to src/main/java/sweebook/logic/parser/exceptions/ParseException.java index 158a1a54c1c..c7b5edb4357 100644 --- a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java +++ b/src/main/java/sweebook/logic/parser/exceptions/ParseException.java @@ -1,6 +1,6 @@ -package seedu.address.logic.parser.exceptions; +package sweebook.logic.parser.exceptions; -import seedu.address.commons.exceptions.IllegalValueException; +import sweebook.commons.exceptions.IllegalValueException; /** * Represents a parse error encountered by a parser. diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/sweebook/model/ContactList.java similarity index 63% rename from src/main/java/seedu/address/model/AddressBook.java rename to src/main/java/sweebook/model/ContactList.java index 1a943a0781a..e6c34757767 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/sweebook/model/ContactList.java @@ -1,18 +1,18 @@ -package seedu.address.model; +package sweebook.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; +import sweebook.model.person.Person; +import sweebook.model.person.UniquePersonList; /** - * Wraps all data at the address-book level + * Wraps all data at the contact list level * Duplicates are not allowed (by .isSamePerson comparison) */ -public class AddressBook implements ReadOnlyAddressBook { +public class ContactList implements ReadOnlyContactList { private final UniquePersonList persons; @@ -27,12 +27,12 @@ public class AddressBook implements ReadOnlyAddressBook { persons = new UniquePersonList(); } - public AddressBook() {} + public ContactList() {} /** - * Creates an AddressBook using the Persons in the {@code toBeCopied} + * Creates an ContactList using the Persons in the {@code toBeCopied} */ - public AddressBook(ReadOnlyAddressBook toBeCopied) { + public ContactList(ReadOnlyContactList toBeCopied) { this(); resetData(toBeCopied); } @@ -48,9 +48,9 @@ public void setPersons(List persons) { } /** - * Resets the existing data of this {@code AddressBook} with {@code newData}. + * Resets the existing data of this {@code ContactList} with {@code newData}. */ - public void resetData(ReadOnlyAddressBook newData) { + public void resetData(ReadOnlyContactList newData) { requireNonNull(newData); setPersons(newData.getPersonList()); @@ -59,7 +59,7 @@ public void resetData(ReadOnlyAddressBook newData) { //// person-level operations /** - * Returns true if a person with the same identity as {@code person} exists in the address book. + * Returns true if a person with the same identity as {@code person} exists in the contact list. */ public boolean hasPerson(Person person) { requireNonNull(person); @@ -67,8 +67,18 @@ public boolean hasPerson(Person person) { } /** - * Adds a person to the address book. - * The person must not already exist in the address book. + * Returns the message constraint that comes from {@code person} having the same equality + * (as defined in Person#isSamePerson) with another person in the contact list. + * PRECONDITION: otherPerson returns false with Person#isSamePerson. + */ + public String getSamePersonConstraintMessage(Person person) { + requireNonNull(person); + return persons.getSamePersonConstrantMessage(person); + } + + /** + * Adds a person to the contact list. + * The person must not already exist in the contact list. */ public void addPerson(Person p) { persons.add(p); @@ -76,8 +86,8 @@ public void addPerson(Person 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. + * {@code target} must exist in the contact list. + * The person identity of {@code editedPerson} must not be the same as another existing person in the contact list. */ public void setPerson(Person target, Person editedPerson) { requireNonNull(editedPerson); @@ -86,8 +96,8 @@ public void setPerson(Person target, Person editedPerson) { } /** - * Removes {@code key} from this {@code AddressBook}. - * {@code key} must exist in the address book. + * Removes {@code key} from this {@code ContactList}. + * {@code key} must exist in the contact list. */ public void removePerson(Person key) { persons.remove(key); @@ -98,7 +108,6 @@ public void removePerson(Person key) { @Override public String toString() { return persons.asUnmodifiableObservableList().size() + " persons"; - // TODO: refine later } @Override @@ -109,8 +118,8 @@ public ObservableList getPersonList() { @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)); + || (other instanceof ContactList // instanceof handles nulls + && persons.equals(((ContactList) other).persons)); } @Override diff --git a/src/main/java/sweebook/model/Model.java b/src/main/java/sweebook/model/Model.java new file mode 100644 index 00000000000..8eba2869664 --- /dev/null +++ b/src/main/java/sweebook/model/Model.java @@ -0,0 +1,119 @@ +package sweebook.model; + +import java.nio.file.Path; +import java.util.function.Predicate; + +import javafx.collections.ObservableList; +import sweebook.commons.core.GuiSettings; +import sweebook.model.person.Person; +import sweebook.model.task.SortTaskComparator; +import sweebook.model.task.Task; + +/** + * The API of the Model component. + */ +public interface Model { + /** {@code Predicate} that always evaluate to true */ + Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; + Predicate PREDICATE_SHOW_ALL_TASKS = 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 contact list file path. + */ + Path getContactListFilePath(); + + /** + * Sets the contact list file path. + */ + void setContactListFilePath(Path contactListFilePath); + + /** + * Replaces contact list data with the data in {@code contactList}. + */ + void setContactList(ReadOnlyContactList contactList); + + /** Returns the ContactList */ + ReadOnlyContactList getContactList(); + + /** + * Returns true if a person with the same identity as {@code person} exists in the contact list. + */ + boolean hasPerson(Person person); + + /** + * Returns the message constraint that comes from {@code person} having the same equality + * (as defined in Person#isSamePerson) with another person in the contact list. + * PRECONDITION: there is another person in the contact list that returns true with {@code person} when called by + * Person#isSamePerson. + */ + String getSamePersonConstraintMessage(Person person); + + /** + * Deletes the given person. + * The person must exist in the contact list. + */ + void deletePerson(Person target); + + /** + * Adds the given person. + * {@code person} must not already exist in the contact list. + */ + void addPerson(Person person); + + /** + * Replaces the given person {@code target} with {@code editedPerson}. + * {@code target} must exist in the contact list. + * The person identity of {@code editedPerson} must not be the same as another existing person in the contact list. + */ + void setPerson(Person target, Person editedPerson); + + ReadOnlyTaskRecords getTaskList(); + + void addTask(Task toAdd); + + Task deleteTask(Task task); + + void setTask(Task target, Task editedTask); + + void doneTask(Task task); + + void undoDoneTask(Task task); + + /** 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); + + /** + * Updates TaskList according to the specified SortTaskComparator + */ + void updateSortedTaskList(SortTaskComparator comparator); + + ObservableList getTasks(); + + void updateFilteredTaskList(Predicate predicate); +} diff --git a/src/main/java/sweebook/model/ModelManager.java b/src/main/java/sweebook/model/ModelManager.java new file mode 100644 index 00000000000..412f8a1e3a4 --- /dev/null +++ b/src/main/java/sweebook/model/ModelManager.java @@ -0,0 +1,225 @@ +package sweebook.model; + +import static java.util.Objects.requireNonNull; +import static sweebook.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 javafx.collections.transformation.SortedList; +import sweebook.commons.core.GuiSettings; +import sweebook.commons.core.LogsCenter; +import sweebook.model.person.Person; +import sweebook.model.task.SortTaskComparator; +import sweebook.model.task.Task; + +/** + * Represents the in-memory model of the SWEe-book data. + */ +public class ModelManager implements Model { + private static final Logger logger = LogsCenter.getLogger(ModelManager.class); + + private final ContactList contactList; + private final UserPrefs userPrefs; + private final FilteredList filteredPersons; + private final FilteredList filteredTaskList; + private final SortedList sortedTaskList; + private final TaskRecords tasks; + + /** + * Initializes a ModelManager with the given contactList and userPrefs. + */ + public ModelManager(ReadOnlyContactList contactList, ReadOnlyUserPrefs userPrefs, ReadOnlyTaskRecords tasks) { + super(); + requireAllNonNull(contactList, userPrefs, tasks); + + logger.fine("Initializing with contact list: " + contactList + " and user prefs " + userPrefs); + + this.contactList = new ContactList(contactList); + this.userPrefs = new UserPrefs(userPrefs); + this.tasks = new TaskRecords(tasks); + this.tasks.updateRecurringTasks(); + filteredPersons = new FilteredList<>(this.contactList.getPersonList()); + filteredTaskList = new FilteredList<>(this.tasks.getTaskList()); + sortedTaskList = new SortedList<>(filteredTaskList); + } + + public ModelManager() { + this(new ContactList(), new UserPrefs(), new TaskRecords()); + } + + //=========== UserPrefs ================================================================================== + + @Override + public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { + requireAllNonNull(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 getContactListFilePath() { + return userPrefs.getContactListFilePath(); + } + + @Override + public void setContactListFilePath(Path contactListFilePath) { + requireNonNull(contactListFilePath); + userPrefs.setContactListFilePath(contactListFilePath); + } + + //=========== ContactList ================================================================================ + + @Override + public void setContactList(ReadOnlyContactList contactList) { + this.contactList.resetData(contactList); + } + + @Override + public ReadOnlyContactList getContactList() { + return contactList; + } + + @Override + public boolean hasPerson(Person person) { + requireNonNull(person); + return contactList.hasPerson(person); + } + + @Override + public String getSamePersonConstraintMessage(Person person) { + requireNonNull(person); + return contactList.getSamePersonConstraintMessage(person); + } + + @Override + public void deletePerson(Person target) { + contactList.removePerson(target); + } + + @Override + public void addPerson(Person person) { + contactList.addPerson(person); + updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + } + + @Override + public void setPerson(Person target, Person editedPerson) { + requireAllNonNull(target, editedPerson); + contactList.setPerson(target, editedPerson); + } + + //=========== TaskRecords ================================================================================ + + public void setTaskRecords(ReadOnlyTaskRecords records) { + this.tasks.resetData(records); + } + + @Override + public ReadOnlyTaskRecords getTaskList() { + return tasks; + } + + @Override + public void addTask(Task toAdd) { + tasks.addTask(toAdd); + } + + @Override + public Task deleteTask(Task task) { + return tasks.deleteTask(task); + } + + @Override + public void setTask(Task target, Task editedTask) { + requireAllNonNull(target, editedTask); + tasks.setTask(target, editedTask); + } + + @Override + public void doneTask(Task task) { + tasks.doneTask(task); + } + + @Override + public void undoDoneTask(Task task) { + tasks.undoDoneTask(task); + } + + + //=========== Task List Accessors ============================================================= + + @Override + public ObservableList getTasks() { + return sortedTaskList; + } + + @Override + public void updateSortedTaskList(SortTaskComparator comparator) { + requireNonNull(comparator); + filteredTaskList.setPredicate(null); + sortedTaskList.setComparator(comparator); + } + + @Override + public void updateFilteredTaskList(Predicate predicate) { + requireNonNull(predicate); + sortedTaskList.setComparator(null); + filteredTaskList.setPredicate(predicate); + } + + //=========== Filtered Person List Accessors ============================================================= + + /** + * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of + * {@code versionedContactList} + */ + @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 contactList.equals(other.contactList) + && userPrefs.equals(other.userPrefs) + && filteredPersons.equals(other.filteredPersons); + } + +} diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/sweebook/model/ReadOnlyContactList.java similarity index 60% rename from src/main/java/seedu/address/model/ReadOnlyAddressBook.java rename to src/main/java/sweebook/model/ReadOnlyContactList.java index 6ddc2cd9a29..badddf65059 100644 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ b/src/main/java/sweebook/model/ReadOnlyContactList.java @@ -1,12 +1,12 @@ -package seedu.address.model; +package sweebook.model; import javafx.collections.ObservableList; -import seedu.address.model.person.Person; +import sweebook.model.person.Person; /** - * Unmodifiable view of an address book + * Unmodifiable view of a contact list */ -public interface ReadOnlyAddressBook { +public interface ReadOnlyContactList { /** * Returns an unmodifiable view of the persons list. diff --git a/src/main/java/sweebook/model/ReadOnlyTaskRecords.java b/src/main/java/sweebook/model/ReadOnlyTaskRecords.java new file mode 100644 index 00000000000..ae3bcf2d270 --- /dev/null +++ b/src/main/java/sweebook/model/ReadOnlyTaskRecords.java @@ -0,0 +1,11 @@ +package sweebook.model; + +import javafx.collections.ObservableList; +import sweebook.model.task.Task; + +public interface ReadOnlyTaskRecords { + /** + * Returns an unmodifiable view of the task list. + */ + ObservableList getTaskList(); +} diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/sweebook/model/ReadOnlyUserPrefs.java similarity index 52% rename from src/main/java/seedu/address/model/ReadOnlyUserPrefs.java rename to src/main/java/sweebook/model/ReadOnlyUserPrefs.java index befd58a4c73..68ffdeeb7bc 100644 --- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java +++ b/src/main/java/sweebook/model/ReadOnlyUserPrefs.java @@ -1,8 +1,8 @@ -package seedu.address.model; +package sweebook.model; import java.nio.file.Path; -import seedu.address.commons.core.GuiSettings; +import sweebook.commons.core.GuiSettings; /** * Unmodifiable view of user prefs. @@ -11,6 +11,8 @@ public interface ReadOnlyUserPrefs { GuiSettings getGuiSettings(); - Path getAddressBookFilePath(); + Path getContactListFilePath(); + + Path getTaskRecordsFilePath(); } diff --git a/src/main/java/sweebook/model/TaskRecords.java b/src/main/java/sweebook/model/TaskRecords.java new file mode 100644 index 00000000000..e4ac9bbecce --- /dev/null +++ b/src/main/java/sweebook/model/TaskRecords.java @@ -0,0 +1,106 @@ +package sweebook.model; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import javafx.collections.ObservableList; +import sweebook.model.task.Task; +import sweebook.model.task.TaskList; + +public class TaskRecords implements ReadOnlyTaskRecords { + private final TaskList records; + + { + records = new TaskList(); + } + public TaskRecords() {} + + /** + * Self function to get current Task records. + * @param existingRecords Current TaskRecords. + */ + public TaskRecords(ReadOnlyTaskRecords existingRecords) { + this(); + resetData(existingRecords); + } + + /** + * Resets Task List to the newData provided. + * @param newData Data to change to. + */ + public void resetData(ReadOnlyTaskRecords newData) { + requireNonNull(newData); + setTasks(newData.getTaskList()); + } + + public void setTasks(List tasks) { + this.records.setTasks(tasks); + } + + public void addTask(Task toAdd) { + records.add(toAdd); + } + + public void updateRecurringTasks() { + records.updateRecurringTasksDates(); + } + + /** + * Returns deleted task. + * @param task task to be deleted. + * @return deleted Task + */ + public Task deleteTask(Task task) { + return records.delete(task); + } + + /** + * Update given task as done. + * @param task + */ + public void doneTask(Task task) { + Task original = task; + Task doneTask = task; + doneTask.markAsDone(); + this.records.setTask(original, doneTask); + } + + /** + * Update given done task as not done. + * @param task + */ + public void undoDoneTask(Task task) { + Task original = task; + Task undoDoneTask = task; + undoDoneTask.markAsUndone(); + this.records.setTask(original, undoDoneTask); + } + + /** + * Replaces the given task {@code target} in the list with {@code editedTask}. + * {@code target} must exist in the task list. + */ + public void setTask(Task target, Task editedTask) { + requireNonNull(editedTask); + + records.setTask(target, editedTask); + } + + @Override + public String toString() { + return records.asUnmodifiableObservableList().size() + " tasks"; + } + + @Override + public ObservableList getTaskList() { + return records.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TaskRecords // instanceof handles nulls + && records.equals(((TaskRecords) other).records)); + } +} diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/sweebook/model/UserPrefs.java similarity index 59% rename from src/main/java/seedu/address/model/UserPrefs.java rename to src/main/java/sweebook/model/UserPrefs.java index 25a5fd6eab9..0f7bc7949b8 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/sweebook/model/UserPrefs.java @@ -1,4 +1,4 @@ -package seedu.address.model; +package sweebook.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 sweebook.commons.core.GuiSettings; /** * Represents User's preferences. @@ -14,7 +14,8 @@ public class UserPrefs implements ReadOnlyUserPrefs { private GuiSettings guiSettings = new GuiSettings(); - private Path addressBookFilePath = Paths.get("data" , "addressbook.json"); + private Path contactListFilePath = Paths.get("data" , "contactlist.json"); + private Path taskRecordsFilePath = Paths.get("data", "taskrecords.json"); /** * Creates a {@code UserPrefs} with default values. @@ -35,7 +36,8 @@ public UserPrefs(ReadOnlyUserPrefs userPrefs) { public void resetData(ReadOnlyUserPrefs newUserPrefs) { requireNonNull(newUserPrefs); setGuiSettings(newUserPrefs.getGuiSettings()); - setAddressBookFilePath(newUserPrefs.getAddressBookFilePath()); + setContactListFilePath(newUserPrefs.getContactListFilePath()); + setTaskRecordsFilePath(newUserPrefs.getTaskRecordsFilePath()); } public GuiSettings getGuiSettings() { @@ -47,13 +49,22 @@ public void setGuiSettings(GuiSettings guiSettings) { this.guiSettings = guiSettings; } - public Path getAddressBookFilePath() { - return addressBookFilePath; + public Path getContactListFilePath() { + return contactListFilePath; } - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - this.addressBookFilePath = addressBookFilePath; + public void setContactListFilePath(Path contactListFilePath) { + requireNonNull(contactListFilePath); + this.contactListFilePath = contactListFilePath; + } + + public Path getTaskRecordsFilePath() { + return taskRecordsFilePath; + } + + public void setTaskRecordsFilePath(Path taskRecordsFilePath) { + requireNonNull(taskRecordsFilePath); + this.taskRecordsFilePath = taskRecordsFilePath; } @Override @@ -68,19 +79,20 @@ public boolean equals(Object other) { UserPrefs o = (UserPrefs) other; return guiSettings.equals(o.guiSettings) - && addressBookFilePath.equals(o.addressBookFilePath); + && contactListFilePath.equals(o.contactListFilePath); } @Override public int hashCode() { - return Objects.hash(guiSettings, addressBookFilePath); + return Objects.hash(guiSettings, contactListFilePath); } @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 : " + contactListFilePath); + sb.append("\nLocal task data file location : " + taskRecordsFilePath); return sb.toString(); } diff --git a/src/main/java/sweebook/model/group/Group.java b/src/main/java/sweebook/model/group/Group.java new file mode 100644 index 00000000000..1397020e0f2 --- /dev/null +++ b/src/main/java/sweebook/model/group/Group.java @@ -0,0 +1,59 @@ +package sweebook.model.group; + +import static java.util.Objects.requireNonNull; +import static sweebook.commons.util.AppUtil.checkArgument; + +/** + * This class encapsulates a group. A group can be CS2103T or CS2101. + */ +public class Group { + public static final String MESSAGE_CONSTRAINTS = + "Group can be either CS2101 or CS2103T, and it should not be blank"; + public static final String[] VALID_GROUPS = {"CS2103T", "CS2101"}; + public final String group; + + /** + * Constructs an {@code Group}. + * + * @param group A valid group. + */ + public Group(String group) { + requireNonNull(group); + group = group.toUpperCase(); + checkArgument(isValidGroup(group), MESSAGE_CONSTRAINTS); + this.group = group; + } + + /** + * Returns true if a given string is a valid group. + */ + public static boolean isValidGroup(String test) { + for (String validGroup : VALID_GROUPS) { + if (test.equalsIgnoreCase(validGroup)) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return group; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Group // instanceof handles nulls + && group.equalsIgnoreCase(((Group) other).group)); // state check + } + + @Override + public int hashCode() { + return group.hashCode(); + } + + public int compareTo(Group otherGroup) { + return this.group.toLowerCase().compareTo(otherGroup.group.toLowerCase()); + } +} diff --git a/src/main/java/sweebook/model/group/GroupPredicate.java b/src/main/java/sweebook/model/group/GroupPredicate.java new file mode 100644 index 00000000000..6c629654fd4 --- /dev/null +++ b/src/main/java/sweebook/model/group/GroupPredicate.java @@ -0,0 +1,29 @@ +package sweebook.model.group; + +import java.util.function.Predicate; + +import sweebook.model.person.Person; + +/** + * Tests that a {@code Person}'s {@code Group} matches the specified group. + */ +public class GroupPredicate implements Predicate { + private Group group; + + public GroupPredicate(Group group) { + this.group = group; + } + + @Override + public boolean test(Person person) { + return person.getGroups().contains(group); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof GroupPredicate // instanceof handles nulls + && group.equals(((GroupPredicate) other).group)); // state check + } + +} diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/sweebook/model/person/Email.java similarity index 94% rename from src/main/java/seedu/address/model/person/Email.java rename to src/main/java/sweebook/model/person/Email.java index f866e7133de..58c89ed9ca6 100644 --- a/src/main/java/seedu/address/model/person/Email.java +++ b/src/main/java/sweebook/model/person/Email.java @@ -1,10 +1,10 @@ -package seedu.address.model.person; +package sweebook.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static sweebook.commons.util.AppUtil.checkArgument; /** - * Represents a Person's email in the address book. + * Represents a Person's email in the contact list. * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)} */ public class Email { diff --git a/src/main/java/sweebook/model/person/Name.java b/src/main/java/sweebook/model/person/Name.java new file mode 100644 index 00000000000..6a67edb22f3 --- /dev/null +++ b/src/main/java/sweebook/model/person/Name.java @@ -0,0 +1,91 @@ +package sweebook.model.person; + +import static java.util.Objects.requireNonNull; +import static sweebook.commons.util.AppUtil.checkArgument; + +/** + * Represents a Person's name in the contact list. + * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} + */ +public class Name { + + public static final String MESSAGE_CONSTRAINTS = + "Names should only contain alphabetical letters 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 = "[a-zA-Z][a-zA-Z ]*"; + + 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 + } + + /** + * A less strct equivalent relation for names, where names can be equal where + * 1. Case-sensitivity is ignored. + * 2. Whitespaces between words in their names are ignored. + * @param other The other name. + * @return Returns true if the above condition is met. + */ + public boolean equalsIgnoreCaseAndWhiteSpaces(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Name)) { + return false; + } + + Name otherName = (Name) other; + String[] otherNameSplit = otherName.fullName.split(" "); + String[] thisNameSplit = fullName.split(" "); + + if (otherNameSplit.length != thisNameSplit.length) { + return false; + } + + for (int i = 0; i < thisNameSplit.length; i++) { + if (!(thisNameSplit[i].trim().equalsIgnoreCase(otherNameSplit[i].trim()))) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + return fullName.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/sweebook/model/person/NameContainsKeywordsPredicate.java similarity index 91% rename from src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java rename to src/main/java/sweebook/model/person/NameContainsKeywordsPredicate.java index c9b5868427c..56f6dd65bfd 100644 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ b/src/main/java/sweebook/model/person/NameContainsKeywordsPredicate.java @@ -1,9 +1,9 @@ -package seedu.address.model.person; +package sweebook.model.person; import java.util.List; import java.util.function.Predicate; -import seedu.address.commons.util.StringUtil; +import sweebook.commons.util.StringUtil; /** * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. diff --git a/src/main/java/sweebook/model/person/Person.java b/src/main/java/sweebook/model/person/Person.java new file mode 100644 index 00000000000..4f273a0318d --- /dev/null +++ b/src/main/java/sweebook/model/person/Person.java @@ -0,0 +1,171 @@ +package sweebook.model.person; + +import static java.util.Collections.unmodifiableSet; +import static java.util.Objects.requireNonNull; +import static sweebook.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import sweebook.model.group.Group; +import sweebook.model.person.social.GitHub; +import sweebook.model.person.social.Telegram; + +/** + * Represents a Person in the contact list. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Person { + public static final String MESSAGE_DUPLICATE_PERSON = + "A person with this %s (%s) already exists in the contact list."; + + // Identity fields + private final Name name; + private final Set groups = new HashSet<>(); // a person can be in 1 or 2 groups (CS2103T/CS2101) + + // Contact fields + private final Phone phone; + private final Email email; + private final Telegram tg; + private final GitHub gh; + + /** + * Every field must be present and not null. + */ + public Person(Name name, Set group, Phone phone, Email email, Telegram tele, GitHub git) { + requireAllNonNull(name, group, phone, email, tele, git); + this.name = name; + this.groups.addAll(group); + this.phone = phone; + this.email = email; + this.tg = tele; + this.gh = git; + } + + public Name getName() { + return name; + } + + public Phone getPhone() { + return phone; + } + + public Email getEmail() { + return email; + } + + public Set getGroups() { + return unmodifiableSet(groups); + } + + public Telegram getTelegram() { + return tg; + } + + public GitHub getGitHub() { + return gh; + } + + /** + * Returns true if both persons are the same, defined by + * a weaker notion of equality between two persons. + */ + public boolean isSamePerson(Person otherPerson) { + if (otherPerson == this) { + return true; + } + + return otherPerson != null + && (otherPerson.getName().equalsIgnoreCaseAndWhiteSpaces(getName()) + || otherPerson.getPhone().equals(getPhone()) + || otherPerson.getEmail().value.equalsIgnoreCase(getEmail().value) + || otherPerson.getTelegram().username.equalsIgnoreCase(getTelegram().username) + || otherPerson.getGitHub().username.equalsIgnoreCase(getGitHub().username)); + } + + /** + * Returns an empty string if both persons are the same, defined by + * a weaker notion of equality between two persons. Else, returns the corresponding constraint message. + * PRECONDITION: otherPerson returns false with this person, when used with Person#isSamePerson. + */ + public String getSamePersonConstraintMessage(Person otherPerson) { + requireNonNull(otherPerson); + if (otherPerson.getName().equalsIgnoreCaseAndWhiteSpaces(getName())) { + return String.format(MESSAGE_DUPLICATE_PERSON, "name", getName()); + } + + if (otherPerson.getPhone().equals(getPhone())) { + return String.format(MESSAGE_DUPLICATE_PERSON, "phone number", getPhone()); + } + + if (otherPerson.getEmail().value.equalsIgnoreCase(getEmail().value)) { + return String.format(MESSAGE_DUPLICATE_PERSON, "email address", getEmail()); + } + + if (otherPerson.getTelegram().username.equalsIgnoreCase(getTelegram().username)) { + return String.format(MESSAGE_DUPLICATE_PERSON, "telegram username", getTelegram()); + } + + if (otherPerson.getGitHub().username.equalsIgnoreCase(getGitHub().username)) { + return String.format(MESSAGE_DUPLICATE_PERSON, "github username", getGitHub()); + } + + assert false; // should not reach here due to precondition + return ""; + } + + /** + * 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.getGroups().equals(getGroups()) + && otherPerson.getPhone().equals(getPhone()) + && otherPerson.getEmail().equals(getEmail()) + && otherPerson.getTelegram().equals(getTelegram()) + && otherPerson.getGitHub().equals(getGitHub()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(name, groups, phone, email, tg, gh); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getName()) + .append("; Phone: ") + .append(getPhone()) + .append("; Email: ") + .append(getEmail()) + .append("; Telegram: ") + .append(getTelegram()) + .append("; GitHub: ") + .append(getGitHub()); + + Set groups = getGroups(); + builder.append("; Group(s): "); + String groupsString = groups.stream() + .map(group -> group.toString()) + .collect(Collectors.joining(", ")); + builder.append(groupsString); + + return builder.toString(); + } + +} diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/sweebook/model/person/Phone.java similarity index 88% rename from src/main/java/seedu/address/model/person/Phone.java rename to src/main/java/sweebook/model/person/Phone.java index 872c76b382f..1d7b9ede843 100644 --- a/src/main/java/seedu/address/model/person/Phone.java +++ b/src/main/java/sweebook/model/person/Phone.java @@ -1,10 +1,10 @@ -package seedu.address.model.person; +package sweebook.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static sweebook.commons.util.AppUtil.checkArgument; /** - * Represents a Person's phone number in the address book. + * Represents a Person's phone number in the contact list. * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)} */ public class Phone { diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/sweebook/model/person/UniquePersonList.java similarity index 82% rename from src/main/java/seedu/address/model/person/UniquePersonList.java rename to src/main/java/sweebook/model/person/UniquePersonList.java index 0fee4fe57e6..decccf97503 100644 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ b/src/main/java/sweebook/model/person/UniquePersonList.java @@ -1,15 +1,15 @@ -package seedu.address.model.person; +package sweebook.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static sweebook.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; +import sweebook.model.person.exceptions.DuplicatePersonException; +import sweebook.model.person.exceptions.PersonNotFoundException; /** * A list of persons that enforces uniqueness between its elements and does not allow nulls. @@ -134,4 +134,20 @@ private boolean personsAreUnique(List persons) { } return true; } + + /** + * Returns the message constraint that comes from {@code person} having the same equality + * (as defined in Person#isSamePerson) with another person in the contact list. + * PRECONDITION: otherPerson returns false with some person in the contact list as defined by Person#isSamePerson. + */ + public String getSamePersonConstrantMessage(Person otherPerson) { + requireNonNull(otherPerson); + for (Person p: internalList) { + if (p.isSamePerson(otherPerson)) { + return p.getSamePersonConstraintMessage(otherPerson); + } + } + assert false; // should not reach here due to precondition + return ""; + } } diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/sweebook/model/person/exceptions/DuplicatePersonException.java similarity index 87% rename from src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java rename to src/main/java/sweebook/model/person/exceptions/DuplicatePersonException.java index d7290f59442..16d1a8f5c24 100644 --- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java +++ b/src/main/java/sweebook/model/person/exceptions/DuplicatePersonException.java @@ -1,4 +1,4 @@ -package seedu.address.model.person.exceptions; +package sweebook.model.person.exceptions; /** * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/sweebook/model/person/exceptions/PersonNotFoundException.java similarity index 75% rename from src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java rename to src/main/java/sweebook/model/person/exceptions/PersonNotFoundException.java index fa764426ca7..b3040031234 100644 --- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java +++ b/src/main/java/sweebook/model/person/exceptions/PersonNotFoundException.java @@ -1,4 +1,4 @@ -package seedu.address.model.person.exceptions; +package sweebook.model.person.exceptions; /** * Signals that the operation is unable to find the specified person. diff --git a/src/main/java/sweebook/model/person/social/GitHub.java b/src/main/java/sweebook/model/person/social/GitHub.java new file mode 100644 index 00000000000..95127221268 --- /dev/null +++ b/src/main/java/sweebook/model/person/social/GitHub.java @@ -0,0 +1,17 @@ +package sweebook.model.person.social; + +/** + * This class encapsulates a URL to a GitHub Profile. + */ +public class GitHub extends Social { + public static final String BASE_URL = "https://github.com/"; + + /** + * Constructor for the Github class. + * + * @param username The GitHub username. + */ + public GitHub(String username) { + super(BASE_URL, username); + } +} diff --git a/src/main/java/sweebook/model/person/social/Social.java b/src/main/java/sweebook/model/person/social/Social.java new file mode 100644 index 00000000000..0ee65f6c6dc --- /dev/null +++ b/src/main/java/sweebook/model/person/social/Social.java @@ -0,0 +1,66 @@ +package sweebook.model.person.social; + +import sweebook.commons.util.AppUtil; +import sweebook.commons.util.CollectionUtil; + +/** + * This class encapsulates a URL to an online profile. + */ +public abstract class Social { + public static final String MESSAGE_CONSTRAINTS = "Usernames cannot be blank," + + " and may only contain alphanumeric characters or single hyphens/underscores," + + " and cannot begin or end with a hyphen/underscores."; + // Adapted from https://github.com/regexhq/regex-username + private static final String VALIDATION_REGEX = "^([a-zA-Z\\d]+[-_])*[a-zA-Z\\d]+$"; + public final String username; + private final String baseUrl; // the base url that is to be prepended to the username, that gives the user profile. + + /** + * Constructor for the Social class. + * + * @param baseurl The base url of the online platform. + * @param username The username of the user on that platform. + */ + public Social(String baseurl, String username) { + CollectionUtil.requireAllNonNull(baseurl, username); + AppUtil.checkArgument(isValidUsername(username), MESSAGE_CONSTRAINTS); + this.baseUrl = baseurl; + this.username = username; + } + + /** + * Checks if the username is valid. Note that this is just a baseline check, + * and may not fufill requirement of all platforms. + * + * @param test The username to be checked. + * @return True if the username is valid. + */ + public static boolean isValidUsername(String test) { + return test.matches(VALIDATION_REGEX); + } + /** + * Returns the profile URL. + * + * @return the profile URL. + */ + public String toUrl() { + return baseUrl + username; + } + + @Override + public String toString() { + return "@" + username; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Social // instanceof handles nulls + && username.equals(((Social) other).username)); // state check + } + + @Override + public int hashCode() { + return username.hashCode(); + } +} diff --git a/src/main/java/sweebook/model/person/social/Telegram.java b/src/main/java/sweebook/model/person/social/Telegram.java new file mode 100644 index 00000000000..05323cc504e --- /dev/null +++ b/src/main/java/sweebook/model/person/social/Telegram.java @@ -0,0 +1,17 @@ +package sweebook.model.person.social; + +/** + * This class encapsulates a URL to a Telegram profile. + */ +public class Telegram extends Social { + public static final String BASE_URL = "https://t.me/"; + + /** + * Constructor for the Telegram class. + * + * @param username The telegram username. + */ + public Telegram(String username) { + super(BASE_URL, username); + } +} diff --git a/src/main/java/sweebook/model/task/Date.java b/src/main/java/sweebook/model/task/Date.java new file mode 100644 index 00000000000..32f23b3d98a --- /dev/null +++ b/src/main/java/sweebook/model/task/Date.java @@ -0,0 +1,165 @@ +package sweebook.model.task; + +import static java.util.Objects.requireNonNull; +import static sweebook.commons.util.AppUtil.checkArgument; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +public class Date { + public static final String MESSAGE_CONSTRAINTS = "Date should be in YYYY-MM-DD, and it should not be blank"; + public static final DateTimeFormatter DTF_STORAGE = DateTimeFormatter.ofPattern("MMM dd yyyy"); + public static final DateTimeFormatter DTF_COMMAND = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + private final LocalDate date; + private final String dateString; + + + /** + * Constructs an {@code Date}. + * + * @param date A valid date. + */ + public Date(String date) { + requireNonNull(date); + checkArgument(isValidDate(date), MESSAGE_CONSTRAINTS); + this.date = LocalDate.parse(date); + this.dateString = date; + } + + /** + * Returns true if a given string is a valid group. + */ + public static boolean isValidDate(String test) { + try { + LocalDate.parse(test); + } catch (DateTimeParseException e) { + return false; + } + return true; + } + + @Override + public String toString() { + return date.format(DTF_STORAGE); + } + + public LocalDate getLocalDate() { + return this.date; + } + + /** + * + * @return String representation of date , i.e. "YYYY-MM-DD" + */ + public String getString() { + return this.dateString; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Date) { + return date.equals(((Date) obj).date); + } else { + return false; + } + } + + public int compareTo(Date otherDate) { + return date.compareTo(otherDate.date); + } + + /** + * Checks if current date is from last week (or even before that). + * @return true if date is from last week, false otherwise + */ + public boolean isLastWeek() { + LocalDate today = LocalDate.now(); + LocalDate monday = today.with(DayOfWeek.MONDAY); + if (this.date.compareTo(monday) < 0) { + return true; + } else { + return false; + } + } + + /** + * Checks if current date is from last month (or even before that). + * @return true if date is from last month, false otherwise + */ + public boolean isLastMonth() { + LocalDate today = LocalDate.now(); + LocalDate firstDayOfMonth = today.withDayOfMonth(1); + if (this.date.compareTo(firstDayOfMonth) < 0) { + return true; + } else { + return false; + } + } + + /** + * Checks if current date is from last year (or even before that). + * @return true if date is from last year, false otherwise + */ + public boolean isLastYear() { + LocalDate today = LocalDate.now(); + LocalDate firstDayOfYear = today.withDayOfYear(1); + if (this.date.compareTo(firstDayOfYear) < 0) { + return true; + } else { + return false; + } + } + + /** + * Get the day for this current date and return the same day for the current week. + * @return Date with the same day as current instance but for the current week. + */ + public Date getDateForThisWeek() { + LocalDate today = LocalDate.now(); + LocalDate monday = today.with(DayOfWeek.MONDAY); + switch (date.getDayOfWeek()) { + case MONDAY: + return new Date(monday.format(DTF_COMMAND)); + case TUESDAY: + return new Date(monday.plusDays(1).format(DTF_COMMAND)); + case WEDNESDAY: + return new Date(monday.plusDays(2).format(DTF_COMMAND)); + case THURSDAY: + return new Date(monday.plusDays(3).format(DTF_COMMAND)); + case FRIDAY: + return new Date(monday.plusDays(4).format(DTF_COMMAND)); + case SATURDAY: + return new Date(monday.plusDays(5).format(DTF_COMMAND)); + case SUNDAY: + return new Date(monday.plusDays(6).format(DTF_COMMAND)); + default: + return null; + } + } + + /** + * Get the day for this current date and return the same day for the current month. + * @return Date with the same day as current instance but for the current month. + */ + public Date getDateForThisMonth() { + LocalDate today = LocalDate.now(); + int todayMonth = today.getMonthValue(); + int month = this.date.getMonthValue(); + return new Date(this.date.plusMonths(todayMonth - month).format(DTF_COMMAND)); + } + + /** + * Get the day for this current date and return the same day for the current year. + * @return Date with the same day as current instance but for the current year. + */ + public Date getDateForThisYear() { + LocalDate today = LocalDate.now(); + int todayYear = today.getYear(); + int year = this.date.getYear(); + return new Date(this.date.plusYears(todayYear - year).format(DTF_COMMAND)); + } + +} diff --git a/src/main/java/sweebook/model/task/Deadline.java b/src/main/java/sweebook/model/task/Deadline.java new file mode 100644 index 00000000000..406ddc20456 --- /dev/null +++ b/src/main/java/sweebook/model/task/Deadline.java @@ -0,0 +1,17 @@ +package sweebook.model.task; + +import sweebook.model.group.Group; + +public class Deadline extends Task { + private final String symbol = "[D]"; + + public Deadline(Description description, Group group, Date date, TaskType type, + RecurringFrequency recurringFrequency, Priority priority) { + super(description, group, date, type, recurringFrequency, priority); + } + + @Override + public String toString() { + return this.symbol + super.toString() + " (by: " + date.toString() + ")"; + } +} diff --git a/src/main/java/sweebook/model/task/Description.java b/src/main/java/sweebook/model/task/Description.java new file mode 100644 index 00000000000..c6e70825bd6 --- /dev/null +++ b/src/main/java/sweebook/model/task/Description.java @@ -0,0 +1,57 @@ +package sweebook.model.task; + +import static java.util.Objects.requireNonNull; +import static sweebook.commons.util.AppUtil.checkArgument; + +public class Description { + public static final String MESSAGE_CONSTRAINTS = "Description can take any value, 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 description; + + /** + * Constructs an {@code Description}. + * + * @param description A valid description. + */ + public Description(String description) { + requireNonNull(description); + checkArgument(isValidDescription(description), MESSAGE_CONSTRAINTS); + this.description = description; + } + + /** + * Returns true if a given string is a valid description. + */ + public static boolean isValidDescription(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return description; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Description) { + return description.equals(((Description) obj).description); + } else { + return false; + } + } + + public int compareTo(Description otherDesc) { + return description.toLowerCase().compareTo(otherDesc.description.toLowerCase()); + } + + public boolean contains(Description desc) { + return description.toLowerCase().contains(desc.toString().toLowerCase()); + } + +} diff --git a/src/main/java/sweebook/model/task/Event.java b/src/main/java/sweebook/model/task/Event.java new file mode 100644 index 00000000000..12e852f1392 --- /dev/null +++ b/src/main/java/sweebook/model/task/Event.java @@ -0,0 +1,17 @@ +package sweebook.model.task; + +import sweebook.model.group.Group; + +public class Event extends Task { + private final String symbol = "[E]"; + + public Event(Description description, Group group, Date date, TaskType type, + RecurringFrequency recurringFrequency, Priority priority) { + super(description, group, date, type, recurringFrequency, priority); + } + + @Override + public String toString() { + return this.symbol + super.toString() + " (at: " + date.toString() + ")"; + } +} diff --git a/src/main/java/sweebook/model/task/FilterTaskPredicate.java b/src/main/java/sweebook/model/task/FilterTaskPredicate.java new file mode 100644 index 00000000000..a478e23ac6b --- /dev/null +++ b/src/main/java/sweebook/model/task/FilterTaskPredicate.java @@ -0,0 +1,138 @@ +package sweebook.model.task; + +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; +import static sweebook.commons.util.AppUtil.checkArgument; + +import java.util.function.Predicate; + +import sweebook.model.group.Group; + +public class FilterTaskPredicate implements Predicate { + public static final String MESSAGE_CONSTRAINTS = + "FilterTaskPredicate can be either 'g/GROUP', 'date/DATE', 'type/TASKTYPE', 'pty/PRIORITY', " + + "or 'd/DESCRIPTION' "; + + private final String paramAndKeywords; + private boolean isValid = true; + + /** + * Constructs a {@code FilterTaskPredicate}. + * + * @param paramAndKeywords A valid parameter and keywords to filter by. + */ + public FilterTaskPredicate(String paramAndKeywords) { + requireNonNull(paramAndKeywords); + try { + checkArgument(isValidCriterion(paramAndKeywords), MESSAGE_CONSTRAINTS); + } catch (IllegalArgumentException e) { + this.isValid = false; + } + this.paramAndKeywords = paramAndKeywords; + } + + /** + * Returns true if a given string is a valid FilterTaskPredicate. + */ + public static boolean isValidCriterion(String test) { + if (isNull(test)) { + return false; + } + boolean isValid; + String[] criterion = test.split("/"); + String prefix = criterion[0]; + switch(prefix) { + case "date": + isValid = Date.isValidDate(test.substring(5)); + break; + case "type": + isValid = TaskType.isValidTaskType(test.substring(5)); + break; + case "g": + isValid = Group.isValidGroup(test.substring(2)); + break; + case "pty": + isValid = Priority.isValidPriority(test.substring(4)); + break; + case "d": + isValid = Description.isValidDescription(test.substring(2)); + break; + default: + isValid = false; + break; + } + return isValid; + } + + @Override + public boolean test(Task task) { + requireNonNull(task); + String[] criterion = paramAndKeywords.split("/"); + String prefix = criterion[0]; + switch(prefix) { + case "date": + Date date = new Date(paramAndKeywords.substring(5)); + if (isNull(task.getDate())) { + return false; + } else { + return task.getDate().equals(date); + } + case "type": + TaskType taskType = new TaskType(paramAndKeywords.substring(5)); + return task.getTaskType().equals(taskType); + case "g": + Group group = new Group(paramAndKeywords.substring(2)); + return task.getGroup().equals(group); + case "pty": + Priority priority = new Priority(paramAndKeywords.substring(4)); + return task.getPriority().equals(priority); + case "d": + Description description = new Description(paramAndKeywords.substring(2)); + return task.getDescription().contains(description); + default: + return false; + } + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FilterTaskPredicate // instanceof handles nulls + && paramAndKeywords.equals(((FilterTaskPredicate) other).paramAndKeywords)); // state check + } + + @Override + public String toString() { + String[] criterion = paramAndKeywords.split("/"); + String prefix = criterion[0]; + String suffix = criterion[1]; + String string = ""; + switch(prefix) { + case "date": + string = "date"; + break; + case "type": + string = "task type"; + break; + case "g": + string = "group"; + break; + case "d": + string = "description"; + suffix = "\"" + suffix + "\""; + break; + case "pty": + string = "priority"; + break; + default: + break; + } + return string + ": " + suffix; + + } + + public boolean isValidPredicate() { + return isValid; + } + +} diff --git a/src/main/java/sweebook/model/task/Priority.java b/src/main/java/sweebook/model/task/Priority.java new file mode 100644 index 00000000000..9da16c64df6 --- /dev/null +++ b/src/main/java/sweebook/model/task/Priority.java @@ -0,0 +1,79 @@ +package sweebook.model.task; + +import static java.util.Objects.requireNonNull; +import static sweebook.commons.util.AppUtil.checkArgument; + +import java.util.Arrays; + +public class Priority { + public static final String MESSAGE_CONSTRAINTS = "Priority should be low, med or high."; + public static final String[] PRIORITIES = new String[]{"low", "med", "high"}; + + public final String priority; + public final int priorityInt; + + /** + * Constructs an {@code Priority}. + * + * @param priority A valid priority. + */ + public Priority(String priority) { + requireNonNull(priority); + checkArgument(isValidPriority(priority), MESSAGE_CONSTRAINTS); + this.priority = priority; + switch (priority) { + case "low": + priorityInt = 1; + break; + case "high": + priorityInt = 3; + break; + default: + priorityInt = 2; + } + } + + /** + * Returns true if a given string is a valid priority. + */ + public static boolean isValidPriority(String test) { + return Arrays.asList(PRIORITIES).contains(test); + } + + @Override + public String toString() { + return priority; + } + + public int compareTo(Priority priority) { + return Integer.compare(this.priorityInt, (priority.getPriorityInt())); } + + /** + * + * @return Integer representation of priority. + */ + public int getPriorityInt() { + return this.priorityInt; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Priority) { + return priorityInt == ((Priority) obj).getPriorityInt(); + } else { + return false; + } + } + + public String getPriorityIcon() { + switch (priority) { + case "low": + return "!"; + case "med": + return "!!"; + default: + return "!!!"; + } + } + +} diff --git a/src/main/java/sweebook/model/task/RecurringFrequency.java b/src/main/java/sweebook/model/task/RecurringFrequency.java new file mode 100644 index 00000000000..a4f0902997a --- /dev/null +++ b/src/main/java/sweebook/model/task/RecurringFrequency.java @@ -0,0 +1,72 @@ +package sweebook.model.task; + +import static java.util.Objects.requireNonNull; +import static sweebook.commons.util.AppUtil.checkArgument; + +public class RecurringFrequency { + public static final String MESSAGE_CONSTRAINTS = "Recurring frequency of task can be either week, month or year"; + + public final String recurringFrequency; + + /** + * Constructs an {@code Group}. + * + * @param recurringFrequency A valid recurring frequency of a task. + */ + public RecurringFrequency(String recurringFrequency) { + requireNonNull(recurringFrequency); + checkArgument(isValidRecurringFrequency(recurringFrequency), MESSAGE_CONSTRAINTS); + this.recurringFrequency = recurringFrequency.toLowerCase(); + } + + /** + * Returns true if a given string is a valid task type. + */ + public static boolean isValidRecurringFrequency(String test) { + String[] validrecurringFreqs = {"week", "month", "year", "none"}; + for (String validrecurringFreq : validrecurringFreqs) { + if (test.equalsIgnoreCase(validrecurringFreq)) { + return true; + } + } + return false; + } + + /** + * Display for show in TaskCard + * @return "Recurring: weekly/monthly/yearly" + */ + public String display() { + if (recurringFrequency.equals("none")) { + return ""; + } else { + return "Recurring: " + recurringFrequency + "ly"; + } + } + + @Override + public String toString() { + return recurringFrequency; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof RecurringFrequency) { + return recurringFrequency.toLowerCase().equals(((RecurringFrequency) obj).recurringFrequency.toLowerCase()); + } else { + return false; + } + } + + /** + * Determines if recurring frequency is active. + * @return true if is recurring, false otherwise + */ + public boolean isRecurring() { + if (this.recurringFrequency.equals("none")) { + return false; + } else { + return true; + } + } +} diff --git a/src/main/java/sweebook/model/task/SortTaskComparator.java b/src/main/java/sweebook/model/task/SortTaskComparator.java new file mode 100644 index 00000000000..4423c879504 --- /dev/null +++ b/src/main/java/sweebook/model/task/SortTaskComparator.java @@ -0,0 +1,95 @@ +package sweebook.model.task; + +import static java.util.Objects.requireNonNull; +import static sweebook.commons.util.AppUtil.checkArgument; + +import java.util.Arrays; +import java.util.Comparator; + +/** + * Abstracts the compare method for task into a class of its own. + */ +public class SortTaskComparator implements Comparator { + public static final String MESSAGE_CONSTRAINTS = + "SortTaskComparator can either have 'desc', 'date', 'group' or 'pty' as PARAM in param/PARAM and" + + "'a' (ascending) or 'd' (descending) as ORDER in o/ORDER."; + + public static final String[] PARAMETERS = {"desc", "date", "group", "pty"}; + public static final String[] ORDERS = {"a", "d"}; + + private final String param; + private final boolean isAscending; + + /** + * Constructs a {@code SortTaskComparator}. + * + * @param param A valid parameter to sort the taskList. + * @param order A valid order to sort the taskList with. + */ + public SortTaskComparator(String param, String order) { + requireNonNull(param); + requireNonNull(order); + checkArgument(isValidComparator(param, order), MESSAGE_CONSTRAINTS); + this.param = param; + this.isAscending = order.equals("a"); + } + + /** + * Returns true if a given string is a valid SortTaskComparator. + */ + public static boolean isValidComparator(String testParam, String testOrder) { + requireNonNull(testParam); + requireNonNull(testOrder); + return Arrays.asList(PARAMETERS).contains(testParam) + && (Arrays.asList(ORDERS).contains(testOrder)); + } + + @Override + public int compare(Task task1, Task task2) { + requireNonNull(task1); + requireNonNull(task2); + switch (param) { + case "desc": + return isAscending ? task1.compareDescription(task2) : task2.compareDescription(task1); + case "date": + return isAscending ? task1.compareBefore(task2) : task1.compareAfter(task2); + case "group": + return isAscending ? task1.compareGroup(task2) : task2.compareGroup(task1); + case "pty": + return isAscending ? task1.comparePriority(task2) : task2.comparePriority(task1); + default: + return 0; + } + } + + @Override + public String toString() { + String parameter = ""; + String orderString = isAscending ? "ascending" : "descending"; + switch (param) { + case "desc": + parameter = "description"; + break; + case "date": + parameter = "deadline / event date"; + break; + case "group": + parameter = "group"; + break; + case "pty": + parameter = "priority"; + break; + default: + break; + } + return parameter + " in " + orderString + " order."; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof SortTaskComparator // instanceof handles nulls + && param.equals(((SortTaskComparator) other).param)) + && isAscending == ((SortTaskComparator) other).isAscending; // state check + } +} diff --git a/src/main/java/sweebook/model/task/Task.java b/src/main/java/sweebook/model/task/Task.java new file mode 100644 index 00000000000..fe116310072 --- /dev/null +++ b/src/main/java/sweebook/model/task/Task.java @@ -0,0 +1,192 @@ +package sweebook.model.task; + +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; +import static sweebook.commons.util.CollectionUtil.requireAllNonNull; + +import sweebook.model.group.Group; + +public class Task { + protected Description description; + protected Group group; + protected TaskType type; + protected Date date; + protected boolean isDone; + protected RecurringFrequency recurringFrequency; + protected Priority priority; + + /** + * Constructor for Task. + * @param description Description of task + * @param group Group of task + * @param type Type of task (Deadline, Event, Todo) + * @param date Date of task + */ + public Task(Description description, Group group, Date date, TaskType type, + RecurringFrequency recurringFrequency, Priority priority) { + requireAllNonNull(description, group, type, recurringFrequency, priority); + if (!type.equals(new TaskType("todo"))) { + requireNonNull(date); + } + this.description = description; + this.group = group; + this.type = type; + this.date = date; + this.isDone = false; + this.recurringFrequency = recurringFrequency; + this.priority = priority; + } + + /** + * Returns X if isDone true. + * + * @return "X" if isDone true. + */ + public String getStatusIcon() { + return isDone ? "[X]" : "[ ]"; + } + public Description getDescription() { + return description; + } + public Group getGroup() { + return group; + } + public Date getDate() { + return date; + } + public TaskType getTaskType() { + return type; + } + public RecurringFrequency getRecurringFrequency() { + return recurringFrequency; + } + public Priority getPriority() { + return priority; + } + public String getPriorityIcon() { + return priority.getPriorityIcon(); + } + public boolean isDone() { + return isDone; + } + + /** + * Marks task as done. + */ + public void markAsDone() { + isDone = true; + } + + public void markAsUndone() { + isDone = false; + } + + @Override + public String toString() { + return String.format("%1$s %2$s", getStatusIcon(), description.toString()); + } + + /** + * Comparator for the task's description. + * @param otherTask the otherTask + * @return an integer for comparison + */ + public int compareDescription(Task otherTask) { + return this.getDescription().compareTo(otherTask.getDescription()); + } + + /** + * Updates a recurring task's date to the current week/month/year. + */ + public void updateRecurringTaskDate() { + switch (recurringFrequency.toString()) { + case "week": + if (date.isLastWeek()) { + this.date = date.getDateForThisWeek(); + } + break; + case "month": + if (date.isLastMonth()) { + this.date = date.getDateForThisMonth(); + } + break; + case "year": + if (date.isLastYear()) { + this.date = date.getDateForThisYear(); + } + break; + default: + break; + } + } + + /** + * Comparator for the task's date. + * @param otherTask the otherTask + * @return an integer for comparison and ensure that dateless tasks are always after tasks with dates + */ + public int compareBefore(Task otherTask) { + if (isNull(getDate())) { + return 1; + } else if (isNull(otherTask.getDate())) { + return -1; + } else { + return this.getDate().compareTo(otherTask.getDate()); + } + } + + /** + * Comparator for the task's date. + * @param otherTask the otherTask + * @return an integer for comparison and ensure that dateless tasks are always after tasks with dates + */ + public int compareAfter(Task otherTask) { + if (isNull(getDate())) { + return 1; + } else if (isNull(otherTask.getDate())) { + return -1; + } else { + return otherTask.getDate().compareTo(this.getDate()); + } + } + + /** + * Comparator for the task's priority. + * @param otherTask the otherTask + * @return an integer for comparison + */ + public int comparePriority(Task otherTask) { + return this.getPriority().compareTo(otherTask.getPriority()); + } + + /** + * Comparator for the task's group. + * @param otherTask the otherTask + * @return an integer for comparison + */ + public int compareGroup(Task otherTask) { + return this.getGroup().compareTo(otherTask.getGroup()); + } + + /** + * Equals method. + * + * @param obj the object + * @return true if they are equal + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof Task) { + Task otherTask = (Task) obj; + return getDescription().equals(otherTask.getDescription()) + && getGroup().equals(otherTask.getGroup()) + && getTaskType().equals(otherTask.getTaskType()) + && ((isNull(getDate()) && isNull(otherTask.getDate())) + || getDate().equals(otherTask.getDate())) + && isDone == otherTask.isDone + && getRecurringFrequency().equals(otherTask.getRecurringFrequency()); + } else { + return false; + } + } +} diff --git a/src/main/java/sweebook/model/task/TaskList.java b/src/main/java/sweebook/model/task/TaskList.java new file mode 100644 index 00000000000..ce39495f467 --- /dev/null +++ b/src/main/java/sweebook/model/task/TaskList.java @@ -0,0 +1,91 @@ +package sweebook.model.task; + +import static sweebook.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import sweebook.commons.util.CollectionUtil; + +public class TaskList implements Iterable { + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Adds a task to the list. + */ + public void add(Task toAdd) { + requireAllNonNull(toAdd); + internalList.add(toAdd); + } + + public void setTasks(TaskList replacement) { + requireAllNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code tasks}. + */ + public void setTasks(List tasks) { + CollectionUtil.requireAllNonNull(tasks); + internalList.setAll(tasks); + } + + /** + * Deletes a task in the task list. + * + * @param task task to be deleted. + */ + public Task delete(Task task) { + internalList.remove(task); + return task; + } + + /** + * Updates all recurring tasks' dates. + */ + public void updateRecurringTasksDates() { + for (Task t : internalList) { + if (t.getRecurringFrequency().isRecurring()) { + t.updateRecurringTaskDate(); + } + } + } + + /** + * Replaces the task {@code target} in the list with {@code editedTask}. + * {@code target} must exist in the list. + */ + public void setTask(Task target, Task editedTask) { + CollectionUtil.requireAllNonNull(target, editedTask); + int index = internalList.indexOf(target); + + internalList.set(index, editedTask); + } + + /** + * 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) { + if (other instanceof TaskList) { + return internalList.equals(((TaskList) other).internalList); + } else { + return false; + } + } + +} diff --git a/src/main/java/sweebook/model/task/TaskType.java b/src/main/java/sweebook/model/task/TaskType.java new file mode 100644 index 00000000000..d0383f53d7f --- /dev/null +++ b/src/main/java/sweebook/model/task/TaskType.java @@ -0,0 +1,49 @@ +package sweebook.model.task; + +import static java.util.Objects.requireNonNull; +import static sweebook.commons.util.AppUtil.checkArgument; + +public class TaskType { + public static final String MESSAGE_CONSTRAINTS = "Type of task can be either todo or event or deadline, " + + "and it should not be blank"; + + public final String taskType; + + /** + * Constructs a {@code Group}. + * + * @param taskType A valid taskType. + */ + public TaskType(String taskType) { + requireNonNull(taskType); + checkArgument(isValidTaskType(taskType), MESSAGE_CONSTRAINTS); + this.taskType = taskType.toLowerCase(); + } + + /** + * Returns true if a given string is a valid task type. + */ + public static boolean isValidTaskType(String test) { + String[] validTaskTypes = {"todo", "event", "deadline"}; + for (String validTaskType : validTaskTypes) { + if (test.equalsIgnoreCase(validTaskType)) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return taskType; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof TaskType) { + return taskType.equalsIgnoreCase(((TaskType) obj).taskType); + } else { + return false; + } + } +} diff --git a/src/main/java/sweebook/model/task/Todo.java b/src/main/java/sweebook/model/task/Todo.java new file mode 100644 index 00000000000..1cf47f35ac8 --- /dev/null +++ b/src/main/java/sweebook/model/task/Todo.java @@ -0,0 +1,21 @@ +package sweebook.model.task; + +import sweebook.model.group.Group; + +public class Todo extends Task { + private final String symbol = "[T]"; + + public Todo(Description description, Group group, Date date, TaskType type, + RecurringFrequency recurringFrequency, Priority priority) { + super(description, group, date, type, recurringFrequency, priority); + } + + @Override + public String toString() { + if (date == null) { + return this.symbol + super.toString(); + } else { + return this.symbol + super.toString() + " (finish at: " + date.toString() + ")"; + } + } +} diff --git a/src/main/java/sweebook/model/util/SampleDataUtil.java b/src/main/java/sweebook/model/util/SampleDataUtil.java new file mode 100644 index 00000000000..0424f94b65f --- /dev/null +++ b/src/main/java/sweebook/model/util/SampleDataUtil.java @@ -0,0 +1,100 @@ +package sweebook.model.util; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +import sweebook.model.ContactList; +import sweebook.model.ReadOnlyContactList; +import sweebook.model.ReadOnlyTaskRecords; +import sweebook.model.TaskRecords; +import sweebook.model.group.Group; +import sweebook.model.person.Email; +import sweebook.model.person.Name; +import sweebook.model.person.Person; +import sweebook.model.person.Phone; +import sweebook.model.person.social.GitHub; +import sweebook.model.person.social.Telegram; +import sweebook.model.task.Date; +import sweebook.model.task.Deadline; +import sweebook.model.task.Description; +import sweebook.model.task.Event; +import sweebook.model.task.Priority; +import sweebook.model.task.RecurringFrequency; +import sweebook.model.task.Task; +import sweebook.model.task.TaskType; +import sweebook.model.task.Todo; + +/** + * Contains utility methods for populating {@code ContactList} with sample data. + */ +public class SampleDataUtil { + public static Person[] getSamplePersons() { + return new Person[] { + new Person(new Name("Alex Yeoh"), getGroupSet("CS2103T"), + new Phone("87438807"), new Email("alexyeoh@example.com"), + new Telegram("alexyeoh"), new GitHub("alexyeoh")), + new Person(new Name("Bernice Yu"), getGroupSet("CS2103T"), + new Phone("99272758"), new Email("berniceyu@example.com"), + new Telegram("berniceyu"), new GitHub("berniceyu")), + new Person(new Name("Charlotte Oliveiro"), getGroupSet("CS2103T"), + new Phone("93210283"), new Email("charlotte@example.com"), + new Telegram("charlotteoliverio"), new GitHub("charlotteoliverio")), + new Person(new Name("David Li"), getGroupSet("CS2103T"), + new Phone("91031282"), new Email("lidavid@example.com"), + new Telegram("davidli"), new GitHub("davidli")), + new Person(new Name("Irfan Ibrahim"), getGroupSet("CS2103T", "CS2101"), + new Phone("92492021"), new Email("irfan@example.com"), + new Telegram("irfan"), new GitHub("irfan")), + new Person(new Name("Roy Balakrishnan"), getGroupSet("CS2103T", "CS2101"), + new Phone("92624417"), new Email("royb@example.com"), + new Telegram("roybalakrishnan"), new GitHub("roybalakrishnan")) + }; + } + + public static ReadOnlyContactList getSampleContactList() { + ContactList sampleAb = new ContactList(); + for (Person samplePerson : getSamplePersons()) { + sampleAb.addPerson(samplePerson); + } + return sampleAb; + } + + public static Task[] getSampleTasks() { + return new Task[] { + new Deadline(new Description("OP1 script"), new Group("CS2101"), + new Date("2021-11-11"), new TaskType("deadline"), new RecurringFrequency("none"), + new Priority("med")), + new Deadline(new Description("Update User Guide"), new Group("CS2103T"), + new Date("2021-12-11"), new TaskType("deadline"), + new RecurringFrequency("none"), new Priority("med")), + new Event(new Description("Project Meeting"), new Group("CS2103T"), + new Date("2021-12-21"), new TaskType("event"), + new RecurringFrequency("none"), new Priority("med")), + new Event(new Description("OP1 Presentation"), new Group("CS2101"), + new Date("2021-11-21"), new TaskType("event"), new RecurringFrequency("none"), + new Priority("med")), + new Todo(new Description("Create slides"), new Group("CS2101"), + new Date("2021-11-17"), new TaskType("todo"), new RecurringFrequency("none"), + new Priority("med")) + }; + } + + public static ReadOnlyTaskRecords getSampleTaskRecords() { + TaskRecords sampleTr = new TaskRecords(); + for (Task sampleTask : getSampleTasks()) { + sampleTr.addTask(sampleTask); + } + return sampleTr; + } + + /** + * Returns a group set containing the list of strings given. + */ + public static Set getGroupSet(String... strings) { + return Arrays.stream(strings) + .map(Group::new) + .collect(Collectors.toSet()); + } + +} diff --git a/src/main/java/sweebook/storage/ContactListStorage.java b/src/main/java/sweebook/storage/ContactListStorage.java new file mode 100644 index 00000000000..0a04859ca38 --- /dev/null +++ b/src/main/java/sweebook/storage/ContactListStorage.java @@ -0,0 +1,46 @@ +package sweebook.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import sweebook.commons.exceptions.DataConversionException; +import sweebook.model.ContactList; +import sweebook.model.ReadOnlyContactList; + +/** + * Represents a storage for {@link ContactList}. + */ +public interface ContactListStorage { + + /** + * Returns the file path of the data file. + */ + Path getContactListFilePath(); + + /** + * Returns ContactList data as a {@link ReadOnlyContactList}. + * 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 readContactList() throws DataConversionException, IOException; + + /** + * @see #getContactListFilePath() + */ + Optional readContactList(Path filePath) throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyContactList} to the storage. + * @param contactList cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveContactList(ReadOnlyContactList contactList) throws IOException; + + /** + * @see #saveContactList(ReadOnlyContactList) + */ + void saveContactList(ReadOnlyContactList contactList, Path filePath) throws IOException; + +} diff --git a/src/main/java/sweebook/storage/JsonAdaptedGroup.java b/src/main/java/sweebook/storage/JsonAdaptedGroup.java new file mode 100644 index 00000000000..9e4975471d4 --- /dev/null +++ b/src/main/java/sweebook/storage/JsonAdaptedGroup.java @@ -0,0 +1,48 @@ +package sweebook.storage; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import sweebook.commons.exceptions.IllegalValueException; +import sweebook.model.group.Group; + +/** + * Jackson-friendly version of {@link Group}. + */ +class JsonAdaptedGroup { + + private final String group; + + /** + * Constructs a {@code JsonAdaptedGroup} with the given {@code group}. + */ + @JsonCreator + public JsonAdaptedGroup(String group) { + this.group = group; + } + + /** + * Converts a given {@code Group} into this class for Jackson use. + */ + public JsonAdaptedGroup(Group source) { + group = source.group; + } + + @JsonValue + public String getGroup() { + return group; + } + + /** + * Converts this Jackson-friendly adapted group object into the model's {@code Group} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted group. + */ + public Group toModelType() throws IllegalValueException { + if (!Group.isValidGroup(group)) { + throw new IllegalValueException(Group.MESSAGE_CONSTRAINTS); + } + return new Group(group); + } + +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/sweebook/storage/JsonAdaptedPerson.java similarity index 52% rename from src/main/java/seedu/address/storage/JsonAdaptedPerson.java rename to src/main/java/sweebook/storage/JsonAdaptedPerson.java index a6321cec2ea..49845c49cce 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ b/src/main/java/sweebook/storage/JsonAdaptedPerson.java @@ -1,4 +1,4 @@ -package seedu.address.storage; +package sweebook.storage; import java.util.ArrayList; import java.util.HashSet; @@ -9,13 +9,15 @@ 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; +import sweebook.commons.exceptions.IllegalValueException; +import sweebook.model.group.Group; +import sweebook.model.person.Email; +import sweebook.model.person.Name; +import sweebook.model.person.Person; +import sweebook.model.person.Phone; +import sweebook.model.person.social.GitHub; +import sweebook.model.person.social.Social; +import sweebook.model.person.social.Telegram; /** * Jackson-friendly version of {@link Person}. @@ -25,25 +27,25 @@ class JsonAdaptedPerson { public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!"; private final String name; + private final List groups = new ArrayList<>(); private final String phone; private final String email; - private final String address; - private final List tagged = new ArrayList<>(); + private final String telegram; + private final String github; /** * 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) { + public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("groups") List groups, + @JsonProperty("phone") String phone, @JsonProperty("email") String email, + @JsonProperty("telegram") String telegram, @JsonProperty("github") String github) { this.name = name; + this.groups.addAll(groups); this.phone = phone; this.email = email; - this.address = address; - if (tagged != null) { - this.tagged.addAll(tagged); - } + this.telegram = telegram; + this.github = github; } /** @@ -51,12 +53,13 @@ public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone */ public JsonAdaptedPerson(Person source) { name = source.getName().fullName; + groups.addAll(source.getGroups().stream() + .map(JsonAdaptedGroup::new) + .collect(Collectors.toSet())); phone = source.getPhone().value; email = source.getEmail().value; - address = source.getAddress().value; - tagged.addAll(source.getTags().stream() - .map(JsonAdaptedTag::new) - .collect(Collectors.toList())); + telegram = source.getTelegram().username; + github = source.getGitHub().username; } /** @@ -65,11 +68,6 @@ public JsonAdaptedPerson(Person source) { * @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())); } @@ -78,6 +76,17 @@ public Person toModelType() throws IllegalValueException { } final Name modelName = new Name(name); + if (groups.size() == 0) { + throw new IllegalValueException(Group.MESSAGE_CONSTRAINTS); + } + final Set modelGroups = new HashSet<>(); + for (JsonAdaptedGroup group : groups) { + modelGroups.add(group.toModelType()); + } + if (modelGroups.size() > 2) { + throw new IllegalValueException(Group.MESSAGE_CONSTRAINTS); + } + if (phone == null) { throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName())); } @@ -94,16 +103,25 @@ public Person toModelType() throws IllegalValueException { } final Email modelEmail = new Email(email); - if (address == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); + if (telegram == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + Telegram.class.getSimpleName())); + } + if (!Social.isValidUsername(telegram)) { + throw new IllegalValueException(Social.MESSAGE_CONSTRAINTS); + } + final Telegram modelTelegram = new Telegram(telegram); + + if (github == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + GitHub.class.getSimpleName())); } - if (!Address.isValidAddress(address)) { - throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); + if (!Social.isValidUsername(github)) { + throw new IllegalValueException(Social.MESSAGE_CONSTRAINTS); } - final Address modelAddress = new Address(address); + final GitHub modelGithub = new GitHub(github); - final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); + return new Person(modelName, modelGroups, modelPhone, modelEmail, modelTelegram, modelGithub); } } diff --git a/src/main/java/sweebook/storage/JsonAdaptedTask.java b/src/main/java/sweebook/storage/JsonAdaptedTask.java new file mode 100644 index 00000000000..a42591c8acc --- /dev/null +++ b/src/main/java/sweebook/storage/JsonAdaptedTask.java @@ -0,0 +1,186 @@ +package sweebook.storage; + +import static java.util.Objects.isNull; + +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import sweebook.commons.exceptions.IllegalValueException; +import sweebook.model.group.Group; +import sweebook.model.task.Date; +import sweebook.model.task.Deadline; +import sweebook.model.task.Description; +import sweebook.model.task.Event; +import sweebook.model.task.Priority; +import sweebook.model.task.RecurringFrequency; +import sweebook.model.task.Task; +import sweebook.model.task.TaskType; +import sweebook.model.task.Todo; + +/** + * Jackson-friendly version of {@link Task}. + */ +class JsonAdaptedTask { + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!"; + + private final String description; + private final String status; + private final String group; + private final String date; + private final String taskType; + private final String recurringFrequency; + private final String priority; + + @JsonCreator + public JsonAdaptedTask(@JsonProperty("description") String description, @JsonProperty("status") String status, + @JsonProperty("group") String group, @JsonProperty("date") String date, + @JsonProperty("taskType") String taskType, + @JsonProperty("recurringFrequency") String recurringFrequency, + @JsonProperty("priority") String priority) { + this.description = description; + this.status = status; + this.group = group; + this.date = date; + this.taskType = taskType; + this.recurringFrequency = recurringFrequency; + this.priority = priority; + } + + /** + * Converts a given {@code Task} into this class for Jackson use. + */ + public JsonAdaptedTask(Task source) { + description = source.getDescription().description; + status = source.getStatusIcon(); + group = source.getGroup().group; + date = isNull(source.getDate()) ? null : source.getDate().toString(); + taskType = source.getTaskType().taskType; + recurringFrequency = source.getRecurringFrequency().toString(); + priority = source.getPriority().priority; + } + + /** + * Converts this Jackson-friendly adapted person object into the model's {@code Task} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted task. + */ + public Task toModelType() throws IllegalValueException { + final Description modelDescription = getDescriptionFromJson(description); + + final Group modelGroup = getGroupFromJson(group); + + final TaskType modelTaskType = getTaskTypeFromJson(taskType); + + final Date modelDate = getDateFromJson(date); + + final Priority modelPriority = getPriorityFromJson(priority); + + final RecurringFrequency modelRecurringFreq = getRecurringFrequencyFromJson(recurringFrequency); + + Task taskFromJson; + switch (taskType) { + case "todo": + taskFromJson = new Todo(modelDescription, modelGroup, modelDate, modelTaskType, + modelRecurringFreq, modelPriority); + break; + case "event": + taskFromJson = new Event(modelDescription, modelGroup, modelDate, modelTaskType, + modelRecurringFreq, modelPriority); + break; + case "deadline": + taskFromJson = new Deadline(modelDescription, modelGroup, modelDate, modelTaskType, + modelRecurringFreq, modelPriority); + break; + default: + throw new IllegalValueException(TaskType.MESSAGE_CONSTRAINTS); + } + updateIfTaskDone(taskFromJson, status); + return taskFromJson; + } + + private Description getDescriptionFromJson(String descriptionFromJson) throws IllegalValueException { + if (isNull(descriptionFromJson)) { + throw new IllegalValueException(String.format( + MISSING_FIELD_MESSAGE_FORMAT, Description.class.getSimpleName())); + } + if (!Description.isValidDescription(descriptionFromJson)) { + throw new IllegalValueException(Description.MESSAGE_CONSTRAINTS); + } + return new Description(descriptionFromJson); + } + + private Group getGroupFromJson(String groupFromJson) throws IllegalValueException { + if (isNull(groupFromJson)) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Group.class.getSimpleName())); + } + if (!Group.isValidGroup(groupFromJson)) { + throw new IllegalValueException(Group.MESSAGE_CONSTRAINTS); + } + return new Group(groupFromJson); + } + + private TaskType getTaskTypeFromJson(String taskTypeFromJson) throws IllegalValueException { + if (isNull(taskTypeFromJson)) { + throw new IllegalValueException(String.format( + MISSING_FIELD_MESSAGE_FORMAT, TaskType.class.getSimpleName())); + } + if (!TaskType.isValidTaskType(taskTypeFromJson)) { + throw new IllegalValueException(TaskType.MESSAGE_CONSTRAINTS); + } + return new TaskType(taskTypeFromJson); + } + + private Date getDateFromJson(String dateFromJson) throws IllegalValueException { + Date finalDate; + if (isNull(dateFromJson)) { + finalDate = null; + } else { + try { + DateTimeFormatter storageDtf = Date.DTF_STORAGE; + DateTimeFormatter commandDtf = Date.DTF_COMMAND; + + String formattedDate = commandDtf.format(storageDtf.parse(dateFromJson)); + finalDate = new Date(formattedDate); + } catch (DateTimeParseException e) { + throw new IllegalValueException(Date.MESSAGE_CONSTRAINTS); + } + } + return finalDate; + } + + private Priority getPriorityFromJson(String priorityFromJson) throws IllegalValueException { + if (isNull(priorityFromJson)) { + throw new IllegalValueException(String.format( + MISSING_FIELD_MESSAGE_FORMAT, Priority.class.getSimpleName())); + } + if (!Priority.isValidPriority(priorityFromJson)) { + throw new IllegalValueException(RecurringFrequency.MESSAGE_CONSTRAINTS); + } + return new Priority(priorityFromJson); + } + + private RecurringFrequency getRecurringFrequencyFromJson(String recurringFrequencyFromJson) + throws IllegalValueException { + if (isNull(recurringFrequencyFromJson)) { + throw new IllegalValueException(String.format( + MISSING_FIELD_MESSAGE_FORMAT, RecurringFrequency.class.getSimpleName())); + } + if (!RecurringFrequency.isValidRecurringFrequency(recurringFrequencyFromJson)) { + throw new IllegalValueException(RecurringFrequency.MESSAGE_CONSTRAINTS); + } + return new RecurringFrequency(recurringFrequencyFromJson); + } + + private void updateIfTaskDone(Task task, String status) { + if (isDone(status)) { + task.markAsDone(); + } + } + + private boolean isDone(String status) { + return status.equals("[X]"); + } +} diff --git a/src/main/java/sweebook/storage/JsonContactListStorage.java b/src/main/java/sweebook/storage/JsonContactListStorage.java new file mode 100644 index 00000000000..c0d9f064ee4 --- /dev/null +++ b/src/main/java/sweebook/storage/JsonContactListStorage.java @@ -0,0 +1,80 @@ +package sweebook.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 sweebook.commons.core.LogsCenter; +import sweebook.commons.exceptions.DataConversionException; +import sweebook.commons.exceptions.IllegalValueException; +import sweebook.commons.util.FileUtil; +import sweebook.commons.util.JsonUtil; +import sweebook.model.ReadOnlyContactList; + +/** + * A class to access ContactList data stored as a json file on the hard disk. + */ +public class JsonContactListStorage implements ContactListStorage { + + private static final Logger logger = LogsCenter.getLogger(JsonContactListStorage.class); + + private Path filePath; + + public JsonContactListStorage(Path filePath) { + this.filePath = filePath; + } + + public Path getContactListFilePath() { + return filePath; + } + + @Override + public Optional readContactList() throws DataConversionException { + return readContactList(filePath); + } + + /** + * Similar to {@link #readContactList()}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readContactList(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional jsonContactList = JsonUtil.readJsonFile( + filePath, JsonSerializableContactList.class); + if (!jsonContactList.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonContactList.get().toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + @Override + public void saveContactList(ReadOnlyContactList contactList) throws IOException { + saveContactList(contactList, filePath); + } + + /** + * Similar to {@link #saveContactList(ReadOnlyContactList)}. + * + * @param filePath location of the data. Cannot be null. + */ + public void saveContactList(ReadOnlyContactList contactList, Path filePath) throws IOException { + requireNonNull(contactList); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonSerializableContactList(contactList), filePath); + } + +} diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/sweebook/storage/JsonSerializableContactList.java similarity index 53% rename from src/main/java/seedu/address/storage/JsonSerializableAddressBook.java rename to src/main/java/sweebook/storage/JsonSerializableContactList.java index 5efd834091d..decabfd9138 100644 --- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java +++ b/src/main/java/sweebook/storage/JsonSerializableContactList.java @@ -1,4 +1,4 @@ -package seedu.address.storage; +package sweebook.storage; import java.util.ArrayList; import java.util.List; @@ -8,53 +8,53 @@ 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; +import sweebook.commons.exceptions.IllegalValueException; +import sweebook.model.ContactList; +import sweebook.model.ReadOnlyContactList; +import sweebook.model.person.Person; /** - * An Immutable AddressBook that is serializable to JSON format. + * An Immutable ContactList that is serializable to JSON format. */ -@JsonRootName(value = "addressbook") -class JsonSerializableAddressBook { +@JsonRootName(value = "contactlist") +class JsonSerializableContactList { 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. + * Constructs a {@code JsonSerializableContactList} with the given persons. */ @JsonCreator - public JsonSerializableAddressBook(@JsonProperty("persons") List persons) { + public JsonSerializableContactList(@JsonProperty("persons") List persons) { this.persons.addAll(persons); } /** - * Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use. + * Converts a given {@code ReadOnlyContactList} into this class for Jackson use. * - * @param source future changes to this will not affect the created {@code JsonSerializableAddressBook}. + * @param source future changes to this will not affect the created {@code JsonSerializableContactList}. */ - public JsonSerializableAddressBook(ReadOnlyAddressBook source) { + public JsonSerializableContactList(ReadOnlyContactList source) { persons.addAll(source.getPersonList().stream().map(JsonAdaptedPerson::new).collect(Collectors.toList())); } /** - * Converts this address book into the model's {@code AddressBook} object. + * Converts this contact list into the model's {@code ContactList} object. * * @throws IllegalValueException if there were any data constraints violated. */ - public AddressBook toModelType() throws IllegalValueException { - AddressBook addressBook = new AddressBook(); + public ContactList toModelType() throws IllegalValueException { + ContactList contactList = new ContactList(); for (JsonAdaptedPerson jsonAdaptedPerson : persons) { Person person = jsonAdaptedPerson.toModelType(); - if (addressBook.hasPerson(person)) { + if (contactList.hasPerson(person)) { throw new IllegalValueException(MESSAGE_DUPLICATE_PERSON); } - addressBook.addPerson(person); + contactList.addPerson(person); } - return addressBook; + return contactList; } } diff --git a/src/main/java/sweebook/storage/JsonSerializableTaskRecords.java b/src/main/java/sweebook/storage/JsonSerializableTaskRecords.java new file mode 100644 index 00000000000..fd96374121f --- /dev/null +++ b/src/main/java/sweebook/storage/JsonSerializableTaskRecords.java @@ -0,0 +1,54 @@ +package sweebook.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 sweebook.commons.exceptions.IllegalValueException; +import sweebook.model.ReadOnlyTaskRecords; +import sweebook.model.TaskRecords; +import sweebook.model.task.Task; + +/** + * An Immutable TaskRecords that is serializable to JSON format. + */ +@JsonRootName(value = "taskrecords") +class JsonSerializableTaskRecords { + private final List tasks = new ArrayList<>(); + + /** + * Constructs a {@code JsonSerializableTaskRecords} with the given persons. + */ + @JsonCreator + public JsonSerializableTaskRecords(@JsonProperty("tasks") List tasks) { + this.tasks.addAll(tasks); + } + + /** + * Converts a given {@code ReadOnlyTaskRecords} into this class for Jackson use. + * + * @param source future changes to this will not affect the created {@code JsonSerializableATaskRecords}. + */ + public JsonSerializableTaskRecords(ReadOnlyTaskRecords source) { + tasks.addAll(source.getTaskList().stream().map(JsonAdaptedTask::new).collect(Collectors.toList())); + } + + /** + * Converts this TaskRecords into the model's {@code TaskRecords} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public TaskRecords toModelType() throws IllegalValueException { + TaskRecords taskRecords = new TaskRecords(); + for (JsonAdaptedTask jsonAdaptedTask : tasks) { + Task task = jsonAdaptedTask.toModelType(); + taskRecords.addTask(task); + } + return taskRecords; + } + +} diff --git a/src/main/java/sweebook/storage/JsonTaskRecordsStorage.java b/src/main/java/sweebook/storage/JsonTaskRecordsStorage.java new file mode 100644 index 00000000000..2c29733fcc7 --- /dev/null +++ b/src/main/java/sweebook/storage/JsonTaskRecordsStorage.java @@ -0,0 +1,76 @@ +package sweebook.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 sweebook.commons.core.LogsCenter; +import sweebook.commons.exceptions.DataConversionException; +import sweebook.commons.exceptions.IllegalValueException; +import sweebook.commons.util.FileUtil; +import sweebook.commons.util.JsonUtil; +import sweebook.model.ReadOnlyTaskRecords; + +public class JsonTaskRecordsStorage implements TaskRecordsStorage { + private static final Logger logger = LogsCenter.getLogger(JsonTaskRecordsStorage.class); + + private Path filePath; + + public JsonTaskRecordsStorage(Path filePath) { + this.filePath = filePath; + } + + public Path getTaskRecordsFilePath() { + return filePath; + } + + @Override + public Optional readTaskRecords() throws DataConversionException { + return readTaskRecords(filePath); + } + + /** + * Read in task records from filepath specified. + * @param filePath Data file path + * @return TaskRecords which may be present or not + * @throws DataConversionException If there is any data conversion error + */ + public Optional readTaskRecords(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional jsonTaskRecords = JsonUtil.readJsonFile( + filePath, JsonSerializableTaskRecords.class); + if (!jsonTaskRecords.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonTaskRecords.get().toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + @Override + public void saveTaskRecords(ReadOnlyTaskRecords taskRecords) throws IOException { + saveTaskRecords(taskRecords, filePath); + } + + /** + * Save in task records into local data. + * @param taskRecords Task records to be saved + * @param filePath File path of local data file + * @throws IOException For any error with IO + */ + public void saveTaskRecords(ReadOnlyTaskRecords taskRecords, Path filePath) throws IOException { + requireNonNull(taskRecords); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonSerializableTaskRecords(taskRecords), filePath); + } +} diff --git a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java b/src/main/java/sweebook/storage/JsonUserPrefsStorage.java similarity index 83% rename from src/main/java/seedu/address/storage/JsonUserPrefsStorage.java rename to src/main/java/sweebook/storage/JsonUserPrefsStorage.java index bc2bbad84aa..aa652d53301 100644 --- a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java +++ b/src/main/java/sweebook/storage/JsonUserPrefsStorage.java @@ -1,13 +1,13 @@ -package seedu.address.storage; +package sweebook.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 sweebook.commons.exceptions.DataConversionException; +import sweebook.commons.util.JsonUtil; +import sweebook.model.ReadOnlyUserPrefs; +import sweebook.model.UserPrefs; /** * A class to access UserPrefs stored in the hard disk as a json file diff --git a/src/main/java/sweebook/storage/Storage.java b/src/main/java/sweebook/storage/Storage.java new file mode 100644 index 00000000000..b93fd56cbe9 --- /dev/null +++ b/src/main/java/sweebook/storage/Storage.java @@ -0,0 +1,42 @@ +package sweebook.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import sweebook.commons.exceptions.DataConversionException; +import sweebook.model.ReadOnlyContactList; +import sweebook.model.ReadOnlyTaskRecords; +import sweebook.model.ReadOnlyUserPrefs; +import sweebook.model.UserPrefs; + +/** + * API of the Storage component + */ +public interface Storage extends ContactListStorage, UserPrefsStorage, TaskRecordsStorage { + + @Override + Optional readUserPrefs() throws DataConversionException, IOException; + + @Override + void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; + + @Override + Path getContactListFilePath(); + + @Override + Optional readContactList() throws DataConversionException, IOException; + + @Override + void saveContactList(ReadOnlyContactList contactList) throws IOException; + + @Override + Path getTaskRecordsFilePath(); + + @Override + Optional readTaskRecords() throws DataConversionException, IOException; + + @Override + void saveTaskRecords(ReadOnlyTaskRecords taskRecords) throws IOException; + +} diff --git a/src/main/java/sweebook/storage/StorageManager.java b/src/main/java/sweebook/storage/StorageManager.java new file mode 100644 index 00000000000..a7812bb9028 --- /dev/null +++ b/src/main/java/sweebook/storage/StorageManager.java @@ -0,0 +1,113 @@ +package sweebook.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import sweebook.commons.core.LogsCenter; +import sweebook.commons.exceptions.DataConversionException; +import sweebook.model.ReadOnlyContactList; +import sweebook.model.ReadOnlyTaskRecords; +import sweebook.model.ReadOnlyUserPrefs; +import sweebook.model.UserPrefs; + +/** + * Manages storage of ContactList and TaskRecords data in local storage. + */ +public class StorageManager implements Storage { + + private static final Logger logger = LogsCenter.getLogger(StorageManager.class); + private ContactListStorage contactListStorage; + private UserPrefsStorage userPrefsStorage; + private TaskRecordsStorage taskRecordsStorage; + + /** + * Creates a {@code StorageManager} with the given {@code ContactListStorage} and {@code UserPrefStorage}. + */ + public StorageManager(ContactListStorage contactListStorage, UserPrefsStorage userPrefsStorage, + TaskRecordsStorage taskRecordsStorage) { + super(); + this.contactListStorage = contactListStorage; + this.userPrefsStorage = userPrefsStorage; + this.taskRecordsStorage = taskRecordsStorage; + } + + // ================ UserPrefs methods ============================== + + @Override + public Path getUserPrefsFilePath() { + return userPrefsStorage.getUserPrefsFilePath(); + } + + @Override + public Optional readUserPrefs() throws DataConversionException, IOException { + return userPrefsStorage.readUserPrefs(); + } + + @Override + public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { + userPrefsStorage.saveUserPrefs(userPrefs); + } + + + // ================ ContactList methods ============================== + + @Override + public Path getContactListFilePath() { + return contactListStorage.getContactListFilePath(); + } + + @Override + public Optional readContactList() throws DataConversionException, IOException { + return readContactList(contactListStorage.getContactListFilePath()); + } + + @Override + public Optional readContactList(Path filePath) throws DataConversionException, IOException { + logger.fine("Attempting to read data from file: " + filePath); + return contactListStorage.readContactList(filePath); + } + + @Override + public void saveContactList(ReadOnlyContactList contactList) throws IOException { + saveContactList(contactList, contactListStorage.getContactListFilePath()); + } + + @Override + public void saveContactList(ReadOnlyContactList contactList, Path filePath) throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + contactListStorage.saveContactList(contactList, filePath); + } + + + // ================ TaskRecords methods ============================== + + @Override + public Path getTaskRecordsFilePath() { + return taskRecordsStorage.getTaskRecordsFilePath(); + } + + @Override + public Optional readTaskRecords() throws DataConversionException, IOException { + return readTaskRecords(taskRecordsStorage.getTaskRecordsFilePath()); + } + + @Override + public Optional readTaskRecords(Path filePath) throws DataConversionException, IOException { + logger.fine("Attempting to read data from file: " + filePath); + return taskRecordsStorage.readTaskRecords(filePath); + } + + @Override + public void saveTaskRecords(ReadOnlyTaskRecords taskRecords) throws IOException { + saveTaskRecords(taskRecords, taskRecordsStorage.getTaskRecordsFilePath()); + } + + @Override + public void saveTaskRecords(ReadOnlyTaskRecords taskRecords, Path filePath) throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + taskRecordsStorage.saveTaskRecords(taskRecords, filePath); + } + +} diff --git a/src/main/java/sweebook/storage/TaskRecordsStorage.java b/src/main/java/sweebook/storage/TaskRecordsStorage.java new file mode 100644 index 00000000000..46333c801a8 --- /dev/null +++ b/src/main/java/sweebook/storage/TaskRecordsStorage.java @@ -0,0 +1,34 @@ +package sweebook.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import sweebook.commons.exceptions.DataConversionException; +import sweebook.model.ReadOnlyTaskRecords; + +public interface TaskRecordsStorage { + /** + * Returns the file path of the data file. + */ + Path getTaskRecordsFilePath(); + + /** + * Returns ContactList data as a {@link ReadOnlyTaskRecords}. + * 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 readTaskRecords() throws DataConversionException, IOException; + + Optional readTaskRecords(Path filePath) throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyTaskRecords} to the storage. + * @param taskRecords cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveTaskRecords(ReadOnlyTaskRecords taskRecords) throws IOException; + + void saveTaskRecords(ReadOnlyTaskRecords taskRecords, Path filePath) throws IOException; +} diff --git a/src/main/java/seedu/address/storage/UserPrefsStorage.java b/src/main/java/sweebook/storage/UserPrefsStorage.java similarity index 71% rename from src/main/java/seedu/address/storage/UserPrefsStorage.java rename to src/main/java/sweebook/storage/UserPrefsStorage.java index 29eef178dbc..ff9143178c2 100644 --- a/src/main/java/seedu/address/storage/UserPrefsStorage.java +++ b/src/main/java/sweebook/storage/UserPrefsStorage.java @@ -1,15 +1,15 @@ -package seedu.address.storage; +package sweebook.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 sweebook.commons.exceptions.DataConversionException; +import sweebook.model.ReadOnlyUserPrefs; +import sweebook.model.UserPrefs; /** - * Represents a storage for {@link seedu.address.model.UserPrefs}. + * Represents a storage for {@link 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 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/sweebook/ui/CommandBox.java similarity index 89% rename from src/main/java/seedu/address/ui/CommandBox.java rename to src/main/java/sweebook/ui/CommandBox.java index 9e75478664b..1f5380d9635 100644 --- a/src/main/java/seedu/address/ui/CommandBox.java +++ b/src/main/java/sweebook/ui/CommandBox.java @@ -1,12 +1,13 @@ -package seedu.address.ui; +package sweebook.ui; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.control.TextField; 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 sweebook.logic.Logic; +import sweebook.logic.commands.CommandResult; +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.logic.parser.exceptions.ParseException; /** * The UI component that is responsible for receiving user command inputs. @@ -77,7 +78,7 @@ public interface CommandExecutor { /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see 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/sweebook/ui/HelpWindow.java similarity index 92% rename from src/main/java/seedu/address/ui/HelpWindow.java rename to src/main/java/sweebook/ui/HelpWindow.java index 9a665915949..cca5bc03e2d 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/sweebook/ui/HelpWindow.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package sweebook.ui; import java.util.logging.Logger; @@ -8,14 +8,14 @@ import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.stage.Stage; -import seedu.address.commons.core.LogsCenter; +import sweebook.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 USERGUIDE_URL = "https://ay2122s1-cs2103t-w12-2.github.io/tp/UserGuide.html"; public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL; private static final Logger logger = LogsCenter.getLogger(HelpWindow.class); diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/sweebook/ui/MainWindow.java similarity index 90% rename from src/main/java/seedu/address/ui/MainWindow.java rename to src/main/java/sweebook/ui/MainWindow.java index 9106c3aa6e5..531f72f955b 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/sweebook/ui/MainWindow.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package sweebook.ui; import java.util.logging.Logger; @@ -10,12 +10,12 @@ import javafx.scene.input.KeyEvent; import javafx.scene.layout.StackPane; 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 sweebook.commons.core.GuiSettings; +import sweebook.commons.core.LogsCenter; +import sweebook.logic.Logic; +import sweebook.logic.commands.CommandResult; +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.logic.parser.exceptions.ParseException; /** * The Main Window. Provides the basic application layout containing @@ -32,6 +32,7 @@ public class MainWindow extends UiPart { // Independent Ui parts residing in this Ui container private PersonListPanel personListPanel; + private TaskListPanel taskListPanel; private ResultDisplay resultDisplay; private HelpWindow helpWindow; @@ -44,6 +45,9 @@ public class MainWindow extends UiPart { @FXML private StackPane personListPanelPlaceholder; + @FXML + private StackPane taskListPanelPlaceholder; + @FXML private StackPane resultDisplayPlaceholder; @@ -113,10 +117,13 @@ void fillInnerParts() { personListPanel = new PersonListPanel(logic.getFilteredPersonList()); personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); + taskListPanel = new TaskListPanel(logic.getFilteredTaskList()); + taskListPanelPlaceholder.getChildren().add(taskListPanel.getRoot()); + resultDisplay = new ResultDisplay(); resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); - StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath()); + StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getContactListFilePath()); statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot()); CommandBox commandBox = new CommandBox(this::executeCommand); @@ -170,7 +177,7 @@ public PersonListPanel getPersonListPanel() { /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see Logic#execute(String) */ private CommandResult executeCommand(String commandText) throws CommandException, ParseException { try { diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/sweebook/ui/PersonCard.java similarity index 54% rename from src/main/java/seedu/address/ui/PersonCard.java rename to src/main/java/sweebook/ui/PersonCard.java index 7fc927bc5d9..fcd1f18e3fb 100644 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ b/src/main/java/sweebook/ui/PersonCard.java @@ -1,13 +1,18 @@ -package seedu.address.ui; +package sweebook.ui; +import java.awt.Desktop; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; import java.util.Comparator; import javafx.fxml.FXML; +import javafx.scene.control.Hyperlink; 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; +import sweebook.model.person.Person; /** * An UI component that displays information of a {@code Person}. @@ -20,8 +25,6 @@ public class PersonCard extends UiPart { * 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; @@ -33,13 +36,15 @@ public class PersonCard extends UiPart { @FXML private Label id; @FXML - private Label phone; + private FlowPane groups; @FXML - private Label address; + private Label phone; @FXML private Label email; @FXML - private FlowPane tags; + private Hyperlink telegram; + @FXML + private Hyperlink github; /** * Creates a {@code PersonCode} with the given {@code Person} and index to display. @@ -50,11 +55,39 @@ public PersonCard(Person person, int displayedIndex) { 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))); + + person.getGroups().stream() + .sorted(Comparator.comparing(group -> group.group)) + .forEach(group -> groups.getChildren().add(new Label(group.group))); + + telegram.setText("Telegram: " + person.getTelegram().toString()); + String telegramUrl = person.getTelegram().toUrl(); + telegram.setOnAction(e -> { + openWebpage(telegramUrl); + }); + + github.setText("GitHub: " + person.getGitHub().toString()); + String githubUrl = person.getGitHub().toUrl(); + github.setOnAction(e -> { + openWebpage(githubUrl); + }); + } + + /** + * Loads a webpage by the specified url on the user's default browser + * Adapted from https://stackoverflow.com/a/5226244 + * + * @param url The url to load. + */ + public static void openWebpage(String url) { + if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { + try { + Desktop.getDesktop().browse(new URI(url)); + } catch (IOException | URISyntaxException e) { + e.printStackTrace(); + } + } } @Override diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/sweebook/ui/PersonListPanel.java similarity index 92% rename from src/main/java/seedu/address/ui/PersonListPanel.java rename to src/main/java/sweebook/ui/PersonListPanel.java index f4c501a897b..4b754ed51fc 100644 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ b/src/main/java/sweebook/ui/PersonListPanel.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package sweebook.ui; import java.util.logging.Logger; @@ -7,8 +7,8 @@ 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; +import sweebook.commons.core.LogsCenter; +import sweebook.model.person.Person; /** * Panel containing the list of persons. diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/sweebook/ui/ResultDisplay.java similarity index 95% rename from src/main/java/seedu/address/ui/ResultDisplay.java rename to src/main/java/sweebook/ui/ResultDisplay.java index 7d98e84eedf..edfbf220cbe 100644 --- a/src/main/java/seedu/address/ui/ResultDisplay.java +++ b/src/main/java/sweebook/ui/ResultDisplay.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package sweebook.ui; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/sweebook/ui/StatusBarFooter.java similarity index 96% rename from src/main/java/seedu/address/ui/StatusBarFooter.java rename to src/main/java/sweebook/ui/StatusBarFooter.java index b577f829423..7ce19cbc7ae 100644 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ b/src/main/java/sweebook/ui/StatusBarFooter.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package sweebook.ui; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/main/java/sweebook/ui/TaskCard.java b/src/main/java/sweebook/ui/TaskCard.java new file mode 100644 index 00000000000..3f6a5d79ae3 --- /dev/null +++ b/src/main/java/sweebook/ui/TaskCard.java @@ -0,0 +1,79 @@ +package sweebook.ui; + +import static java.util.Objects.isNull; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import sweebook.model.task.Task; + +/** + * An UI component that displays information of a {@code Task}. + */ +public class TaskCard extends UiPart { + + private static final String FXML = "TaskCard.fxml"; + + public final Task task; + + @FXML + private HBox cardPane; + @FXML + private Label id; + @FXML + private Label description; + @FXML + private Label taskType; + @FXML + private Label done; + @FXML + private Label date; + @FXML + private Label group; + @FXML + private Label recurringFrequency; + @FXML + private Label priority; + + /** + * Creates a {@code TaskCard} with the given {@code Task} and index to display. + */ + public TaskCard(Task task, int displayedIndex) { + super(FXML); + this.task = task; + id.setText(displayedIndex + ". "); + description.setText(task.getDescription().toString()); + taskType.setText(task.getTaskType().toString()); + done.setText(task.getStatusIcon()); + date.setText(isNull(task.getDate()) ? "" : task.getDate().toString()); + group.setText(task.getGroup().toString()); + recurringFrequency.setText(task.getRecurringFrequency().display()); + priority.setText(task.getPriorityIcon()); + if (task.getPriorityIcon().equals("!")) { + priority.setStyle("-fx-text-fill: white; -fx-background-color: green"); + } else if (task.getPriorityIcon().equals("!!")) { + priority.setStyle("-fx-text-fill: black; -fx-background-color: orange"); + } else { + priority.setStyle("-fx-text-fill: white; -fx-background-color: maroon"); + } + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof TaskCard)) { + return false; + } + + // state check + TaskCard card = (TaskCard) other; + return id.getText().equals(card.id.getText()) + && task.equals(card.task); + } +} diff --git a/src/main/java/sweebook/ui/TaskListPanel.java b/src/main/java/sweebook/ui/TaskListPanel.java new file mode 100644 index 00000000000..e58a9da444f --- /dev/null +++ b/src/main/java/sweebook/ui/TaskListPanel.java @@ -0,0 +1,49 @@ +package sweebook.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 sweebook.commons.core.LogsCenter; +import sweebook.model.task.Task; + +/** + * Panel containing the list of persons. + */ +public class TaskListPanel extends UiPart { + private static final String FXML = "TaskListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(TaskListPanel.class); + + @FXML + private ListView taskListView; + + /** + * Creates a {@code TaskListPanel} with the given {@code ObservableList}. + */ + public TaskListPanel(ObservableList taskList) { + super(FXML); + taskListView.setItems(taskList); + taskListView.setCellFactory(listView -> new TaskListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Task} using a {@code TaskCard}. + */ + class TaskListViewCell extends ListCell { + @Override + protected void updateItem(Task task, boolean empty) { + super.updateItem(task, empty); + + if (empty || task == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new TaskCard(task, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/Ui.java b/src/main/java/sweebook/ui/Ui.java similarity index 86% rename from src/main/java/seedu/address/ui/Ui.java rename to src/main/java/sweebook/ui/Ui.java index 17aa0b494fe..aa6de962072 100644 --- a/src/main/java/seedu/address/ui/Ui.java +++ b/src/main/java/sweebook/ui/Ui.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package sweebook.ui; import javafx.stage.Stage; diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/sweebook/ui/UiManager.java similarity index 74% rename from src/main/java/seedu/address/ui/UiManager.java rename to src/main/java/sweebook/ui/UiManager.java index 882027e4537..1d4723b1ecc 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/sweebook/ui/UiManager.java @@ -1,5 +1,6 @@ -package seedu.address.ui; +package sweebook.ui; +import java.util.ArrayList; import java.util.logging.Logger; import javafx.application.Platform; @@ -7,10 +8,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 sweebook.MainApp; +import sweebook.commons.core.LogsCenter; +import sweebook.commons.util.StringUtil; +import sweebook.logic.Logic; /** * The manager of the UI component. @@ -20,17 +21,19 @@ 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/sweebook.png"; private Logic logic; private MainWindow mainWindow; + private ArrayList warnings; /** * Creates a {@code UiManager} with the given {@code Logic}. */ - public UiManager(Logic logic) { + public UiManager(Logic logic, ArrayList warnings) { super(); this.logic = logic; + this.warnings = warnings; } @Override @@ -45,6 +48,15 @@ public void start(Stage primaryStage) { mainWindow.show(); //This should be called before creating other UI parts mainWindow.fillInnerParts(); + if (!warnings.isEmpty()) { + StringBuilder warningContentText = new StringBuilder(); + for (String s: warnings) { + warningContentText.append(s + "\n\n"); + } + showAlertDialogAndWait(AlertType.ERROR, "Error", "Error in initialising data file(s)", + warningContentText.toString()); + } + } catch (Throwable e) { logger.severe(StringUtil.getDetails(e)); showFatalErrorDialogAndShutdown("Fatal error during initializing", e); @@ -55,7 +67,7 @@ private Image getImage(String imagePath) { return new Image(MainApp.class.getResourceAsStream(imagePath)); } - void showAlertDialogAndWait(Alert.AlertType type, String title, String headerText, String contentText) { + private void showAlertDialogAndWait(Alert.AlertType type, String title, String headerText, String contentText) { showAlertDialogAndWait(mainWindow.getPrimaryStage(), type, title, headerText, contentText); } diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/sweebook/ui/UiPart.java similarity index 96% rename from src/main/java/seedu/address/ui/UiPart.java rename to src/main/java/sweebook/ui/UiPart.java index fc820e01a9c..96b06575eb5 100644 --- a/src/main/java/seedu/address/ui/UiPart.java +++ b/src/main/java/sweebook/ui/UiPart.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package sweebook.ui; import static java.util.Objects.requireNonNull; @@ -6,7 +6,7 @@ import java.net.URL; import javafx.fxml.FXMLLoader; -import seedu.address.MainApp; +import sweebook.MainApp; /** * Represents a distinct part of the UI. e.g. Windows, dialogs, panels, status bars, etc. @@ -45,7 +45,7 @@ public UiPart(URL fxmlFileUrl, T root) { /** * Constructs a UiPart with the specified FXML file within {@link #FXML_FILE_FOLDER} and root object. - * @see #UiPart(URL, T) + * @see #UiPart(URL, Object) */ public UiPart(String fxmlFileName, T root) { this(getFxmlFileUrl(fxmlFileName), root); diff --git a/src/main/resources/images/github-icon.png b/src/main/resources/images/github-icon.png new file mode 100644 index 00000000000..68df063c358 Binary files /dev/null and b/src/main/resources/images/github-icon.png differ diff --git a/src/main/resources/images/address_book_32.png b/src/main/resources/images/sweebook.png similarity index 100% rename from src/main/resources/images/address_book_32.png rename to src/main/resources/images/sweebook.png diff --git a/src/main/resources/images/telegram-icon.png b/src/main/resources/images/telegram-icon.png new file mode 100644 index 00000000000..f087ac031af Binary files /dev/null and b/src/main/resources/images/telegram-icon.png differ diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 9ce9bcfb569..efa6483e2d3 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -314,7 +314,7 @@ #commandTypeLabel { -fx-font-size: 11px; - -fx-text-fill: #F70D1A; + -fx-text-fill: #f70d1a; } #commandTextField { @@ -337,12 +337,20 @@ -fx-background-radius: 0; } -#tags { +#groups { -fx-hgap: 7; -fx-vgap: 3; } +#groups .label { + -fx-text-fill: white; + -fx-background-color: #3e7b91; + -fx-padding: 1 3 1 3; + -fx-border-radius: 2; + -fx-background-radius: 2; + -fx-font-size: 11; +} -#tags .label { +.group_badge { -fx-text-fill: white; -fx-background-color: #3e7b91; -fx-padding: 1 3 1 3; @@ -350,3 +358,19 @@ -fx-background-radius: 2; -fx-font-size: 11; } + +.taskType_badge { + -fx-text-fill: white; + -fx-background-color: #4B0082; + -fx-padding: 1 3 1 3; + -fx-border-radius: 2; + -fx-background-radius: 2; + -fx-font-size: 11; +} + +.priority_badge { + -fx-padding: 1 3 1 3; + -fx-border-radius: 2; + -fx-background-radius: 2; + -fx-font-size: 11; +} diff --git a/src/main/resources/view/HelpWindow.fxml b/src/main/resources/view/HelpWindow.fxml index c9a38f2b105..93604e2fb13 100644 --- a/src/main/resources/view/HelpWindow.fxml +++ b/src/main/resources/view/HelpWindow.fxml @@ -9,7 +9,7 @@ - + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index 32bcf2c8e70..924d2a14ea8 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -6,20 +6,20 @@ - + + - + - + - + @@ -34,25 +34,35 @@ - + - + - + - + - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml index f08ea32ad55..8e09947dab8 100644 --- a/src/main/resources/view/PersonListCard.fxml +++ b/src/main/resources/view/PersonListCard.fxml @@ -1,12 +1,14 @@ + + @@ -16,21 +18,40 @@ - + - + - - - + + + diff --git a/src/main/resources/view/TaskCard.fxml b/src/main/resources/view/TaskCard.fxml new file mode 100644 index 00000000000..0c6b3b3a6f6 --- /dev/null +++ b/src/main/resources/view/TaskCard.fxml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/TaskListPanel.fxml b/src/main/resources/view/TaskListPanel.fxml new file mode 100644 index 00000000000..9c4917370a7 --- /dev/null +++ b/src/main/resources/view/TaskListPanel.fxml @@ -0,0 +1,8 @@ + + + + + + + + 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/JsonContactListStorageTest/invalidAndValidPersonContactList.json b/src/test/data/JsonContactListStorageTest/invalidAndValidPersonContactList.json new file mode 100644 index 00000000000..67c10258a79 --- /dev/null +++ b/src/test/data/JsonContactListStorageTest/invalidAndValidPersonContactList.json @@ -0,0 +1,22 @@ +{ + "persons": [ { + "name": "Valid Person", + "group": [ + "CS2103T" + ], + "phone": "9482424", + "email": "hans@example.com", + "telegram": "valid", + "github": "valid" + }, + { + "name": "Person With Invalid Phone Field", + "group": [ + "CS2103T" + ], + "phone": "948asdf2424", + "email": "hans@example.com", + "telegram": "valid", + "github": "valid" + } ] +} diff --git a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json b/src/test/data/JsonContactListStorageTest/invalidPersonContactList.json similarity index 63% rename from src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json rename to src/test/data/JsonContactListStorageTest/invalidPersonContactList.json index ccd21f7d1a9..edab3a44aca 100644 --- a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json +++ b/src/test/data/JsonContactListStorageTest/invalidPersonContactList.json @@ -1,8 +1,12 @@ { "persons": [ { "name": "Person with invalid name field: Ha!ns Mu@ster", + "group": [ + "CS2103T" + ], "phone": "9482424", "email": "hans@example.com", - "address": "4th street" + "telegram": "hans", + "github": "hans" } ] } diff --git a/src/test/data/JsonAddressBookStorageTest/notJsonFormatAddressBook.json b/src/test/data/JsonContactListStorageTest/notJsonFormatContactList.json similarity index 100% rename from src/test/data/JsonAddressBookStorageTest/notJsonFormatAddressBook.json rename to src/test/data/JsonContactListStorageTest/notJsonFormatContactList.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/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/JsonSerializableContactListTest/duplicatePersonContactList.json b/src/test/data/JsonSerializableContactListTest/duplicatePersonContactList.json new file mode 100644 index 00000000000..705783d2ea0 --- /dev/null +++ b/src/test/data/JsonSerializableContactListTest/duplicatePersonContactList.json @@ -0,0 +1,24 @@ +{ + "persons": [ + { + "name": "Alice Pauline", + "groups": [ + "CS2103T" + ], + "phone": "94351253", + "email": "alice@example.com", + "github": "alice", + "telegram": "alice" + }, + { + "name": "Alice Pauline", + "groups": [ + "CS2101" + ], + "phone": "94351253", + "email": "pauline@example.com", + "github": "alice1", + "telegram": "alice1" + } + ] +} diff --git a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json b/src/test/data/JsonSerializableContactListTest/invalidPersonContactList.json similarity index 56% rename from src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json rename to src/test/data/JsonSerializableContactListTest/invalidPersonContactList.json index ad3f135ae42..ab1187a2239 100644 --- a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json +++ b/src/test/data/JsonSerializableContactListTest/invalidPersonContactList.json @@ -1,8 +1,12 @@ { "persons": [ { "name": "Hans Muster", + "groups": [ + "CS2103T" + ], "phone": "9482424", "email": "invalid@email!3e", - "address": "4th street" + "telegram": "hans", + "github": "hans" } ] } diff --git a/src/test/data/JsonSerializableContactListTest/typicalPersonsContactList.json b/src/test/data/JsonSerializableContactListTest/typicalPersonsContactList.json new file mode 100644 index 00000000000..098d0180520 --- /dev/null +++ b/src/test/data/JsonSerializableContactListTest/typicalPersonsContactList.json @@ -0,0 +1,76 @@ +{ + "_comment": "ContactList save file which contains the same Person values as in TypicalPersons#getTypicalContactList()", + "persons" : [ { + "name": "Alice Pauline", + "groups": [ + "CS2103T", + "CS2101" + ], + "phone": "94351253", + "email": "alice@example.com", + "telegram": "alice", + "github": "alice" + }, + { + "name": "Benson Meier", + "groups": [ + "CS2103T" + ], + "phone": "98765432", + "email": "johnd@example.com", + "telegram": "Bensonmeirer", + "github": "benson" + }, + { + "name": "Carl Kurz", + "groups": [ + "cs2101", + "cs2103t" + ], + "phone": "95352563", + "email": "heinz@example.com", + "telegram": "carl", + "github": "carl" + }, + { + "name": "Daniel Meier", + "groups": [ + "Cs2101" + ], + "phone": "87652533", + "email": "cornelia@example.com", + "telegram": "Daniel", + "github": "daniel" + }, + { + "name": "Elle Meyer", + "groups": [ + "CS2101", + "cs2103t" + ], + "phone": "9482224", + "email": "werner@example.com", + "telegram": "elle", + "github": "elle" + }, + { + "name": "Fiona Kunz", + "groups": [ + "CS2103T" + ], + "phone": "9482427", + "email": "lydia@example.com", + "telegram": "lydia", + "github": "lydia" + }, + { + "name": "George Best", + "groups": [ + "CS2103T" + ], + "phone": "9482442", + "email": "george@example.com", + "telegram": "george", + "github": "george" + } ] +} diff --git a/src/test/data/JsonSerializableTaskRecordsTest/invalidTaskTaskRecords.json b/src/test/data/JsonSerializableTaskRecordsTest/invalidTaskTaskRecords.json new file mode 100644 index 00000000000..f9a70ec1349 --- /dev/null +++ b/src/test/data/JsonSerializableTaskRecordsTest/invalidTaskTaskRecords.json @@ -0,0 +1,11 @@ +{ + "tasks" : [ { + "description" : "Deadline 1", + "status" : "[ ]", + "group" : "CS2101", + "date" : "Dec 03 2021", + "taskType" : "dead", + "recurringFrequency": "none", + "priority": "med" + } ] +} diff --git a/src/test/data/JsonSerializableTaskRecordsTest/typicalTaskTaskRecords.json b/src/test/data/JsonSerializableTaskRecordsTest/typicalTaskTaskRecords.json new file mode 100644 index 00000000000..4212051dac7 --- /dev/null +++ b/src/test/data/JsonSerializableTaskRecordsTest/typicalTaskTaskRecords.json @@ -0,0 +1,35 @@ +{ + "tasks" : [ { + "description" : "Deadline 1", + "status" : "[ ]", + "group" : "CS2101", + "date" : "Dec 03 2021", + "taskType" : "deadline", + "recurringFrequency": "none", + "priority": "med" + }, { + "description" : "event 1", + "status" : "[ ]", + "group" : "CS2103T", + "date" : "Feb 03 2022", + "taskType" : "event", + "recurringFrequency": "year", + "priority": "low" + }, { + "description": "todo 1", + "status": "[ ]", + "group": "CS2101", + "date": "Oct 29 2022", + "taskType": "todo", + "recurringFrequency": "week", + "priority": "high" + }, { + "description": "Deadline 2", + "status": "[ ]", + "group": "CS2103T", + "date": "Jul 01 2022", + "taskType": "deadline", + "recurringFrequency": "month", + "priority": "med" + } ] +} diff --git a/src/test/data/JsonTaskRecordsStorageTest/invalidAndValidTaskTaskRecords.json b/src/test/data/JsonTaskRecordsStorageTest/invalidAndValidTaskTaskRecords.json new file mode 100644 index 00000000000..6712edf9e1c --- /dev/null +++ b/src/test/data/JsonTaskRecordsStorageTest/invalidAndValidTaskTaskRecords.json @@ -0,0 +1,27 @@ +{ + "tasks" : [ { + "description" : "Update User Guide", + "status" : "[ ]", + "group" : "CS2103T", + "date" : "Dec 11 2021", + "taskType" : "deadline", + "recurringFrequency": "none", + "priority": "med" + }, { + "description" : "Project Meeting", + "status" : "[ ]", + "group" : "CS2103T", + "date" : "Dec 21 2021", + "taskType" : "event", + "recurringFrequency": "none", + "priority": "med" + }, { + "description" : "Task with invalid group", + "status" : "[ ]", + "group" : "bu83333", + "date" : "Nov 21 2021", + "taskType" : "event", + "recurringFrequency": "none", + "priority": "med" + } ] +} diff --git a/src/test/data/JsonTaskRecordsStorageTest/invalidTaskTaskRecords.json b/src/test/data/JsonTaskRecordsStorageTest/invalidTaskTaskRecords.json new file mode 100644 index 00000000000..fc6235451c1 --- /dev/null +++ b/src/test/data/JsonTaskRecordsStorageTest/invalidTaskTaskRecords.json @@ -0,0 +1,13 @@ +{ + "tasks": [ + { + "description": "Task with invalid date", + "status": "[ ]", + "group": "CS2103T", + "date": "44339900", + "taskType": "deadline", + "recurringFrequency": "none", + "priority": "med" + } + ] +} diff --git a/src/test/data/JsonTaskRecordsStorageTest/notJsonFormatTaskRecords.json b/src/test/data/JsonTaskRecordsStorageTest/notJsonFormatTaskRecords.json new file mode 100644 index 00000000000..a1097343b5d --- /dev/null +++ b/src/test/data/JsonTaskRecordsStorageTest/notJsonFormatTaskRecords.json @@ -0,0 +1 @@ +not json format! diff --git a/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json b/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json index 1037548a9cd..10b370fc35a 100644 --- a/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json +++ b/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json @@ -9,5 +9,5 @@ "z" : 99 } }, - "addressBookFilePath" : "addressbook.json" + "contactListFilePath" : "contactlist.json" } diff --git a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json index b819bed900a..dd6de1c426b 100644 --- a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json +++ b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json @@ -7,5 +7,5 @@ "y" : 100 } }, - "addressBookFilePath" : "addressbook.json" + "contactListFilePath" : "contactlist.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/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/ListCommandTest.java b/src/test/java/seedu/address/logic/commands/ListCommandTest.java deleted file mode 100644 index 435ff1f7275..00000000000 --- a/src/test/java/seedu/address/logic/commands/ListCommandTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package seedu.address.logic.commands; - -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; - -/** - * Contains integration tests (interaction with the Model) and unit tests for ListCommand. - */ -public class ListCommandTest { - - private Model model; - private Model expectedModel; - - @BeforeEach - public void setUp() { - model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - } - - @Test - public void execute_listIsNotFiltered_showsSameList() { - assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel); - } - - @Test - public void execute_listIsFiltered_showsEverything() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel); - } -} 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/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/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/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/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/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/sweebook/AppParametersTest.java similarity index 98% rename from src/test/java/seedu/address/AppParametersTest.java rename to src/test/java/sweebook/AppParametersTest.java index 61326b2d31a..7c6c46b8002 100644 --- a/src/test/java/seedu/address/AppParametersTest.java +++ b/src/test/java/sweebook/AppParametersTest.java @@ -1,4 +1,4 @@ -package seedu.address; +package sweebook; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/seedu/address/commons/core/ConfigTest.java b/src/test/java/sweebook/commons/core/ConfigTest.java similarity index 95% rename from src/test/java/seedu/address/commons/core/ConfigTest.java rename to src/test/java/sweebook/commons/core/ConfigTest.java index 07cd7f73d53..8b59dc87662 100644 --- a/src/test/java/seedu/address/commons/core/ConfigTest.java +++ b/src/test/java/sweebook/commons/core/ConfigTest.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package sweebook.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/sweebook/commons/core/VersionTest.java similarity index 98% rename from src/test/java/seedu/address/commons/core/VersionTest.java rename to src/test/java/sweebook/commons/core/VersionTest.java index 495cd231554..fcb9db550bd 100644 --- a/src/test/java/seedu/address/commons/core/VersionTest.java +++ b/src/test/java/sweebook/commons/core/VersionTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.core; +package sweebook.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 sweebook.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/sweebook/commons/core/index/IndexTest.java similarity index 95% rename from src/test/java/seedu/address/commons/core/index/IndexTest.java rename to src/test/java/sweebook/commons/core/index/IndexTest.java index a3ec6f8e747..cda4ed7dfc3 100644 --- a/src/test/java/seedu/address/commons/core/index/IndexTest.java +++ b/src/test/java/sweebook/commons/core/index/IndexTest.java @@ -1,9 +1,9 @@ -package seedu.address.commons.core.index; +package sweebook.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 sweebook.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/commons/util/AppUtilTest.java b/src/test/java/sweebook/commons/util/AppUtilTest.java similarity index 85% rename from src/test/java/seedu/address/commons/util/AppUtilTest.java rename to src/test/java/sweebook/commons/util/AppUtilTest.java index 594de1e6365..b89ead939f0 100644 --- a/src/test/java/seedu/address/commons/util/AppUtilTest.java +++ b/src/test/java/sweebook/commons/util/AppUtilTest.java @@ -1,7 +1,7 @@ -package seedu.address.commons.util; +package sweebook.commons.util; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static seedu.address.testutil.Assert.assertThrows; +import static sweebook.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/sweebook.png")); } @Test diff --git a/src/test/java/seedu/address/commons/util/CollectionUtilTest.java b/src/test/java/sweebook/commons/util/CollectionUtilTest.java similarity index 96% rename from src/test/java/seedu/address/commons/util/CollectionUtilTest.java rename to src/test/java/sweebook/commons/util/CollectionUtilTest.java index b467a3dc025..76ea55b8764 100644 --- a/src/test/java/seedu/address/commons/util/CollectionUtilTest.java +++ b/src/test/java/sweebook/commons/util/CollectionUtilTest.java @@ -1,9 +1,9 @@ -package seedu.address.commons.util; +package sweebook.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 sweebook.commons.util.CollectionUtil.requireAllNonNull; +import static sweebook.testutil.Assert.assertThrows; import java.util.Arrays; import java.util.Collection; @@ -12,6 +12,7 @@ import org.junit.jupiter.api.Test; + public class CollectionUtilTest { @Test public void requireAllNonNullVarargs() { diff --git a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java b/src/test/java/sweebook/commons/util/ConfigUtilTest.java similarity index 94% rename from src/test/java/seedu/address/commons/util/ConfigUtilTest.java rename to src/test/java/sweebook/commons/util/ConfigUtilTest.java index d2ab2839a52..e9fce00470b 100644 --- a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java +++ b/src/test/java/sweebook/commons/util/ConfigUtilTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.util; +package sweebook.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 sweebook.testutil.Assert.assertThrows; import java.io.IOException; import java.nio.file.Path; @@ -13,8 +13,9 @@ 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 sweebook.commons.core.Config; +import sweebook.commons.exceptions.DataConversionException; + public class ConfigUtilTest { diff --git a/src/test/java/seedu/address/commons/util/FileUtilTest.java b/src/test/java/sweebook/commons/util/FileUtilTest.java similarity index 84% rename from src/test/java/seedu/address/commons/util/FileUtilTest.java rename to src/test/java/sweebook/commons/util/FileUtilTest.java index 1fe5478c756..74bbb700b5b 100644 --- a/src/test/java/seedu/address/commons/util/FileUtilTest.java +++ b/src/test/java/sweebook/commons/util/FileUtilTest.java @@ -1,11 +1,12 @@ -package seedu.address.commons.util; +package sweebook.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 sweebook.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; + public class FileUtilTest { @Test diff --git a/src/test/java/seedu/address/commons/util/JsonUtilTest.java b/src/test/java/sweebook/commons/util/JsonUtilTest.java similarity index 92% rename from src/test/java/seedu/address/commons/util/JsonUtilTest.java rename to src/test/java/sweebook/commons/util/JsonUtilTest.java index d4907539dee..0e766f82e21 100644 --- a/src/test/java/seedu/address/commons/util/JsonUtilTest.java +++ b/src/test/java/sweebook/commons/util/JsonUtilTest.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package sweebook.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 sweebook.testutil.SerializableTestClass; +import sweebook.testutil.TestUtil; /** * Tests JSON Read and Write diff --git a/src/test/java/seedu/address/commons/util/StringUtilTest.java b/src/test/java/sweebook/commons/util/StringUtilTest.java similarity index 98% rename from src/test/java/seedu/address/commons/util/StringUtilTest.java rename to src/test/java/sweebook/commons/util/StringUtilTest.java index c56d407bf3f..7369cc1ce1e 100644 --- a/src/test/java/seedu/address/commons/util/StringUtilTest.java +++ b/src/test/java/sweebook/commons/util/StringUtilTest.java @@ -1,13 +1,14 @@ -package seedu.address.commons.util; +package sweebook.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 sweebook.testutil.Assert.assertThrows; import java.io.FileNotFoundException; import org.junit.jupiter.api.Test; + public class StringUtilTest { //---------------- Tests for isNonZeroUnsignedInteger -------------------------------------- diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/sweebook/logic/LogicManagerTest.java similarity index 63% rename from src/test/java/seedu/address/logic/LogicManagerTest.java rename to src/test/java/sweebook/logic/LogicManagerTest.java index ad923ac249a..3d90699c9a2 100644 --- a/src/test/java/seedu/address/logic/LogicManagerTest.java +++ b/src/test/java/sweebook/logic/LogicManagerTest.java @@ -1,14 +1,15 @@ -package seedu.address.logic; +package sweebook.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 sweebook.commons.core.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; +import static sweebook.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static sweebook.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; +import static sweebook.logic.commands.CommandTestUtil.GITHUB_DESC_AMY; +import static sweebook.logic.commands.CommandTestUtil.GROUP_DESC_AMY; +import static sweebook.logic.commands.CommandTestUtil.NAME_DESC_AMY; +import static sweebook.logic.commands.CommandTestUtil.PHONE_DESC_AMY; +import static sweebook.logic.commands.CommandTestUtil.TELEGRAM_DESC_AMY; +import static sweebook.testutil.Assert.assertThrows; import java.io.IOException; import java.nio.file.Path; @@ -17,20 +18,23 @@ 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 sweebook.logic.commands.AddCommand; +import sweebook.logic.commands.CommandResult; +import sweebook.logic.commands.ListCommand; +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.logic.parser.exceptions.ParseException; +import sweebook.model.Model; +import sweebook.model.ModelManager; +import sweebook.model.ReadOnlyContactList; +import sweebook.model.TaskRecords; +import sweebook.model.UserPrefs; +import sweebook.model.person.Person; +import sweebook.storage.JsonContactListStorage; +import sweebook.storage.JsonTaskRecordsStorage; +import sweebook.storage.JsonUserPrefsStorage; +import sweebook.storage.StorageManager; +import sweebook.testutil.PersonBuilder; +import sweebook.testutil.TypicalPersons; public class LogicManagerTest { private static final IOException DUMMY_IO_EXCEPTION = new IOException("dummy exception"); @@ -43,10 +47,12 @@ public class LogicManagerTest { @BeforeEach public void setUp() { - JsonAddressBookStorage addressBookStorage = - new JsonAddressBookStorage(temporaryFolder.resolve("addressBook.json")); + JsonContactListStorage contactListStorage = + new JsonContactListStorage(temporaryFolder.resolve("contactList.json")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("userPrefs.json")); - StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage); + JsonTaskRecordsStorage taskRecordsStorage = + new JsonTaskRecordsStorage(temporaryFolder.resolve("taskRecords.json")); + StorageManager storage = new StorageManager(contactListStorage, userPrefsStorage, taskRecordsStorage); logic = new LogicManager(model, storage); } @@ -70,18 +76,20 @@ 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 JsonContactListIoExceptionThrowingStub + JsonContactListStorage contactListStorage = + new JsonContactListIoExceptionThrowingStub(temporaryFolder.resolve("ioExceptionContactList.json")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("ioExceptionUserPrefs.json")); - StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage); + JsonTaskRecordsStorage taskRecordsStorage = + new JsonTaskRecordsStorage(temporaryFolder.resolve("taskRecords.json")); + StorageManager storage = new StorageManager(contactListStorage, userPrefsStorage, taskRecordsStorage); 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 + NAME_DESC_AMY + GROUP_DESC_AMY + + PHONE_DESC_AMY + EMAIL_DESC_AMY + TELEGRAM_DESC_AMY + GITHUB_DESC_AMY; + Person expectedPerson = new PersonBuilder(TypicalPersons.AMY).build(); ModelManager expectedModel = new ModelManager(); expectedModel.addPerson(expectedPerson); String expectedMessage = LogicManager.FILE_OPS_ERROR_MESSAGE + DUMMY_IO_EXCEPTION; @@ -129,7 +137,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.getContactList(), new UserPrefs(), new TaskRecords()); assertCommandFailure(inputCommand, expectedException, expectedMessage, expectedModel); } @@ -149,13 +157,13 @@ private void assertCommandFailure(String inputCommand, Class addCommand.execute(modelStub)); + assertThrows(CommandException.class, + String.format(Person.MESSAGE_DUPLICATE_PERSON, "name", validPerson.getName()), () -> + addCommand.execute(modelStub)); } @Test @@ -99,12 +104,12 @@ public void setGuiSettings(GuiSettings guiSettings) { } @Override - public Path getAddressBookFilePath() { + public Path getContactListFilePath() { throw new AssertionError("This method should not be called."); } @Override - public void setAddressBookFilePath(Path addressBookFilePath) { + public void setContactListFilePath(Path contactListFilePath) { throw new AssertionError("This method should not be called."); } @@ -114,12 +119,12 @@ public void addPerson(Person person) { } @Override - public void setAddressBook(ReadOnlyAddressBook newData) { + public void setContactList(ReadOnlyContactList newData) { throw new AssertionError("This method should not be called."); } @Override - public ReadOnlyAddressBook getAddressBook() { + public ReadOnlyContactList getContactList() { throw new AssertionError("This method should not be called."); } @@ -128,6 +133,11 @@ public boolean hasPerson(Person person) { throw new AssertionError("This method should not be called."); } + @Override + public String getSamePersonConstraintMessage(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."); @@ -147,6 +157,51 @@ public ObservableList getFilteredPersonList() { public void updateFilteredPersonList(Predicate predicate) { throw new AssertionError("This method should not be called."); } + + @Override + public ReadOnlyTaskRecords getTaskList() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addTask(Task toAdd) { + throw new AssertionError("This method should not be called."); + } + + @Override + public Task deleteTask(Task task) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setTask(Task target, Task editedTask) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ObservableList getTasks() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void doneTask(Task task) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void undoDoneTask(Task task) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateSortedTaskList(SortTaskComparator comparator) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateFilteredTaskList(Predicate predicate) { + throw new AssertionError("This method should not be called."); + } } /** @@ -165,6 +220,12 @@ public boolean hasPerson(Person person) { requireNonNull(person); return this.person.isSamePerson(person); } + + @Override + public String getSamePersonConstraintMessage(Person otherPerson) { + requireNonNull(person); + return person.getSamePersonConstraintMessage(otherPerson); + } } /** @@ -185,9 +246,10 @@ public void addPerson(Person person) { personsAdded.add(person); } + @Override - public ReadOnlyAddressBook getAddressBook() { - return new AddressBook(); + public ReadOnlyContactList getContactList() { + return new ContactList(); } } diff --git a/src/test/java/sweebook/logic/commands/AddTaskCommandTest.java b/src/test/java/sweebook/logic/commands/AddTaskCommandTest.java new file mode 100644 index 00000000000..d7bbdcf4e8e --- /dev/null +++ b/src/test/java/sweebook/logic/commands/AddTaskCommandTest.java @@ -0,0 +1,202 @@ +package sweebook.logic.commands; + +import static java.util.Objects.requireNonNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static sweebook.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 sweebook.commons.core.GuiSettings; +import sweebook.model.Model; +import sweebook.model.ReadOnlyContactList; +import sweebook.model.ReadOnlyTaskRecords; +import sweebook.model.ReadOnlyUserPrefs; +import sweebook.model.TaskRecords; +import sweebook.model.person.Person; +import sweebook.model.task.SortTaskComparator; +import sweebook.model.task.Task; +import sweebook.testutil.TaskBuilder; + +/** + * Contains integration tests (interaction with the Model). + */ +public class AddTaskCommandTest { + //null model + @Test + public void constructor_nullTask_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new AddTaskCommand(null)); + } + + //successful Task added + @Test + public void execute_taskAcceptedByModel_addSuccessful() throws Exception { + AddTaskCommandTest.ModelStubAcceptingTaskAdded modelStub = new AddTaskCommandTest.ModelStubAcceptingTaskAdded(); + Task validTask = new TaskBuilder().build(); + + CommandResult commandResult = new AddTaskCommand(validTask).execute(modelStub); + + assertEquals(String.format(AddTaskCommand.MESSAGE_SUCCESS, validTask), commandResult.getFeedbackToUser()); + assertEquals(Arrays.asList(validTask), modelStub.tasksAdded); + } + + /** + * 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 getContactListFilePath() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setContactListFilePath(Path contactListFilePath) { + 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 setContactList(ReadOnlyContactList newData) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ReadOnlyContactList getContactList() { + 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 String getSamePersonConstraintMessage(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."); + } + + @Override + public ReadOnlyTaskRecords getTaskList() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addTask(Task toAdd) { + throw new AssertionError("This method should not be called."); + } + + @Override + public Task deleteTask(Task task) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setTask(Task target, Task editedTask) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void doneTask(Task task) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void undoDoneTask(Task task) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ObservableList getTasks() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateSortedTaskList(SortTaskComparator comparator) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateFilteredTaskList(Predicate predicate) { + throw new AssertionError("This method should not be called."); + } + } + + /** + * A Model stub that contains a single Task. + */ + private class ModelStubWithTask extends AddTaskCommandTest.ModelStub { + private final Task task; + + ModelStubWithTask(Task task) { + requireNonNull(task); + this.task = task; + } + } + + /** + * A Model stub that always accept the task being added. + */ + private class ModelStubAcceptingTaskAdded extends AddTaskCommandTest.ModelStub { + final ArrayList tasksAdded = new ArrayList<>(); + + @Override + public void addTask(Task toAdd) { + requireNonNull(toAdd); + tasksAdded.add(toAdd); + } + + @Override + public ReadOnlyTaskRecords getTaskList() { + return new TaskRecords(); + } + } +} diff --git a/src/test/java/sweebook/logic/commands/ClearCommandTest.java b/src/test/java/sweebook/logic/commands/ClearCommandTest.java new file mode 100644 index 00000000000..7c65c4f02e7 --- /dev/null +++ b/src/test/java/sweebook/logic/commands/ClearCommandTest.java @@ -0,0 +1,34 @@ +package sweebook.logic.commands; + +import static sweebook.logic.commands.ClearCommand.MESSAGE_SUCCESS; +import static sweebook.logic.commands.CommandTestUtil.assertCommandSuccess; +import static sweebook.testutil.TypicalPersons.getTypicalContactList; + +import org.junit.jupiter.api.Test; + +import sweebook.model.ContactList; +import sweebook.model.Model; +import sweebook.model.ModelManager; +import sweebook.model.TaskRecords; +import sweebook.model.UserPrefs; + +public class ClearCommandTest { + + @Test + public void execute_emptyContactList_success() { + Model model = new ModelManager(); + Model expectedModel = new ModelManager(); + + assertCommandSuccess(new ClearCommand(), model, MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_nonEmptyContactList_success() { + Model model = new ModelManager(getTypicalContactList(), new UserPrefs(), new TaskRecords()); + Model expectedModel = new ModelManager(getTypicalContactList(), new UserPrefs(), new TaskRecords()); + expectedModel.setContactList(new ContactList()); + + assertCommandSuccess(new ClearCommand(), model, MESSAGE_SUCCESS, expectedModel); + } + +} diff --git a/src/test/java/seedu/address/logic/commands/CommandResultTest.java b/src/test/java/sweebook/logic/commands/CommandResultTest.java similarity index 98% rename from src/test/java/seedu/address/logic/commands/CommandResultTest.java rename to src/test/java/sweebook/logic/commands/CommandResultTest.java index 4f3eb46e9ef..8c2c8993958 100644 --- a/src/test/java/seedu/address/logic/commands/CommandResultTest.java +++ b/src/test/java/sweebook/logic/commands/CommandResultTest.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package sweebook.logic.commands; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/src/test/java/sweebook/logic/commands/CommandTestUtil.java b/src/test/java/sweebook/logic/commands/CommandTestUtil.java new file mode 100644 index 00000000000..84ac4d0fd9a --- /dev/null +++ b/src/test/java/sweebook/logic/commands/CommandTestUtil.java @@ -0,0 +1,226 @@ +package sweebook.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static sweebook.logic.parser.CliSyntax.PREFIX_DATE; +import static sweebook.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static sweebook.logic.parser.CliSyntax.PREFIX_EMAIL; +import static sweebook.logic.parser.CliSyntax.PREFIX_GITHUB; +import static sweebook.logic.parser.CliSyntax.PREFIX_GROUP; +import static sweebook.logic.parser.CliSyntax.PREFIX_NAME; +import static sweebook.logic.parser.CliSyntax.PREFIX_PHONE; +import static sweebook.logic.parser.CliSyntax.PREFIX_PRIORITY; +import static sweebook.logic.parser.CliSyntax.PREFIX_RECURRING_FREQUENCY; +import static sweebook.logic.parser.CliSyntax.PREFIX_TASKTYPE; +import static sweebook.logic.parser.CliSyntax.PREFIX_TELEGRAM; +import static sweebook.testutil.Assert.assertThrows; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import sweebook.commons.core.index.Index; +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.model.ContactList; +import sweebook.model.Model; +import sweebook.model.person.NameContainsKeywordsPredicate; +import sweebook.model.person.Person; +import sweebook.model.task.FilterTaskPredicate; +import sweebook.model.task.Task; +import sweebook.testutil.EditPersonDescriptorBuilder; +import sweebook.testutil.EditTaskDescriptorBuilder; + +/** + * Contains helper methods for testing commands. + */ +public class CommandTestUtil { + // contact list + 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_GROUP_AMY_CS2103T = "CS2103T"; + public static final String VALID_GROUP_AMY_CS2101 = "CS2101"; + public static final String VALID_GROUP_BOB = "CS2101"; + public static final String VALID_TELEGRAM_AMY = "amy"; + public static final String VALID_TELEGRAM_BOB = "bob"; + public static final String VALID_GITHUB_AMY = "amy"; + public static final String VALID_GITHUB_BOB = "bob"; + + + public static final String NAME_DESC_AMY = " " + PREFIX_NAME + VALID_NAME_AMY; + public static final String NAME_DESC_BOB = " " + PREFIX_NAME + VALID_NAME_BOB; + public static final String PHONE_DESC_AMY = " " + PREFIX_PHONE + VALID_PHONE_AMY; + public static final String PHONE_DESC_BOB = " " + PREFIX_PHONE + VALID_PHONE_BOB; + public static final String EMAIL_DESC_AMY = " " + PREFIX_EMAIL + VALID_EMAIL_AMY; + public static final String EMAIL_DESC_BOB = " " + PREFIX_EMAIL + VALID_EMAIL_BOB; + public static final String GROUP_DESC_AMY = " " + PREFIX_GROUP + VALID_GROUP_AMY_CS2103T + + " " + PREFIX_GROUP + VALID_GROUP_AMY_CS2101; + public static final String GROUP_DESC_BOB = " " + PREFIX_GROUP + VALID_GROUP_BOB; + public static final String TELEGRAM_DESC_AMY = " " + PREFIX_TELEGRAM + VALID_TELEGRAM_AMY; + public static final String TELEGRAM_DESC_BOB = " " + PREFIX_TELEGRAM + VALID_TELEGRAM_BOB; + public static final String GITHUB_DESC_AMY = " " + PREFIX_GITHUB + VALID_GITHUB_AMY; + public static final String GITHUB_DESC_BOB = " " + PREFIX_GITHUB + VALID_GITHUB_BOB; + + + 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_GROUP_DESC = " " + PREFIX_GROUP + "CS2122"; // group must be cs2103t or cs2101 + + 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) + .withGroups(VALID_GROUP_AMY_CS2103T, VALID_GROUP_AMY_CS2101) + .withTelegram(VALID_TELEGRAM_AMY).withGitHub(VALID_GITHUB_AMY).build(); + DESC_BOB = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB) + .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB) + .withGroups(VALID_GROUP_BOB).withTelegram(VALID_TELEGRAM_BOB).withGitHub(VALID_GITHUB_BOB).build(); + } + + // tasks + public static final String VALID_DESCRIPTION_TODO = "Update DG"; + public static final String VALID_DESCRIPTION_DEADLINE = "v1.3 milestone"; + public static final String VALID_DESCRIPTION_EVENT = "Project meeting"; + public static final String VALID_PRIORITY_LOW = "low"; + public static final String VALID_PRIORITY_MED = "med"; + public static final String VALID_PRIORITY_HIGH = "high"; + public static final String VALID_TASK_TYPE_TODO = "todo"; + public static final String VALID_TASK_TYPE_DEADLINE = "deadline"; + public static final String VALID_TASK_TYPE_EVENT = "event"; + public static final String VALID_DATE_1 = "2021-11-11"; + public static final String VALID_DATE_2 = "2021-11-12"; + public static final String VALID_DATE_3 = "2021-11-13"; + public static final String VALID_RECURRING_FREQUENCY_WEEK = "week"; + public static final String VALID_RECURRING_FREQUENCY_MONTH = "month"; + public static final String VALID_RECURRING_FREQUENCY_YEAR = "year"; + public static final String VALID_GROUP_CS2103T = "CS2103T"; + public static final String VALID_GROUP_CS2101 = "CS2101"; + + public static final String DESCRIPTION_DESC_TODO = " " + PREFIX_DESCRIPTION + VALID_DESCRIPTION_TODO; + public static final String DESCRIPTION_DESC_DEADLINE = " " + PREFIX_DESCRIPTION + VALID_DESCRIPTION_DEADLINE; + public static final String DESCRIPTION_DESC_EVENT = " " + PREFIX_DESCRIPTION + VALID_DESCRIPTION_EVENT; + public static final String PRIORITY_DESC_TODO = " " + PREFIX_PRIORITY + VALID_PRIORITY_LOW; + public static final String PRIORITY_DESC_DEADLINE = " " + PREFIX_PRIORITY + VALID_PRIORITY_MED; + public static final String PRIORITY_DESC_EVENT = " " + PREFIX_PRIORITY + VALID_PRIORITY_HIGH; + public static final String TASK_TYPE_DESC_TODO = " " + PREFIX_TASKTYPE + VALID_TASK_TYPE_TODO; + public static final String TASK_TYPE_DESC_DEADLINE = " " + PREFIX_TASKTYPE + VALID_TASK_TYPE_DEADLINE; + public static final String TASK_TYPE_DESC_EVENT = " " + PREFIX_TASKTYPE + VALID_TASK_TYPE_EVENT; + public static final String DATE_DESC_TODO = " " + PREFIX_DATE + VALID_DATE_1; + public static final String DATE_DESC_DEADLINE = " " + PREFIX_DATE + VALID_DATE_2; + public static final String DATE_DESC_EVENT = " " + PREFIX_DATE + VALID_DATE_3; + public static final String RECURRING_FREQUENCY_DESC_TODO = " " + PREFIX_RECURRING_FREQUENCY + + VALID_RECURRING_FREQUENCY_WEEK; + public static final String RECURRING_FREQUENCY_DESC_DEADLINE = " " + PREFIX_RECURRING_FREQUENCY + + VALID_RECURRING_FREQUENCY_MONTH; + public static final String RECURRING_FREQUENCY_DESC_EVENT = " " + PREFIX_RECURRING_FREQUENCY + + VALID_RECURRING_FREQUENCY_YEAR; + public static final String GROUP_DESC_TODO = " " + PREFIX_GROUP + VALID_GROUP_CS2101; + public static final String GROUP_DESC_DEADLINE = " " + PREFIX_GROUP + VALID_GROUP_CS2103T; + public static final String GROUP_DESC_EVENT = " " + PREFIX_GROUP + VALID_GROUP_CS2101; + + public static final String INVALID_DESCRIPTION_DESC = " " + PREFIX_DESCRIPTION; // empty description not allowed + // priority is only low, med, or high + public static final String INVALID_PRIORITY_DESC = " " + PREFIX_PRIORITY + "extreme"; + public static final String INVALID_TASK_TYPE_DESC = " " + PREFIX_TASKTYPE + "aaa"; // should be todo/deadline/event + public static final String INVALID_DATE_DESC = " " + PREFIX_DATE + "2021-20-20"; // invalid month + // should be only week/month/year + public static final String INVALID_RECURRING_FREQUENCY_DESC = " " + PREFIX_RECURRING_FREQUENCY + "biweekly"; + + public static final EditTaskCommand.EditTaskDescriptor DESC_TODO; + public static final EditTaskCommand.EditTaskDescriptor DESC_DEADLINE; + public static final EditTaskCommand.EditTaskDescriptor DESC_EVENT; + public static final String PREAMBLE_WHITESPACE = "\t \r \n"; + public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble"; + + static { + DESC_TODO = new EditTaskDescriptorBuilder().withDescription(VALID_DESCRIPTION_TODO).withDate(VALID_DATE_1) + .withPriority(VALID_PRIORITY_LOW).withTaskType(VALID_TASK_TYPE_TODO).withGroup(VALID_GROUP_CS2101) + .withRecurringFrequency(VALID_RECURRING_FREQUENCY_WEEK).build(); + DESC_DEADLINE = new EditTaskDescriptorBuilder().withDescription(VALID_DESCRIPTION_DEADLINE) + .withDate(VALID_DATE_2) + .withPriority(VALID_PRIORITY_MED).withTaskType(VALID_TASK_TYPE_DEADLINE).withGroup(VALID_GROUP_CS2103T) + .withRecurringFrequency(VALID_RECURRING_FREQUENCY_MONTH).build(); + DESC_EVENT = new EditTaskDescriptorBuilder().withDescription(VALID_DESCRIPTION_EVENT).withDate(VALID_DATE_3) + .withPriority(VALID_PRIORITY_HIGH).withTaskType(VALID_TASK_TYPE_EVENT).withGroup(VALID_GROUP_CS2101) + .withRecurringFrequency(VALID_RECURRING_FREQUENCY_YEAR).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 contact list, 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. + ContactList expectedContactList = new ContactList(actualModel.getContactList()); + List expectedFilteredList = new ArrayList<>(actualModel.getFilteredPersonList()); + + assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel)); + assertEquals(expectedContactList, actualModel.getContactList()); + 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 contact list. + */ + 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()); + } + + /** + * Updates {@code model}'s filtered list to show the task at the given {@code targetIndex} in the + * {@code model}'s task records. + * TODO: Implement duplicate detection for tasks so this method only shows a unique task + */ + public static void showTaskAtIndex(Model model, Index targetIndex) { + assertTrue(targetIndex.getZeroBased() < model.getTasks().size()); + + Task task = model.getTasks().get(targetIndex.getZeroBased()); + model.updateFilteredTaskList( + new FilterTaskPredicate(PREFIX_DESCRIPTION.getPrefix() + + task.getDescription().description)); + + assertEquals(1, model.getTasks().size()); + } + +} diff --git a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java b/src/test/java/sweebook/logic/commands/DeleteCommandTest.java similarity index 65% rename from src/test/java/seedu/address/logic/commands/DeleteCommandTest.java rename to src/test/java/sweebook/logic/commands/DeleteCommandTest.java index 45a8c910ba1..56c4d136ec8 100644 --- a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java +++ b/src/test/java/sweebook/logic/commands/DeleteCommandTest.java @@ -1,22 +1,21 @@ -package seedu.address.logic.commands; +package sweebook.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 static sweebook.logic.commands.CommandTestUtil.assertCommandSuccess; +import static sweebook.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static sweebook.testutil.TypicalIndexes.INDEX_SECOND_PERSON; 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; +import sweebook.commons.core.Messages; +import sweebook.commons.core.index.Index; +import sweebook.model.Model; +import sweebook.model.ModelManager; +import sweebook.model.TaskRecords; +import sweebook.model.UserPrefs; +import sweebook.model.person.Person; +import sweebook.testutil.TypicalPersons; /** * Contains integration tests (interaction with the Model) and unit tests for @@ -24,7 +23,7 @@ */ public class DeleteCommandTest { - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + private Model model = new ModelManager(TypicalPersons.getTypicalContactList(), new UserPrefs(), new TaskRecords()); @Test public void execute_validIndexUnfilteredList_success() { @@ -33,7 +32,7 @@ public void execute_validIndexUnfilteredList_success() { String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); - ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + ModelManager expectedModel = new ModelManager(model.getContactList(), new UserPrefs(), new TaskRecords()); expectedModel.deletePerson(personToDelete); assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); @@ -44,19 +43,19 @@ 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); + CommandTestUtil.assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); } @Test public void execute_validIndexFilteredList_success() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); + CommandTestUtil.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()); + Model expectedModel = new ModelManager(model.getContactList(), new UserPrefs(), new TaskRecords()); expectedModel.deletePerson(personToDelete); showNoPerson(expectedModel); @@ -65,15 +64,15 @@ public void execute_validIndexFilteredList_success() { @Test public void execute_invalidIndexFilteredList_throwsCommandException() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); + CommandTestUtil.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()); + // ensures that outOfBoundIndex is still in bounds of contact list + assertTrue(outOfBoundIndex.getZeroBased() < model.getContactList().getPersonList().size()); DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex); - assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + CommandTestUtil.assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); } @Test diff --git a/src/test/java/sweebook/logic/commands/DeleteTaskCommandTest.java b/src/test/java/sweebook/logic/commands/DeleteTaskCommandTest.java new file mode 100644 index 00000000000..73afcea9c17 --- /dev/null +++ b/src/test/java/sweebook/logic/commands/DeleteTaskCommandTest.java @@ -0,0 +1,70 @@ +package sweebook.logic.commands; + +import static sweebook.commons.core.Messages.MESSAGE_INVALID_TASK_INDEX; +import static sweebook.logic.commands.CommandTestUtil.assertCommandSuccess; +import static sweebook.testutil.TypicalIndexes.INDEX_FIRST_TASK; +import static sweebook.testutil.TypicalIndexes.INDEX_SECOND_TASK; +import static sweebook.testutil.TypicalPersons.getTypicalContactList; +import static sweebook.testutil.TypicalTasks.getTypicalTaskRecords; + +import org.junit.jupiter.api.Test; + +import javafx.collections.ObservableList; +import sweebook.commons.core.index.Index; +import sweebook.model.Model; +import sweebook.model.ModelManager; +import sweebook.model.UserPrefs; +import sweebook.model.task.Task; + +/** + * Contains integration tests (interaction with the Model) and unit tests for + * {@code DeleteTaskCommand}. + */ +public class DeleteTaskCommandTest { + private Model model = new ModelManager(getTypicalContactList(), new UserPrefs(), getTypicalTaskRecords()); + private ObservableList taskList = model.getTasks(); + private Index lastTaskIndex = Index.fromOneBased(taskList.size()); + + // valid task index [1...total_number_of_tasks] + @Test + public void execute_validIndex_success() { + Task lastTask = model.getTasks().get(lastTaskIndex.getZeroBased()); //Boundary value + DeleteTaskCommand deleteTaskCommand = new DeleteTaskCommand(lastTaskIndex); + String deleteLastTaskExpectedMessage = String.format(DeleteTaskCommand.MESSAGE_SUCCESS, lastTask); + + ModelManager deleteLastTaskExpectedModel = new ModelManager(model.getContactList(), + new UserPrefs(), model.getTaskList()); + deleteLastTaskExpectedModel.deleteTask(lastTask); + assertCommandSuccess(deleteTaskCommand, model, deleteLastTaskExpectedMessage, deleteLastTaskExpectedModel); + + Task firstTask = taskList.get(INDEX_FIRST_TASK.getZeroBased()); //Boundary value + DeleteTaskCommand deleteFirstTaskCommand = new DeleteTaskCommand(INDEX_FIRST_TASK); + String deleteFirstTaskExpectedMessage = String.format(DeleteTaskCommand.MESSAGE_SUCCESS, firstTask); + + ModelManager deleteFirstTaskExpectedModel = new ModelManager(model.getContactList(), + new UserPrefs(), model.getTaskList()); + deleteFirstTaskExpectedModel.deleteTask(firstTask); + assertCommandSuccess(deleteFirstTaskCommand, model, deleteFirstTaskExpectedMessage, + deleteFirstTaskExpectedModel); + + Task secondTask = model.getTasks().get(INDEX_SECOND_TASK.getZeroBased()); + DeleteTaskCommand deleteSecondTaskCommand = new DeleteTaskCommand(INDEX_SECOND_TASK); + String deleteSecondTaskExpectedMessage = String.format(DeleteTaskCommand.MESSAGE_SUCCESS, secondTask); + + ModelManager deleteSecondTaskExpectedModel = new ModelManager(model.getContactList(), + new UserPrefs(), model.getTaskList()); + deleteSecondTaskExpectedModel.deleteTask(secondTask); + assertCommandSuccess(deleteSecondTaskCommand, model, deleteSecondTaskExpectedMessage, + deleteSecondTaskExpectedModel); + } + + // invalid index [total_number_of_tasks + 1 ... MAX_INT] + @Test + public void execute_invalidIndex_throwsCommandException() { + Index moreThanTotalTasksIndex = Index.fromOneBased(lastTaskIndex.getOneBased() + 1); //Boundary value + DeleteTaskCommand deleteMoreThanTotalTasksCommand = new DeleteTaskCommand(moreThanTotalTasksIndex); + + CommandTestUtil.assertCommandFailure(deleteMoreThanTotalTasksCommand, model, MESSAGE_INVALID_TASK_INDEX); + } + +} diff --git a/src/test/java/sweebook/logic/commands/DoneTaskCommandTest.java b/src/test/java/sweebook/logic/commands/DoneTaskCommandTest.java new file mode 100644 index 00000000000..97bd9c9d074 --- /dev/null +++ b/src/test/java/sweebook/logic/commands/DoneTaskCommandTest.java @@ -0,0 +1,115 @@ +package sweebook.logic.commands; + +import static sweebook.logic.commands.CommandTestUtil.assertCommandSuccess; +import static sweebook.logic.commands.DoneTaskCommand.MESSAGE_ALREADY_DONE; +import static sweebook.logic.commands.DoneTaskCommand.MESSAGE_SUCCESS; +import static sweebook.testutil.TypicalIndexes.INDEX_FIRST_TASK; +import static sweebook.testutil.TypicalIndexes.INDEX_SECOND_TASK; +import static sweebook.testutil.TypicalPersons.getTypicalContactList; +import static sweebook.testutil.TypicalTasks.getTypicalTaskRecords; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +import javafx.collections.ObservableList; +import sweebook.commons.core.Messages; +import sweebook.commons.core.index.Index; +import sweebook.model.ContactList; +import sweebook.model.Model; +import sweebook.model.ModelManager; +import sweebook.model.ReadOnlyTaskRecords; +import sweebook.model.UserPrefs; +import sweebook.model.task.Task; +import sweebook.model.task.TaskList; +/** + * Contains integration tests (interaction with the Model) and unit tests for + * {@code DoneTaskCommand}. + */ +public class DoneTaskCommandTest { + private Model model = new ModelManager(getTypicalContactList(), new UserPrefs(), getTypicalTaskRecords()); + private ObservableList taskList = model.getTasks(); + + private Model modelCopy = new ModelManager(new ContactList(), new UserPrefs(), new TaskRecordsStub(taskList)); + private ObservableList taskListCopy = modelCopy.getTasks(); + private Index lastTaskIndex = Index.fromOneBased(taskListCopy.size()); + + // valid task index [1...total_number_of_tasks] + @Test + public void execute_validIndex_success() { + DoneTaskCommand doneFirstTaskCommand = new DoneTaskCommand(INDEX_FIRST_TASK); //Boundary Value + Task referenceFirstTask = taskListCopy.get(INDEX_FIRST_TASK.getZeroBased()); + Task expectedFirstTask = new Task(referenceFirstTask.getDescription(), referenceFirstTask.getGroup(), + referenceFirstTask.getDate(), referenceFirstTask.getTaskType(), referenceFirstTask.getRecurringFrequency(), + referenceFirstTask.getPriority()); + expectedFirstTask.markAsDone(); + String expectedDoneFirstTaskMessage = String.format(MESSAGE_SUCCESS, expectedFirstTask); + ModelManager expectedDoneFirstTaskModel = new ModelManager(modelCopy.getContactList(), new UserPrefs(), + modelCopy.getTaskList()); + + assertCommandSuccess(doneFirstTaskCommand, modelCopy, expectedDoneFirstTaskMessage, expectedDoneFirstTaskModel); + + DoneTaskCommand doneLastTaskCommand = new DoneTaskCommand(lastTaskIndex); //Boundary value + Task referenceLastTask = modelCopy.getTasks().get(lastTaskIndex.getZeroBased()); + Task expectedLastTask = new Task(referenceLastTask.getDescription(), referenceLastTask.getGroup(), + referenceLastTask.getDate(), referenceLastTask.getTaskType(), referenceLastTask.getRecurringFrequency(), + referenceLastTask.getPriority()); + expectedLastTask.markAsDone(); + String expectedDoneLastTaskMessage = String.format(MESSAGE_SUCCESS, expectedLastTask); + ModelManager expectedDoneLastTaskModel = new ModelManager(modelCopy.getContactList(), new UserPrefs(), + modelCopy.getTaskList()); + + assertCommandSuccess(doneLastTaskCommand, modelCopy, expectedDoneLastTaskMessage, expectedDoneLastTaskModel); + + DoneTaskCommand doneSecondTaskCommand = new DoneTaskCommand(INDEX_SECOND_TASK); + Task referenceSecondTask = modelCopy.getTasks().get(INDEX_SECOND_TASK.getZeroBased()); + Task expectedSecondTask = new Task(referenceSecondTask.getDescription(), referenceSecondTask.getGroup(), + referenceSecondTask.getDate(), referenceSecondTask.getTaskType(), + referenceSecondTask.getRecurringFrequency(), referenceSecondTask.getPriority()); + expectedSecondTask.markAsDone(); + String expectedDoneSecondTaskMessage = String.format(MESSAGE_SUCCESS, expectedSecondTask); + ModelManager expectedDoneSecondTaskModel = new ModelManager(modelCopy.getContactList(), new UserPrefs(), + modelCopy.getTaskList()); + + assertCommandSuccess(doneSecondTaskCommand, modelCopy, expectedDoneSecondTaskMessage, + expectedDoneSecondTaskModel); + } + + // invalid task index [total_number_of_tasks + 1 ... MAX_INT] + @Test + public void execute_invalidIndex_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(lastTaskIndex.getOneBased() + 1); + DoneTaskCommand doneTaskCommand = new DoneTaskCommand(outOfBoundIndex); + + CommandTestUtil.assertCommandFailure(doneTaskCommand, modelCopy, Messages.MESSAGE_INVALID_TASK_INDEX); + } + + // task from given task index is already marked as done + @Test + public void execute_alreadyDone_throwsCommandException() { + Task doneTask = modelCopy.getTasks().get(INDEX_FIRST_TASK.getZeroBased()); + DoneTaskCommand doneTaskCommand = new DoneTaskCommand(INDEX_FIRST_TASK); + doneTask.markAsDone(); + CommandTestUtil.assertCommandFailure(doneTaskCommand, modelCopy, String.format(MESSAGE_ALREADY_DONE, + doneTask.toString())); + } + + private class TaskRecordsStub implements ReadOnlyTaskRecords { + private final TaskList records = new TaskList(); + public TaskRecordsStub(List tasks) { + for (int i = 0; i < tasks.size(); i++) { + Task toBeCopied = tasks.get(i); + Task copy = new Task(toBeCopied.getDescription(), toBeCopied.getGroup(), toBeCopied.getDate(), + toBeCopied.getTaskType(), toBeCopied.getRecurringFrequency(), toBeCopied.getPriority()); + records.add(copy); + } + } + @Override + public ObservableList getTaskList() { + return records.asUnmodifiableObservableList(); + } + + } + +} + diff --git a/src/test/java/seedu/address/logic/commands/EditCommandTest.java b/src/test/java/sweebook/logic/commands/EditCommandTest.java similarity index 62% rename from src/test/java/seedu/address/logic/commands/EditCommandTest.java rename to src/test/java/sweebook/logic/commands/EditCommandTest.java index 214c6c2507b..21c9d968d35 100644 --- a/src/test/java/seedu/address/logic/commands/EditCommandTest.java +++ b/src/test/java/sweebook/logic/commands/EditCommandTest.java @@ -1,38 +1,39 @@ -package seedu.address.logic.commands; +package sweebook.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 static sweebook.logic.commands.CommandTestUtil.DESC_AMY; +import static sweebook.logic.commands.CommandTestUtil.DESC_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_NAME_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +import static sweebook.logic.commands.CommandTestUtil.assertCommandFailure; +import static sweebook.logic.commands.CommandTestUtil.assertCommandSuccess; +import static sweebook.logic.commands.CommandTestUtil.showPersonAtIndex; +import static sweebook.logic.commands.EditCommand.MESSAGE_EDIT_PERSON_SUCCESS; +import static sweebook.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static sweebook.testutil.TypicalIndexes.INDEX_SECOND_PERSON; +import static sweebook.testutil.TypicalPersons.getTypicalContactList; 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; +import sweebook.commons.core.Messages; +import sweebook.commons.core.index.Index; +import sweebook.logic.commands.EditCommand.EditPersonDescriptor; +import sweebook.model.ContactList; +import sweebook.model.Model; +import sweebook.model.ModelManager; +import sweebook.model.TaskRecords; +import sweebook.model.UserPrefs; +import sweebook.model.person.Person; +import sweebook.testutil.EditPersonDescriptorBuilder; +import sweebook.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()); + private Model model = new ModelManager(getTypicalContactList(), new UserPrefs(), new TaskRecords()); @Test public void execute_allFieldsSpecifiedUnfilteredList_success() { @@ -40,9 +41,10 @@ public void execute_allFieldsSpecifiedUnfilteredList_success() { EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(editedPerson).build(); EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, descriptor); - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); + String expectedMessage = String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + Model expectedModel = + new ModelManager(new ContactList(model.getContactList()), new UserPrefs(), new TaskRecords()); expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson); assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); @@ -54,16 +56,16 @@ public void execute_someFieldsSpecifiedUnfilteredList_success() { 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(); + Person editedPerson = personInList.withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB).build(); + EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder() + .withName(VALID_NAME_BOB) + .withPhone(VALID_PHONE_BOB).build(); EditCommand editCommand = new EditCommand(indexLastPerson, descriptor); - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); + String expectedMessage = String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + Model expectedModel = + new ModelManager(new ContactList(model.getContactList()), new UserPrefs(), new TaskRecords()); expectedModel.setPerson(lastPerson, editedPerson); assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); @@ -74,9 +76,10 @@ 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); + String expectedMessage = String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + Model expectedModel = + new ModelManager(new ContactList(model.getContactList()), new UserPrefs(), new TaskRecords()); assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); } @@ -90,9 +93,10 @@ public void execute_filteredList_success() { EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build()); - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); + String expectedMessage = String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + Model expectedModel = + new ModelManager(new ContactList(model.getContactList()), new UserPrefs(), new TaskRecords()); expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson); assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); @@ -104,19 +108,21 @@ public void execute_duplicatePersonUnfilteredList_failure() { EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(firstPerson).build(); EditCommand editCommand = new EditCommand(INDEX_SECOND_PERSON, descriptor); - assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_PERSON); + assertCommandFailure(editCommand, model, + String.format(Person.MESSAGE_DUPLICATE_PERSON, "name", firstPerson.getName())); } @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()); + // edit person in filtered list into a duplicate in contact list + Person personInList = model.getContactList().getPersonList().get(INDEX_SECOND_PERSON.getZeroBased()); EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, new EditPersonDescriptorBuilder(personInList).build()); - assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_PERSON); + assertCommandFailure(editCommand, model, + String.format(Person.MESSAGE_DUPLICATE_PERSON, "name", personInList.getName())); } @Test @@ -130,14 +136,14 @@ public void execute_invalidPersonIndexUnfilteredList_failure() { /** * Edit filtered list where index is larger than size of filtered list, - * but smaller than size of address book + * but smaller than size of contact list */ @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()); + // ensures that outOfBoundIndex is still in bounds of contact list + assertTrue(outOfBoundIndex.getZeroBased() < model.getContactList().getPersonList().size()); EditCommand editCommand = new EditCommand(outOfBoundIndex, new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build()); diff --git a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java b/src/test/java/sweebook/logic/commands/EditPersonDescriptorTest.java similarity index 60% rename from src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java rename to src/test/java/sweebook/logic/commands/EditPersonDescriptorTest.java index e0288792e72..bb5851b5b4b 100644 --- a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java +++ b/src/test/java/sweebook/logic/commands/EditPersonDescriptorTest.java @@ -1,19 +1,20 @@ -package seedu.address.logic.commands; +package sweebook.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 static sweebook.logic.commands.CommandTestUtil.DESC_AMY; +import static sweebook.logic.commands.CommandTestUtil.DESC_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_GITHUB_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_GROUP_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_NAME_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_TELEGRAM_BOB; import org.junit.jupiter.api.Test; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.testutil.EditPersonDescriptorBuilder; +import sweebook.logic.commands.EditCommand.EditPersonDescriptor; +import sweebook.testutil.EditPersonDescriptorBuilder; public class EditPersonDescriptorTest { @@ -47,12 +48,16 @@ public void equals() { 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(); + // different group -> returns false + editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withGroups(VALID_GROUP_BOB).build(); assertFalse(DESC_AMY.equals(editedAmy)); - // different tags -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withTags(VALID_TAG_HUSBAND).build(); + // different telegram -> returns false + editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withTelegram(VALID_TELEGRAM_BOB).build(); + assertFalse(DESC_AMY.equals(editedAmy)); + + // different github -> returns false + editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withGitHub(VALID_GITHUB_BOB).build(); assertFalse(DESC_AMY.equals(editedAmy)); } } diff --git a/src/test/java/sweebook/logic/commands/EditTaskCommandTest.java b/src/test/java/sweebook/logic/commands/EditTaskCommandTest.java new file mode 100644 index 00000000000..886f4708719 --- /dev/null +++ b/src/test/java/sweebook/logic/commands/EditTaskCommandTest.java @@ -0,0 +1,158 @@ +package sweebook.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static sweebook.logic.commands.CommandTestUtil.DESC_DEADLINE; +import static sweebook.logic.commands.CommandTestUtil.DESC_EVENT; +import static sweebook.logic.commands.CommandTestUtil.VALID_DATE_1; +import static sweebook.logic.commands.CommandTestUtil.VALID_DESCRIPTION_DEADLINE; +import static sweebook.logic.commands.CommandTestUtil.VALID_DESCRIPTION_EVENT; +import static sweebook.logic.commands.CommandTestUtil.assertCommandFailure; +import static sweebook.logic.commands.CommandTestUtil.assertCommandSuccess; +import static sweebook.logic.commands.CommandTestUtil.showTaskAtIndex; +import static sweebook.logic.commands.EditTaskCommand.MESSAGE_EDIT_TASK_SUCCESS; +import static sweebook.testutil.TypicalIndexes.INDEX_FIRST_TASK; +import static sweebook.testutil.TypicalIndexes.INDEX_SECOND_TASK; +import static sweebook.testutil.TypicalTasks.getTypicalTaskRecords; + +import org.junit.jupiter.api.Test; + +import sweebook.commons.core.Messages; +import sweebook.commons.core.index.Index; +import sweebook.logic.commands.EditTaskCommand.EditTaskDescriptor; +import sweebook.model.ContactList; +import sweebook.model.Model; +import sweebook.model.ModelManager; +import sweebook.model.TaskRecords; +import sweebook.model.UserPrefs; +import sweebook.model.task.Task; +import sweebook.testutil.EditTaskDescriptorBuilder; +import sweebook.testutil.TaskBuilder; + +/** + * Contains integration tests (interaction with the Model) and unit tests for EditTaskCommand. + */ +public class EditTaskCommandTest { + + private Model model = new ModelManager(new ContactList(), new UserPrefs(), getTypicalTaskRecords()); + + @Test + public void execute_allFieldsSpecifiedUnfilteredList_success() { + Task editedTask = new TaskBuilder().build(); + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder(editedTask).build(); + EditTaskCommand editTaskCommand = new EditTaskCommand(INDEX_FIRST_TASK, descriptor); + + String expectedMessage = String.format(MESSAGE_EDIT_TASK_SUCCESS, editedTask); + + Model expectedModel = + new ModelManager(new ContactList(), new UserPrefs(), new TaskRecords(model.getTaskList())); + expectedModel.setTask(model.getTasks().get(0), editedTask); + + assertCommandSuccess(editTaskCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_someFieldsSpecifiedUnfilteredList_success() { + Index indexLastTask = Index.fromOneBased(model.getTasks().size()); + Task lastTask = model.getTasks().get(indexLastTask.getZeroBased()); + + TaskBuilder taskInList = new TaskBuilder(lastTask); + Task editedTask = taskInList.withDescription(VALID_DESCRIPTION_EVENT).withDate(VALID_DATE_1).build(); + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder() + .withDescription(VALID_DESCRIPTION_EVENT) + .withDate(VALID_DATE_1).build(); + EditTaskCommand editTaskCommand = new EditTaskCommand(indexLastTask, descriptor); + + String expectedMessage = String.format(MESSAGE_EDIT_TASK_SUCCESS, editedTask); + + Model expectedModel = + new ModelManager(new ContactList(), new UserPrefs(), new TaskRecords(model.getTaskList())); + expectedModel.setTask(lastTask, editedTask); + + assertCommandSuccess(editTaskCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_noFieldSpecifiedUnfilteredList_success() { + EditTaskCommand editTaskCommand = new EditTaskCommand(INDEX_FIRST_TASK, new EditTaskDescriptor()); + Task editedTask = model.getTasks().get(INDEX_FIRST_TASK.getZeroBased()); + + String expectedMessage = String.format(MESSAGE_EDIT_TASK_SUCCESS, editedTask); + + Model expectedModel = + new ModelManager(new ContactList(), new UserPrefs(), new TaskRecords(model.getTaskList())); + + assertCommandSuccess(editTaskCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_filteredList_success() { + showTaskAtIndex(model, INDEX_FIRST_TASK); + + Task taskInFilteredList = model.getTasks().get(INDEX_FIRST_TASK.getZeroBased()); + Task editedTask = new TaskBuilder(taskInFilteredList).withDescription(VALID_DESCRIPTION_DEADLINE).build(); + EditTaskCommand editTaskCommand = new EditTaskCommand(INDEX_FIRST_TASK, + new EditTaskDescriptorBuilder().withDescription(VALID_DESCRIPTION_DEADLINE).build()); + + String expectedMessage = String.format(MESSAGE_EDIT_TASK_SUCCESS, editedTask); + + Model expectedModel = + new ModelManager(new ContactList(), new UserPrefs(), new TaskRecords(model.getTaskList())); + expectedModel.setTask(model.getTasks().get(0), editedTask); + + assertCommandSuccess(editTaskCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidTaskIndexUnfilteredList_failure() { + Index outOfBoundIndex = Index.fromOneBased(model.getTasks().size() + 1); + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder() + .withDescription(VALID_DESCRIPTION_DEADLINE).build(); + EditTaskCommand editTaskCommand = new EditTaskCommand(outOfBoundIndex, descriptor); + + assertCommandFailure(editTaskCommand, model, Messages.MESSAGE_INVALID_TASK_INDEX); + } + + /** + * Edit filtered list where index is larger than size of filtered list, + * but smaller than size of contact list + */ + @Test + public void execute_invalidTaskIndexFilteredList_failure() { + showTaskAtIndex(model, INDEX_FIRST_TASK); + Index outOfBoundIndex = INDEX_SECOND_TASK; + // ensures that outOfBoundIndex is still in bounds of contact list + assertTrue(outOfBoundIndex.getZeroBased() < model.getTaskList().getTaskList().size()); + + EditTaskCommand editTaskCommand = new EditTaskCommand(outOfBoundIndex, + new EditTaskDescriptorBuilder().withDescription(VALID_DESCRIPTION_DEADLINE).build()); + + assertCommandFailure(editTaskCommand, model, Messages.MESSAGE_INVALID_TASK_INDEX); + } + + @Test + public void equals() { + final EditTaskCommand standardCommand = new EditTaskCommand(INDEX_FIRST_TASK, DESC_DEADLINE); + + // same values -> returns true + EditTaskDescriptor copyDescriptor = new EditTaskDescriptor(DESC_DEADLINE); + EditTaskCommand commandWithSameValues = new EditTaskCommand(INDEX_FIRST_TASK, 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 EditTaskCommand(INDEX_SECOND_TASK, DESC_DEADLINE))); + + // different descriptor -> returns false + assertFalse(standardCommand.equals(new EditTaskCommand(INDEX_FIRST_TASK, DESC_EVENT))); + } + +} diff --git a/src/test/java/sweebook/logic/commands/EditTaskDescriptorTest.java b/src/test/java/sweebook/logic/commands/EditTaskDescriptorTest.java new file mode 100644 index 00000000000..2fd7e3f4971 --- /dev/null +++ b/src/test/java/sweebook/logic/commands/EditTaskDescriptorTest.java @@ -0,0 +1,68 @@ +package sweebook.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static sweebook.logic.commands.CommandTestUtil.DESC_DEADLINE; +import static sweebook.logic.commands.CommandTestUtil.DESC_TODO; +import static sweebook.logic.commands.CommandTestUtil.VALID_DATE_3; +import static sweebook.logic.commands.CommandTestUtil.VALID_DESCRIPTION_DEADLINE; +import static sweebook.logic.commands.CommandTestUtil.VALID_GROUP_CS2103T; +import static sweebook.logic.commands.CommandTestUtil.VALID_PRIORITY_HIGH; +import static sweebook.logic.commands.CommandTestUtil.VALID_RECURRING_FREQUENCY_YEAR; +import static sweebook.logic.commands.CommandTestUtil.VALID_TASK_TYPE_EVENT; + +import org.junit.jupiter.api.Test; + +import sweebook.logic.commands.EditTaskCommand.EditTaskDescriptor; +import sweebook.testutil.EditTaskDescriptorBuilder; + +public class EditTaskDescriptorTest { + + @Test + public void equals() { + // same values -> returns true + EditTaskDescriptor descriptorWithSameValues = new EditTaskDescriptor(DESC_TODO); + assertTrue(DESC_TODO.equals(descriptorWithSameValues)); + + // same object -> returns true + assertTrue(DESC_TODO.equals(DESC_TODO)); + + // null -> returns false + assertFalse(DESC_TODO.equals(null)); + + // different types -> returns false + assertFalse(DESC_TODO.equals(5)); + + // different values -> returns false + assertFalse(DESC_TODO.equals(DESC_DEADLINE)); + + // different description -> return false + EditTaskDescriptor editedTodo = + new EditTaskDescriptorBuilder(DESC_TODO).withDescription(VALID_DESCRIPTION_DEADLINE).build(); + assertFalse(DESC_TODO.equals(editedTodo)); + + // different group -> return false + editedTodo = new EditTaskDescriptorBuilder(DESC_TODO).withGroup(VALID_GROUP_CS2103T).build(); + assertFalse(DESC_TODO.equals(editedTodo)); + + // different priority -> return false + editedTodo = new EditTaskDescriptorBuilder(DESC_TODO).withPriority(VALID_PRIORITY_HIGH).build(); + assertFalse(DESC_TODO.equals(editedTodo)); + + // different recurring frequency -> return false + editedTodo = new EditTaskDescriptorBuilder(DESC_TODO) + .withRecurringFrequency(VALID_RECURRING_FREQUENCY_YEAR).build(); + assertFalse(DESC_TODO.equals(editedTodo)); + + // different date -> return false + editedTodo = + new EditTaskDescriptorBuilder(DESC_TODO).withDate(VALID_DATE_3).build(); + assertFalse(DESC_TODO.equals(editedTodo)); + + // different task type -> return false + editedTodo = + new EditTaskDescriptorBuilder(DESC_TODO).withTaskType(VALID_TASK_TYPE_EVENT).build(); + assertFalse(DESC_TODO.equals(editedTodo)); + } + +} diff --git a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java b/src/test/java/sweebook/logic/commands/ExitCommandTest.java similarity index 60% rename from src/test/java/seedu/address/logic/commands/ExitCommandTest.java rename to src/test/java/sweebook/logic/commands/ExitCommandTest.java index 9533c473875..67ec13b3b19 100644 --- a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java +++ b/src/test/java/sweebook/logic/commands/ExitCommandTest.java @@ -1,12 +1,12 @@ -package seedu.address.logic.commands; +package sweebook.logic.commands; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.ExitCommand.MESSAGE_EXIT_ACKNOWLEDGEMENT; +import static sweebook.logic.commands.CommandTestUtil.assertCommandSuccess; +import static sweebook.logic.commands.ExitCommand.MESSAGE_EXIT_ACKNOWLEDGEMENT; import org.junit.jupiter.api.Test; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; +import sweebook.model.Model; +import sweebook.model.ModelManager; public class ExitCommandTest { private Model model = new ModelManager(); diff --git a/src/test/java/sweebook/logic/commands/FilterTasksCommandTest.java b/src/test/java/sweebook/logic/commands/FilterTasksCommandTest.java new file mode 100644 index 00000000000..367e4942917 --- /dev/null +++ b/src/test/java/sweebook/logic/commands/FilterTasksCommandTest.java @@ -0,0 +1,124 @@ +package sweebook.logic.commands; + +import static sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.logic.commands.CommandTestUtil.assertCommandSuccess; +import static sweebook.testutil.Assert.assertThrows; +import static sweebook.testutil.TypicalPersons.getTypicalContactList; +import static sweebook.testutil.TypicalTasks.FIRST_DEADLINE; +import static sweebook.testutil.TypicalTasks.FIRST_EVENT; +import static sweebook.testutil.TypicalTasks.getTypicalTaskRecords; + +import org.junit.jupiter.api.Test; + +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.model.Model; +import sweebook.model.ModelManager; +import sweebook.model.UserPrefs; +import sweebook.model.task.FilterTaskPredicate; +import sweebook.model.task.Task; + +/** + * Contains integration tests (interaction with the Model). + */ +public class FilterTasksCommandTest { + + private Model model = new ModelManager(getTypicalContactList(), new UserPrefs(), getTypicalTaskRecords()); + + @Test + public void constructor_nullTask_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new FilterTasksCommand(null)); + } + + @Test + public void execute_validDate_success() throws CommandException { + ModelManager expectedModel = + new ModelManager(getTypicalContactList(), new UserPrefs(), getTypicalTaskRecords()); + Task toFilter = FIRST_DEADLINE; + FilterTaskPredicate criterion = new FilterTaskPredicate("date/" + toFilter.getDate().getString()); + FilterTasksCommand command = new FilterTasksCommand(criterion); + String expectedMessage = command.execute(expectedModel).getFeedbackToUser(); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + + } + + @Test + public void execute_validType_success() throws CommandException { + ModelManager expectedModel = + new ModelManager(getTypicalContactList(), new UserPrefs(), getTypicalTaskRecords()); + Task toFilter = FIRST_EVENT; + FilterTaskPredicate criterion = new FilterTaskPredicate("type/" + toFilter.getTaskType().toString()); + FilterTasksCommand command = new FilterTasksCommand(criterion); + String expectedMessage = command.execute(expectedModel).getFeedbackToUser(); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + } + + @Test + public void execute_validGroup_success() throws CommandException { + ModelManager expectedModel = + new ModelManager(getTypicalContactList(), new UserPrefs(), getTypicalTaskRecords()); + Task toFilter = FIRST_EVENT; + FilterTaskPredicate criterion = new FilterTaskPredicate("g/" + toFilter.getGroup().toString()); + FilterTasksCommand command = new FilterTasksCommand(criterion); + String expectedMessage = command.execute(expectedModel).getFeedbackToUser(); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + } + + @Test + public void execute_validDesc_success() throws CommandException { + ModelManager expectedModel = + new ModelManager(getTypicalContactList(), new UserPrefs(), getTypicalTaskRecords()); + Task toFilter = FIRST_EVENT; + FilterTaskPredicate criterion = new FilterTaskPredicate("d/" + toFilter.getDescription().toString()); + FilterTasksCommand command = new FilterTasksCommand(criterion); + String expectedMessage = command.execute(expectedModel).getFeedbackToUser(); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + } + + @Test + public void execute_validPty_success() throws CommandException { + ModelManager expectedModel = + new ModelManager(getTypicalContactList(), new UserPrefs(), getTypicalTaskRecords()); + Task toFilter = FIRST_EVENT; + FilterTaskPredicate criterion = new FilterTaskPredicate("pty/" + toFilter.getPriority().toString()); + FilterTasksCommand command = new FilterTasksCommand(criterion); + String expectedMessage = command.execute(expectedModel).getFeedbackToUser(); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidDate_failure() { + Task toFilter = FIRST_EVENT; + String invalidDate = "date/" + toFilter.getDate().toString() + "invalid date"; + FilterTaskPredicate criterion = new FilterTaskPredicate(invalidDate); + FilterTasksCommand command = new FilterTasksCommand(criterion); + CommandTestUtil.assertCommandFailure(command, model, MESSAGE_INVALID_COMMAND_FORMAT); + } + + @Test + public void execute_invalidPty_failure() { + Task toFilter = FIRST_EVENT; + String invalidPty = "pty/" + toFilter.getPriority().toString() + "invalid pty"; + FilterTaskPredicate criterion = new FilterTaskPredicate(invalidPty); + FilterTasksCommand command = new FilterTasksCommand(criterion); + CommandTestUtil.assertCommandFailure(command, model, MESSAGE_INVALID_COMMAND_FORMAT); + } + + @Test + public void execute_invalidGroup_failure() { + Task toFilter = FIRST_EVENT; + String invalidGroup = "g/" + toFilter.getGroup().toString() + "invalid group"; + FilterTaskPredicate criterion = new FilterTaskPredicate(invalidGroup); + FilterTasksCommand command = new FilterTasksCommand(criterion); + CommandTestUtil.assertCommandFailure(command, model, MESSAGE_INVALID_COMMAND_FORMAT); + } + + @Test + public void execute_invalidCriterionSuffix_failure() { + Task toFilter = FIRST_EVENT; + String invalidSuffix = "y/" + toFilter.getPriority().toString() + "invalid pty"; + FilterTaskPredicate criterion = new FilterTaskPredicate(invalidSuffix); + FilterTasksCommand command = new FilterTasksCommand(criterion); + CommandTestUtil.assertCommandFailure(command, model, MESSAGE_INVALID_COMMAND_FORMAT); + } + +} diff --git a/src/test/java/seedu/address/logic/commands/FindCommandTest.java b/src/test/java/sweebook/logic/commands/FindCommandTest.java similarity index 76% rename from src/test/java/seedu/address/logic/commands/FindCommandTest.java rename to src/test/java/sweebook/logic/commands/FindCommandTest.java index 9b15db28bbb..e2fbe730bcf 100644 --- a/src/test/java/seedu/address/logic/commands/FindCommandTest.java +++ b/src/test/java/sweebook/logic/commands/FindCommandTest.java @@ -1,31 +1,32 @@ -package seedu.address.logic.commands; +package sweebook.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 static sweebook.commons.core.Messages.MESSAGE_PERSONS_LISTED_OVERVIEW; +import static sweebook.logic.commands.CommandTestUtil.assertCommandSuccess; +import static sweebook.testutil.TypicalPersons.CARL; +import static sweebook.testutil.TypicalPersons.ELLE; +import static sweebook.testutil.TypicalPersons.FIONA; +import static sweebook.testutil.TypicalPersons.getTypicalContactList; 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; +import sweebook.model.Model; +import sweebook.model.ModelManager; +import sweebook.model.TaskRecords; +import sweebook.model.UserPrefs; +import sweebook.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()); + private Model model = new ModelManager(getTypicalContactList(), new UserPrefs(), new TaskRecords()); + private Model expectedModel = new ModelManager(getTypicalContactList(), new UserPrefs(), new TaskRecords()); @Test public void equals() { diff --git a/src/test/java/sweebook/logic/commands/GroupCommandTest.java b/src/test/java/sweebook/logic/commands/GroupCommandTest.java new file mode 100644 index 00000000000..51be54b533d --- /dev/null +++ b/src/test/java/sweebook/logic/commands/GroupCommandTest.java @@ -0,0 +1,90 @@ +package sweebook.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 sweebook.commons.core.Messages.MESSAGE_PERSONS_LISTED_OVERVIEW; +import static sweebook.logic.commands.CommandTestUtil.assertCommandSuccess; +import static sweebook.testutil.TypicalPersons.ALICE; +import static sweebook.testutil.TypicalPersons.BENSON; +import static sweebook.testutil.TypicalPersons.CARL; +import static sweebook.testutil.TypicalPersons.DANIEL; +import static sweebook.testutil.TypicalPersons.ELLE; +import static sweebook.testutil.TypicalPersons.FIONA; +import static sweebook.testutil.TypicalPersons.GEORGE; +import static sweebook.testutil.TypicalPersons.getTypicalContactList; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import sweebook.model.ContactList; +import sweebook.model.Model; +import sweebook.model.ModelManager; +import sweebook.model.TaskRecords; +import sweebook.model.UserPrefs; +import sweebook.model.group.Group; +import sweebook.model.group.GroupPredicate; +import sweebook.model.person.Person; + +public class GroupCommandTest { + @Test + public void equals() { + GroupPredicate firstPredicate = + new GroupPredicate(new Group("CS2103T")); + GroupPredicate secondPredicate = + new GroupPredicate(new Group("CS2101")); + + GroupCommand groupFirstCommand = new GroupCommand(firstPredicate); + GroupCommand groupSecondCommand = new GroupCommand(secondPredicate); + + // same object -> returns true + assertTrue(groupFirstCommand.equals(groupFirstCommand)); + + // same values -> returns true + GroupCommand groupFirstCommandCopy = new GroupCommand(firstPredicate); + assertTrue(groupFirstCommand.equals(groupFirstCommandCopy)); + + // different types -> returns false + assertFalse(groupFirstCommand.equals(1)); + + // null -> returns false + assertFalse(groupFirstCommand.equals(null)); + + // different person -> returns false + assertFalse(groupFirstCommand.equals(groupSecondCommand)); + } + + @Test + public void execute_validGroup_multiplePersonsFound() { + Model expectedModel = new ModelManager(getTypicalContactList(), new UserPrefs(), new TaskRecords()); + Model actualModel = new ModelManager(getTypicalContactList(), new UserPrefs(), new TaskRecords()); + + String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 4); + GroupPredicate predicate = new GroupPredicate(new Group("CS2101")); + GroupCommand command = new GroupCommand(predicate); + expectedModel.updateFilteredPersonList(predicate); + assertCommandSuccess(command, actualModel, expectedMessage, expectedModel); + assertEquals(Arrays.asList(ALICE, CARL, DANIEL, ELLE), actualModel.getFilteredPersonList()); + } + + @Test + public void execute_validGroup_noPersonsFound() { + ContactList ab = new ContactList(); + List personsInOnlyCs2103T = new ArrayList<>(Arrays.asList(GEORGE, FIONA, BENSON)); + for (Person person : personsInOnlyCs2103T) { + ab.addPerson(person); + } + Model actualModel = new ModelManager(ab, new UserPrefs(), new TaskRecords()); + Model expectedModel = new ModelManager(ab, new UserPrefs(), new TaskRecords()); + + String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 0); + GroupPredicate predicate = new GroupPredicate(new Group("CS2101")); + GroupCommand command = new GroupCommand(predicate); + expectedModel.updateFilteredPersonList(predicate); + assertCommandSuccess(command, actualModel, expectedMessage, expectedModel); + assertEquals(Arrays.asList(), actualModel.getFilteredPersonList()); + } +} diff --git a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java b/src/test/java/sweebook/logic/commands/HelpCommandTest.java similarity index 61% rename from src/test/java/seedu/address/logic/commands/HelpCommandTest.java rename to src/test/java/sweebook/logic/commands/HelpCommandTest.java index 4904fc4352e..3b08cf86825 100644 --- a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java +++ b/src/test/java/sweebook/logic/commands/HelpCommandTest.java @@ -1,12 +1,12 @@ -package seedu.address.logic.commands; +package sweebook.logic.commands; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.HelpCommand.SHOWING_HELP_MESSAGE; +import static sweebook.logic.commands.CommandTestUtil.assertCommandSuccess; +import static sweebook.logic.commands.HelpCommand.SHOWING_HELP_MESSAGE; import org.junit.jupiter.api.Test; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; +import sweebook.model.Model; +import sweebook.model.ModelManager; public class HelpCommandTest { private Model model = new ModelManager(); diff --git a/src/test/java/sweebook/logic/commands/ListCommandTest.java b/src/test/java/sweebook/logic/commands/ListCommandTest.java new file mode 100644 index 00000000000..eb9b3cd2b0b --- /dev/null +++ b/src/test/java/sweebook/logic/commands/ListCommandTest.java @@ -0,0 +1,40 @@ +package sweebook.logic.commands; + +import static sweebook.logic.commands.CommandTestUtil.assertCommandSuccess; +import static sweebook.logic.commands.CommandTestUtil.showPersonAtIndex; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import sweebook.model.Model; +import sweebook.model.ModelManager; +import sweebook.model.TaskRecords; +import sweebook.model.UserPrefs; +import sweebook.testutil.TypicalIndexes; +import sweebook.testutil.TypicalPersons; + +/** + * Contains integration tests (interaction with the Model) and unit tests for ListCommand. + */ +public class ListCommandTest { + + private Model model; + private Model expectedModel; + + @BeforeEach + public void setUp() { + model = new ModelManager(TypicalPersons.getTypicalContactList(), new UserPrefs(), new TaskRecords()); + expectedModel = new ModelManager(model.getContactList(), new UserPrefs(), new TaskRecords()); + } + + @Test + public void execute_listIsNotFiltered_showsSameList() { + assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_listIsFiltered_showsEverything() { + showPersonAtIndex(model, TypicalIndexes.INDEX_FIRST_PERSON); + assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel); + } +} diff --git a/src/test/java/sweebook/logic/commands/ListTasksCommandTest.java b/src/test/java/sweebook/logic/commands/ListTasksCommandTest.java new file mode 100644 index 00000000000..cea54a2cfd8 --- /dev/null +++ b/src/test/java/sweebook/logic/commands/ListTasksCommandTest.java @@ -0,0 +1,39 @@ +package sweebook.logic.commands; + +import static sweebook.logic.commands.CommandTestUtil.assertCommandSuccess; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import sweebook.model.Model; +import sweebook.model.ModelManager; +import sweebook.model.TaskRecords; +import sweebook.model.UserPrefs; +import sweebook.testutil.TypicalIndexes; +import sweebook.testutil.TypicalPersons; + +/** + * Contains integration tests (interaction with the Model) and unit tests for RemarkCommand. + */ +public class ListTasksCommandTest { + + private Model model; + private Model expectedModel; + + @BeforeEach + public void setUp() { + model = new ModelManager(TypicalPersons.getTypicalContactList(), new UserPrefs(), new TaskRecords()); + expectedModel = new ModelManager(model.getContactList(), new UserPrefs(), new TaskRecords()); + } + + @Test + public void execute_listIsNotFiltered_showsSameList() { + assertCommandSuccess(new ListTasksCommand(), model, ListTasksCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_listIsFiltered_showsEverything() { + CommandTestUtil.showPersonAtIndex(model, TypicalIndexes.INDEX_FIRST_TASK); + assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel); + } +} diff --git a/src/test/java/sweebook/logic/commands/SortTasksCommandTest.java b/src/test/java/sweebook/logic/commands/SortTasksCommandTest.java new file mode 100644 index 00000000000..61b60308bc9 --- /dev/null +++ b/src/test/java/sweebook/logic/commands/SortTasksCommandTest.java @@ -0,0 +1,37 @@ +package sweebook.logic.commands; + +import static sweebook.logic.commands.CommandTestUtil.assertCommandSuccess; +import static sweebook.testutil.Assert.assertThrows; +import static sweebook.testutil.TypicalPersons.getTypicalContactList; +import static sweebook.testutil.TypicalTasks.getTypicalTaskRecords; + +import org.junit.jupiter.api.Test; + +import sweebook.logic.commands.exceptions.CommandException; +import sweebook.model.Model; +import sweebook.model.ModelManager; +import sweebook.model.UserPrefs; +import sweebook.model.task.SortTaskComparator; + +/** + * Contains integration tests (interaction with the Model). + */ +public class SortTasksCommandTest { + + private Model model = new ModelManager(getTypicalContactList(), new UserPrefs(), getTypicalTaskRecords()); + + @Test + public void constructor_nullTask_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new SortTasksCommand(null)); + } + + @Test + public void execute_validSortTaskCriterion_success() throws CommandException { + ModelManager expectedModel = + new ModelManager(getTypicalContactList(), new UserPrefs(), getTypicalTaskRecords()); + SortTaskComparator criterion = new SortTaskComparator("desc", "d"); + SortTasksCommand command = new SortTasksCommand(criterion); + String expectedMessage = command.execute(model).getFeedbackToUser(); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + } +} diff --git a/src/test/java/sweebook/logic/parser/AddCommandParserTest.java b/src/test/java/sweebook/logic/parser/AddCommandParserTest.java new file mode 100644 index 00000000000..487cea9f290 --- /dev/null +++ b/src/test/java/sweebook/logic/parser/AddCommandParserTest.java @@ -0,0 +1,141 @@ +package sweebook.logic.parser; + +import static sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; +import static sweebook.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; +import static sweebook.logic.commands.CommandTestUtil.GITHUB_DESC_AMY; +import static sweebook.logic.commands.CommandTestUtil.GITHUB_DESC_BOB; +import static sweebook.logic.commands.CommandTestUtil.GROUP_DESC_BOB; +import static sweebook.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; +import static sweebook.logic.commands.CommandTestUtil.INVALID_GROUP_DESC; +import static sweebook.logic.commands.CommandTestUtil.INVALID_NAME_DESC; +import static sweebook.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; +import static sweebook.logic.commands.CommandTestUtil.NAME_DESC_AMY; +import static sweebook.logic.commands.CommandTestUtil.NAME_DESC_BOB; +import static sweebook.logic.commands.CommandTestUtil.PHONE_DESC_AMY; +import static sweebook.logic.commands.CommandTestUtil.PHONE_DESC_BOB; +import static sweebook.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY; +import static sweebook.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; +import static sweebook.logic.commands.CommandTestUtil.TELEGRAM_DESC_AMY; +import static sweebook.logic.commands.CommandTestUtil.TELEGRAM_DESC_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_GITHUB_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_GROUP_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_NAME_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_TELEGRAM_BOB; +import static sweebook.logic.parser.CommandParserTestUtil.assertParseFailure; +import static sweebook.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import sweebook.logic.commands.AddCommand; +import sweebook.model.group.Group; +import sweebook.model.person.Email; +import sweebook.model.person.Name; +import sweebook.model.person.Person; +import sweebook.model.person.Phone; +import sweebook.testutil.PersonBuilder; +import sweebook.testutil.TypicalPersons; + +public class AddCommandParserTest { + private AddCommandParser parser = new AddCommandParser(); + + @Test + public void parse_allFieldsPresent_success() { + Person expectedPerson = new PersonBuilder(TypicalPersons.BOB).build(); + + // whitespace only preamble + assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + + GROUP_DESC_BOB + TELEGRAM_DESC_BOB + GITHUB_DESC_BOB, new AddCommand(expectedPerson)); + + // multiple names - last name accepted + assertParseSuccess(parser, NAME_DESC_AMY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + + GROUP_DESC_BOB + TELEGRAM_DESC_BOB + GITHUB_DESC_BOB, new AddCommand(expectedPerson)); + + // multiple phones - last phone accepted + assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_AMY + PHONE_DESC_BOB + EMAIL_DESC_BOB + + GROUP_DESC_BOB + TELEGRAM_DESC_BOB + GITHUB_DESC_BOB, new AddCommand(expectedPerson)); + + // multiple emails - last email accepted + assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_AMY + EMAIL_DESC_BOB + + GROUP_DESC_BOB + TELEGRAM_DESC_BOB + GITHUB_DESC_BOB, new AddCommand(expectedPerson)); + + // multiple groups - last groups accepted + assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_AMY + EMAIL_DESC_BOB + + GROUP_DESC_BOB + GROUP_DESC_BOB + TELEGRAM_DESC_BOB + GITHUB_DESC_BOB, + new AddCommand(expectedPerson)); + + // multiple telegrams - last telegram accepted + assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_AMY + EMAIL_DESC_BOB + + GROUP_DESC_BOB + TELEGRAM_DESC_AMY + + TELEGRAM_DESC_BOB + GITHUB_DESC_BOB, new AddCommand(expectedPerson)); + + // multiple githubs - last github accepted + assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_AMY + EMAIL_DESC_BOB + + GROUP_DESC_BOB + TELEGRAM_DESC_BOB + + GITHUB_DESC_AMY + GITHUB_DESC_BOB, 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 + + GROUP_DESC_BOB + TELEGRAM_DESC_BOB + GITHUB_DESC_BOB, expectedMessage); + + // missing phone prefix + assertParseFailure(parser, NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB + + GROUP_DESC_BOB + TELEGRAM_DESC_BOB + GITHUB_DESC_BOB, expectedMessage); + + // missing email prefix + assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB + + GROUP_DESC_BOB + TELEGRAM_DESC_BOB + GITHUB_DESC_BOB, expectedMessage); + + // missing group prefix + assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + + VALID_GROUP_BOB + TELEGRAM_DESC_BOB + GITHUB_DESC_BOB, expectedMessage); + + // missing telegram prefix + assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + + GROUP_DESC_BOB + VALID_TELEGRAM_BOB + GITHUB_DESC_BOB, expectedMessage); + + // missing github prefix + assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + + GROUP_DESC_BOB + TELEGRAM_DESC_BOB + VALID_GITHUB_BOB, expectedMessage); + + // all prefixes missing + assertParseFailure(parser, VALID_NAME_BOB + VALID_PHONE_BOB + VALID_EMAIL_BOB + + VALID_GROUP_BOB + VALID_TELEGRAM_BOB + VALID_GITHUB_BOB, expectedMessage); + } + + @Test + public void parse_invalidValue_failure() { + // invalid name + assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + + GROUP_DESC_BOB + TELEGRAM_DESC_BOB + GITHUB_DESC_BOB, Name.MESSAGE_CONSTRAINTS); + + // invalid phone + assertParseFailure(parser, NAME_DESC_BOB + INVALID_PHONE_DESC + EMAIL_DESC_BOB + + GROUP_DESC_BOB + TELEGRAM_DESC_BOB + GITHUB_DESC_BOB, Phone.MESSAGE_CONSTRAINTS); + + // invalid email + assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + INVALID_EMAIL_DESC + + GROUP_DESC_BOB + TELEGRAM_DESC_BOB + GITHUB_DESC_BOB, Email.MESSAGE_CONSTRAINTS); + + // invalid group + assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + + INVALID_GROUP_DESC + TELEGRAM_DESC_BOB + GITHUB_DESC_BOB, Group.MESSAGE_CONSTRAINTS); + + // two invalid values, only first invalid value reported + assertParseFailure(parser, INVALID_NAME_DESC + INVALID_GROUP_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + + TELEGRAM_DESC_BOB + GITHUB_DESC_BOB, Name.MESSAGE_CONSTRAINTS); + + // non-empty preamble + assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + + GROUP_DESC_BOB + TELEGRAM_DESC_BOB + GITHUB_DESC_BOB, + 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/sweebook/logic/parser/ArgumentTokenizerTest.java similarity index 98% rename from src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java rename to src/test/java/sweebook/logic/parser/ArgumentTokenizerTest.java index c97308935f5..3eda4ac26a8 100644 --- a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java +++ b/src/test/java/sweebook/logic/parser/ArgumentTokenizerTest.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package sweebook.logic.parser; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -55,7 +55,7 @@ private void assertArgumentAbsent(ArgumentMultimap argMultimap, Prefix prefix) { @Test public void tokenize_noPrefixes_allTakenAsPreamble() { - String argsString = " some random string /t tag with leading and trailing spaces "; + String argsString = " some random string /t group with leading and trailing spaces "; ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString); // Same string expected as preamble, but leading/trailing spaces should be trimmed diff --git a/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java b/src/test/java/sweebook/logic/parser/CommandParserTestUtil.java similarity index 89% rename from src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java rename to src/test/java/sweebook/logic/parser/CommandParserTestUtil.java index e4c33515768..1098b6265fc 100644 --- a/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java +++ b/src/test/java/sweebook/logic/parser/CommandParserTestUtil.java @@ -1,9 +1,9 @@ -package seedu.address.logic.parser; +package sweebook.logic.parser; import static org.junit.jupiter.api.Assertions.assertEquals; -import seedu.address.logic.commands.Command; -import seedu.address.logic.parser.exceptions.ParseException; +import sweebook.logic.commands.Command; +import sweebook.logic.parser.exceptions.ParseException; /** * Contains helper methods for testing command parsers. diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/sweebook/logic/parser/ContactListParserTest.java similarity index 62% rename from src/test/java/seedu/address/logic/parser/AddressBookParserTest.java rename to src/test/java/sweebook/logic/parser/ContactListParserTest.java index d9659205b57..fab2a6562b2 100644 --- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java +++ b/src/test/java/sweebook/logic/parser/ContactListParserTest.java @@ -1,11 +1,13 @@ -package seedu.address.logic.parser; +package sweebook.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 static sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static sweebook.testutil.Assert.assertThrows; +import static sweebook.testutil.PersonUtil.getAddCommand; +import static sweebook.testutil.PersonUtil.getEditPersonDescriptorDetails; +import static sweebook.testutil.TypicalIndexes.INDEX_FIRST_PERSON; import java.util.Arrays; import java.util.List; @@ -13,30 +15,30 @@ 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(); +import sweebook.logic.commands.AddCommand; +import sweebook.logic.commands.ClearCommand; +import sweebook.logic.commands.DeleteCommand; +import sweebook.logic.commands.EditCommand; +import sweebook.logic.commands.EditCommand.EditPersonDescriptor; +import sweebook.logic.commands.ExitCommand; +import sweebook.logic.commands.FindCommand; +import sweebook.logic.commands.GroupCommand; +import sweebook.logic.commands.HelpCommand; +import sweebook.logic.commands.ListCommand; +import sweebook.logic.parser.exceptions.ParseException; +import sweebook.model.person.NameContainsKeywordsPredicate; +import sweebook.model.person.Person; +import sweebook.testutil.EditPersonDescriptorBuilder; +import sweebook.testutil.PersonBuilder; + +public class ContactListParserTest { + + private final SweeBookParser parser = new SweeBookParser(); @Test public void parseCommand_add() throws Exception { Person person = new PersonBuilder().build(); - AddCommand command = (AddCommand) parser.parseCommand(PersonUtil.getAddCommand(person)); + AddCommand command = (AddCommand) parser.parseCommand(getAddCommand(person)); assertEquals(new AddCommand(person), command); } @@ -58,7 +60,7 @@ 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)); + + INDEX_FIRST_PERSON.getOneBased() + " " + getEditPersonDescriptorDetails(descriptor)); assertEquals(new EditCommand(INDEX_FIRST_PERSON, descriptor), command); } @@ -88,10 +90,15 @@ public void parseCommand_list() throws Exception { assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD + " 3") instanceof ListCommand); } + @Test + public void parseCommand_group() throws Exception { + assertTrue(parser.parseCommand(GroupCommand.COMMAND_WORD + " CS2103T") instanceof GroupCommand); + } + @Test public void parseCommand_unrecognisedInput_throwsParseException() { - assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE), () - -> parser.parseCommand("")); + assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT, + HelpCommand.MESSAGE_USAGE), () -> parser.parseCommand("")); } @Test diff --git a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java b/src/test/java/sweebook/logic/parser/DeleteCommandParserTest.java similarity index 63% rename from src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java rename to src/test/java/sweebook/logic/parser/DeleteCommandParserTest.java index 27eaec84450..f6c83c4912b 100644 --- a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java +++ b/src/test/java/sweebook/logic/parser/DeleteCommandParserTest.java @@ -1,13 +1,14 @@ -package seedu.address.logic.parser; +package sweebook.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 static sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.logic.commands.DeleteCommand.MESSAGE_USAGE; +import static sweebook.logic.parser.CommandParserTestUtil.assertParseFailure; +import static sweebook.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static sweebook.testutil.TypicalIndexes.INDEX_FIRST_PERSON; import org.junit.jupiter.api.Test; -import seedu.address.logic.commands.DeleteCommand; +import sweebook.logic.commands.DeleteCommand; /** * As we are only doing white-box testing, our test cases do not cover path variations @@ -27,6 +28,6 @@ public void parse_validArgs_returnsDeleteCommand() { @Test public void parse_invalidArgs_throwsParseException() { - assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE)); + assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE)); } } diff --git a/src/test/java/sweebook/logic/parser/EditCommandParserTest.java b/src/test/java/sweebook/logic/parser/EditCommandParserTest.java new file mode 100644 index 00000000000..45d94d3977a --- /dev/null +++ b/src/test/java/sweebook/logic/parser/EditCommandParserTest.java @@ -0,0 +1,190 @@ +package sweebook.logic.parser; + +import static sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; +import static sweebook.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; +import static sweebook.logic.commands.CommandTestUtil.GITHUB_DESC_AMY; +import static sweebook.logic.commands.CommandTestUtil.GITHUB_DESC_BOB; +import static sweebook.logic.commands.CommandTestUtil.GROUP_DESC_AMY; +import static sweebook.logic.commands.CommandTestUtil.GROUP_DESC_BOB; +import static sweebook.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; +import static sweebook.logic.commands.CommandTestUtil.INVALID_GROUP_DESC; +import static sweebook.logic.commands.CommandTestUtil.INVALID_NAME_DESC; +import static sweebook.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; +import static sweebook.logic.commands.CommandTestUtil.NAME_DESC_AMY; +import static sweebook.logic.commands.CommandTestUtil.PHONE_DESC_AMY; +import static sweebook.logic.commands.CommandTestUtil.PHONE_DESC_BOB; +import static sweebook.logic.commands.CommandTestUtil.TELEGRAM_DESC_AMY; +import static sweebook.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; +import static sweebook.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_GITHUB_AMY; +import static sweebook.logic.commands.CommandTestUtil.VALID_GITHUB_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_GROUP_AMY_CS2101; +import static sweebook.logic.commands.CommandTestUtil.VALID_GROUP_AMY_CS2103T; +import static sweebook.logic.commands.CommandTestUtil.VALID_GROUP_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_NAME_AMY; +import static sweebook.logic.commands.CommandTestUtil.VALID_PHONE_AMY; +import static sweebook.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_TELEGRAM_AMY; +import static sweebook.logic.parser.CommandParserTestUtil.assertParseFailure; +import static sweebook.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import sweebook.commons.core.index.Index; +import sweebook.logic.commands.EditCommand; +import sweebook.logic.commands.EditCommand.EditPersonDescriptor; +import sweebook.model.group.Group; +import sweebook.model.person.Email; +import sweebook.model.person.Name; +import sweebook.model.person.Phone; +import sweebook.testutil.EditPersonDescriptorBuilder; +import sweebook.testutil.TypicalIndexes; + +public class EditCommandParserTest { + + 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_GROUP_DESC, Group.MESSAGE_CONSTRAINTS); // invalid group + + // 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); + + // valid groups followed by an invalid group. + assertParseFailure(parser, "1" + GROUP_DESC_AMY + INVALID_GROUP_DESC, Group.MESSAGE_CONSTRAINTS); + + // multiple invalid values, but only the first invalid value is captured + assertParseFailure(parser, "1" + INVALID_NAME_DESC + INVALID_EMAIL_DESC + INVALID_PHONE_DESC, + Name.MESSAGE_CONSTRAINTS); + } + + @Test + public void parse_allFieldsSpecified_success() { + Index targetIndex = TypicalIndexes.INDEX_SECOND_PERSON; + String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + GROUP_DESC_BOB + + EMAIL_DESC_AMY + NAME_DESC_AMY + TELEGRAM_DESC_AMY + GITHUB_DESC_BOB; + + EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY) + .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_AMY) + .withGroups(VALID_GROUP_BOB).withGitHub(VALID_GITHUB_BOB).withTelegram(VALID_TELEGRAM_AMY).build(); + EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_someFieldsSpecified_success() { + Index targetIndex = TypicalIndexes.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 = TypicalIndexes.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); + + // group + userInput = targetIndex.getOneBased() + GROUP_DESC_AMY; + descriptor = new EditPersonDescriptorBuilder().withGroups(VALID_GROUP_AMY_CS2101, + VALID_GROUP_AMY_CS2103T).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // telegram + userInput = targetIndex.getOneBased() + TELEGRAM_DESC_AMY; + descriptor = new EditPersonDescriptorBuilder().withTelegram(VALID_TELEGRAM_AMY).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // github + userInput = targetIndex.getOneBased() + GITHUB_DESC_AMY; + descriptor = new EditPersonDescriptorBuilder().withGitHub(VALID_GITHUB_AMY).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_invalidValueFollowedByValidValue_success() { + // no other valid values specified + Index targetIndex = TypicalIndexes.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 + + PHONE_DESC_BOB; + descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // an invalid group followed by 2 valid groups + userInput = targetIndex.getOneBased() + INVALID_GROUP_DESC + GROUP_DESC_BOB + GROUP_DESC_AMY; + descriptor = new EditPersonDescriptorBuilder().withGroups(VALID_GROUP_AMY_CS2101, + VALID_GROUP_AMY_CS2103T).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + } +} diff --git a/src/test/java/sweebook/logic/parser/EditTaskCommandParserTest.java b/src/test/java/sweebook/logic/parser/EditTaskCommandParserTest.java new file mode 100644 index 00000000000..b921bd2dc1e --- /dev/null +++ b/src/test/java/sweebook/logic/parser/EditTaskCommandParserTest.java @@ -0,0 +1,198 @@ +package sweebook.logic.parser; + +import static sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.logic.commands.CommandTestUtil.DATE_DESC_EVENT; +import static sweebook.logic.commands.CommandTestUtil.DATE_DESC_TODO; +import static sweebook.logic.commands.CommandTestUtil.DESCRIPTION_DESC_TODO; +import static sweebook.logic.commands.CommandTestUtil.GROUP_DESC_EVENT; +import static sweebook.logic.commands.CommandTestUtil.GROUP_DESC_TODO; +import static sweebook.logic.commands.CommandTestUtil.INVALID_DATE_DESC; +import static sweebook.logic.commands.CommandTestUtil.INVALID_DESCRIPTION_DESC; +import static sweebook.logic.commands.CommandTestUtil.INVALID_PRIORITY_DESC; +import static sweebook.logic.commands.CommandTestUtil.INVALID_RECURRING_FREQUENCY_DESC; +import static sweebook.logic.commands.CommandTestUtil.INVALID_TASK_TYPE_DESC; +import static sweebook.logic.commands.CommandTestUtil.PRIORITY_DESC_EVENT; +import static sweebook.logic.commands.CommandTestUtil.PRIORITY_DESC_TODO; +import static sweebook.logic.commands.CommandTestUtil.RECURRING_FREQUENCY_DESC_EVENT; +import static sweebook.logic.commands.CommandTestUtil.RECURRING_FREQUENCY_DESC_TODO; +import static sweebook.logic.commands.CommandTestUtil.TASK_TYPE_DESC_DEADLINE; +import static sweebook.logic.commands.CommandTestUtil.TASK_TYPE_DESC_TODO; +import static sweebook.logic.commands.CommandTestUtil.VALID_DATE_1; +import static sweebook.logic.commands.CommandTestUtil.VALID_DATE_3; +import static sweebook.logic.commands.CommandTestUtil.VALID_DESCRIPTION_TODO; +import static sweebook.logic.commands.CommandTestUtil.VALID_GROUP_CS2101; +import static sweebook.logic.commands.CommandTestUtil.VALID_PRIORITY_HIGH; +import static sweebook.logic.commands.CommandTestUtil.VALID_PRIORITY_LOW; +import static sweebook.logic.commands.CommandTestUtil.VALID_RECURRING_FREQUENCY_WEEK; +import static sweebook.logic.commands.CommandTestUtil.VALID_RECURRING_FREQUENCY_YEAR; +import static sweebook.logic.commands.CommandTestUtil.VALID_TASK_TYPE_DEADLINE; +import static sweebook.logic.commands.CommandTestUtil.VALID_TASK_TYPE_TODO; +import static sweebook.logic.commands.EditTaskCommand.EditTaskDescriptor; +import static sweebook.logic.commands.EditTaskCommand.MESSAGE_NOT_EDITED; +import static sweebook.logic.commands.EditTaskCommand.MESSAGE_USAGE; +import static sweebook.logic.parser.CommandParserTestUtil.assertParseFailure; +import static sweebook.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import sweebook.commons.core.index.Index; +import sweebook.logic.commands.EditTaskCommand; +import sweebook.model.task.Date; +import sweebook.model.task.Description; +import sweebook.model.task.Priority; +import sweebook.model.task.RecurringFrequency; +import sweebook.model.task.TaskType; +import sweebook.testutil.EditTaskDescriptorBuilder; +import sweebook.testutil.TypicalIndexes; + + +public class EditTaskCommandParserTest { + + private static final String MESSAGE_INVALID_FORMAT = + String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE); + + private EditTaskCommandParser parser = new EditTaskCommandParser(); + + @Test + public void parse_missingParts_failure() { + // no index specified + assertParseFailure(parser, VALID_DESCRIPTION_TODO, MESSAGE_INVALID_FORMAT); + + // no field specified + assertParseFailure(parser, "1", 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" + DESCRIPTION_DESC_TODO, MESSAGE_INVALID_FORMAT); + + // zero index + assertParseFailure(parser, "0" + DESCRIPTION_DESC_TODO, 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_DESCRIPTION_DESC, + Description.MESSAGE_CONSTRAINTS); // invalid description + assertParseFailure(parser, "1" + INVALID_PRIORITY_DESC, + Priority.MESSAGE_CONSTRAINTS); // invalid priority + assertParseFailure(parser, "1" + INVALID_TASK_TYPE_DESC, + TaskType.MESSAGE_CONSTRAINTS); // invalid task type + assertParseFailure(parser, "1" + INVALID_DATE_DESC, + Date.MESSAGE_CONSTRAINTS); // invalid date + assertParseFailure(parser, "1" + INVALID_RECURRING_FREQUENCY_DESC, + RecurringFrequency.MESSAGE_CONSTRAINTS); // invalid recurring freq + + // invalid description followed by valid date + assertParseFailure(parser, "1" + INVALID_DESCRIPTION_DESC + DATE_DESC_TODO, + Description.MESSAGE_CONSTRAINTS); + + // valid description followed by invalid description. The test case for invalid description + // followed by valid description is tested at {@code parse_invalidValueFollowedByValidValue_success()} + assertParseFailure(parser, "1" + DESCRIPTION_DESC_TODO + INVALID_DESCRIPTION_DESC, + Description.MESSAGE_CONSTRAINTS); + + // multiple invalid values, but only the first invalid value is captured + assertParseFailure(parser, "1" + INVALID_DESCRIPTION_DESC + INVALID_DATE_DESC + INVALID_PRIORITY_DESC, + Description.MESSAGE_CONSTRAINTS); + } + + @Test + public void parse_allFieldsSpecified_success() { + Index targetIndex = TypicalIndexes.INDEX_SECOND_TASK; + String userInput = targetIndex.getOneBased() + DESCRIPTION_DESC_TODO + GROUP_DESC_TODO + PRIORITY_DESC_TODO + + DATE_DESC_TODO + RECURRING_FREQUENCY_DESC_TODO + TASK_TYPE_DESC_TODO; + + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder() + .withDescription(VALID_DESCRIPTION_TODO).withGroup(VALID_GROUP_CS2101).withPriority(VALID_PRIORITY_LOW) + .withDate(VALID_DATE_1).withRecurringFrequency(VALID_RECURRING_FREQUENCY_WEEK) + .withTaskType(VALID_TASK_TYPE_TODO).build(); + EditTaskCommand expectedCommand = new EditTaskCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_someFieldsSpecified_success() { + Index targetIndex = TypicalIndexes.INDEX_FIRST_TASK; + String userInput = targetIndex.getOneBased() + DESCRIPTION_DESC_TODO + RECURRING_FREQUENCY_DESC_EVENT; + + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder() + .withDescription(VALID_DESCRIPTION_TODO) + .withRecurringFrequency(VALID_RECURRING_FREQUENCY_YEAR).build(); + EditTaskCommand expectedCommand = new EditTaskCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_oneFieldSpecified_success() { + // description + Index targetIndex = TypicalIndexes.INDEX_SECOND_TASK; + String userInput = targetIndex.getOneBased() + DESCRIPTION_DESC_TODO; + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder() + .withDescription(VALID_DESCRIPTION_TODO).build(); + EditTaskCommand expectedCommand = new EditTaskCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // group + userInput = targetIndex.getOneBased() + GROUP_DESC_EVENT; + descriptor = new EditTaskDescriptorBuilder().withGroup(VALID_GROUP_CS2101).build(); + expectedCommand = new EditTaskCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // priority + userInput = targetIndex.getOneBased() + PRIORITY_DESC_EVENT; + descriptor = new EditTaskDescriptorBuilder().withPriority(VALID_PRIORITY_HIGH).build(); + expectedCommand = new EditTaskCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // recurring frequency + userInput = targetIndex.getOneBased() + RECURRING_FREQUENCY_DESC_EVENT; + descriptor = new EditTaskDescriptorBuilder().withRecurringFrequency(VALID_RECURRING_FREQUENCY_YEAR).build(); + expectedCommand = new EditTaskCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // date + userInput = targetIndex.getOneBased() + DATE_DESC_EVENT; + descriptor = new EditTaskDescriptorBuilder().withDate(VALID_DATE_3).build(); + expectedCommand = new EditTaskCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // task type + userInput = targetIndex.getOneBased() + TASK_TYPE_DESC_DEADLINE; + descriptor = new EditTaskDescriptorBuilder().withTaskType(VALID_TASK_TYPE_DEADLINE).build(); + expectedCommand = new EditTaskCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_invalidValueFollowedByValidValue_success() { + // no other valid values specified + Index targetIndex = TypicalIndexes.INDEX_FIRST_TASK; + String userInput = targetIndex.getOneBased() + INVALID_DESCRIPTION_DESC + DESCRIPTION_DESC_TODO; + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder() + .withDescription(VALID_DESCRIPTION_TODO).build(); + EditTaskCommand expectedCommand = new EditTaskCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // other valid values specified + userInput = targetIndex.getOneBased() + DATE_DESC_TODO + INVALID_DESCRIPTION_DESC + DESCRIPTION_DESC_TODO; + descriptor = new EditTaskDescriptorBuilder() + .withDate(VALID_DATE_1) + .withDescription(VALID_DESCRIPTION_TODO) + .build(); + expectedCommand = new EditTaskCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + } +} diff --git a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java b/src/test/java/sweebook/logic/parser/FindCommandParserTest.java similarity index 61% rename from src/test/java/seedu/address/logic/parser/FindCommandParserTest.java rename to src/test/java/sweebook/logic/parser/FindCommandParserTest.java index 70f4f0e79c4..9de830b6a0c 100644 --- a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java +++ b/src/test/java/sweebook/logic/parser/FindCommandParserTest.java @@ -1,15 +1,16 @@ -package seedu.address.logic.parser; +package sweebook.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 sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.logic.commands.FindCommand.MESSAGE_USAGE; +import static sweebook.logic.parser.CommandParserTestUtil.assertParseFailure; +import static sweebook.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; +import sweebook.logic.commands.FindCommand; +import sweebook.model.person.NameContainsKeywordsPredicate; public class FindCommandParserTest { @@ -17,7 +18,7 @@ public class FindCommandParserTest { @Test public void parse_emptyArg_throwsParseException() { - assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE)); } @Test diff --git a/src/test/java/sweebook/logic/parser/GroupCommandParserTest.java b/src/test/java/sweebook/logic/parser/GroupCommandParserTest.java new file mode 100644 index 00000000000..93ba34e1dfe --- /dev/null +++ b/src/test/java/sweebook/logic/parser/GroupCommandParserTest.java @@ -0,0 +1,39 @@ +package sweebook.logic.parser; + +import static sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.logic.commands.GroupCommand.MESSAGE_USAGE; +import static sweebook.logic.parser.CommandParserTestUtil.assertParseFailure; +import static sweebook.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import sweebook.logic.commands.GroupCommand; +import sweebook.model.group.Group; +import sweebook.model.group.GroupPredicate; + +public class GroupCommandParserTest { + private GroupCommandParser parser = new GroupCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE)); + } + + @Test + public void parse_validArgs_returnsGroupCommand() { + // no leading and trailing whitespaces + GroupCommand expectedGroupCommand = + new GroupCommand(new GroupPredicate(new Group("CS2101"))); + assertParseSuccess(parser, "CS2101", expectedGroupCommand); + + // multiple whitespaces between keywords + assertParseSuccess(parser, " \n CS2101 \n \t ", expectedGroupCommand); + } + + @Test + public void parse_invalidArgs_failure() { + assertParseFailure(parser, "CS2103", Group.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "CS210A", Group.MESSAGE_CONSTRAINTS); + } + +} diff --git a/src/test/java/sweebook/logic/parser/ParserUtilTest.java b/src/test/java/sweebook/logic/parser/ParserUtilTest.java new file mode 100644 index 00000000000..0116698514f --- /dev/null +++ b/src/test/java/sweebook/logic/parser/ParserUtilTest.java @@ -0,0 +1,206 @@ +package sweebook.logic.parser; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static sweebook.logic.parser.ParserUtil.MESSAGE_INVALID_INDEX; +import static sweebook.logic.parser.ParserUtil.parseEmail; +import static sweebook.logic.parser.ParserUtil.parseGroup; +import static sweebook.logic.parser.ParserUtil.parseGroups; +import static sweebook.logic.parser.ParserUtil.parseIndex; +import static sweebook.logic.parser.ParserUtil.parseName; +import static sweebook.logic.parser.ParserUtil.parsePhone; +import static sweebook.logic.parser.ParserUtil.parseUsername; +import static sweebook.testutil.Assert.assertThrows; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import sweebook.logic.parser.exceptions.ParseException; +import sweebook.model.group.Group; +import sweebook.model.person.Email; +import sweebook.model.person.Name; +import sweebook.model.person.Phone; +import sweebook.testutil.TypicalIndexes; + +public class ParserUtilTest { + private static final String INVALID_NAME = "R@chel"; + private static final String INVALID_PHONE = "+651234"; + private static final String INVALID_EMAIL = "example.com"; + private static final String INVALID_TAG = "#friend"; + private static final String INVALID_GROUP = "CS1234"; + private static final String INVALID_USERNAME = "-foo"; + + private static final String VALID_NAME = "Rachel Walker"; + private static final String VALID_PHONE = "123456"; + private static final String VALID_EMAIL = "rachel@example.com"; + private static final String VALID_GROUP_1 = "CS2101"; + private static final String VALID_GROUP_2 = "CS2103T"; + private static final String VALID_USERNAME_WITHOUT_AT = "rachel"; // AT refers to the symbol '@' + private static final String VALID_USERNAME_WITH_AT = "@rachel"; + + private static final String WHITESPACE = " \t\r\n"; + + @Test + public void parseIndex_invalidInput_throwsParseException() { + assertThrows(ParseException.class, () -> parseIndex("10 a")); + } + + @Test + public void parseIndex_outOfRangeInput_throwsParseException() { + assertThrows(ParseException.class, MESSAGE_INVALID_INDEX, () + -> parseIndex(Long.toString(Integer.MAX_VALUE + 1))); + } + + @Test + public void parseIndex_validInput_success() throws Exception { + // No whitespaces + assertEquals(TypicalIndexes.INDEX_FIRST_PERSON, parseIndex("1")); + + // Leading and trailing whitespaces + assertEquals(TypicalIndexes.INDEX_FIRST_PERSON, parseIndex(" 1 ")); + } + + @Test + public void parseName_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> parseName((String) null)); + } + + @Test + public void parseName_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> parseName(INVALID_NAME)); + } + + @Test + public void parseName_validValueWithoutWhitespace_returnsName() throws Exception { + Name expectedName = new Name(VALID_NAME); + assertEquals(expectedName, 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, parseName(nameWithWhitespace)); + } + + @Test + public void parsePhone_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> parsePhone((String) null)); + } + + @Test + public void parsePhone_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> parsePhone(INVALID_PHONE)); + } + + @Test + public void parsePhone_validValueWithoutWhitespace_returnsPhone() throws Exception { + Phone expectedPhone = new Phone(VALID_PHONE); + assertEquals(expectedPhone, 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, parsePhone(phoneWithWhitespace)); + } + + @Test + public void parseEmail_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> parseEmail((String) null)); + } + + @Test + public void parseEmail_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> parseEmail(INVALID_EMAIL)); + } + + @Test + public void parseEmail_validValueWithoutWhitespace_returnsEmail() throws Exception { + Email expectedEmail = new Email(VALID_EMAIL); + assertEquals(expectedEmail, 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, parseEmail(emailWithWhitespace)); + } + + @Test + public void parseGroup_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> parseGroup(null)); + } + + @Test + public void parseGroup_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> parseGroup(INVALID_GROUP)); + } + + @Test + public void parseGroup_validValueWithoutWhitespace_returnsGroup() throws Exception { + Group expectedGroup = new Group(VALID_GROUP_1); + assertEquals(expectedGroup, parseGroup(VALID_GROUP_1)); + } + + @Test + public void parseGroup_validValueWithWhitespace_returnsTrimmedGroup() throws Exception { + String groupWithWhitespace = WHITESPACE + VALID_GROUP_1 + WHITESPACE; + Group expectedGroup = new Group(VALID_GROUP_1); + assertEquals(expectedGroup, parseGroup(groupWithWhitespace)); + } + + @Test + public void parseGroups_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> parseGroups(null)); + } + + @Test + public void parseGroups_collectionWithInvalidGroups_throwsParseException() { + assertThrows(ParseException.class, () -> parseGroups(Arrays.asList(VALID_GROUP_1, INVALID_TAG))); + } + + @Test + public void parseGroups_collectionWithValidGroups_returnsGroupSet() throws Exception { + Set actualGroupSet = parseGroups(Arrays.asList(VALID_GROUP_1, VALID_GROUP_2)); + Set expectedGroupSet = new HashSet<>(Arrays.asList(new Group(VALID_GROUP_1), + new Group(VALID_GROUP_2))); + + assertEquals(expectedGroupSet, actualGroupSet); + } + + @Test + public void parseUsername_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> parseUsername((String) null)); + } + + @Test + public void parseUsername_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> parseUsername(INVALID_USERNAME)); + } + + @Test + public void parseUsername_validValueWithAtSymbol_returnsValueWithoutSymbol() throws Exception { + String expectedUsername = VALID_USERNAME_WITHOUT_AT; + String actualUsername = parseUsername(VALID_USERNAME_WITH_AT); + assertEquals(expectedUsername, actualUsername); + } + + @Test + public void parseUsername_validValueWithoutAtSymbol_returnsValueWithoutSymbol() throws Exception { + String expectedUsername = VALID_USERNAME_WITHOUT_AT; + String actualUsername = parseUsername(VALID_USERNAME_WITHOUT_AT); + assertEquals(expectedUsername, actualUsername); + } + + @Test + public void parseUsername_validValueWithWhitespace_returnsTrimmedUsername() throws Exception { + String usernameWithWhitespace = WHITESPACE + VALID_USERNAME_WITHOUT_AT + WHITESPACE; + String actualUsername = parseUsername(VALID_USERNAME_WITHOUT_AT); + assertEquals(VALID_USERNAME_WITHOUT_AT, actualUsername); + } +} diff --git a/src/test/java/sweebook/logic/parser/SortTasksCommandParserTest.java b/src/test/java/sweebook/logic/parser/SortTasksCommandParserTest.java new file mode 100644 index 00000000000..17f9363d715 --- /dev/null +++ b/src/test/java/sweebook/logic/parser/SortTasksCommandParserTest.java @@ -0,0 +1,41 @@ +package sweebook.logic.parser; + +import static sweebook.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static sweebook.logic.commands.SortTasksCommand.MESSAGE_USAGE; +import static sweebook.logic.parser.CommandParserTestUtil.assertParseFailure; +import static sweebook.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import sweebook.logic.commands.SortTasksCommand; +import sweebook.model.task.SortTaskComparator; + +class SortTasksCommandParserTest { + + private final SortTasksCommandParser parser = new SortTasksCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE)); + } + + @Test + public void parse_validArgs_returnsSortTasksCommand() { + + SortTasksCommand expectedSortTasksCommand = + new SortTasksCommand(new SortTaskComparator("desc", "d")); + + // no leading and trailing whitespaces + assertParseSuccess(parser, " param/desc o/d", expectedSortTasksCommand); + + // multiple whitespaces between keywords + assertParseSuccess(parser, " \n param/desc \n \t o/d \t", expectedSortTasksCommand); + } + + @Test + public void parse_invalidArgs_failure() { + assertParseFailure(parser, "param/notAParam o/d", String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE)); + assertParseFailure(parser, "param/pty o/a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE)); + + } +} diff --git a/src/test/java/sweebook/model/ContactListTest.java b/src/test/java/sweebook/model/ContactListTest.java new file mode 100644 index 00000000000..5c9b6885609 --- /dev/null +++ b/src/test/java/sweebook/model/ContactListTest.java @@ -0,0 +1,97 @@ +package sweebook.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 sweebook.testutil.Assert.assertThrows; + +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 sweebook.model.person.Person; +import sweebook.model.person.exceptions.DuplicatePersonException; +import sweebook.testutil.PersonBuilder; +import sweebook.testutil.TypicalPersons; + +public class ContactListTest { + + private final ContactList contactList = new ContactList(); + + @Test + public void constructor() { + assertEquals(Collections.emptyList(), contactList.getPersonList()); + } + + @Test + public void resetData_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> contactList.resetData(null)); + } + + @Test + public void resetData_withValidReadOnlyContactList_replacesData() { + ContactList newData = TypicalPersons.getTypicalContactList(); + contactList.resetData(newData); + assertEquals(newData, contactList); + } + + @Test + public void resetData_withDuplicatePersons_throwsDuplicatePersonException() { + // Two persons with the same identity fields + Person editedAlice = new PersonBuilder(TypicalPersons.ALICE).build(); + List newPersons = Arrays.asList(TypicalPersons.ALICE, editedAlice); + ContactListStub newData = new ContactListStub(newPersons); + + assertThrows(DuplicatePersonException.class, () -> contactList.resetData(newData)); + } + + @Test + public void hasPerson_nullPerson_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> contactList.hasPerson(null)); + } + + @Test + public void hasPerson_personNotInContactList_returnsFalse() { + assertFalse(contactList.hasPerson(TypicalPersons.ALICE)); + } + + @Test + public void hasPerson_personInContactList_returnsTrue() { + contactList.addPerson(TypicalPersons.ALICE); + assertTrue(contactList.hasPerson(TypicalPersons.ALICE)); + } + + @Test + public void hasPerson_personWithSameIdentityFieldsInContactList_returnsTrue() { + contactList.addPerson(TypicalPersons.ALICE); + Person editedAlice = new PersonBuilder(TypicalPersons.ALICE).build(); + assertTrue(contactList.hasPerson(editedAlice)); + } + + @Test + public void getPersonList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> contactList.getPersonList().remove(0)); + } + + /** + * A stub ReadOnlyContactList whose persons list can violate interface constraints. + */ + private static class ContactListStub implements ReadOnlyContactList { + private final ObservableList persons = FXCollections.observableArrayList(); + + ContactListStub(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/sweebook/model/ModelManagerTest.java similarity index 66% rename from src/test/java/seedu/address/model/ModelManagerTest.java rename to src/test/java/sweebook/model/ModelManagerTest.java index 2cf1418d116..dd0ef6791f2 100644 --- a/src/test/java/seedu/address/model/ModelManagerTest.java +++ b/src/test/java/sweebook/model/ModelManagerTest.java @@ -1,12 +1,12 @@ -package seedu.address.model; +package sweebook.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 static sweebook.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import static sweebook.testutil.Assert.assertThrows; +import static sweebook.testutil.TypicalPersons.ALICE; +import static sweebook.testutil.TypicalPersons.BENSON; import java.nio.file.Path; import java.nio.file.Paths; @@ -14,9 +14,9 @@ import org.junit.jupiter.api.Test; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.testutil.AddressBookBuilder; +import sweebook.commons.core.GuiSettings; +import sweebook.model.person.NameContainsKeywordsPredicate; +import sweebook.testutil.ContactListBuilder; public class ModelManagerTest { @@ -26,7 +26,7 @@ public class ModelManagerTest { public void constructor() { assertEquals(new UserPrefs(), modelManager.getUserPrefs()); assertEquals(new GuiSettings(), modelManager.getGuiSettings()); - assertEquals(new AddressBook(), new AddressBook(modelManager.getAddressBook())); + assertEquals(new ContactList(), new ContactList(modelManager.getContactList())); } @Test @@ -37,14 +37,14 @@ public void setUserPrefs_nullUserPrefs_throwsNullPointerException() { @Test public void setUserPrefs_validUserPrefs_copiesUserPrefs() { UserPrefs userPrefs = new UserPrefs(); - userPrefs.setAddressBookFilePath(Paths.get("address/book/file/path")); + userPrefs.setContactListFilePath(Paths.get("sweebook/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")); + userPrefs.setContactListFilePath(Paths.get("new/sweebook/file/path")); assertEquals(oldUserPrefs, modelManager.getUserPrefs()); } @@ -61,15 +61,15 @@ public void setGuiSettings_validGuiSettings_setsGuiSettings() { } @Test - public void setAddressBookFilePath_nullPath_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> modelManager.setAddressBookFilePath(null)); + public void setContactListFilePath_nullPath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> modelManager.setContactListFilePath(null)); } @Test - public void setAddressBookFilePath_validPath_setsAddressBookFilePath() { - Path path = Paths.get("address/book/file/path"); - modelManager.setAddressBookFilePath(path); - assertEquals(path, modelManager.getAddressBookFilePath()); + public void setContactListFilePath_validPath_setsContactListFilePath() { + Path path = Paths.get("sweebook/file/path"); + modelManager.setContactListFilePath(path); + assertEquals(path, modelManager.getContactListFilePath()); } @Test @@ -78,12 +78,12 @@ public void hasPerson_nullPerson_throwsNullPointerException() { } @Test - public void hasPerson_personNotInAddressBook_returnsFalse() { + public void hasPerson_personNotInContactList_returnsFalse() { assertFalse(modelManager.hasPerson(ALICE)); } @Test - public void hasPerson_personInAddressBook_returnsTrue() { + public void hasPerson_personInContactList_returnsTrue() { modelManager.addPerson(ALICE); assertTrue(modelManager.hasPerson(ALICE)); } @@ -95,13 +95,14 @@ public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException @Test public void equals() { - AddressBook addressBook = new AddressBookBuilder().withPerson(ALICE).withPerson(BENSON).build(); - AddressBook differentAddressBook = new AddressBook(); + ContactList contactList = new ContactListBuilder().withPerson(ALICE).withPerson(BENSON).build(); + ContactList differentContactList = new ContactList(); UserPrefs userPrefs = new UserPrefs(); + TaskRecords taskRecords = new TaskRecords(); // same values -> returns true - modelManager = new ModelManager(addressBook, userPrefs); - ModelManager modelManagerCopy = new ModelManager(addressBook, userPrefs); + modelManager = new ModelManager(contactList, userPrefs, taskRecords); + ModelManager modelManagerCopy = new ModelManager(contactList, userPrefs, taskRecords); assertTrue(modelManager.equals(modelManagerCopy)); // same object -> returns true @@ -113,20 +114,20 @@ public void equals() { // different types -> returns false assertFalse(modelManager.equals(5)); - // different addressBook -> returns false - assertFalse(modelManager.equals(new ModelManager(differentAddressBook, userPrefs))); + // different contactList -> returns false + assertFalse(modelManager.equals(new ModelManager(differentContactList, userPrefs, taskRecords))); // 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))); + assertFalse(modelManager.equals(new ModelManager(contactList, userPrefs, taskRecords))); // 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))); + differentUserPrefs.setContactListFilePath(Paths.get("differentFilePath")); + assertFalse(modelManager.equals(new ModelManager(contactList, differentUserPrefs, taskRecords))); } } diff --git a/src/test/java/seedu/address/model/UserPrefsTest.java b/src/test/java/sweebook/model/UserPrefsTest.java similarity index 70% rename from src/test/java/seedu/address/model/UserPrefsTest.java rename to src/test/java/sweebook/model/UserPrefsTest.java index b1307a70d52..2edafa8b6a5 100644 --- a/src/test/java/seedu/address/model/UserPrefsTest.java +++ b/src/test/java/sweebook/model/UserPrefsTest.java @@ -1,6 +1,6 @@ -package seedu.address.model; +package sweebook.model; -import static seedu.address.testutil.Assert.assertThrows; +import static sweebook.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 setContactListFilePath_nullPath_throwsNullPointerException() { UserPrefs userPrefs = new UserPrefs(); - assertThrows(NullPointerException.class, () -> userPrefs.setAddressBookFilePath(null)); + assertThrows(NullPointerException.class, () -> userPrefs.setContactListFilePath(null)); } } diff --git a/src/test/java/sweebook/model/group/GroupPredicateTest.java b/src/test/java/sweebook/model/group/GroupPredicateTest.java new file mode 100644 index 00000000000..662f7c8a46a --- /dev/null +++ b/src/test/java/sweebook/model/group/GroupPredicateTest.java @@ -0,0 +1,53 @@ +package sweebook.model.group; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import sweebook.testutil.PersonBuilder; + +public class GroupPredicateTest { + @Test + public void equals() { + Group firstGroup = new Group("CS2103T"); + Group secondGroup = new Group("CS2101"); + + GroupPredicate firstPredicate = new GroupPredicate(firstGroup); + GroupPredicate secondPredicate = new GroupPredicate(secondGroup); + + // same object -> returns true + assertTrue(firstPredicate.equals(firstPredicate)); + + // same values -> returns true + GroupPredicate firstPredicateCopy = new GroupPredicate(firstGroup); + 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_personInGroup_returnsTrue() { + GroupPredicate predicate = new GroupPredicate(new Group("CS2103T")); + + // Person with one matching group + assertTrue(predicate.test(new PersonBuilder().withGroups("CS2103T").build())); + + // Person with two groups and one matching group + assertTrue(predicate.test(new PersonBuilder().withGroups("CS2103T", "CS2101").build())); + } + + @Test + public void test_nameDoesNotContainKeywords_returnsFalse() { + // Person with no matching group + GroupPredicate predicate = new GroupPredicate(new Group("CS2103T")); + assertFalse(predicate.test(new PersonBuilder().withGroups("CS2101").build())); + } +} diff --git a/src/test/java/sweebook/model/group/GroupTest.java b/src/test/java/sweebook/model/group/GroupTest.java new file mode 100644 index 00000000000..ac8370eeb1b --- /dev/null +++ b/src/test/java/sweebook/model/group/GroupTest.java @@ -0,0 +1,37 @@ +package sweebook.model.group; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static sweebook.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class GroupTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Group(null)); + } + + @Test + public void constructor_invalidGroupName_throwsIllegalArgumentException() { + String invalidGroupName = "CS2104T"; + assertThrows(IllegalArgumentException.class, () -> new Group(invalidGroupName)); + } + + @Test + public void isValidGroup() { + // null group name + assertThrows(NullPointerException.class, () -> Group.isValidGroup(null)); + + // invalid group name + assertFalse(Group.isValidGroup("CS2100")); + assertFalse(Group.isValidGroup("CS2103")); + + // valid group name + assertTrue(Group.isValidGroup("CS2103T")); + assertTrue(Group.isValidGroup("CS2101")); + + } + +} diff --git a/src/test/java/seedu/address/model/person/EmailTest.java b/src/test/java/sweebook/model/person/EmailTest.java similarity index 97% rename from src/test/java/seedu/address/model/person/EmailTest.java rename to src/test/java/sweebook/model/person/EmailTest.java index bbcc6c8c98e..7c58055ab29 100644 --- a/src/test/java/seedu/address/model/person/EmailTest.java +++ b/src/test/java/sweebook/model/person/EmailTest.java @@ -1,8 +1,8 @@ -package seedu.address.model.person; +package sweebook.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 static sweebook.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java b/src/test/java/sweebook/model/person/NameContainsKeywordsPredicateTest.java similarity index 90% rename from src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java rename to src/test/java/sweebook/model/person/NameContainsKeywordsPredicateTest.java index f136664e017..fd09b9e9398 100644 --- a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java +++ b/src/test/java/sweebook/model/person/NameContainsKeywordsPredicateTest.java @@ -1,4 +1,4 @@ -package seedu.address.model.person; +package sweebook.model.person; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -9,7 +9,7 @@ import org.junit.jupiter.api.Test; -import seedu.address.testutil.PersonBuilder; +import sweebook.testutil.PersonBuilder; public class NameContainsKeywordsPredicateTest { @@ -67,9 +67,8 @@ public void test_nameDoesNotContainKeywords_returnsFalse() { predicate = new NameContainsKeywordsPredicate(Arrays.asList("Carol")); assertFalse(predicate.test(new PersonBuilder().withName("Alice Bob").build())); - // Keywords match phone, email and address, but does not match name - 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())); + // Keywords match phone and email, but does not match name + predicate = new NameContainsKeywordsPredicate(Arrays.asList("12345", "alice@email.com", "Main")); + assertFalse(predicate.test(new PersonBuilder().withName("Alice").withPhone("12345").build())); } } diff --git a/src/test/java/seedu/address/model/person/NameTest.java b/src/test/java/sweebook/model/person/NameTest.java similarity index 82% rename from src/test/java/seedu/address/model/person/NameTest.java rename to src/test/java/sweebook/model/person/NameTest.java index c9801392874..807be9038cb 100644 --- a/src/test/java/seedu/address/model/person/NameTest.java +++ b/src/test/java/sweebook/model/person/NameTest.java @@ -1,8 +1,8 @@ -package seedu.address.model.person; +package sweebook.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 static sweebook.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; @@ -32,9 +32,8 @@ public void isValidName() { // 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("peter the second")); // alphanumeric characters assertTrue(Name.isValidName("Capital Tan")); // with capital letters - assertTrue(Name.isValidName("David Roger Jackson Ray Jr 2nd")); // long names + assertTrue(Name.isValidName("David Roger Jackson Ray Jr Second")); // long names } } diff --git a/src/test/java/seedu/address/model/person/PersonTest.java b/src/test/java/sweebook/model/person/PersonTest.java similarity index 60% rename from src/test/java/seedu/address/model/person/PersonTest.java rename to src/test/java/sweebook/model/person/PersonTest.java index b29c097cfd4..7e099e4e16c 100644 --- a/src/test/java/seedu/address/model/person/PersonTest.java +++ b/src/test/java/sweebook/model/person/PersonTest.java @@ -1,28 +1,22 @@ -package seedu.address.model.person; +package sweebook.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 static sweebook.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_GITHUB_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_GROUP_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_NAME_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_TELEGRAM_BOB; +import static sweebook.testutil.TypicalPersons.ALICE; +import static sweebook.testutil.TypicalPersons.BOB; import org.junit.jupiter.api.Test; -import seedu.address.testutil.PersonBuilder; +import sweebook.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 @@ -33,21 +27,21 @@ public void isSamePerson() { // 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(); + .withGroups(VALID_GROUP_BOB).withTelegram(VALID_TELEGRAM_BOB).withGitHub(VALID_GITHUB_BOB).build(); assertTrue(ALICE.isSamePerson(editedAlice)); - // different name, all other attributes same -> returns false + // different name, all phone/email/github/telegram same -> returns true editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build(); - assertFalse(ALICE.isSamePerson(editedAlice)); + assertTrue(ALICE.isSamePerson(editedAlice)); - // name differs in case, all other attributes same -> returns false + // name differs in case, all other attributes same -> returns true Person editedBob = new PersonBuilder(BOB).withName(VALID_NAME_BOB.toLowerCase()).build(); - assertFalse(BOB.isSamePerson(editedBob)); + assertTrue(BOB.isSamePerson(editedBob)); - // name has trailing spaces, all other attributes same -> returns false + // name has trailing spaces, all other attributes same -> returns True String nameWithTrailingSpaces = VALID_NAME_BOB + " "; editedBob = new PersonBuilder(BOB).withName(nameWithTrailingSpaces).build(); - assertFalse(BOB.isSamePerson(editedBob)); + assertTrue(BOB.isSamePerson(editedBob)); } @Test @@ -80,12 +74,16 @@ public void equals() { editedAlice = new PersonBuilder(ALICE).withEmail(VALID_EMAIL_BOB).build(); assertFalse(ALICE.equals(editedAlice)); - // different address -> returns false - editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).build(); + // different group -> returns false + editedAlice = new PersonBuilder(ALICE).withGroups(VALID_GROUP_BOB).build(); + assertFalse(ALICE.equals(editedAlice)); + + // different telegram -> returns false + editedAlice = new PersonBuilder(ALICE).withTelegram(VALID_TELEGRAM_BOB).build(); assertFalse(ALICE.equals(editedAlice)); - // different tags -> returns false - editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND).build(); + // different github -> returns false + editedAlice = new PersonBuilder(ALICE).withGitHub(VALID_GITHUB_BOB).build(); assertFalse(ALICE.equals(editedAlice)); } } diff --git a/src/test/java/seedu/address/model/person/PhoneTest.java b/src/test/java/sweebook/model/person/PhoneTest.java similarity index 93% rename from src/test/java/seedu/address/model/person/PhoneTest.java rename to src/test/java/sweebook/model/person/PhoneTest.java index 8dd52766a5f..0bf974f69bf 100644 --- a/src/test/java/seedu/address/model/person/PhoneTest.java +++ b/src/test/java/sweebook/model/person/PhoneTest.java @@ -1,8 +1,8 @@ -package seedu.address.model.person; +package sweebook.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 static sweebook.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/model/person/UniquePersonListTest.java b/src/test/java/sweebook/model/person/UniquePersonListTest.java similarity index 87% rename from src/test/java/seedu/address/model/person/UniquePersonListTest.java rename to src/test/java/sweebook/model/person/UniquePersonListTest.java index 1cc5fe9e0fe..d962aec2b94 100644 --- a/src/test/java/seedu/address/model/person/UniquePersonListTest.java +++ b/src/test/java/sweebook/model/person/UniquePersonListTest.java @@ -1,13 +1,12 @@ -package seedu.address.model.person; +package sweebook.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 static sweebook.logic.commands.CommandTestUtil.VALID_GROUP_BOB; +import static sweebook.testutil.Assert.assertThrows; +import static sweebook.testutil.TypicalPersons.ALICE; +import static sweebook.testutil.TypicalPersons.BOB; import java.util.Arrays; import java.util.Collections; @@ -15,9 +14,9 @@ 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; +import sweebook.model.person.exceptions.DuplicatePersonException; +import sweebook.model.person.exceptions.PersonNotFoundException; +import sweebook.testutil.PersonBuilder; public class UniquePersonListTest { @@ -42,8 +41,7 @@ public void contains_personInList_returnsTrue() { @Test public void contains_personWithSameIdentityFieldsInList_returnsTrue() { uniquePersonList.add(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); + Person editedAlice = new PersonBuilder(ALICE).withGroups(VALID_GROUP_BOB).build(); assertTrue(uniquePersonList.contains(editedAlice)); } @@ -85,8 +83,7 @@ public void setPerson_editedPersonIsSamePerson_success() { @Test public void setPerson_editedPersonHasSameIdentity_success() { uniquePersonList.add(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); + Person editedAlice = new PersonBuilder(ALICE).withGroups(VALID_GROUP_BOB).build(); uniquePersonList.setPerson(ALICE, editedAlice); UniquePersonList expectedUniquePersonList = new UniquePersonList(); expectedUniquePersonList.add(editedAlice); diff --git a/src/test/java/sweebook/model/person/social/SocialTest.java b/src/test/java/sweebook/model/person/social/SocialTest.java new file mode 100644 index 00000000000..92d01ff4cad --- /dev/null +++ b/src/test/java/sweebook/model/person/social/SocialTest.java @@ -0,0 +1,25 @@ +package sweebook.model.person.social; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static sweebook.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class SocialTest { + @Test + public void isValidUsername() { + // null username + assertThrows(NullPointerException.class, () -> Social.isValidUsername(null)); + + // invalid usernames + assertFalse(Social.isValidUsername("-foo")); + assertFalse(Social.isValidUsername("foo-")); + assertFalse(Social.isValidUsername("foo@foo.com")); + + // valid usernames + assertTrue(Social.isValidUsername("foo")); + assertTrue(Social.isValidUsername("foo_foo")); + assertTrue(Social.isValidUsername("foo-foo")); + } +} diff --git a/src/test/java/sweebook/model/person/social/TelegramTest.java b/src/test/java/sweebook/model/person/social/TelegramTest.java new file mode 100644 index 00000000000..d4c71d3e747 --- /dev/null +++ b/src/test/java/sweebook/model/person/social/TelegramTest.java @@ -0,0 +1,22 @@ +package sweebook.model.person.social; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static sweebook.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class TelegramTest { + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Telegram(null)); + } + + @Test + public void toUrl_validUsername_returnsValidUrl() { + String username = "alice"; + String expectedUrl = Telegram.BASE_URL + username; + Telegram actual = new Telegram(username); + assertEquals(expectedUrl, actual.toUrl()); + } + +} diff --git a/src/test/java/sweebook/model/task/DateTest.java b/src/test/java/sweebook/model/task/DateTest.java new file mode 100644 index 00000000000..071c0490342 --- /dev/null +++ b/src/test/java/sweebook/model/task/DateTest.java @@ -0,0 +1,202 @@ +package sweebook.model.task; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +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 sweebook.testutil.Assert.assertThrows; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +class DateTest { + //invalid date string: empty string + private static final String INV_DATE_STRING1 = ""; + //invalid date string: incorrect format + private static final String INV_DATE_STRING2 = "05-02-2022"; + private static final String INV_DATE_STRING3 = "2022.02.05"; + private static final String INV_DATE_STRING4 = "5 Feb 2022"; + private static final String INV_DATE_STRING5 = "5 February 2022"; + private static final String INV_DATE_STRING6 = "2022-02-5"; + private static final String INV_DATE_STRING7 = "2022-2-05"; + //invalid date string: not a date + private static final String INV_DATE_STRING8 = "Not a date"; + //invalid date string: invalid year + private static final String INV_DATE_STRING9 = "10000-02-05"; + //invalid date string: invalid month + private static final String INV_DATE_STRING10 = "2022-00-05"; + private static final String INV_DATE_STRING11 = "2022-13-05"; + //invalid date string: invalid day + private static final String INV_DATE_STRING12 = "2022-01-32"; + private static final String INV_DATE_STRING13 = "2022-02-29"; + private static final String INV_DATE_STRING14 = "2022-02-30"; + private static final String INV_DATE_STRING15 = "2022-04-00"; + private static final String INV_DATE_STRING16 = "2022-04-31"; + //valid date string + private static final String DATE_STRING1 = "2022-01-31"; + private static final String DATE_STRING2 = "2022-04-30"; + private static final String DATE_STRING3 = "2022-02-28"; + private static final String DATE_STRING4 = "2022-02-05"; + private static final String DATE_STRING5 = "2024-02-29"; + private static final String DATE_STRING6 = "2030-01-01"; + private static final String DATE_STRING7 = "9999-12-31"; + + private static final Date DATE1 = new Date(DATE_STRING1); + private static final Date DATE2 = new Date(DATE_STRING2); + private static final Date DATE3 = new Date(DATE_STRING3); + private static final Date DATE4 = new Date(DATE_STRING4); + private static final Date DATE5 = new Date(DATE_STRING5); + private static final Date DATE6 = new Date(DATE_STRING6); + private static final Date DATE7 = new Date(DATE_STRING7); + + private static final String[] INV_DATE_STRINGS = {INV_DATE_STRING1, INV_DATE_STRING2, INV_DATE_STRING3, + INV_DATE_STRING4, INV_DATE_STRING5, INV_DATE_STRING6, INV_DATE_STRING7, INV_DATE_STRING8, INV_DATE_STRING9, + INV_DATE_STRING10, INV_DATE_STRING11, INV_DATE_STRING12, INV_DATE_STRING13, INV_DATE_STRING14, + INV_DATE_STRING15, INV_DATE_STRING16}; + private static final String[] DATE_STRINGS = {DATE_STRING1, DATE_STRING2, DATE_STRING3, DATE_STRING4, + DATE_STRING5, DATE_STRING6, DATE_STRING7}; + private static final Date[] DATES = {DATE1, DATE2, DATE3, DATE4, DATE5, DATE6, DATE7}; + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Date(null)); + } + + @Test + public void constructor_invalidDate_throwsIllegalArgumentException() { + Arrays.stream(INV_DATE_STRINGS).forEach(str -> + assertThrows(IllegalArgumentException.class, () -> new Date(str))); + } + + @Test + void isValidDate() { + //valid dates, as the product is released on 2021, + //there is no point testing dates before 2021 even if they are accepted by SWEe-book + Arrays.stream(DATE_STRINGS).forEach(str -> assertTrue(Date.isValidDate(str))); + Arrays.stream(INV_DATE_STRINGS).forEach(str -> assertFalse(Date.isValidDate(str))); + } + + @Test + void testToString() { + String[] formattedDateStrings = {"Jan 31 2022", "Apr 30 2022", "Feb 28 2022", + "Feb 05 2022", "Feb 29 2024", "Jan 01 2030", "Dec 31 9999"}; + assertArrayEquals(formattedDateStrings, Arrays.stream(DATES).map(Date::toString).toArray()); + } + + @Test + void getLocalDate() { + LocalDate[] localDates = {LocalDate.of(2022, 1, 31), LocalDate.of(2022, 4, 30), + LocalDate.of(2022, 2, 28), LocalDate.of(2022, 2, 5), LocalDate.of(2024, 2, 29), + LocalDate.of(2030, 1, 1), LocalDate.of(9999, 12, 31)}; + assertArrayEquals(localDates, Arrays.stream(DATES).map(Date::getLocalDate).toArray()); + } + + @Test + void getString() { + assertArrayEquals(DATE_STRINGS, Arrays.stream(DATES).map(Date::getString).toArray()); + } + + @Test + void testEquals() { + assertArrayEquals(Arrays.stream(DATE_STRINGS).map(Date::new).toArray(), DATES); + assertNotEquals(DATE2, DATE1); + } + + @Test + void compareTo() { + assertTrue(DATE1.compareTo(DATE7) < 0); + assertTrue(DATE6.compareTo(DATE2) > 0); + assertEquals(0, DATE3.compareTo(new Date(DATE_STRING3))); + } + + @Test + void isLastWeek() { + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + LocalDate today = LocalDate.now(); + int differenceFromMon = today.getDayOfWeek().getValue() - 1; + + Date tomorrow = new Date(dtf.format(today.plusDays(1))); + Date todayDate = new Date(dtf.format(today)); + Date thisWeekMon = new Date(dtf.format(today.minusDays(differenceFromMon))); + + Date lastWeekSun = new Date(dtf.format(today.minusDays(differenceFromMon + 1))); + Date lastWeek = new Date(dtf.format(today.minusWeeks(1))); + Date lastMonth = new Date(dtf.format(today.minusMonths(1))); + Date lastYear = new Date(dtf.format(today.minusYears(1))); + + assertFalse(tomorrow.isLastWeek()); + assertFalse(todayDate.isLastWeek()); + assertFalse(thisWeekMon.isLastWeek()); + + assertTrue(lastWeekSun.isLastWeek()); + assertTrue(lastWeek.isLastWeek()); + assertTrue(lastMonth.isLastWeek()); + assertTrue(lastYear.isLastWeek()); + } + + @Test + void isLastMonth() { + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + LocalDate today = LocalDate.now(); + int differenceFromFirstDayOfMonth = today.getDayOfMonth() - 1; + + Date tomorrow = new Date(dtf.format(today.plusDays(1))); + Date todayDate = new Date(dtf.format(today)); + Date firstDayOfSameMonth = new Date(dtf.format(today.minusDays(differenceFromFirstDayOfMonth))); + + Date lastDayOfPreviousMonth = new Date(dtf.format(today.minusDays(differenceFromFirstDayOfMonth + 1))); + Date lastMonth = new Date(dtf.format(today.minusMonths(1))); + Date lastYear = new Date(dtf.format(today.minusYears(1))); + + assertFalse(tomorrow.isLastMonth()); + assertFalse(todayDate.isLastMonth()); + assertFalse(firstDayOfSameMonth.isLastMonth()); + + assertTrue(lastDayOfPreviousMonth.isLastMonth()); + assertTrue(lastMonth.isLastMonth()); + assertTrue(lastYear.isLastMonth()); + } + + @Test + void isLastYear() { + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + LocalDate today = LocalDate.now(); + int differenceFromFirstDayOfYear = today.getDayOfYear() - 1; + + Date tomorrow = new Date(dtf.format(today.plusDays(1))); + Date todayDate = new Date(dtf.format(today)); + Date firstDayOfSameYear = new Date(dtf.format(today.minusDays(differenceFromFirstDayOfYear))); + + Date lastDayOfLastYear = new Date(dtf.format(today.minusDays(differenceFromFirstDayOfYear + 1))); + Date lastYear = new Date(dtf.format(today.minusYears(1))); + + assertFalse(tomorrow.isLastYear()); + assertFalse(todayDate.isLastYear()); + assertFalse(firstDayOfSameYear.isLastYear()); + + assertTrue(lastDayOfLastYear.isLastYear()); + assertTrue(lastYear.isLastYear()); + } + + @Test + void getDateForThisWeek() { + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + LocalDate today = LocalDate.now(); + int differenceOfTodayFromMon = today.getDayOfWeek().getValue() - 1; + Date thisWeekMon = new Date(dtf.format(today.minusDays(differenceOfTodayFromMon))); + + for (Date date: DATES) { + int differenceOfDateFromMon = date.getLocalDate().getDayOfWeek().getValue() - 1; + Date thisWeek = new Date(dtf.format(thisWeekMon.getLocalDate().plusDays(differenceOfDateFromMon))); + assertEquals(thisWeek, date.getDateForThisWeek()); + } + } +} diff --git a/src/test/java/sweebook/model/task/FilterTaskPredicateTest.java b/src/test/java/sweebook/model/task/FilterTaskPredicateTest.java new file mode 100644 index 00000000000..980ee0934a0 --- /dev/null +++ b/src/test/java/sweebook/model/task/FilterTaskPredicateTest.java @@ -0,0 +1,76 @@ +package sweebook.model.task; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static sweebook.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + + +public class FilterTaskPredicateTest { + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new FilterTaskPredicate(null)); + } + + @Test + public void isValidPredicate() { + assertFalse(FilterTaskPredicate.isValidCriterion("")); // empty string + assertFalse(FilterTaskPredicate.isValidCriterion(" ")); // spaces only + assertFalse(FilterTaskPredicate.isValidCriterion("fail")); // invalid predicate + assertFalse(FilterTaskPredicate.isValidCriterion(null)); // null + + assertTrue(FilterTaskPredicate.isValidCriterion("g/cs2101")); // valid, lowercase + assertTrue(FilterTaskPredicate.isValidCriterion("g/cS2103t")); // valid, lowercase and uppercase + assertTrue(FilterTaskPredicate.isValidCriterion("type/event")); // valid, lowercase + assertTrue(FilterTaskPredicate.isValidCriterion("type/toDO")); // valid, lowercase and uppercase + assertFalse(FilterTaskPredicate.isValidCriterion("date/2010-21-21")); // invalid date + assertTrue(FilterTaskPredicate.isValidCriterion("date/2012-12-12")); // valid date + assertTrue(FilterTaskPredicate.isValidCriterion("d/aaaa")); // valid, lowercase + assertTrue(FilterTaskPredicate.isValidCriterion("d/AaAAa")); // valid, lowercase and uppercase + assertFalse(FilterTaskPredicate.isValidCriterion("pty/MeD")); // invalid, contains uppercase + assertTrue(FilterTaskPredicate.isValidCriterion("pty/med")); // valid, lowercase and uppercase + } + + @Test + public void equals() { + final FilterTaskPredicate date = new FilterTaskPredicate("date/2021-11-11"); + final FilterTaskPredicate pty = new FilterTaskPredicate("pty/high"); + final FilterTaskPredicate desc = new FilterTaskPredicate("d/swimming"); + final FilterTaskPredicate type = new FilterTaskPredicate("type/deadline"); + final FilterTaskPredicate group = new FilterTaskPredicate("g/CS2103T"); + + // same object -> returns true + assertTrue(date.equals(date)); + assertTrue(pty.equals(pty)); + assertTrue(desc.equals(desc)); + assertTrue(type.equals(type)); + assertTrue(group.equals(group)); + + // same value -> returns true + assertTrue(date.equals(new FilterTaskPredicate("date/2021-11-11"))); + assertTrue(pty.equals(new FilterTaskPredicate("pty/high"))); + assertTrue(desc.equals(new FilterTaskPredicate("d/swimming"))); + assertTrue(type.equals(new FilterTaskPredicate("type/deadline"))); + assertTrue(group.equals(new FilterTaskPredicate("g/CS2103T"))); + + // null -> returns false + assertFalse(date.equals(null)); + assertFalse(pty.equals(null)); + assertFalse(desc.equals(null)); + assertFalse(type.equals(null)); + assertFalse(group.equals(null)); + + // different types -> returns false + assertFalse(date.equals(pty)); + assertFalse(desc.equals(type)); + assertFalse(date.equals(group)); + + // different value -> returns false + assertFalse(date.equals(new FilterTaskPredicate("date/2021-10-11"))); + assertFalse(pty.equals(new FilterTaskPredicate("pty/low"))); + assertFalse(desc.equals(new FilterTaskPredicate("d/swim"))); + assertFalse(type.equals(new FilterTaskPredicate("type/event"))); + assertFalse(group.equals(new FilterTaskPredicate("g/CS2101"))); + } +} diff --git a/src/test/java/sweebook/model/task/PriorityTest.java b/src/test/java/sweebook/model/task/PriorityTest.java new file mode 100644 index 00000000000..13376c4f57b --- /dev/null +++ b/src/test/java/sweebook/model/task/PriorityTest.java @@ -0,0 +1,64 @@ +package sweebook.model.task; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static sweebook.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +class PriorityTest { + + private static final Priority LOW = new Priority("low"); + private static final Priority MED = new Priority("med"); + private static final Priority HIGH = new Priority("high"); + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Priority(null)); + } + + @Test + public void constructor_invalidPriority_throwsIllegalArgumentException() { + String invalidName1 = ""; + String invalidName2 = "extreme"; + assertThrows(IllegalArgumentException.class, () -> new Priority(invalidName1)); + assertThrows(IllegalArgumentException.class, () -> new Priority(invalidName2)); + } + + @Test + public void isValidPriority() { + assertTrue(Priority.isValidPriority("low")); + assertTrue(Priority.isValidPriority("med")); + assertTrue(Priority.isValidPriority("high")); + + assertFalse(Priority.isValidPriority("")); + assertFalse(Priority.isValidPriority("extreme")); + } + + @Test + void compareTo() { + assertTrue(LOW.compareTo(MED) < 0); + assertEquals(0, MED.compareTo(new Priority("med"))); + assertTrue(HIGH.compareTo(MED) > 0); + } + + @Test + void testEquals() { + assertEquals(new Priority("low"), LOW); + assertEquals(new Priority("med"), MED); + assertEquals(new Priority("high"), HIGH); + + assertNotEquals(LOW, MED); + assertNotEquals(MED, HIGH); + assertNotEquals(LOW, HIGH); + } + + @Test + void getPriorityIcon() { + assertEquals("!", LOW.getPriorityIcon()); + assertEquals("!!", MED.getPriorityIcon()); + assertEquals("!!!", HIGH.getPriorityIcon()); + } +} diff --git a/src/test/java/sweebook/model/task/RecurringFrequencyTest.java b/src/test/java/sweebook/model/task/RecurringFrequencyTest.java new file mode 100644 index 00000000000..0a51079d3bf --- /dev/null +++ b/src/test/java/sweebook/model/task/RecurringFrequencyTest.java @@ -0,0 +1,45 @@ +package sweebook.model.task; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static sweebook.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +class RecurringFrequencyTest { + + private static final RecurringFrequency NONE = new RecurringFrequency("none"); + private static final RecurringFrequency WEEK = new RecurringFrequency("week"); + private static final RecurringFrequency MONTH = new RecurringFrequency("month"); + private static final RecurringFrequency YEAR = new RecurringFrequency("year"); + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new RecurringFrequency(null)); + } + + @Test + public void constructor_invalidRecurringFrequency_throwsIllegalArgumentException() { + String invalidRecurringFrequency1 = ""; + String invalidRecurringFrequency2 = "extreme"; + assertThrows(IllegalArgumentException.class, () -> new RecurringFrequency(invalidRecurringFrequency1)); + assertThrows(IllegalArgumentException.class, () -> new RecurringFrequency(invalidRecurringFrequency2)); + } + + @Test + void display() { + assertEquals("", NONE.display()); + assertEquals("Recurring: weekly", WEEK.display()); + assertEquals("Recurring: monthly", MONTH.display()); + assertEquals("Recurring: yearly", YEAR.display()); + } + + @Test + void isRecurring() { + assertFalse(NONE.isRecurring()); + assertTrue(WEEK.isRecurring()); + assertTrue(MONTH.isRecurring()); + assertTrue(YEAR.isRecurring()); + } +} diff --git a/src/test/java/sweebook/model/task/SortTaskComparatorTest.java b/src/test/java/sweebook/model/task/SortTaskComparatorTest.java new file mode 100644 index 00000000000..0ec141f5368 --- /dev/null +++ b/src/test/java/sweebook/model/task/SortTaskComparatorTest.java @@ -0,0 +1,95 @@ +package sweebook.model.task; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static sweebook.model.task.SortTaskComparator.ORDERS; +import static sweebook.model.task.SortTaskComparator.PARAMETERS; +import static sweebook.testutil.Assert.assertThrows; +import static sweebook.testutil.TypicalTasks.FIRST_DEADLINE; +import static sweebook.testutil.TypicalTasks.FIRST_EVENT; + +import org.junit.jupiter.api.Test; + +public class SortTaskComparatorTest { + + private static final SortTaskComparator STC_DESC_A = new SortTaskComparator("desc", "a"); + private static final SortTaskComparator STC_DESC_D = new SortTaskComparator("desc", "d"); + private static final SortTaskComparator STC_DATE_A = new SortTaskComparator("date", "a"); + private static final SortTaskComparator STC_DATE_D = new SortTaskComparator("date", "d"); + private static final SortTaskComparator STC_GROUP_A = new SortTaskComparator("group", "a"); + private static final SortTaskComparator STC_GROUP_D = new SortTaskComparator("group", "d"); + private static final SortTaskComparator STC_PTY_A = new SortTaskComparator("pty", "a"); + private static final SortTaskComparator STC_PTY_D = new SortTaskComparator("pty", "d"); + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new SortTaskComparator(null, null)); + } + + @Test + public void constructor_invalidParam_throwsIllegalArgumentException() { + assertThrows(IllegalArgumentException.class, () -> new SortTaskComparator("love", "a")); + } + + @Test + public void constructor_invalidOrder_throwsIllegalArgumentException() { + assertThrows(IllegalArgumentException.class, () -> new SortTaskComparator("desc", "f")); + } + + @Test + public void isValidComparator() { + // null name + assertThrows(NullPointerException.class, () -> SortTaskComparator.isValidComparator(null, null)); + + // invalid param + assertFalse(SortTaskComparator.isValidComparator("", "a")); // empty string + assertFalse(SortTaskComparator.isValidComparator(" ", "a")); // spaces only + assertFalse(SortTaskComparator.isValidComparator("fail", "a")); // invalid param + + // invalid order + assertFalse(SortTaskComparator.isValidComparator("desc", "")); // empty string + assertFalse(SortTaskComparator.isValidComparator("desc", " ")); // spaces only + assertFalse(SortTaskComparator.isValidComparator("desc", "fail")); // invalid param + + // valid param and order + for (String param: PARAMETERS) { + for (String order: ORDERS) { + assertTrue(SortTaskComparator.isValidComparator(param, order)); + } + } + } + + @Test + public void compare() { + assertTrue(STC_DESC_A.compare(FIRST_DEADLINE, FIRST_EVENT) < 0); + assertTrue(STC_DESC_D.compare(FIRST_DEADLINE, FIRST_EVENT) > 0); + assertTrue(STC_DATE_A.compare(FIRST_DEADLINE, FIRST_EVENT) < 0); + assertTrue(STC_DATE_D.compare(FIRST_DEADLINE, FIRST_EVENT) > 0); + assertTrue(STC_GROUP_A.compare(FIRST_DEADLINE, FIRST_EVENT) < 0); + assertTrue(STC_GROUP_D.compare(FIRST_DEADLINE, FIRST_EVENT) > 0); + assertTrue(STC_PTY_A.compare(FIRST_DEADLINE, FIRST_EVENT) > 0); + assertTrue(STC_PTY_D.compare(FIRST_DEADLINE, FIRST_EVENT) < 0); + } + + @Test + public void testToString() { + assertEquals("description in ascending order.", STC_DESC_A.toString()); + assertEquals("description in descending order.", STC_DESC_D.toString()); + assertEquals("deadline / event date in ascending order.", STC_DATE_A.toString()); + assertEquals("deadline / event date in descending order.", STC_DATE_D.toString()); + assertEquals("group in ascending order.", STC_GROUP_A.toString()); + assertEquals("group in descending order.", STC_GROUP_D.toString()); + assertEquals("priority in ascending order.", STC_PTY_A.toString()); + assertEquals("priority in descending order.", STC_PTY_D.toString()); + } + + @Test + public void testEquals() { + assertEquals(STC_DESC_A, STC_DESC_A); + assertEquals(new SortTaskComparator("desc", "a"), STC_DESC_A); + assertNotEquals(new SortTaskComparator("desc", "d"), STC_DESC_A); + assertNotEquals(new SortTaskComparator("date", "a"), STC_DESC_A); + } +} diff --git a/src/test/java/sweebook/model/task/TaskListTest.java b/src/test/java/sweebook/model/task/TaskListTest.java new file mode 100644 index 00000000000..e00f88184a4 --- /dev/null +++ b/src/test/java/sweebook/model/task/TaskListTest.java @@ -0,0 +1,83 @@ +package sweebook.model.task; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static sweebook.testutil.Assert.assertThrows; +import static sweebook.testutil.TypicalTasks.FIRST_DEADLINE; +import static sweebook.testutil.TypicalTasks.FIRST_EVENT; + +import org.junit.jupiter.api.Test; + +import sweebook.testutil.TaskBuilder; +import sweebook.testutil.TypicalTasks; + +class TaskListTest { + + @Test + public void setTasks_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new TaskList().setTasks((TaskList) null)); + } + + @Test + void setTasks() { + TaskList taskList1 = new TaskList(); + TaskList taskList2 = new TaskList(); + for (Task task: TypicalTasks.getTypicalTasks()) { + Task task1 = new TaskBuilder(task).build(); + Task task2 = new TaskBuilder(task).build(); + taskList1.add(task1); + taskList2.add(task2); + } + + TaskList taskList3 = new TaskList(); + taskList3.setTasks(taskList2); + + assertEquals(taskList1, taskList3); + } + + @Test + void updateRecurringTasksDates() { + Task noneTest = new TaskBuilder().withDate("1900-01-01").withRecurringFrequency("none").build(); + Task weekTest = new TaskBuilder().withDate("1900-01-01").withRecurringFrequency("week").build(); + Task monthTest = new TaskBuilder().withDate("1900-01-01").withRecurringFrequency("month").build(); + Task yearTest = new TaskBuilder().withDate("1900-01-01").withRecurringFrequency("year").build(); + + Task noneTestCopy = new TaskBuilder().withDate("1900-01-01").withRecurringFrequency("none").build(); + Task weekTestCopy = new TaskBuilder().withDate("1900-01-01").withRecurringFrequency("week").build(); + Task monthTestCopy = new TaskBuilder().withDate("1900-01-01").withRecurringFrequency("month").build(); + Task yearTestCopy = new TaskBuilder().withDate("1900-01-01").withRecurringFrequency("year").build(); + + TaskList taskList = new TaskList(); + taskList.add(noneTest); + taskList.add(weekTest); + taskList.add(monthTest); + taskList.add(yearTest); + + taskList.updateRecurringTasksDates(); + + assertEquals(noneTestCopy, noneTest); + assertTrue(weekTestCopy.compareBefore(weekTest) < 0); + assertTrue(monthTestCopy.compareBefore(monthTest) < 0); + assertTrue(yearTestCopy.compareBefore(yearTest) < 0); + } + + @Test + void iterator() { + TaskList taskList = new TaskList(); + taskList.add(FIRST_DEADLINE); + taskList.add(FIRST_EVENT); + + assertTrue(taskList.iterator().hasNext()); + assertEquals(FIRST_DEADLINE, taskList.iterator().next()); + taskList.delete(FIRST_DEADLINE); + + assertTrue(taskList.iterator().hasNext()); + assertEquals(FIRST_EVENT, taskList.iterator().next()); + taskList.delete(FIRST_EVENT); + + assertFalse(taskList.iterator().hasNext()); + } + +} + diff --git a/src/test/java/sweebook/model/task/TaskTest.java b/src/test/java/sweebook/model/task/TaskTest.java new file mode 100644 index 00000000000..630cc531d02 --- /dev/null +++ b/src/test/java/sweebook/model/task/TaskTest.java @@ -0,0 +1,321 @@ +package sweebook.model.task; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static sweebook.testutil.Assert.assertThrows; +import static sweebook.testutil.TypicalTasks.FIRST_DEADLINE; +import static sweebook.testutil.TypicalTasks.FIRST_EVENT; +import static sweebook.testutil.TypicalTasks.FIRST_TODO; +import static sweebook.testutil.TypicalTasks.SECOND_DEADLINE; +import static sweebook.testutil.TypicalTasks.SECOND_TODO; +import static sweebook.testutil.TypicalTasks.SECOND_TODO_WITH_CAPS; +import static sweebook.testutil.TypicalTasks.THIRD_TODO; + +import org.junit.jupiter.api.Test; + +import sweebook.model.group.Group; +import sweebook.testutil.TaskBuilder; + +class TaskTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Task(null, null, null, + null, null, null)); + } + + @Test + public void constructor_dateIsNull_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Task(new Description("deadline"), new Group("CS2101"), + null, new TaskType("deadline"), new RecurringFrequency("none"), new Priority("low"))); + assertThrows(NullPointerException.class, () -> new Task(new Description("event"), new Group("CS2101"), + null, new TaskType("event"), new RecurringFrequency("none"), new Priority("low"))); + assertNull(new Task(new Description("todo"), new Group("CS2101"), + null, new TaskType("todo"), new RecurringFrequency("none"), new Priority("low")).getDate()); + } + + @Test + public void constructor_invalidPriority_throwsIllegalArgumentException() { + String invalidName1 = ""; + String invalidName2 = "extreme"; + assertThrows(IllegalArgumentException.class, () -> new Priority(invalidName1)); + assertThrows(IllegalArgumentException.class, () -> new Priority(invalidName2)); + } + + @Test + void getStatusIcon() { + Task deadline1 = new TaskBuilder(FIRST_DEADLINE).build(); + Task event1 = new TaskBuilder(FIRST_EVENT).build(); + Task todo1 = new TaskBuilder(FIRST_TODO).build(); + + // tasks that are not done + assertEquals("[ ]", deadline1.getStatusIcon()); + assertEquals("[ ]", event1.getStatusIcon()); + assertEquals("[ ]", todo1.getStatusIcon()); + + deadline1.markAsDone(); + event1.markAsDone(); + todo1.markAsDone(); + + // tasks that are done + assertEquals("[X]", deadline1.getStatusIcon()); + assertEquals("[X]", event1.getStatusIcon()); + assertEquals("[X]", todo1.getStatusIcon()); + } + + @Test + void getDescription() { + Task deadline1 = new TaskBuilder(FIRST_DEADLINE).build(); + Task event1 = new TaskBuilder(FIRST_EVENT).build(); + Task todo1 = new TaskBuilder(FIRST_TODO).build(); + + assertEquals(new Description("Deadline 1"), deadline1.getDescription()); + assertEquals(new Description("event 1"), event1.getDescription()); + assertEquals(new Description("todo 1"), todo1.getDescription()); + } + + @Test + void getGroup() { + Task deadline1 = new TaskBuilder(FIRST_DEADLINE).build(); + Task event1 = new TaskBuilder(FIRST_EVENT).build(); + Task todo1 = new TaskBuilder(FIRST_TODO).build(); + + assertEquals(new Group("cs2101"), deadline1.getGroup()); + assertEquals(new Group("cs2103t"), event1.getGroup()); + assertEquals(new Group("cs2101"), todo1.getGroup()); + } + + @Test + void getDate() { + Task deadline1 = new TaskBuilder(FIRST_DEADLINE).build(); + Task event1 = new TaskBuilder(FIRST_EVENT).build(); + Task todo1 = new TaskBuilder(FIRST_TODO).build(); + Task todo3 = new TaskBuilder(THIRD_TODO).build(); + + assertEquals(new Date("2021-12-03"), deadline1.getDate()); + assertEquals(new Date("2022-02-03"), event1.getDate()); + assertEquals(new Date("2022-10-29"), todo1.getDate()); + assertNull(todo3.getDate()); + } + + @Test + void getTaskType() { + Task deadline1 = new TaskBuilder(FIRST_DEADLINE).build(); + Task event1 = new TaskBuilder(FIRST_EVENT).build(); + Task todo1 = new TaskBuilder(FIRST_TODO).build(); + + assertEquals(new TaskType("deadline"), deadline1.getTaskType()); + assertEquals(new TaskType("event"), event1.getTaskType()); + assertEquals(new TaskType("todo"), todo1.getTaskType()); + } + + @Test + void getRecurringFrequency() { + Task deadline1 = new TaskBuilder(FIRST_DEADLINE).build(); + Task event1 = new TaskBuilder(FIRST_EVENT).build(); + Task todo1 = new TaskBuilder(FIRST_TODO).build(); + Task deadline2 = new TaskBuilder(SECOND_DEADLINE).build(); + + assertEquals(new RecurringFrequency("none"), deadline1.getRecurringFrequency()); + assertEquals(new RecurringFrequency("year"), event1.getRecurringFrequency()); + assertEquals(new RecurringFrequency("week"), todo1.getRecurringFrequency()); + assertEquals(new RecurringFrequency("month"), deadline2.getRecurringFrequency()); + } + + @Test + void getPriority() { + Task deadline1 = new TaskBuilder(FIRST_DEADLINE).build(); + Task event1 = new TaskBuilder(FIRST_EVENT).build(); + Task todo1 = new TaskBuilder(FIRST_TODO).build(); + + assertEquals(new Priority("med"), deadline1.getPriority()); + assertEquals(new Priority("low"), event1.getPriority()); + assertEquals(new Priority("high"), todo1.getPriority()); + } + + @Test + void getPriorityIcon() { + Task deadline1 = new TaskBuilder(FIRST_DEADLINE).build(); + Task event1 = new TaskBuilder(FIRST_EVENT).build(); + Task todo1 = new TaskBuilder(FIRST_TODO).build(); + + assertEquals("!!", deadline1.getPriorityIcon()); + assertEquals("!", event1.getPriorityIcon()); + assertEquals("!!!", todo1.getPriorityIcon()); + } + + @Test + void isDone() { + Task deadline1 = new TaskBuilder(FIRST_DEADLINE).build(); + Task event1 = new TaskBuilder(FIRST_EVENT).build(); + Task todo1 = new TaskBuilder(FIRST_TODO).build(); + + // tasks that are not done + assertFalse(deadline1.isDone); + assertFalse(event1.isDone); + assertFalse(todo1.isDone); + + deadline1.markAsDone(); + event1.markAsDone(); + todo1.markAsDone(); + + // tasks that are done + assertTrue(deadline1.isDone); + assertTrue(event1.isDone); + assertTrue(todo1.isDone); + } + + @Test + void markAsDone() { + Task deadline1 = new TaskBuilder(FIRST_DEADLINE).build(); + Task event1 = new TaskBuilder(FIRST_EVENT).build(); + Task todo1 = new TaskBuilder(FIRST_TODO).build(); + + // tasks that are not done + assertFalse(deadline1.isDone()); + assertFalse(event1.isDone()); + assertFalse(todo1.isDone()); + + deadline1.markAsDone(); + event1.markAsDone(); + todo1.markAsDone(); + + // tasks that are done + assertTrue(deadline1.isDone()); + assertTrue(event1.isDone()); + assertTrue(todo1.isDone()); + } + + @Test + void markAsUndone() { + Task deadline1 = new TaskBuilder(FIRST_DEADLINE).build(); + Task event1 = new TaskBuilder(FIRST_EVENT).build(); + Task todo1 = new TaskBuilder(FIRST_TODO).build(); + + //mark tasks as done first + deadline1.markAsDone(); + event1.markAsDone(); + todo1.markAsDone(); + + assertTrue(deadline1.isDone()); + assertTrue(event1.isDone()); + assertTrue(todo1.isDone()); + + //mark tasks as undone + deadline1.markAsUndone(); + event1.markAsUndone(); + todo1.markAsUndone(); + + assertFalse(deadline1.isDone()); + assertFalse(event1.isDone()); + assertFalse(todo1.isDone()); + } + + @Test + void testToString() { + Task deadline1 = new TaskBuilder(FIRST_DEADLINE).build(); + Task event1 = new TaskBuilder(FIRST_EVENT).build(); + Task todo1 = new TaskBuilder(FIRST_TODO).build(); + + assertEquals("[ ] Deadline 1", deadline1.toString()); + assertEquals("[ ] event 1", event1.toString()); + assertEquals("[ ] todo 1", todo1.toString()); + + //mark tasks as done + deadline1.markAsDone(); + event1.markAsDone(); + todo1.markAsDone(); + + assertEquals("[X] Deadline 1", deadline1.toString()); + assertEquals("[X] event 1", event1.toString()); + assertEquals("[X] todo 1", todo1.toString()); + } + + @Test + void compareDescription() { + Task deadline1 = new TaskBuilder(FIRST_DEADLINE).build(); + Task event1 = new TaskBuilder(FIRST_EVENT).build(); + Task todo1 = new TaskBuilder(FIRST_TODO).build(); + Task todo2 = new TaskBuilder(SECOND_TODO).build(); + Task todo2Caps = new TaskBuilder(SECOND_TODO_WITH_CAPS).build(); + + //test comparison between lowercase alphabets + assertTrue(deadline1.compareDescription(event1) < 0); + assertTrue(todo2.compareDescription(event1) > 0); + + //test whether case is ignored + assertEquals(0, todo2.compareDescription(todo2Caps)); + + //test comparison between numbers + assertTrue(todo1.compareDescription(todo2Caps) < 0); + } + + @Test + void compareBefore() { + Task deadline1 = new TaskBuilder(FIRST_DEADLINE).build(); + Task event1 = new TaskBuilder(FIRST_EVENT).build(); + Task todo2 = new TaskBuilder(SECOND_TODO).build(); + + //compare between dates + assertTrue(deadline1.compareBefore(event1) < 0); + + //compare between date and null (todo) + assertTrue(todo2.compareBefore(event1) > 0); + assertTrue(event1.compareBefore(todo2) < 0); + + } + + @Test + void compareAfter() { + Task deadline1 = new TaskBuilder(FIRST_DEADLINE).build(); + Task event1 = new TaskBuilder(FIRST_EVENT).build(); + Task todo2 = new TaskBuilder(SECOND_TODO).build(); + + //compare between dates + assertTrue(deadline1.compareAfter(event1) > 0); + + //compare between date and null (todo) + assertTrue(todo2.compareAfter(event1) < 0); + assertTrue(event1.compareAfter(todo2) > 0); + } + + @Test + void comparePriority() { + Task deadline1 = new TaskBuilder(FIRST_DEADLINE).build(); + Task deadline2 = new TaskBuilder(SECOND_DEADLINE).build(); + Task event1 = new TaskBuilder(FIRST_EVENT).build(); + Task todo1 = new TaskBuilder(FIRST_TODO).build(); + + assertTrue(deadline2.comparePriority(event1) > 0); + assertTrue(event1.comparePriority(todo1) < 0); + assertEquals(0, deadline1.comparePriority(deadline2)); + } + + @Test + void compareGroup() { + Task deadline1 = new TaskBuilder(FIRST_DEADLINE).build(); + Task event1 = new TaskBuilder(FIRST_EVENT).build(); + Task todo1 = new TaskBuilder(FIRST_TODO).build(); + + assertTrue(deadline1.compareGroup(event1) < 0); + assertEquals(0, deadline1.compareGroup(todo1)); + assertTrue(event1.compareGroup(todo1) > 0); + } + + @Test + void testEquals() { + Task deadline1 = new TaskBuilder(FIRST_DEADLINE).build(); + Task event1 = new TaskBuilder(FIRST_EVENT).build(); + Task todo1 = new TaskBuilder(FIRST_TODO).build(); + + assertNotEquals(deadline1, event1); + assertNotEquals(todo1, event1); + + assertEquals(new TaskBuilder(FIRST_DEADLINE).build(), deadline1); + assertEquals(new TaskBuilder(FIRST_TODO).build(), todo1); + } + +} diff --git a/src/test/java/sweebook/storage/JsonAdaptedPersonTest.java b/src/test/java/sweebook/storage/JsonAdaptedPersonTest.java new file mode 100644 index 00000000000..cb031a0f003 --- /dev/null +++ b/src/test/java/sweebook/storage/JsonAdaptedPersonTest.java @@ -0,0 +1,134 @@ +package sweebook.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static sweebook.storage.JsonAdaptedPerson.MISSING_FIELD_MESSAGE_FORMAT; +import static sweebook.testutil.Assert.assertThrows; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +import sweebook.commons.exceptions.IllegalValueException; +import sweebook.model.group.Group; +import sweebook.model.person.Email; +import sweebook.model.person.Name; +import sweebook.model.person.Phone; +import sweebook.model.person.social.GitHub; +import sweebook.model.person.social.Social; +import sweebook.model.person.social.Telegram; +import sweebook.testutil.TypicalPersons; + +public class JsonAdaptedPersonTest { + private static final String INVALID_NAME = "R@chel"; + private static final String INVALID_PHONE = "+651234"; + private static final String INVALID_EMAIL = "example.com"; + private static final String INVALID_GROUP = "cs1234"; + private static final String INVALID_USERNAME = "-foo"; + + private static final String VALID_NAME = TypicalPersons.BENSON.getName().toString(); + private static final String VALID_PHONE = TypicalPersons.BENSON.getPhone().toString(); + private static final String VALID_EMAIL = TypicalPersons.BENSON.getEmail().toString(); + private static final List VALID_GROUPS = TypicalPersons.BENSON.getGroups().stream() + .map(JsonAdaptedGroup::new) + .collect(Collectors.toList()); + private static final String VALID_TELEGRAM = TypicalPersons.BENSON.getTelegram().username; + private static final String VALID_GITHUB = TypicalPersons.BENSON.getGitHub().username; + + @Test + public void toModelType_validPersonDetails_returnsPerson() throws Exception { + JsonAdaptedPerson person = new JsonAdaptedPerson(TypicalPersons.BENSON); + assertEquals(TypicalPersons.BENSON, person.toModelType()); + } + + @Test + public void toModelType_invalidName_throwsIllegalValueException() { + JsonAdaptedPerson person = new JsonAdaptedPerson(INVALID_NAME, + VALID_GROUPS, VALID_PHONE, VALID_EMAIL, VALID_TELEGRAM, VALID_GITHUB); + String expectedMessage = Name.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); + } + + @Test + public void toModelType_nullName_throwsIllegalValueException() { + JsonAdaptedPerson person = new JsonAdaptedPerson(null, + VALID_GROUPS, VALID_PHONE, VALID_EMAIL, VALID_TELEGRAM, VALID_GITHUB); + 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, + VALID_GROUPS, INVALID_PHONE, VALID_EMAIL, VALID_TELEGRAM, VALID_GITHUB); + String expectedMessage = Phone.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); + } + + @Test + public void toModelType_nullPhone_throwsIllegalValueException() { + JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, + VALID_GROUPS, null, VALID_EMAIL, VALID_TELEGRAM, VALID_GITHUB); + 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_GROUPS, VALID_PHONE, INVALID_EMAIL, VALID_TELEGRAM, VALID_GITHUB); + String expectedMessage = Email.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); + } + + @Test + public void toModelType_nullEmail_throwsIllegalValueException() { + JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_GROUPS, VALID_PHONE, null, + VALID_TELEGRAM, VALID_GITHUB); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); + } + + @Test + public void toModelType_invalidGroup_throwsIllegalValueException() { + List invalidGroups = new ArrayList<>(); + invalidGroups.add(new JsonAdaptedGroup(INVALID_GROUP)); + JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, invalidGroups, VALID_PHONE, VALID_EMAIL, + VALID_TELEGRAM, VALID_GITHUB); + String expectedMessage = Group.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); + } + + @Test + public void toModelType_nullTelegram_throwsIllegalValueException() { + JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_GROUPS, VALID_PHONE, VALID_EMAIL, + null, VALID_GITHUB); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Telegram.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); + } + + @Test + public void toModelType_invalidTelegram_throwsIllegalValueException() { + JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, + VALID_GROUPS, VALID_PHONE, VALID_EMAIL, INVALID_USERNAME, VALID_GITHUB); + String expectedMessage = Social.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); + } + + @Test + public void toModelType_nullGithub_throwsIllegalValueException() { + JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_GROUPS, VALID_PHONE, VALID_EMAIL, + VALID_TELEGRAM, null); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, GitHub.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); + } + + @Test + public void toModelType_invalidGithub_throwsIllegalValueException() { + JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, + VALID_GROUPS, VALID_PHONE, VALID_EMAIL, VALID_TELEGRAM, INVALID_USERNAME); + String expectedMessage = Social.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); + } +} diff --git a/src/test/java/sweebook/storage/JsonAdaptedTaskTest.java b/src/test/java/sweebook/storage/JsonAdaptedTaskTest.java new file mode 100644 index 00000000000..81e6eab9a53 --- /dev/null +++ b/src/test/java/sweebook/storage/JsonAdaptedTaskTest.java @@ -0,0 +1,89 @@ +package sweebook.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static sweebook.storage.JsonAdaptedTask.MISSING_FIELD_MESSAGE_FORMAT; +import static sweebook.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +import sweebook.commons.exceptions.IllegalValueException; +import sweebook.model.group.Group; +import sweebook.model.task.Date; +import sweebook.model.task.Description; +import sweebook.model.task.TaskType; +import sweebook.testutil.TypicalTasks; + +public class JsonAdaptedTaskTest { + private static final String INVALID_DESCRIPTION = " "; + private static final String INVALID_TASKTYPE = "anytime"; + private static final String INVALID_DATE = "7771-21-33"; + private static final String INVALID_GROUP = "cs1234"; + + private static final String VALID_DESCRIPTION = TypicalTasks.FIRST_DEADLINE.getDescription().toString(); + private static final String VALID_STATUS = TypicalTasks.FIRST_DEADLINE.getStatusIcon(); + private static final String VALID_TASKTYPE = TypicalTasks.FIRST_DEADLINE.getTaskType().toString(); + private static final String VALID_DATE = TypicalTasks.FIRST_DEADLINE.getDate().toString(); + private static final String VALID_GROUP = TypicalTasks.FIRST_DEADLINE.getGroup().toString(); + + @Test + public void toModelType_validTaskDetails_returnsTask() throws Exception { + JsonAdaptedTask task = new JsonAdaptedTask(TypicalTasks.FIRST_DEADLINE); + assertEquals(TypicalTasks.FIRST_DEADLINE, task.toModelType()); + } + + @Test + public void toModelType_invalidDescription_throwsIllegalValueException() { + JsonAdaptedTask task = new JsonAdaptedTask(INVALID_DESCRIPTION, VALID_STATUS, + VALID_GROUP, VALID_DATE, VALID_TASKTYPE, "med", "none"); + String expectedMessage = Description.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, task::toModelType); + } + + @Test + public void toModelType_nullDescription_throwsIllegalValueException() { + JsonAdaptedTask task = new JsonAdaptedTask(null, VALID_STATUS, + VALID_GROUP, VALID_DATE, VALID_TASKTYPE, null, null); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Description.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, task::toModelType); + } + + @Test + public void toModelType_invalidGroup_throwsIllegalValueException() { + JsonAdaptedTask task = new JsonAdaptedTask(VALID_DESCRIPTION, VALID_STATUS, + INVALID_GROUP, VALID_DATE, VALID_TASKTYPE, "med", "none"); + String expectedMessage = Group.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, task::toModelType); + } + + @Test + public void toModelType_nullGroup_throwsIllegalValueException() { + JsonAdaptedTask task = new JsonAdaptedTask(VALID_DESCRIPTION, VALID_STATUS, + null, VALID_DATE, VALID_TASKTYPE, null, null); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Group.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, task::toModelType); + } + + @Test + public void toModelType_invalidDate_throwsIllegalValueException() { + JsonAdaptedTask task = new JsonAdaptedTask(VALID_DESCRIPTION, VALID_STATUS, + VALID_GROUP, INVALID_DATE, VALID_TASKTYPE, "med", "none"); + String expectedMessage = Date.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, task::toModelType); + } + + @Test + public void toModelType_invalidTasktype_throwsIllegalValueException() { + JsonAdaptedTask task = new JsonAdaptedTask(VALID_DESCRIPTION, VALID_STATUS, + VALID_GROUP, VALID_DATE, INVALID_TASKTYPE, "med", "none"); + String expectedMessage = TaskType.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, task::toModelType); + } + + @Test + public void toModelType_nullTasktype_throwsIllegalValueException() { + JsonAdaptedTask task = new JsonAdaptedTask(VALID_DESCRIPTION, VALID_STATUS, + VALID_GROUP, VALID_DATE, null, "med", "none"); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, TaskType.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, task::toModelType); + } +} diff --git a/src/test/java/sweebook/storage/JsonContactListStorageTest.java b/src/test/java/sweebook/storage/JsonContactListStorageTest.java new file mode 100644 index 00000000000..14d67575d2b --- /dev/null +++ b/src/test/java/sweebook/storage/JsonContactListStorageTest.java @@ -0,0 +1,107 @@ +package sweebook.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static sweebook.testutil.Assert.assertThrows; + +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 sweebook.commons.exceptions.DataConversionException; +import sweebook.model.ContactList; +import sweebook.model.ReadOnlyContactList; +import sweebook.testutil.TypicalPersons; + +public class JsonContactListStorageTest { + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonContactListStorageTest"); + + @TempDir + public Path testFolder; + + @Test + public void readContactList_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> readContactList(null)); + } + + private java.util.Optional readContactList(String filePath) throws Exception { + return new JsonContactListStorage(Paths.get(filePath)).readContactList(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(readContactList("NonExistentFile.json").isPresent()); + } + + @Test + public void read_notJsonFormat_exceptionThrown() { + assertThrows(DataConversionException.class, () -> readContactList("notJsonFormatContactList.json")); + } + + @Test + public void readContactList_invalidPersonContactList_throwDataConversionException() { + assertThrows(DataConversionException.class, () -> readContactList("invalidPersonContactList.json")); + } + + @Test + public void readContactList_invalidAndValidPersonContactList_throwDataConversionException() { + assertThrows(DataConversionException.class, () -> readContactList("invalidAndValidPersonContactList.json")); + } + + @Test + public void readAndSaveContactList_allInOrder_success() throws Exception { + Path filePath = testFolder.resolve("TempContactList.json"); + ContactList original = TypicalPersons.getTypicalContactList(); + JsonContactListStorage jsonContactListStorage = new JsonContactListStorage(filePath); + + // Save in new file and read back + jsonContactListStorage.saveContactList(original, filePath); + ReadOnlyContactList readBack = jsonContactListStorage.readContactList(filePath).get(); + assertEquals(original, new ContactList(readBack)); + + // Modify data, overwrite exiting file, and read back + original.addPerson(TypicalPersons.HOON); + original.removePerson(TypicalPersons.ALICE); + jsonContactListStorage.saveContactList(original, filePath); + readBack = jsonContactListStorage.readContactList(filePath).get(); + assertEquals(original, new ContactList(readBack)); + + // Save and read without specifying file path + original.addPerson(TypicalPersons.IDA); + jsonContactListStorage.saveContactList(original); // file path not specified + readBack = jsonContactListStorage.readContactList().get(); // file path not specified + assertEquals(original, new ContactList(readBack)); + + } + + @Test + public void saveContactList_nullContactList_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveContactList(null, "SomeFile.json")); + } + + /** + * Saves {@code contactList} at the specified {@code filePath}. + */ + private void saveContactList(ReadOnlyContactList contactList, String filePath) { + try { + new JsonContactListStorage(Paths.get(filePath)) + .saveContactList(contactList, addToTestDataPathIfNotNull(filePath)); + } catch (IOException ioe) { + throw new AssertionError("There should not be an error writing to the file.", ioe); + } + } + + @Test + public void saveContactList_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveContactList(new ContactList(), null)); + } +} diff --git a/src/test/java/sweebook/storage/JsonSerializableContactListTest.java b/src/test/java/sweebook/storage/JsonSerializableContactListTest.java new file mode 100644 index 00000000000..64810b8363c --- /dev/null +++ b/src/test/java/sweebook/storage/JsonSerializableContactListTest.java @@ -0,0 +1,47 @@ +package sweebook.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static sweebook.testutil.Assert.assertThrows; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; + +import sweebook.commons.exceptions.IllegalValueException; +import sweebook.commons.util.JsonUtil; +import sweebook.model.ContactList; +import sweebook.testutil.TypicalPersons; + +public class JsonSerializableContactListTest { + + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonSerializableContactListTest"); + private static final Path TYPICAL_PERSONS_FILE = TEST_DATA_FOLDER.resolve("typicalPersonsContactList.json"); + private static final Path INVALID_PERSON_FILE = TEST_DATA_FOLDER.resolve("invalidPersonContactList.json"); + private static final Path DUPLICATE_PERSON_FILE = TEST_DATA_FOLDER.resolve("duplicatePersonContactList.json"); + + @Test + public void toModelType_typicalPersonsFile_success() throws Exception { + JsonSerializableContactList dataFromFile = JsonUtil.readJsonFile(TYPICAL_PERSONS_FILE, + JsonSerializableContactList.class).get(); + ContactList contactListFromFile = dataFromFile.toModelType(); + ContactList typicalPersonsContactList = TypicalPersons.getTypicalContactList(); + assertEquals(contactListFromFile, typicalPersonsContactList); + } + + @Test + public void toModelType_invalidPersonFile_throwsIllegalValueException() throws Exception { + JsonSerializableContactList dataFromFile = JsonUtil.readJsonFile(INVALID_PERSON_FILE, + JsonSerializableContactList.class).get(); + assertThrows(IllegalValueException.class, dataFromFile::toModelType); + } + + @Test + public void toModelType_duplicatePersons_throwsIllegalValueException() throws Exception { + JsonSerializableContactList dataFromFile = JsonUtil.readJsonFile(DUPLICATE_PERSON_FILE, + JsonSerializableContactList.class).get(); + assertThrows(IllegalValueException.class, JsonSerializableContactList.MESSAGE_DUPLICATE_PERSON, + dataFromFile::toModelType); + } + +} diff --git a/src/test/java/sweebook/storage/JsonSerializableTaskRecordsTest.java b/src/test/java/sweebook/storage/JsonSerializableTaskRecordsTest.java new file mode 100644 index 00000000000..d9c9333328f --- /dev/null +++ b/src/test/java/sweebook/storage/JsonSerializableTaskRecordsTest.java @@ -0,0 +1,36 @@ +package sweebook.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static sweebook.testutil.Assert.assertThrows; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; + +import sweebook.commons.exceptions.IllegalValueException; +import sweebook.commons.util.JsonUtil; +import sweebook.model.TaskRecords; +import sweebook.testutil.TypicalTasks; + +public class JsonSerializableTaskRecordsTest { + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonSerializableTaskRecordsTest"); + private static final Path TYPICAL_TASKS_FILE = TEST_DATA_FOLDER.resolve("typicalTaskTaskRecords.json"); + private static final Path INVALID_TASK_FILE = TEST_DATA_FOLDER.resolve("invalidTaskTaskRecords.json"); + + @Test + public void toModelType_typicalTasksFile_success() throws Exception { + JsonSerializableTaskRecords dataFromFile = JsonUtil.readJsonFile(TYPICAL_TASKS_FILE, + JsonSerializableTaskRecords.class).get(); + TaskRecords taskRecordsFromFile = dataFromFile.toModelType(); + TaskRecords typicalTaskTaskRecords = TypicalTasks.getTypicalTaskRecords(); + assertEquals(taskRecordsFromFile, typicalTaskTaskRecords); + } + + @Test + public void toModelType_invalidTaskFile_throwsIllegalValueException() throws Exception { + JsonSerializableTaskRecords dataFromFile = JsonUtil.readJsonFile(INVALID_TASK_FILE, + JsonSerializableTaskRecords.class).get(); + assertThrows(IllegalValueException.class, dataFromFile::toModelType); + } +} diff --git a/src/test/java/sweebook/storage/JsonTaskRecordsStorageTest.java b/src/test/java/sweebook/storage/JsonTaskRecordsStorageTest.java new file mode 100644 index 00000000000..bdb05439e89 --- /dev/null +++ b/src/test/java/sweebook/storage/JsonTaskRecordsStorageTest.java @@ -0,0 +1,83 @@ +package sweebook.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static sweebook.testutil.Assert.assertThrows; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import sweebook.commons.exceptions.DataConversionException; +import sweebook.model.ReadOnlyTaskRecords; +import sweebook.model.TaskRecords; +import sweebook.testutil.TypicalTasks; + +public class JsonTaskRecordsStorageTest { + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonTaskRecordsStorageTest"); + + @TempDir + public Path testFolder; + + @Test + public void readTaskRecords_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> readTaskRecords(null)); + } + + private java.util.Optional readTaskRecords(String filePath) throws Exception { + return new JsonTaskRecordsStorage(Paths.get(filePath)).readTaskRecords(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(readTaskRecords("NonExistentFile.json").isPresent()); + } + + @Test + public void read_notJsonFormat_exceptionThrown() { + assertThrows(DataConversionException.class, () -> readTaskRecords("notJsonFormatTaskRecords.json")); + } + + @Test + public void readTaskRecords_invalidTaskTaskRecords_throwDataConversionException() { + assertThrows(DataConversionException.class, () -> readTaskRecords("invalidTaskTaskRecords.json")); + } + + @Test + public void readTaskRecords_invalidAndValidTaskTaskRecords_throwDataConversionException() { + assertThrows(DataConversionException.class, () -> readTaskRecords("invalidAndValidTaskTaskRecords.json")); + } + + @Test + public void readAndSaveTaskRecords_allInOrder_success() throws Exception { + Path filePath = testFolder.resolve("TempTaskRecords.json"); + TaskRecords original = TypicalTasks.getTypicalTaskRecords(); + JsonTaskRecordsStorage jsonTaskRecordsStorage = new JsonTaskRecordsStorage(filePath); + + // Save in new file and read back + jsonTaskRecordsStorage.saveTaskRecords(original, filePath); + ReadOnlyTaskRecords readBack = jsonTaskRecordsStorage.readTaskRecords(filePath).get(); + assertEquals(original, new TaskRecords(readBack)); + + // Modify data, overwrite exiting file, and read back + original.addTask(TypicalTasks.SECOND_EVENT); + original.deleteTask(TypicalTasks.FIRST_DEADLINE); + jsonTaskRecordsStorage.saveTaskRecords(original, filePath); + readBack = jsonTaskRecordsStorage.readTaskRecords(filePath).get(); + assertEquals(original, new TaskRecords(readBack)); + + // Save and read without specifying file path + original.addTask(TypicalTasks.SECOND_TODO); + jsonTaskRecordsStorage.saveTaskRecords(original); // file path not specified + readBack = jsonTaskRecordsStorage.readTaskRecords().get(); // file path not specified + assertEquals(original, new TaskRecords(readBack)); + } +} diff --git a/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java b/src/test/java/sweebook/storage/JsonUserPrefsStorageTest.java similarity index 93% rename from src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java rename to src/test/java/sweebook/storage/JsonUserPrefsStorageTest.java index 16f33f4a6bb..b9476436c5d 100644 --- a/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java +++ b/src/test/java/sweebook/storage/JsonUserPrefsStorageTest.java @@ -1,8 +1,8 @@ -package seedu.address.storage; +package sweebook.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 sweebook.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 sweebook.commons.core.GuiSettings; +import sweebook.commons.exceptions.DataConversionException; +import sweebook.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.setContactListFilePath(Paths.get("contactlist.json")); return userPrefs; } diff --git a/src/test/java/seedu/address/storage/StorageManagerTest.java b/src/test/java/sweebook/storage/StorageManagerTest.java similarity index 58% rename from src/test/java/seedu/address/storage/StorageManagerTest.java rename to src/test/java/sweebook/storage/StorageManagerTest.java index 99a16548970..7775ca5e2f2 100644 --- a/src/test/java/seedu/address/storage/StorageManagerTest.java +++ b/src/test/java/sweebook/storage/StorageManagerTest.java @@ -1,8 +1,7 @@ -package seedu.address.storage; +package sweebook.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 java.nio.file.Path; @@ -10,10 +9,11 @@ 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 sweebook.commons.core.GuiSettings; +import sweebook.model.ContactList; +import sweebook.model.ReadOnlyContactList; +import sweebook.model.UserPrefs; +import sweebook.testutil.TypicalPersons; public class StorageManagerTest { @@ -24,9 +24,10 @@ public class StorageManagerTest { @BeforeEach public void setUp() { - JsonAddressBookStorage addressBookStorage = new JsonAddressBookStorage(getTempFilePath("ab")); + JsonContactListStorage contactListStorage = new JsonContactListStorage(getTempFilePath("ab")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(getTempFilePath("prefs")); - storageManager = new StorageManager(addressBookStorage, userPrefsStorage); + JsonTaskRecordsStorage taskRecordsStorage = new JsonTaskRecordsStorage(getTempFilePath("taskRecords")); + storageManager = new StorageManager(contactListStorage, userPrefsStorage, taskRecordsStorage); } private Path getTempFilePath(String fileName) { @@ -48,21 +49,21 @@ public void prefsReadSave() throws Exception { } @Test - public void addressBookReadSave() throws Exception { + public void contactListReadSave() 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 JsonContactListStorage} class. + * More extensive testing of UserPref saving/reading is done in {@link JsonContactListStorageTest} class. */ - AddressBook original = getTypicalAddressBook(); - storageManager.saveAddressBook(original); - ReadOnlyAddressBook retrieved = storageManager.readAddressBook().get(); - assertEquals(original, new AddressBook(retrieved)); + ContactList original = TypicalPersons.getTypicalContactList(); + storageManager.saveContactList(original); + ReadOnlyContactList retrieved = storageManager.readContactList().get(); + assertEquals(original, new ContactList(retrieved)); } @Test - public void getAddressBookFilePath() { - assertNotNull(storageManager.getAddressBookFilePath()); + public void getContactListFilePath() { + assertNotNull(storageManager.getContactListFilePath()); } } diff --git a/src/test/java/seedu/address/testutil/Assert.java b/src/test/java/sweebook/testutil/Assert.java similarity index 97% rename from src/test/java/seedu/address/testutil/Assert.java rename to src/test/java/sweebook/testutil/Assert.java index 9863093bd6e..816d55b09dd 100644 --- a/src/test/java/seedu/address/testutil/Assert.java +++ b/src/test/java/sweebook/testutil/Assert.java @@ -1,4 +1,4 @@ -package seedu.address.testutil; +package sweebook.testutil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.function.Executable; diff --git a/src/test/java/sweebook/testutil/ContactListBuilder.java b/src/test/java/sweebook/testutil/ContactListBuilder.java new file mode 100644 index 00000000000..12afb35cc73 --- /dev/null +++ b/src/test/java/sweebook/testutil/ContactListBuilder.java @@ -0,0 +1,34 @@ +package sweebook.testutil; + +import sweebook.model.ContactList; +import sweebook.model.person.Person; + +/** + * A utility class to help with building ContactList objects. + * Example usage:
+ * {@code ContactList ab = new ContactListBuilder().withPerson("John", "Doe").build();} + */ +public class ContactListBuilder { + + private ContactList contactList; + + public ContactListBuilder() { + contactList = new ContactList(); + } + + public ContactListBuilder(ContactList contactList) { + this.contactList = contactList; + } + + /** + * Adds a new {@code Person} to the {@code ContactList} that we are building. + */ + public ContactListBuilder withPerson(Person person) { + contactList.addPerson(person); + return this; + } + + public ContactList build() { + return contactList; + } +} diff --git a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java b/src/test/java/sweebook/testutil/EditPersonDescriptorBuilder.java similarity index 58% rename from src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java rename to src/test/java/sweebook/testutil/EditPersonDescriptorBuilder.java index 4584bd5044e..986228e194b 100644 --- a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java +++ b/src/test/java/sweebook/testutil/EditPersonDescriptorBuilder.java @@ -1,16 +1,17 @@ -package seedu.address.testutil; +package sweebook.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; +import sweebook.logic.commands.EditCommand.EditPersonDescriptor; +import sweebook.model.group.Group; +import sweebook.model.person.Email; +import sweebook.model.person.Name; +import sweebook.model.person.Person; +import sweebook.model.person.Phone; +import sweebook.model.person.social.GitHub; +import sweebook.model.person.social.Telegram; /** * A utility class to help with building EditPersonDescriptor objects. @@ -35,8 +36,9 @@ public EditPersonDescriptorBuilder(Person person) { descriptor.setName(person.getName()); descriptor.setPhone(person.getPhone()); descriptor.setEmail(person.getEmail()); - descriptor.setAddress(person.getAddress()); - descriptor.setTags(person.getTags()); + descriptor.setGroups(person.getGroups()); + descriptor.setTelegram(person.getTelegram()); + descriptor.setGitHub(person.getGitHub()); } /** @@ -64,20 +66,28 @@ public EditPersonDescriptorBuilder withEmail(String email) { } /** - * Sets the {@code Address} of the {@code EditPersonDescriptor} that we are building. + * Parses the {@code groups} into a {@code Set} and set it to the {@code EditPersonDescriptor} + * that we are building. */ - public EditPersonDescriptorBuilder withAddress(String address) { - descriptor.setAddress(new Address(address)); + public EditPersonDescriptorBuilder withGroups(String... groups) { + Set tagSet = Stream.of(groups).map(Group::new).collect(Collectors.toSet()); + descriptor.setGroups(tagSet); return this; } /** - * Parses the {@code tags} into a {@code Set} and set it to the {@code EditPersonDescriptor} - * that we are building. + * Sets the {@code Telegram} of the {@code EditPersonDescriptor} that we are building. + */ + public EditPersonDescriptorBuilder withTelegram(String username) { + descriptor.setTelegram(new Telegram(username)); + return this; + } + + /** + * Sets the {@code GitHub} of 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); + public EditPersonDescriptorBuilder withGitHub(String username) { + descriptor.setGitHub(new GitHub(username)); return this; } diff --git a/src/test/java/sweebook/testutil/EditTaskDescriptorBuilder.java b/src/test/java/sweebook/testutil/EditTaskDescriptorBuilder.java new file mode 100644 index 00000000000..af085bd1c0c --- /dev/null +++ b/src/test/java/sweebook/testutil/EditTaskDescriptorBuilder.java @@ -0,0 +1,91 @@ +package sweebook.testutil; + +import sweebook.logic.commands.EditTaskCommand.EditTaskDescriptor; +import sweebook.model.group.Group; +import sweebook.model.task.Date; +import sweebook.model.task.Description; +import sweebook.model.task.Priority; +import sweebook.model.task.RecurringFrequency; +import sweebook.model.task.Task; +import sweebook.model.task.TaskType; + +/** + * A utility class to help with building EditTaskDescriptor objects. + */ +public class EditTaskDescriptorBuilder { + + private EditTaskDescriptor descriptor; + + public EditTaskDescriptorBuilder() { + descriptor = new EditTaskDescriptor(); + } + + public EditTaskDescriptorBuilder(EditTaskDescriptor descriptor) { + this.descriptor = new EditTaskDescriptor(descriptor); + } + + /** + * Returns an {@code EditTaskDescriptor} with fields containing {@code task}'s details + */ + public EditTaskDescriptorBuilder(Task task) { + descriptor = new EditTaskDescriptor(); + descriptor.setPriority(task.getPriority()); + descriptor.setDescription(task.getDescription()); + descriptor.setTaskType(task.getTaskType()); + descriptor.setDate(task.getDate()); + descriptor.setRecurringFrequency(task.getRecurringFrequency()); + descriptor.setGroup(task.getGroup()); + } + + /** + * Sets the {@code Priority} of the {@code EditTaskDescriptor} that we are building. + */ + public EditTaskDescriptorBuilder withPriority(String priority) { + descriptor.setPriority(new Priority(priority)); + return this; + } + + /** + * Sets the {@code Description} of the {@code EditTaskDescriptor} that we are building. + */ + public EditTaskDescriptorBuilder withDescription(String description) { + descriptor.setDescription(new Description(description)); + return this; + } + + /** + * Sets the {@code TaskType} of the {@code EditTaskDescriptor} that we are building. + */ + public EditTaskDescriptorBuilder withTaskType(String taskType) { + descriptor.setTaskType(new TaskType(taskType)); + return this; + } + + /** + * Sets the {@code Date} of the {@code EditTaskDescriptor} that we are building. + */ + public EditTaskDescriptorBuilder withDate(String date) { + descriptor.setDate(new Date(date)); + return this; + } + + /** + * Sets the {@code RecurringFrequency} of the {@code EditTaskDescriptor} that we are building. + */ + public EditTaskDescriptorBuilder withRecurringFrequency(String freq) { + descriptor.setRecurringFrequency(new RecurringFrequency(freq)); + return this; + } + + /** + * Sets the {@code Group} of the {@code EditTaskDescriptor} that we are building. + */ + public EditTaskDescriptorBuilder withGroup(String group) { + descriptor.setGroup(new Group(group)); + return this; + } + + public EditTaskDescriptor build() { + return descriptor; + } +} diff --git a/src/test/java/seedu/address/testutil/PersonBuilder.java b/src/test/java/sweebook/testutil/PersonBuilder.java similarity index 50% rename from src/test/java/seedu/address/testutil/PersonBuilder.java rename to src/test/java/sweebook/testutil/PersonBuilder.java index 6be381d39ba..e31145eca8a 100644 --- a/src/test/java/seedu/address/testutil/PersonBuilder.java +++ b/src/test/java/sweebook/testutil/PersonBuilder.java @@ -1,15 +1,16 @@ -package seedu.address.testutil; +package sweebook.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; +import sweebook.model.group.Group; +import sweebook.model.person.Email; +import sweebook.model.person.Name; +import sweebook.model.person.Person; +import sweebook.model.person.Phone; +import sweebook.model.person.social.GitHub; +import sweebook.model.person.social.Telegram; +import sweebook.model.util.SampleDataUtil; /** * A utility class to help with building Person objects. @@ -19,23 +20,28 @@ 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"; + public static final String DEFAULT_GROUP_CS2101 = "CS2101"; + public static final String DEFAULT_GROUP_CS22103T = "CS2103T"; + public static final String DEFAULT_TELEGRAM = "amybee"; + public static final String DEFAULT_GITHUB = "amybee"; private Name name; + private Set groups; private Phone phone; private Email email; - private Address address; - private Set tags; + private Telegram tele; + private GitHub git; /** * Creates a {@code PersonBuilder} with the default details. */ public PersonBuilder() { name = new Name(DEFAULT_NAME); + groups = SampleDataUtil.getGroupSet(DEFAULT_GROUP_CS2101, DEFAULT_GROUP_CS22103T); phone = new Phone(DEFAULT_PHONE); email = new Email(DEFAULT_EMAIL); - address = new Address(DEFAULT_ADDRESS); - tags = new HashSet<>(); + tele = new Telegram(DEFAULT_TELEGRAM); + git = new GitHub(DEFAULT_GITHUB); } /** @@ -43,10 +49,12 @@ public PersonBuilder() { */ public PersonBuilder(Person personToCopy) { name = personToCopy.getName(); + groups = new HashSet<>(personToCopy.getGroups()); phone = personToCopy.getPhone(); email = personToCopy.getEmail(); - address = personToCopy.getAddress(); - tags = new HashSet<>(personToCopy.getTags()); + tele = personToCopy.getTelegram(); + git = personToCopy.getGitHub(); + } /** @@ -58,39 +66,48 @@ public PersonBuilder withName(String name) { } /** - * Parses the {@code tags} into a {@code Set} and set it to the {@code Person} that we are building. + * Sets the {@code Phone} of the {@code Person} that we are building. */ - public PersonBuilder withTags(String ... tags) { - this.tags = SampleDataUtil.getTagSet(tags); + public PersonBuilder withPhone(String phone) { + this.phone = new Phone(phone); return this; } /** - * Sets the {@code Address} of the {@code Person} that we are building. + * Sets the {@code Email} of the {@code Person} that we are building. */ - public PersonBuilder withAddress(String address) { - this.address = new Address(address); + public PersonBuilder withEmail(String email) { + this.email = new Email(email); return this; } /** - * Sets the {@code Phone} of the {@code Person} that we are building. + * Sets the {@code Group} of the {@code Person} that we are building. */ - public PersonBuilder withPhone(String phone) { - this.phone = new Phone(phone); + public PersonBuilder withGroups(String... groups) { + this.groups = SampleDataUtil.getGroupSet(groups); return this; } /** - * Sets the {@code Email} of the {@code Person} that we are building. + * Sets the {@code Telegram} of the {@code Person} that we are building. */ - public PersonBuilder withEmail(String email) { - this.email = new Email(email); + public PersonBuilder withTelegram(String username) { + this.tele = new Telegram(username); return this; } + /** + * Sets the {@code GitHub} of the {@code Person} that we are building. + */ + public PersonBuilder withGitHub(String username) { + this.git = new GitHub(username); + return this; + } + + public Person build() { - return new Person(name, phone, email, address, tags); + return new Person(name, groups, phone, email, tele, git); } } diff --git a/src/test/java/seedu/address/testutil/PersonUtil.java b/src/test/java/sweebook/testutil/PersonUtil.java similarity index 53% rename from src/test/java/seedu/address/testutil/PersonUtil.java rename to src/test/java/sweebook/testutil/PersonUtil.java index 90849945183..e71dd6d703f 100644 --- a/src/test/java/seedu/address/testutil/PersonUtil.java +++ b/src/test/java/sweebook/testutil/PersonUtil.java @@ -1,17 +1,15 @@ -package seedu.address.testutil; +package sweebook.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 static sweebook.logic.parser.CliSyntax.PREFIX_EMAIL; +import static sweebook.logic.parser.CliSyntax.PREFIX_GITHUB; +import static sweebook.logic.parser.CliSyntax.PREFIX_GROUP; +import static sweebook.logic.parser.CliSyntax.PREFIX_NAME; +import static sweebook.logic.parser.CliSyntax.PREFIX_PHONE; +import static sweebook.logic.parser.CliSyntax.PREFIX_TELEGRAM; -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; +import sweebook.logic.commands.AddCommand; +import sweebook.logic.commands.EditCommand.EditPersonDescriptor; +import sweebook.model.person.Person; /** * A utility class for Person. @@ -33,10 +31,13 @@ public static String getPersonDetails(Person person) { sb.append(PREFIX_NAME + person.getName().fullName + " "); sb.append(PREFIX_PHONE + person.getPhone().value + " "); sb.append(PREFIX_EMAIL + person.getEmail().value + " "); - sb.append(PREFIX_ADDRESS + person.getAddress().value + " "); - person.getTags().stream().forEach( - s -> sb.append(PREFIX_TAG + s.tagName + " ") + sb.append(PREFIX_TELEGRAM + person.getTelegram().username + " "); + sb.append(PREFIX_GITHUB + person.getGitHub().username + " "); + + person.getGroups().stream().forEach( + s -> sb.append(PREFIX_GROUP + s.group + " ") ); + return sb.toString(); } @@ -48,15 +49,12 @@ public static String getEditPersonDescriptorDetails(EditPersonDescriptor descrip 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(" ")); - } - } + descriptor.getTelegram().ifPresent(tele -> sb.append(PREFIX_TELEGRAM).append(tele.username).append(" ")); + descriptor.getGitHub().ifPresent(git -> sb.append(PREFIX_GITHUB).append(git.username).append(" ")); + descriptor.getGroups().ifPresent(groups -> + groups.forEach(s -> sb.append(PREFIX_GROUP).append(s.group).append((" "))) + ); + return sb.toString(); } } diff --git a/src/test/java/seedu/address/testutil/SerializableTestClass.java b/src/test/java/sweebook/testutil/SerializableTestClass.java similarity index 98% rename from src/test/java/seedu/address/testutil/SerializableTestClass.java rename to src/test/java/sweebook/testutil/SerializableTestClass.java index f5a66340489..ee8da160510 100644 --- a/src/test/java/seedu/address/testutil/SerializableTestClass.java +++ b/src/test/java/sweebook/testutil/SerializableTestClass.java @@ -1,4 +1,4 @@ -package seedu.address.testutil; +package sweebook.testutil; import java.time.LocalDateTime; import java.util.ArrayList; diff --git a/src/test/java/sweebook/testutil/TaskBuilder.java b/src/test/java/sweebook/testutil/TaskBuilder.java new file mode 100644 index 00000000000..446a540989c --- /dev/null +++ b/src/test/java/sweebook/testutil/TaskBuilder.java @@ -0,0 +1,112 @@ +package sweebook.testutil; + +import sweebook.model.group.Group; +import sweebook.model.task.Date; +import sweebook.model.task.Description; +import sweebook.model.task.Priority; +import sweebook.model.task.RecurringFrequency; +import sweebook.model.task.Task; +import sweebook.model.task.TaskType; + +/** + * A utility class to help with building Task objects. + */ +public class TaskBuilder { + public static final String DEFAULT_DESCRIPTION = "Final Project"; + public static final String DEFAULT_GROUP = "cs2101"; + public static final String DEFAULT_TYPE = "deadline"; + public static final String DEFAULT_DATE = "2021-12-12"; + public static final String DEFAULT_RECURRING_FREQUENCY = "none"; + public static final String DEFAULT_PRIORITY = "med"; + + private Description description; + private Group group; + private TaskType type; + private Date date; + private RecurringFrequency recurringFrequency; + private Priority priority; + + /** + * Creates a {@code TaskBuilder} with the default details. + */ + public TaskBuilder() { + description = new Description(DEFAULT_DESCRIPTION); + group = new Group(DEFAULT_GROUP); + type = new TaskType(DEFAULT_TYPE); + date = new Date(DEFAULT_DATE); + recurringFrequency = new RecurringFrequency(DEFAULT_RECURRING_FREQUENCY); + priority = new Priority(DEFAULT_PRIORITY); + } + + /** + * Initializes the TaskBuilder with the data of {@code taskToCopy}. + */ + public TaskBuilder(Task taskToCopy) { + description = taskToCopy.getDescription(); + group = taskToCopy.getGroup(); + type = taskToCopy.getTaskType(); + date = taskToCopy.getDate(); + recurringFrequency = taskToCopy.getRecurringFrequency(); + priority = taskToCopy.getPriority(); + } + + /** + * Sets the {@code Description} of the {@code Task} that we are building. + */ + public TaskBuilder withDescription(String description) { + this.description = new Description(description); + return this; + } + + /** + * Sets the {@code Group} of the {@code Task} that we are building. + */ + public TaskBuilder withGroup(String group) { + this.group = new Group(group); + return this; + } + + /** + * Sets the {@code TaskType} of the {@code Task} that we are building. + */ + public TaskBuilder withTaskType(String taskType) { + this.type = new TaskType(taskType); + return this; + } + + /** + * Sets the {@code Date} of the {@code Task} that we are building. + */ + public TaskBuilder withDate(String date) { + this.date = new Date(date); + return this; + } + + /** + * Sets the {@code Date} of the {@code Task} as null (for Todo) that we are building. + */ + public TaskBuilder withoutDate() { + this.date = null; + return this; + } + + /** + * Sets the {@code Priority} of the {@code Task} that we are building. + */ + public TaskBuilder withPriority(String priority) { + this.priority = new Priority(priority); + return this; + } + + /** + * Sets the {@code RecurringFrequency} of the {@code Task} that we are building. + */ + public TaskBuilder withRecurringFrequency(String recurringFrequency) { + this.recurringFrequency = new RecurringFrequency(recurringFrequency); + return this; + } + + public Task build() { + return new Task(description, group, date, type, recurringFrequency, priority); + } +} diff --git a/src/test/java/seedu/address/testutil/TestUtil.java b/src/test/java/sweebook/testutil/TestUtil.java similarity index 90% rename from src/test/java/seedu/address/testutil/TestUtil.java rename to src/test/java/sweebook/testutil/TestUtil.java index 896d103eb0b..ac7f071d5e8 100644 --- a/src/test/java/seedu/address/testutil/TestUtil.java +++ b/src/test/java/sweebook/testutil/TestUtil.java @@ -1,13 +1,13 @@ -package seedu.address.testutil; +package sweebook.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 sweebook.commons.core.index.Index; +import sweebook.model.Model; +import sweebook.model.person.Person; /** * A utility class for test cases. diff --git a/src/test/java/seedu/address/testutil/TypicalIndexes.java b/src/test/java/sweebook/testutil/TypicalIndexes.java similarity index 61% rename from src/test/java/seedu/address/testutil/TypicalIndexes.java rename to src/test/java/sweebook/testutil/TypicalIndexes.java index 1e613937657..e032d4041c7 100644 --- a/src/test/java/seedu/address/testutil/TypicalIndexes.java +++ b/src/test/java/sweebook/testutil/TypicalIndexes.java @@ -1,6 +1,6 @@ -package seedu.address.testutil; +package sweebook.testutil; -import seedu.address.commons.core.index.Index; +import sweebook.commons.core.index.Index; /** * A utility class containing a list of {@code Index} objects to be used in tests. @@ -9,4 +9,6 @@ public class TypicalIndexes { public static final Index INDEX_FIRST_PERSON = Index.fromOneBased(1); public static final Index INDEX_SECOND_PERSON = Index.fromOneBased(2); public static final Index INDEX_THIRD_PERSON = Index.fromOneBased(3); + public static final Index INDEX_FIRST_TASK = Index.fromOneBased(1); + public static final Index INDEX_SECOND_TASK = Index.fromOneBased(2); } diff --git a/src/test/java/sweebook/testutil/TypicalPersons.java b/src/test/java/sweebook/testutil/TypicalPersons.java new file mode 100644 index 00000000000..c5676f7936d --- /dev/null +++ b/src/test/java/sweebook/testutil/TypicalPersons.java @@ -0,0 +1,132 @@ +package sweebook.testutil; + +import static sweebook.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; +import static sweebook.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_GITHUB_AMY; +import static sweebook.logic.commands.CommandTestUtil.VALID_GITHUB_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_GROUP_AMY_CS2101; +import static sweebook.logic.commands.CommandTestUtil.VALID_GROUP_AMY_CS2103T; +import static sweebook.logic.commands.CommandTestUtil.VALID_GROUP_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_NAME_AMY; +import static sweebook.logic.commands.CommandTestUtil.VALID_NAME_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_PHONE_AMY; +import static sweebook.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +import static sweebook.logic.commands.CommandTestUtil.VALID_TELEGRAM_AMY; +import static sweebook.logic.commands.CommandTestUtil.VALID_TELEGRAM_BOB; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import sweebook.model.ContactList; +import sweebook.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") + .withGroups("CS2103T", "CS2101") + .withPhone("94351253") + .withEmail("alice@example.com") + .withTelegram("alice") + .withGitHub("alice") + .build(); + public static final Person BENSON = new PersonBuilder() + .withName("Benson Meier") + .withGroups("CS2103T") + .withPhone("98765432") + .withEmail("johnd@example.com") + .withTelegram("Bensonmeirer") + .withGitHub("benson") + .build(); + public static final Person CARL = new PersonBuilder() + .withName("Carl Kurz") + .withGroups("cs2101", "cs2103t") + .withPhone("95352563") + .withEmail("heinz@example.com") + .withTelegram("carl") + .withGitHub("carl") + .build(); + public static final Person DANIEL = new PersonBuilder() + .withName("Daniel Meier") + .withGroups("Cs2101") + .withPhone("87652533") + .withEmail("cornelia@example.com") + .withTelegram("Daniel") + .withGitHub("daniel") + .build(); + public static final Person ELLE = new PersonBuilder() + .withName("Elle Meyer") + .withGroups("CS2101", "cs2103t") + .withPhone("9482224") + .withEmail("werner@example.com") + .withTelegram("elle") + .withGitHub("elle") + .build(); + public static final Person FIONA = new PersonBuilder() + .withName("Fiona Kunz") + .withGroups("CS2103T") + .withPhone("9482427") + .withEmail("lydia@example.com") + .withTelegram("lydia") + .withGitHub("lydia") + .build(); + public static final Person GEORGE = new PersonBuilder() + .withName("George Best") + .withGroups("CS2103T") + .withPhone("9482442") + .withEmail("george@example.com") + .withTelegram("george") + .withGitHub("george") + .build(); + + // Manually added + public static final Person HOON = new PersonBuilder() + .withName("Hoon Meier") + .withGroups("CS2103T") + .withPhone("8482424") + .withEmail("stefan@example.com") + .withTelegram("STEFAN") + .withGitHub("stefan") + .build(); + public static final Person IDA = new PersonBuilder() + .withName("Ida Mueller") + .withGroups("CS2103T") + .withPhone("8482131") + .withEmail("hans@example.com") + .withTelegram("hans") + .withGitHub("hans") + .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) + .withGroups(VALID_GROUP_AMY_CS2103T, VALID_GROUP_AMY_CS2101) + .withTelegram(VALID_TELEGRAM_AMY).withGitHub(VALID_GITHUB_AMY).build(); + public static final Person BOB = new PersonBuilder().withName(VALID_NAME_BOB) + .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB) + .withGroups(VALID_GROUP_BOB).withTelegram(VALID_TELEGRAM_BOB).withGitHub(VALID_GITHUB_BOB).build(); + + public static final String KEYWORD_MATCHING_MEIER = "Meier"; // A keyword that matches MEIER + + private TypicalPersons() { + } // prevents instantiation + + /** + * Returns an {@code ContactList} with all the typical persons. + */ + public static ContactList getTypicalContactList() { + ContactList ab = new ContactList(); + 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/sweebook/testutil/TypicalTasks.java b/src/test/java/sweebook/testutil/TypicalTasks.java new file mode 100644 index 00000000000..885af645cdf --- /dev/null +++ b/src/test/java/sweebook/testutil/TypicalTasks.java @@ -0,0 +1,56 @@ +package sweebook.testutil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import sweebook.model.TaskRecords; +import sweebook.model.task.Task; + +/** + * A utility class containing a list of {@code Task} objects to be used in tests. + */ +public class TypicalTasks { + + //first set + public static final Task FIRST_DEADLINE = new TaskBuilder().withDescription("Deadline 1") + .withGroup("cs2101").withTaskType("deadline").withDate("2021-12-03").build(); + public static final Task FIRST_EVENT = new TaskBuilder().withDescription("event 1") + .withGroup("cs2103t").withTaskType("event").withDate("2022-02-03").withRecurringFrequency("year") + .withPriority("low").build(); + public static final Task FIRST_TODO = new TaskBuilder().withDescription("todo 1") + .withGroup("cs2101").withTaskType("todo").withDate("2022-10-29").withRecurringFrequency("week") + .withPriority("high").build(); + + //second set + public static final Task SECOND_DEADLINE = new TaskBuilder().withDescription("Deadline 2") + .withGroup("cs2103t").withTaskType("deadline").withDate("2022-07-01").withRecurringFrequency("month") + .withPriority("med").build(); + public static final Task SECOND_EVENT = new TaskBuilder().withDescription("Event 2") + .withGroup("cs2103t").withTaskType("event").withDate("2022-09-01").build(); + public static final Task SECOND_TODO = new TaskBuilder().withDescription("todo 2") + .withGroup("cs2101").withTaskType("todo").withDate("2023-01-21").build(); + public static final Task SECOND_TODO_WITH_CAPS = new TaskBuilder().withDescription("TODO 2") + .withGroup("cs2103t").withTaskType("todo").withoutDate().build(); + + //third set + public static final Task THIRD_TODO = new TaskBuilder().withDescription("todo 3") + .withGroup("cs2103t").withTaskType("todo").withoutDate().build(); + + private TypicalTasks() {} // prevents instantiation + + /** + * Returns an {@code TaskRecords} with all the typical tasks. + */ + public static TaskRecords getTypicalTaskRecords() { + TaskRecords records = new TaskRecords(); + for (Task task : getTypicalTasks()) { + records.addTask(task); + } + return records; + } + + public static List getTypicalTasks() { + return new ArrayList<>(Arrays.asList(FIRST_DEADLINE, FIRST_EVENT, FIRST_TODO, SECOND_DEADLINE)); + } +} diff --git a/src/test/java/seedu/address/ui/TestFxmlObject.java b/src/test/java/sweebook/ui/TestFxmlObject.java similarity index 96% rename from src/test/java/seedu/address/ui/TestFxmlObject.java rename to src/test/java/sweebook/ui/TestFxmlObject.java index 5ecd82656f2..39593d7665b 100644 --- a/src/test/java/seedu/address/ui/TestFxmlObject.java +++ b/src/test/java/sweebook/ui/TestFxmlObject.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package sweebook.ui; import javafx.beans.DefaultProperty; diff --git a/src/test/java/seedu/address/ui/UiPartTest.java b/src/test/java/sweebook/ui/UiPartTest.java similarity index 94% rename from src/test/java/seedu/address/ui/UiPartTest.java rename to src/test/java/sweebook/ui/UiPartTest.java index 33d82d911b8..643f123223d 100644 --- a/src/test/java/seedu/address/ui/UiPartTest.java +++ b/src/test/java/sweebook/ui/UiPartTest.java @@ -1,8 +1,8 @@ -package seedu.address.ui; +package sweebook.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 sweebook.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 sweebook.MainApp; public class UiPartTest { @@ -84,7 +84,7 @@ private URL getTestFileUrl(String testFilePath) { /** * UiPart used for testing. - * It should only be used with invalid FXML files or the valid file located at {@link VALID_FILE_PATH}. + * It should only be used with invalid FXML files or valid file */ private static class TestUiPart extends UiPart { diff --git a/src/test/resources/view/UiPartTest/validFile.fxml b/src/test/resources/view/UiPartTest/validFile.fxml index bab836af0db..65eb4f35484 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..7af9c4e06ef 100644 --- a/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml +++ b/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml @@ -1,6 +1,6 @@ - + Hello World!