diff --git a/README.md b/README.md
index 13f5c77403f..89c73395ce9 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,32 @@
-[](https://github.com/se-edu/addressbook-level3/actions)
+
+[](https://github.com/AY2122S2-CS2103-F11-4/tp/actions)
+[](https://codecov.io/gh/AY2122S2-CS2103-F11-4/tp)

-* This is **a sample project for Software Engineering (SE) students**.
- Example usages:
- * as a starting point of a course project (as opposed to writing everything from scratch)
- * as a case study
-* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details.
- * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big.
- * It comes with a **reasonable level of user and developer documentation**.
-* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...).
-* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**.
-* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info.
+# NUScheduler
+***
+NUScheduler is a desktop app for Year 1 Computing students to assist with more efficient management of tasks and 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, NUScheduler can schedule your tasks faster than traditional GUI apps.
+
+## Uses:
+***
+ * keep track of all school contacts, modules and tasks
+ * organise academic life in a neat manner
+
+## Features:
+***
+ * Add a task/contact
+ * Delete a task/contact
+ * Update a task/contact
+ * Find task(s)
+ * View all tasks and their deadlines
+ * View all contacts
+ * Remind upcoming tasks (coming soon)
+
+## Acknowledgements
+***
+This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative.](https://se-education.org)
+* Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5)
+
diff --git a/build.gradle b/build.gradle
index be2d2905dde..4235689bda4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -20,6 +20,10 @@ checkstyle {
toolVersion = '8.29'
}
+run {
+ enableAssertions = true
+}
+
test {
useJUnitPlatform()
finalizedBy jacocoTestReport
@@ -66,7 +70,7 @@ dependencies {
}
shadowJar {
- archiveName = 'addressbook.jar'
+ archiveName = 'NUScheduler.jar'
}
defaultTasks 'clean', 'test'
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 1c9514e966a..2f860f0f1b2 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -5,55 +5,41 @@ title: About Us
We are a team based in the [School of Computing, National University of Singapore](http://www.comp.nus.edu.sg).
-You can reach us at the email `seer[at]comp.nus.edu.sg`
+You can reach us at the email `e0544443@u.nus.edu`
## Project team
-### John Doe
+### Chen Yu An
-
+
-[[homepage](http://www.comp.nus.edu.sg/~damithch)]
-[[github](https://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
-
-* Role: Project Advisor
-
-### Jane Doe
-
-
-
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](https://github.com/putaojuice)]
+[[portfolio](team/putaojuice.md)]
* Role: Team Lead
-* Responsibilities: UI
-
-### Johnny Doe
-
+### Low Weimin, Ian
-[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)]
+
-* Role: Developer
-* Responsibilities: Data
+[[github](http://github.com/castryl)]
+[[portfolio](team/castryl.md)]
-### Jean Doe
+* Role: Documentation
-
+### Ivor Chua
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+
-* Role: Developer
-* Responsibilities: Dev Ops + Threading
+[[github](http://github.com/ivorcmx)]
+[[portfolio](team/ivorcmx.md)]
+* Role: Testing
-### James Doe
+### Tan Yu Meng
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/yumengtan)]
+[[portfolio](team/yumengtan.md)]
-* Role: Developer
-* Responsibilities: UI
+* Role: Code Quality
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 46eae8ee565..0352f2c030f 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -7,9 +7,12 @@ title: Developer Guide
--------------------------------------------------------------------------------------------------------------------
-## **Acknowledgements**
+
-* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+## **Acknowledgements**
+* This project is based on the [AddressBook-Level3 project](https://github.com/se-edu/addressbook-level3) created by the [SE-EDU initiative](https://se-education.org/).
+* Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5)
+* The regex method to implement `FindTask` feature was referenced and adapted from [stackoverflow](https://stackoverflow.com/questions/25483114/regex-to-find-whole-word-in-text-but-case-insensitive).
--------------------------------------------------------------------------------------------------------------------
@@ -19,6 +22,8 @@ Refer to the guide [_Setting up and getting started_](SettingUp.md).
--------------------------------------------------------------------------------------------------------------------
+
+
## **Design**
@@ -59,7 +64,7 @@ The *Sequence Diagram* below shows how the components interact with each other f
Each of the four main components (also shown in the diagram above),
* defines its *API* in an `interface` with the same name as the Component.
-* implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API `interface` mentioned in the previous point.
+* implements its functionality using a concrete `{Component Name}Manager` class which follows the corresponding API `interface` mentioned in the previous point.
For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below.
@@ -67,13 +72,15 @@ For example, the `Logic` component defines its API in the `Logic.java` interface
The sections below give more details of each component.
+
+
### UI component
The **API** of this component is specified in [`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java)

-The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI.
+The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` , `TaskListPanel` 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)
@@ -82,7 +89,9 @@ The `UI` component,
* executes user commands using the `Logic` component.
* listens for changes to `Model` data so that the UI can be updated with the modified data.
* keeps a reference to the `Logic` component, because the `UI` relies on the `Logic` to execute commands.
-* depends on some classes in the `Model` component, as it displays `Person` object residing in the `Model`.
+* depends on some classes in the `Model` component, as it displays `Person` and `Task` objects residing in the `Model`.
+
+
### Logic component
@@ -96,7 +105,7 @@ 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. The result of the command execution is encapsulated as a `CommandResult` object which is returned from `Logic`.
The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("delete 1")` API call.
@@ -113,6 +122,8 @@ 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.
+
+
### Model component
**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java)
@@ -132,6 +143,7 @@ The `Model` component,
+
### Storage component
@@ -140,8 +152,8 @@ The `Model` component,
The `Storage` component,
-* can save both address book data and user preference data in json format, and read them back into corresponding objects.
-* inherits from both `AddressBookStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed).
+* can save address book data, task list data and user preference data in json format, and read them back into corresponding objects.
+* inherits from `AddressBookStorage`, `TaskListStorage` and `UserPrefStorage`, which means it can be treated as any one (if only the functionality of only one is needed).
* depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`)
### Common classes
@@ -150,97 +162,136 @@ Classes used by multiple components are in the `seedu.addressbook.commons` packa
--------------------------------------------------------------------------------------------------------------------
+
+
## **Implementation**
This section describes some noteworthy details on how certain features are implemented.
-### \[Proposed\] Undo/redo feature
+### \[Proposed\] Task Storage Feature (Yu An)
-#### Proposed Implementation
+The proposed task storage feature is facilitated by `TaskListStorage` that follows original AB3's `AddressBookStorage` implementation closely.
+Everytime when the user successfully executes a task command that either adds, deletes or updates a task, the data will be
+written into `tasklist.json` and be saved to a `data` folder. The `tasklist.json` will also get updated when the user
+executes `exit` command.
-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:
+Below is an activity diagram to aid the understanding when data reading from and writing to the `tasklist.json` will happen:
-* `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.
+
-These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively.
+
-Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.
+### \[Proposed\] Add Task Feature (Yu An)
+#### Proposed Implementation
+The proposed add task feature is facilitated by `AddTaskCommand`. It extends `Command` and makes use of a new model `TaskList` and `Task`.
+The `TaskList` model consists of an `ArrayList` to store the `Task`. The `AddTaskCommand` also has a `AddTaskCommandParser`
+to do the logical parsing of the user's input. Additionally, this feature implements the following operations:
-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.
+* `AddTaskCommand#execute()` — Executes the command.
+* `AddTaskCommandParser#parse()` — Make sense of the user's input and returns an `AddTaskCommand` object.
+* `TaskList#addTask()` — Add a new task to the task list if user's input is valid.
-
+The `TaskList#addTask()` is exposed in the `Model` interface as `Model#addTask()`.
-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.
+Given below is an example usage scenario and how the add task feature works.
-
+The following activity diagram shows the workflow of the add task operation:
-Step 3. The user executes `add n/David …` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`.
+The user will type in the command `addt d/DESCRIPTION [t/DEADLINE (dd/mm/yyyy)]` with the deadline being an optional field.
+If a valid format is detected, the system will create a new task, add it to the task list and prompt the user that a
+task has been successfully added.
-
+
-
: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`.
+The following sequence diagram shows how the add task operation works assuming no exception is thrown:
-
+
-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.
+
-
+### \[Proposed\] Find Task Feature (Yu Meng)
+#### Proposed Implementation
+The proposed find task feature is facilitated by `FindTaskCommand`. It extends `Command` and makes use of the `TaskList` model
+and `Task`. The `TaskList` Model is used to retrieve the `Tasks` that have been stored. The `FindTaskCommand` also has a
+`FindTaskCommandParser` to do the logical parsing of the user's input. It uses regex to ignore case sensitivity of user's input.
+These are the operations that the feature implements:
+* `FindTaskCommand#execute()` : Executes the command.
+* `FindTaskCommandParser#parse()` : Parses the user input and returns a `FindTaskCommand` Object.
+* `TaskList#findTask()` : Finds and returns tasks matching keyword from user input (ignores case sensitivity)
-
: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 and how the find task feature works.
-
+The following activity diagram shows the workflow of add task operation:
-The following sequence diagram shows how the undo operation works:
+The user will type in the command `findt KEYWORD`. If the user inputs a valid format, the system will retrieve matching
+tasks from the TaskList. If there is one or more matching Tasks, the system will prompt the user with the matching Tasks.
-
+
-
:information_source: **Note:** The lifeline for `UndoCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
+The following sequence diagram shows how the add task operation work assuming no exception is thrown:
-
+
-The `redo` command does the opposite — it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state.
+
-
:information_source: **Note:** If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone AddressBook states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
+### \[Proposed\] Add Delete Task Feature (Ivor)
+#### Proposed Implementation
+The proposed delete task feature is facilitated by `DeleteTaskCommand`. It extends `Command` and makes use of a new model `TaskList` and `Task`.
+The `TaskList` model consists of an `ArrayList` to store the `Task`. The `DeleteTaskCommand` also has a `DeleteTaskCommandParser`
+to do the logical parsing of the user's input. Additionally, this feature implements the following operations:
-
+* `DeleteTaskCommand#execute()` — Executes the command.
+* `DeleteTaskCommandParser#parse()` — Make sense of the user's input and returns an `DeleteTaskCommand` object.
+* `TaskList#deleteTask()` — Delete an existing task in the task list if user's input is valid.
+
+The `TaskList#deleteTask()` is exposed in the `Model` interface as `Model#deleteTask()`.
-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.
+Given below is an example usage scenario and how the delete task feature works.
-
+The following activity diagram shows the workflow of the delete task operation:
-Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …` command. This is the behavior that most modern desktop applications follow.
+The user will type in the command `delt `.
+If a valid format is detected, the system will remove the corresponding task with the integer ID, and prompt the user that a
+task has been successfully deleted.
-
+
-The following activity diagram summarizes what happens when a user executes a new command:
+The following sequence diagram shows how the delete task operation works assuming no exception is thrown:
-
+
-#### Design considerations:
+
+
+### \[Proposed\] Add Update Task Feature (Ivor)
+#### Proposed Implementation
+The proposed update task feature is facilitated by `UpdateTaskCommand`. It extends `Command` and make use of a new model `TaskList` and `Task`.
+The `TaskList` model consists of an `ArrayList` to store the `Task`. The `UpdateTaskCommand` also has a `UpdateTaskCommandParser`
+to do the logical parsing of user's input. Additionally, this feature implements the following operations:
-**Aspect: How undo & redo executes:**
+* `UpdateTaskCommand#execute()` — Executes the command.
+* `UpdateTaskCommandParser#parse()` — Make sense of the user's input and returns an `UpdateTaskCommand` object.
+* `TaskList#updateTask()` — Updates an existing task in the task list if user's input is valid.
-* **Alternative 1 (current choice):** Saves the entire address book.
- * Pros: Easy to implement.
- * Cons: May have performance issues in terms of memory usage.
+The `TaskList#updateTask()` is exposed in the `Model` interface as `Model#updateTask()`.
-* **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 and how the update task feature works.
-_{more aspects and alternatives to be added}_
+The following activity diagram shows the workflow of the delete task operation:
-### \[Proposed\] Data archiving
+The user will type in the command `updt d\new description t\ new deadline`. Either parameter is optional but at least one must be provided.
+If a valid format is detected, the system will update the corresponding task with the integer ID with the new attributes, and prompt the user that a
+task has been successfully updated.
-_{Explain here how the data archiving feature will be implemented}_
+
+The following sequence diagram shows how the update task operation works assuming no exception is thrown:
+
+
--------------------------------------------------------------------------------------------------------------------
+
+
## **Documentation, logging, testing, configuration, dev-ops**
* [Documentation guide](Documentation.md)
@@ -257,42 +308,47 @@ _{Explain here how the data archiving feature will be implemented}_
**Target user profile**:
-* has a need to manage a significant number of contacts
-* prefer desktop apps over other types
-* can type fast
-* prefers typing to mouse interactions
-* is reasonably comfortable using CLI apps
-
-**Value proposition**: manage contacts faster than a typical mouse/GUI driven app
+NUScheduler is for Year 1 NUS Computing students who prefer CLI to GUI and have a lot of academic tasks to keep track of.
+**Value proposition**: This app has simple CLI and a sleek GUI that allows the user to manage tasks easily and efficiently. It will mainly be used to keep track of NUS assignments and projects across multiple modules.
### User stories
Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*`
-| Priority | As a … | I want to … | So that I can… |
-| -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- |
-| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App |
-| `* * *` | user | add a new person | |
-| `* * *` | user | delete a person | remove entries that I no longer need |
-| `* * *` | user | find a person by name | locate details of persons without having to go through the entire list |
-| `* *` | user | hide private contact details | minimize chance of someone else seeing them by accident |
-| `*` | user with many persons in the address book | sort persons by name | locate a person easily |
-
-*{More to be added}*
+| Priority | As a … | I want to … | So that I can… |
+|----------|--------------------------|--------------------------------------|--------------------------------------|
+| `* * *` | beginner user | add a task | keep track of the task |
+| `* * *` | beginner user | delete a task | remove a task I have completed |
+| `* * *` | beginner user | edit a task | change details of a task |
+| `* * *` | beginner user | view all tasks | keep track of all my current tasks |
+| `* * *` | beginner user | add a contact | keep track of all my contacts |
+| `* * *` | beginner user | delete a contact | delete an incorrect/unneeded contact |
+| `* * *` | beginner user | delete a task | delete an incorrect/unneeded task |
+| `* * *` | beginner user | edit a contact | correct/update a contact |
+| `* * *` | beginner user | update a task | correct/update a task |
+| `* * *` | beginner user | find tasks based on keyword | search for relevant tasks quickly |
+| `* * *` | beginner user | find contacts based on keyword | search for relevant contacts quickly |
+| `* * *` | beginner user | view all contacts | view all my current contacts |
+| `* * *` | beginner user | add a label | know which task is for which module |
+| `* * *` | potential user | use simple commands | learn the commands easily |
+| `* * *` | year 1 computing student | keep track of assignment deadlines | complete the tasks on time |
+| `*` | beginner user | see a reminder of tasks from the app | know what are my upcoming deadlines |
+
+
### 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 `NUScheduler` and the **Actor** is the `user`, unless specified otherwise)
-**Use case: Delete a person**
+**Use case: Delete a contact**
**MSS**
-1. User requests to list persons
-2. AddressBook shows a list of persons
-3. User requests to delete a specific person in the list
-4. AddressBook deletes the person
+1. User requests to list contacts
+2. NUScheduler shows a list of contacts
+3. User requests to delete a specific contact in the list
+4. NUScheduler deletes the contact
Use case ends.
@@ -304,27 +360,209 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
* 3a. The given index is invalid.
- * 3a1. AddressBook shows an error message.
+ * 3a1. NUScheduler shows an error message.
Use case resumes at step 2.
-*{More to be added}*
+
+
+**Use case: Add a task**
+
+**MSS**
+
+1. User add a task with add task command
+2. NUScheduler provide feedback based on the user input.
+3. NUScheduler adds the user's defined task to the task list
+
+ Use case ends.
+
+**Extensions**
+
+* 2a. The task description is empty.
+ * 2a1. NUScheduler prompts the user to fill up the description.
+
+ Use case ends.
+
+* 2b. The task deadline has a wrong date format.
+ * 2b1. NUScheduler shows an error message.
+
+ Use case ends.
+
+* 2c. The task has multiple description prefix (`d/`) or deadline prefix (`t/`).
+ * 2c1. NUScheduler prompts the user to fill up the description.
+
+ Use case ends.
+
+* 2d. The task has deadline prefix (`t/`) in the task description.
+ * 2d1. NUScheduler prompts the user to fill up the description.
+
+ Use case ends.
+
+* 2e. The task deadline is before today's date.
+ * 2e1. NUScheduler prompts the user to fill up the description.
+
+ Use case ends.
+
+* 2f. The task deadline is invalid (e.g. 30/02/2022).
+ * 2f1. NUScheduler prompts the user to fill up the description.
+
+ Use case ends.
+
+
+
+**Use case: Delete a task**
+
+**MSS**
+
+1. User requests to delete a task with delete task command
+2. NUScheduler deletes task specified from task list
+
+ Use case ends.
+
+**Extensions**
+
+* 2a. The task list is empty.
+ * 2a1. NUScheduler shows an error message.
+
+ Use case ends.
+
+* 2b. The task number to delete is invalid.
+ * 2b1. NUScheduler shows an error message.
+
+ Use case ends.
+
+
+
+**Use case: View all tasks**
+
+**MSS**
+
+1. User requests to view all the tasks with view command
+2. NUScheduler shows all the tasks from task list
+
+ Use case ends.
+
+**Extensions**
+
+* 2a. The task list is empty.
+ * 2a1. NUScheduler shows a message that the task list is empty.
+
+ Use case ends.
+
+
+
+**Use case: Find tasks**
+
+**MSS**
+
+1. User requests to find tasks with find command
+2. NUScheduler provides a list of matching tasks from the task list.
+
+ Use case ends.
+
+**Extensions**
+
+* 2a. The task list is empty.
+ * 2a1. NUScheduler shows a message that the task list is empty.
+
+ Use case ends.
+
+* 2b. No task matches.
+ * 2b1. NUScheduler shows a message that there are no tasks that matches the description.
+
+ Use case ends.
+
+* 2c. Tasks found.
+ * 2c1. NUScheduler shows all the tasks that matches with the keyword.
+
+ Use case ends.
+
+
+
+**Use case: Update a task**
+
+**MSS**
+
+1. User requests to update a task with update task command
+2. NUScheduler updates task specified from task list
+
+ Use case ends.
+
+**Extensions**
+
+* 2a. The task list is empty.
+ * 2a1. NUScheduler shows an error message.
+
+ Use case ends.
+
+* 2b. The task number to update is invalid.
+ * 2b1. NUScheduler shows an error message.
+
+ Use case ends.
+
+* 2c. No prefix is provided.
+ * 2c1. NUScheduler prompts the user use the correct command format.
+
+ Use case ends.
+
+* 2d. The task deadline has a wrong time format.
+ * 2d1. NUScheduler shows an error message.
+
+ Use case ends.
+
+* 2e. The task has multiple description prefix (`d/`) or deadline prefix (`t/`).
+ * 2e1. NUScheduler shows an error message.
+
+ 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` is installed.
+2. Should be used by a single user.
+3. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage.
+4. 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.
+5. The command used should be simple to learn.
+6. New user should be able to access the help page within 3 actions upon launching the application.
+7. The system should be usable by a novice who has never used the system before.
+8. The system should be backward compatible with data produced by earlier versions of the system.
+9. Each command should not take more than 5 second to complete.
+10. Each command should display the result.
+11. The order of the user input parameters when executing a command should not matter.
+12. Texts in the UI should wrap around.
+13. The project is expected to adhere to a schedule that delivers a feature set every milestone.
+14. Data should be stored locally.
+15. Data should be stored in a human-editable format.
+16. Data should be able to port over to another computer.
+17. Should notify the user with necessary information if their input does not follow the format.
+18. Should be portable without using any installer.
+19. Should be packaged into a single file, in JAR format.
+20. Should have a total app size of at most 100 MB.
+21. Should function without a remote server.
+22. Should not allow duplicate entries of person with the same name.
+23. Should allow duplicated tasks with same description.
-*{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
+* **Task**: A task with descriptions and/or deadline
+* **Task list**: A task list of all the tasks
+* **Task list ID**: ID assigned to corresponding task in the list
+* **Prefix**: A header (eg `d/`, `t/`) that is used to identify different clauses in user inputs.
+* **Data**: It refers to the entries of person in `addressbook.json` or task in `tasklist.json`.
--------------------------------------------------------------------------------------------------------------------
+
+
## **Appendix: Instructions for manual testing**
Given below are instructions to test the app manually.
@@ -338,18 +576,19 @@ testers are expected to do more *exploratory* testing.
1. Initial launch
- 1. Download the jar file and copy into an empty folder
+ 1a. Download the jar file and copy into an empty folder
+
+ 1b. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
- 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
+ 1c. If double-click does not work, user can run `java -jar NUScheduler.jar` in terminal in the same directory as the jar file.
-1. Saving window preferences
+2. Saving window preferences
- 1. Resize the window to an optimum size. Move the window to a different location. Close the window.
+ 2a. Resize the window to an optimum size. Move the window to a different location. Close the window.
- 1. Re-launch the app by double-clicking the jar file.
+ 2b. Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained.
-1. _{ more test cases … }_
### Deleting a person
@@ -366,12 +605,10 @@ testers are expected to do more *exploratory* testing.
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 … }_
+### Saving data in JSON
-### Saving data
+1. The system automatically loads up a preset JSON file if there is any missing json files.
+2. The system will automatically save data in JSON format when the user successfully executes command.
+3. The system will automatically save data in JSON format when the user exits the application.
-1. Dealing with missing/corrupted data files
-
- 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_
-
-1. _{ more test cases … }_
+--------------------------------------------------------------------------------------------------------------------
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index 3716f3ca8a4..58811ecae77 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -3,190 +3,313 @@ layout: page
title: User Guide
---
-AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps.
-
-* Table of Contents
-{:toc}
+## NUScheduler
+
+NUScheduler is a desktop app for Year 1 Computing students to assist with more efficient management of tasks and 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, NUScheduler can schedule your tasks faster than traditional GUI apps.
+
+- [Quick Start](#quick-start)
+- [Features](#features)
+ - [Task Management](#task-management)
+ - [Add task](#feature-1)
+ - [Delete task](#feature-2)
+ - [Update task](#feature-3)
+ - [Find task](#feature-4)
+ - [View all tasks and their deadlines](#feature-5)
+ - [Contact Management](#contact-management)
+ - [Add contact](#feature-6)
+ - [Delete contact](#feature-7)
+ - [Edit contact](#feature-8)
+ - [List all contacts](#feature-9)
+ - [Clear all contacts](#feature-10)
+ - [Find all contacts](#feature-11)
+ - [Exit the app](#feature-12)
+- [FAQ](#faq)
+- [Command Summary](#command-summary)
--------------------------------------------------------------------------------------------------------------------
+
## Quick start
-1. Ensure you have Java `11` or above installed in your Computer.
+1. Ensure you have `Java 11` installed in your Computer.
+
+2. Download the latest `NUScheduler.jar` from [here](https://github.com/AY2122S2-CS2103-F11-4/tp/releases/tag/v1.4).
-1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases).
+3. Copy the file to the folder you want to use as the _home folder_ for your NUScheduler.
-1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook.
+4. Double-click the file to start the app. A GUI similar to the image below should appear in a few seconds. Note how the app contains some sample data.
-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.
- 
+
-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.
+6. 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:
+ 1. `add n/Beow Tan p/91234157 e/beow@example.com a/Jurong street 11, block 123, #01-01` : Adds a contact named Beow Tan to NUScheduler.
+ 2. `addt d/CS2105 Assignment 1 t/01/03/2022` : Adds a task to NUScheduler.
+ 3. `findt swimming lesson` : Finds any tasks in the current task list that contains the keyword(s).
+ 4. `updt 1 d/CS2102 Assignment 1` : Updates the specified task in the current list.
+ 5. `viewt` : Lists all tasks.
+ 6. `list` : Lists all contacts.
+ 7. `delete 1` : Deletes the 1st contact shown in the current list.
+ 8. `delt 1` : Deletes the 1st task shown in the current task list.
+ 9. `clear` : Deletes all contacts.
+ 10. `exit` : Exits the app.
+
+7. Refer to the [Features](#features) below for details of each command.
+
+--------------------------------------------------------------------------------------------------------------------
+
- * **`list`** : Lists all contacts.
+## Features
- * **`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.
+
- * **`delete`**`3` : Deletes the 3rd contact shown in the current list.
+**:information_source: Notes about the command format:**
- * **`clear`** : Deletes all contacts.
+**Notes about the command format:**
- * **`exit`** : Exits the app.
+- 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`.
+- 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`.
+- Parameters can be in any order given in the command format.e.g. if the command format. specifies
+`addt d/DESCRIPTION [t/DEADLINE]`, the details can be entered as `[t/DEADLINE] d/DESCRIPTION`.
+- All `` fields must be greater than 0.
+- Additional parameters for commands that do not take in any parameters (such as `list`) will be ignored.
+ e.g. if you input `list aa`, NUScheduler will understand it as `list`.
-1. Refer to the [Features](#features) below for details of each command.
+
--------------------------------------------------------------------------------------------------------------------
-## Features
+
-
+## Task Management
+### Feature 1
+#### Add Task - Adds a task: `addt`
-**:information_source: Notes about the command format:**
+Adds a task to the task list. There will be no restriction on duplicate tasks. The deadline must follow the date format
+specified and the deadline cannot be earlier than today's date or invalid date (e.g.31/02/2022).
-* 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`.
+Format: `addt d/DESCRIPTION [t/DEADLINE (dd/mm/yyyy)]`
-* 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`.
+Example: `addt d/Buy groceries t/01/05/2022`
-* Items with `…` after them can be used multiple times including zero times.
- e.g. `[t/TAG]…` can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc.
+
-* Parameters can be in any order.
- e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable.
+
-* 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.
+### Feature 2
+#### Feature - Deletes a task: `delt`
-* 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`.
+Deletes a task from the task list, where `` is the ID of the task.
-
+Format: `delt `
+
+Example: `delt 1`
+
+
+
+
+
+### Feature 3
+#### Feature - Updates a task description and/or deadline: `updt`
-### Viewing help : `help`
+Updates a task in the task list, where `` is the ID of the task. At least one parameter must be provided.
-Shows a message explaning how to access the help page.
+Format 1: `updt t/DEADLINE`
-
+Format 2: `updt d/DESCRIPTION`
-Format: `help`
+Format 3: `updt d/DESCRIPTION t/DEADLINE`
+Example 1: `updt 3 t/01/06/2022`
-### Adding a person: `add`
+Example 2: `updt 3 d/Buy groceries`
-Adds a person to the address book.
+Example 3: `updt 3 d/Buy groceries later t/01/06/2022`
+
+
+
+
+
+### Feature 4
+#### Feature - Find tasks: `findt`
+
+Locating tasks which match any of given keyword(s).
+
+Format: `findt KEYWORD [MORE_KEYWORDS]`
+
+- The search is case-insensitive. e.g. `lessons` will match `Lessons`.
+- The order of the keywords matters. e.g. `drink water` will not match `water drink`
+- Only full keyword(s) will be accepted. e.g. `drink` will match `drink`; `ink` will not match `drink`
+- All keyword(s) have to be matched for task to be returned.
+- If a word is contained within a parentheses without spaces, the keyword has to contain the parentheses as well.
+e.g. `Assignment` will not match `(Assignment`.
+- Allows the user to easily copy data from command box. Future versions of this program will update the UI according
+to the search parameters.
+
+Example: `findt groceries` returns `Buy groceries later 01/06/2022`.
+
+
+
+
+
+### Feature 5
+#### Feature - List all tasks: `viewt`
+
+View all the tasks currently in the task list. Allows the user to copy the data easily. Future versions of this program
+will update the UI to display all tasks.
+
+Format: `viewt`
+
+
+
+## Contact Management
+### Feature 6
+#### Feature - Adds a contact: `add`
+
+Adds a contact to NUScheduler.
Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…`
:bulb: **Tip:**
-A person can have any number of tags (including 0)
+A contact can have any number of tags (including 0).
+
+Note: Only the Person's name will be deemed as unique. Other contacts are able to share the same email address and phone numbers.
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`
-### Listing all persons : `list`
+
-Shows a list of all persons in the address book.
+
-Format: `list`
+### Feature 7
+#### Feature - Deletes a contact : `delete`
+
+Deletes the specified contact from NUScheduler.
+
+Format: `delete `
+
+* Deletes the contact at the specified ``.
+* The integer refers to the index number shown in the displayed contact list.
+* The integer **must be a positive integer**.
+
+Examples:
+* `list` followed by `delete 2` deletes the 2nd contact in NUScheduler.
+* `find Betsy` followed by `delete 1` deletes the 1st contact in the results of the `find` command.
+
+
+
+
-### Editing a person : `edit`
+### Feature 8
+#### Feature - Edits a contact : `edit`
-Edits an existing person in the address book.
+Edits an existing contact in the NUScheduler.
-Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…`
+Format: `edit [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…`
-* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …
+* Edits the contact at the specified `INTEGER`. The integer refers to the index number shown in the displayed contact list. The integer **must be a positive integer**.
* 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.
+ * Existing values will be updated to the input values.
+* When editing tags, the existing tags of the contact will be removed i.e. adding of tags is not cumulative.
+* You can remove all the contact’s tags by typing `t/` without
+ specifying any tags after it. Note: For this clearing, only this format will be accepted. `edit t/`
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.
+* `edit 1 p/91234567 e/johndoe@example.com` Edits the phone number and email address of the 1st contact to be `91234567` and `johndoe@example.com` respectively.
+* `edit 2 n/Betsy Crower t/` Edits the name of the 2nd contact to be `Betsy Crower` and clears all existing tags.
+
+
+
+
+
+### Feature 9
+#### Feature - List all contacts : `list`
+
+Shows a list of all contacts in NUScheduler.
+
+Format: `list`
+
+### Feature 10
+#### Feature - Clears all contact entries : `clear`
+
+Clears all contacts from NUScheduler.
-### Locating persons by name: `find`
+Format: `clear`
+
+
-Finds persons whose names contain any of the given keywords.
+
+
+### Feature 11
+#### Feature - Finds contacts containing any of the given keywords: `find`
Format: `find KEYWORD [MORE_KEYWORDS]`
-* The search is case-insensitive. e.g `hans` will match `Hans`
+* The search is case-insensitive. e.g. `hans` will match `Hans`
* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans`
* Only the name is searched.
* Only full words will be matched e.g. `Han` will not match `Hans`
-* Persons matching at least one keyword will be returned (i.e. `OR` search).
+* Contacts matching at least one keyword will be returned (i.e. `OR` search).
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`
- 
-
-### Deleting a person : `delete`
-
-Deletes the specified person from the address book.
-
-Format: `delete INDEX`
-
-* Deletes the person at the specified `INDEX`.
-* The index refers to the index number shown in the displayed person list.
-* The index **must be a positive integer** 1, 2, 3, …
-
-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.
-
-### Clearing all entries : `clear`
-Clears all entries from the address book.
-
-Format: `clear`
+
-### Exiting the program : `exit`
+### Feature 12
+#### Feature - Exit the app: `exit`
-Exits the program.
+Exits the app.
Format: `exit`
-### Saving the data
+NUScheduler data is saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
-AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
+
-### Editing the data file
+### Note - Editing the data file
-AddressBook data are saved as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file.
+NUScheduler data are saved as a JSON file `[JAR file location]/data/addressbook.json` for contact list and `[JAR file location]/data/tasklist.json` for task list.
+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.
+If your changes to the data file makes its format invalid, NUScheduler will discard all data and start with an empty data file at the next run.
-### Archiving data files `[coming in v2.0]`
-
-_Details coming soon ..._
-
--------------------------------------------------------------------------------------------------------------------
## 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.
+**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 NUScheduler home folder.
--------------------------------------------------------------------------------------------------------------------
+
+
## Command summary
-Action | Format, Examples
---------|------------------
-**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…` e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague`
-**Clear** | `clear`
-**Delete** | `delete INDEX` e.g., `delete 3`
-**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…` e.g.,`edit 2 n/James Lee e/jameslee@example.com`
-**Find** | `find KEYWORD [MORE_KEYWORDS]` e.g., `find James Jake`
-**List** | `list`
-**Help** | `help`
+| Action | Format, Examples |
+|------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| **addt** | `addt d/DESCRIPTION [t/DEADLINE]` e.g., `addt d/Buy groceries [t/01/01/2023]` |
+| **delt** | `delt ` e.g., `delt 3` |
+| **updt** | `updt [d/DESCRIPTION] [t/DEADLINE]` e.g., `updt 3 [d/Buy groceries] [t/01/01/2023]` |
+| **findt** | `findt KEYWORD [MORE_KEYWORDS]` e.g., `findt swimming [lessons]` |
+| **viewt** | `viewt` |
+| **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]` |
+| **delete** | `delete ` e.g., `delete 3` |
+| **list** | `list` |
+| **clear** | `clear` |
+| **edit** | `edit [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…` e.g., `edit 1 [p/91234567] [e/johndoe@example.com]` |
+| **find** | `find KEYWORD [MORE_KEYWORDS]` e.g., `find James [Jake]` |
+| **exit** | `exit` |
diff --git a/docs/_config.yml b/docs/_config.yml
index 6bd245d8f4e..09f4fbdb7db 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -1,5 +1,5 @@
-title: "AB-3"
-theme: minima
+title: "NUScheduler"
+theme: jekyll-theme-cayman
header_pages:
- UserGuide.md
@@ -8,7 +8,7 @@ header_pages:
markdown: kramdown
-repository: "se-edu/addressbook-level3"
+repository: "AY2122S2-CS2103-F11-4/tp"
github_icon: "images/github-icon.png"
plugins:
diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss
index 0d3f6e80ced..a91e07f732e 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: "NUScheduler";
font-size: 32px;
}
}
diff --git a/docs/diagrams/AddTaskCommandActivityDiagram.xml b/docs/diagrams/AddTaskCommandActivityDiagram.xml
new file mode 100644
index 00000000000..bd025942627
--- /dev/null
+++ b/docs/diagrams/AddTaskCommandActivityDiagram.xml
@@ -0,0 +1 @@
+5VpRc5s4EP41nj5dxiBD8GNsJ73O5Dq5ca9tHhVQjFpAjBDBvl9/kpEMQmDTxIZ0zg8ZaZGE9O3ut7siE7CMtx8pTMO/SICiiT0NthOwmtj2fObyv0KwKwWONy8FG4qDUmRVgjX+F0nhVEpzHKBMG8gIiRhOdaFPkgT5TJNBSkmhD3smkf7WFG6QIVj7MDKl33DAwlLq2deV/E+EN6F6s+XK88VQDZYnyUIYkKImArcTsKSEsLIVb5coEtgpXMp5dx1PDxujKGF9Jjzhl88Phf93/mVRrG7Wj8t4cf+HXOUFRrk8sNws2ykE+CocbN5ZFCFmaJ1CXzwpuLq5LGRxxHsWb8IsLTXwjLeIv3TxjKNoSSJC9wuB6f7H5ebO1TYQZWhbE8mTfEQkRozu+BD5FFjW1dwpZ0nLmkmci0pNCvqwpiElg9IwNoe1K+x4Q8L3C1DaPaBMghthk7znRzDLsK8jSEmeBAK6lYAJbTH7LtpXjuw9ynGivdrWhq12stMJbUZy6qPTlsAg3SB2ZBwox6FA8xtTUTU1OC1qUDKKIsjwi+5tbbqRb3ggmJ/sYAf2/LppBwfqUKuUR5cT6w7SWAvYhk1ZTWsp4THW2hvM4fCvt6GZaTIc57XsEspCsiEJjG4racNoqjH3hKTSXn4gxnaSW2HOiG51r7cZ0NNmnJ4280ZjaFHggRUGUiAwSOCfDFGB0S5FYnIi2qFowiDYY5f93IevOIZJYKi/Uq51moAbfHu3/52Hb2232zdqnm61Ma57Kca1LAOvd+0uTk93sexh/MW2TfL0hvUXaxTGU5HVqsXVKsq2R1aubbr7Xu/UZoluNW3fe3tE7msudt+Q/EZzcSxvVFtxTidYNCTxU56NS5VedxSqUaXXwpRNQM/GlK4JnbP4lPA+FkHojlBervAGD1bBxFkZuPKjMx3AjFHyEykIE5KgBqpSxN+wSUS+ywHlgRAsBJCY11c38kGMgyDqqi50Tz+DclpSBNAWx0CLdsDF4hgYhQQHJzQV10bKF3npBXe1Aakgqqyb7yzPCI+zaaPGPj1FOXVlIOU+zhtGzeLzgZI4FVvLywS0wNy4Ve4p080P2f5KpHT+3yfzBNejZ54tschZfP3/0ampnENlPxqd2qOUBa+5rakouGLdR410z07Bbk8KVrneYBx89srCzHmalMjCvZeWlCgLcZyVpTkSjpzlvo+y7DmPot27JUjg6Wn5zBubHdXLjmXqGw5f2v/0hwt++KRWmB5FZQZ0VNzp2NfDZojuC8pxkF8P1VhQKG65/FcHGTVTRDHfOqJiGZxsJl03Occ1d9olR0N0PhSi9e84AaZ8ICYi7+CRR5z8ApgqmrsC+k9z746nWg7SGGKb+ukac34yGPXaa5gPSnbfrwP2QJ8HnGtPV69uQc1weOHbLNv8VPAZFVzwpUxEyNMP4YUyI/EpgkzkJO80CTHuBtuqgGGTEPd3cbHhqwC795cI99yu+TadmmV3q7eo/J1rR6X3Ytw9zt7vPQf3oKvptJOdLnjlwbvVf6GU9Fb9Kw+4/Q8=
diff --git a/docs/diagrams/AddTaskCommandSequenceDiagram.xml b/docs/diagrams/AddTaskCommandSequenceDiagram.xml
new file mode 100644
index 00000000000..15cfc2b0e66
--- /dev/null
+++ b/docs/diagrams/AddTaskCommandSequenceDiagram.xml
@@ -0,0 +1 @@
+5Vxbc5s4FP41nuk+xAMIcXnMtZ1pOtvdZrbbfSMg22ww8oDc2PvrV9jioouxcIA4jR8y0QELON93rpKZgOvl5mMWrBZfcISSiWVEmwm4mViWbzv0byHY7gXQ8/eCeRZHe5FZC77F/yEmNJh0HUco504kGCckXvHCEKcpCgknC7IMP/OnzXDCX3UVzJEk+BYGiSz9HkdkwaSmYdQHPqF4vmCX9iA7sAzKk5kgXwQRfm6IwO0EXGcYk/1/y801SgrdlXrZf+/uwNHqxjKUEp0v/BO7n/Gd8emvh++3W/PHZ/JHnl0AdnM/g2TNnpjdLdmWKsjwOo1QMYsxAVfPi5igb6sgLI4+U8ypbEGWCR2Z9N8ZTglD0dyN4yS5xgnOdnOBKEDeLKTynGT4CTWOOKGHHmf0iPxc7FF/ooygTUPEnvMjwktEsi09hR112VMxznls+FwD6JX8WjSwg6UwYKSZVzPXeqX/MNV2ULPlj6vmmReiUKnmRw/a0OhHzZ53VM+mM5SeQbq+uttkfri9N1bp798frODjhSmrGVze43kcfglSaqmZpHVqlavi3/UyuY9nKIlTOrpaoSymd4QKpSVM/LWWXVFvQwIqyxgcIU6SYJXHj7tpC/VmKFxnefwT/YnyPU47KYdx5RGMCqnSx5j9IGQKCJmWAiJDAZHtDgWRddwSVjhOye668GoCbwQ8cEYWeI7TIGki8kLNtrJJW9025LTtqpQt6xp4Q+kaSLpGG8pLgj4EUUSKqEynjdAS/yZhUDx0TMPgZRLPUyp6xITg5U6bQUYui8hKpSnemQtKo1LymODw6TgCNVxmOeddXDzcjdo3teFDvzxHbecxhqGIC/UyihlKAkJNlk8iFKCwr34teFqjf1FZkoB/OUWO11mI2LcEaKvbOD3ImPC4aR2EtQFhmAR5HhfRIwryRQUSPYNFHK8YbWLydwHX1HBcNv5RwtmOfXHqzYYd3g22jUHDqPeyNGLM6G65e4WfITNsKFBjz2GJGtJMtjE1faP+QNg+7wHKUaiDbeM05nFbHkC6zhSYfv2xBULvL3AqvZVYKdgNLi+jKEN5foXx09cgy99dgAe2RoC3Rg3wzhkEeCEdvtt99N0H7AqDYIaqRFiBQZUc915vmDIIZeRfFhV6h3jfPbaLtQnVdbYtYsUFDRamW0p+VA6eDup4sBttmyMxInQDsZkdqHU1XhDgWQIsqo/GR5jxgOuW5oWdZj0QaPpy0u7rpCCm30hBjKnve2OnIboZh3NeOSsAPHss19HiS9cEQoxTwDEE+g2QMXgSGVdFjnCNl8sgjV5QAyl8YuXkDNG/dfSWp1PL0qSW80rUMkYuh/pxRRLARRQzTA7lKfXsp4XFLnGv9Hcm9Xce5+8MB3R2aL26MHXTcxye2SbvWiyxu6gdSB0oTjROJG3TnlTuPAT5E/NgEp9/7VrH9nh8aCE80at1RM/TW54tN9gkTF6hmdnKp1NrGluzmTmUqjUyy19F1SpeK1TdRwWvjpz2cV0rk/XGAhXf4JViHwtlu1hqC7m71TmWcUVCN5CORqcGCFABQil7YRCDkAs1hsN7OrFRoBvSLlx/6sNGn84T550W3rH6uKOGOFNO0NtC2g0qLHJ7fHGUN1xwfLm0pT/Uj4ELC0OmYmUIjOlMNVYK3pKBKzLXtnB9LjYPbN44OY5Y0J+a/mlmbwudJkv0HwMbtoZdj9VvdPxmpcbXbicWXy7fbIJnWHq5mkR/aekl8syzp97LO5q2sGvBKhP/keirsVlnSPqOwbShmQGhAKF/YgbjgCMTDZ2jyBvkyHsvxCVMVBtdVIU4sAYrWTS6fedSHlaUequleLli9i6UrcrVlYu5g3Hb0kinXpatd9Tl0Txbe0l1nERb2A5c2U/XWFRBfGiioWORRruxUPk3Nqyt7LaWClSoz7nHeMWE/yJCtowgwZpgnktcMSZSSbU9UNzepyQrv8Ba5fDm1AQOv9jiwZMy+IOB7zidFRsNXzPthkJ+DDR52HXR1jFd5XX6WrRVK/udbCFoMFzcFDP1XdArw89ltwG0/Kl/sBsJHWFToRapZdb6xtT0Dl4EKm+h5/2SjrD2rX60Ye1IrmIJrVju47yoXx7Kf99X5eKXLYlX2S6pzu40foF1Rvsl27l2apINgF6SLQa6/lDQKGkG7Ploee5W+gztuh3/tJ3m0kS+vJVcy8/3tq9CTqKDfTPnQ+Ef5V1ghWbvg0eU8FlwwFgQUsh2BifSYxlH0T7hLjxeULtC2ZpPrMcq0opmV/0cll110vzFqcocaf7jukwxpxKF4Xkhbr/Bs1mOhoFSZ53616iHDjqIo8XLWA5Csmu/l93Nkt9pn7anysfzDfGqrXd55PxhMjxLXsWlfuiS9aRpnrVO3lR+x75n9fTrZFdYgoCGZq4nrjD0l2S8rZ/GtJPu1FRP+QtkGYThuqka/YVu3dRW/191GBrdhe6tgxe4f90l43Pr2QpNWyiuZugGEFdYrIIj74UA8srU7oUEEumoOZGJYh9TaawseWjaLxPpp6KqjVO899CuTPp8G4fqJRGDrV4BudRjb615L4BovLejJ0TosH7Bzd6i6rcEgdv/AQ==
diff --git a/docs/diagrams/DeleteTaskActivityDiagram.puml b/docs/diagrams/DeleteTaskActivityDiagram.puml
new file mode 100644
index 00000000000..4b5d2a56fc4
--- /dev/null
+++ b/docs/diagrams/DeleteTaskActivityDiagram.puml
@@ -0,0 +1,18 @@
+@startuml
+start
+:User types in the
+delete task command;
+
+'Since the beta syntax does not support placing the condition outside the
+'diamond we place it as the true branch instead.
+
+if () then ([Valid format and/or input])
+ :Corresponding task is
+ removed from Tasklist;
+ :Prompt user that the task
+ has been successfully deleted;
+else ([Invalid Format Used])
+ -Prompt user with the command's format
+endif
+stop
+@enduml
diff --git a/docs/diagrams/DeleteTaskSequenceDiagram.puml b/docs/diagrams/DeleteTaskSequenceDiagram.puml
new file mode 100644
index 00000000000..4d76d1fd955
--- /dev/null
+++ b/docs/diagrams/DeleteTaskSequenceDiagram.puml
@@ -0,0 +1,63 @@
+@startuml
+!include style.puml
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":DeleteTaskCommandParser" as DeleteTaskCommandParser LOGIC_COLOR
+participant "d:DeleteTaskCommand" as DeleteTaskCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Tasklist MODEL_COLOR_T1
+participant "taskList:Tasklist" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("delt 1")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("delt 1")
+activate AddressBookParser
+
+create DeleteTaskCommandParser
+AddressBookParser -> DeleteTaskCommandParser
+activate DeleteTaskCommandParser
+
+create DeleteTaskCommand
+DeleteTaskCommandParser -> DeleteTaskCommand
+activate DeleteTaskCommand
+
+DeleteTaskCommand --> DeleteTaskCommandParser : d
+deactivate DeleteTaskCommand
+
+DeleteTaskCommandParser --> AddressBookParser : d
+deactivate DeleteTaskCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+DeleteTaskCommandParser -[hidden]-> AddressBookParser
+destroy DeleteTaskCommandParser
+
+AddressBookParser --> LogicManager : d
+deactivate AddressBookParser
+
+LogicManager -> DeleteTaskCommand : execute(model)
+activate DeleteTaskCommand
+
+DeleteTaskCommand -> Model : deleteTask(1)
+activate Model
+
+Model --> DeleteTaskCommand
+deactivate Model
+
+create CommandResult
+DeleteTaskCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> DeleteTaskCommand
+deactivate CommandResult
+
+DeleteTaskCommand --> LogicManager : result
+deactivate DeleteTaskCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/FindTaskActivityDiagram.puml b/docs/diagrams/FindTaskActivityDiagram.puml
new file mode 100644
index 00000000000..111d8a6b500
--- /dev/null
+++ b/docs/diagrams/FindTaskActivityDiagram.puml
@@ -0,0 +1,33 @@
+@startuml
+
+skinparam backgroundColor #F6E3D4
+skinparam classFontSize 12
+skinparam maxMessageSize 20
+skinparam wrapWidth 160
+skinparam activity {
+ StartColor Chocolate
+ BackGroundColor OldLace
+ BorderColor Black
+ ArrowColor Sienna
+ EndColor Chocolate
+}
+
+ start
+ :User type in the find task command;
+ 'Since the beta syntax does not support placing the condition outside the
+ 'diamond we place it as the true branch instead.
+ if () then ([Valid format and/or input])
+ -Retrieve matching tasks from TaskList;
+ if () then ([Contain one or more matching tasks])
+ -Prompt user with matching tasks in Tasklist
+ else ([else])
+ -Prompt user no matching tasks found
+ endif
+
+ else ([else])
+ -"Prompt user with the command's format"
+
+ endif
+ stop
+
+@enduml
diff --git a/docs/diagrams/FindTaskSequenceDiagram.puml b/docs/diagrams/FindTaskSequenceDiagram.puml
new file mode 100644
index 00000000000..2c3fae9fb4c
--- /dev/null
+++ b/docs/diagrams/FindTaskSequenceDiagram.puml
@@ -0,0 +1,63 @@
+@startuml
+!include style.puml
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":FindTaskCommandParser" as FindTaskCommandParser LOGIC_COLOR
+participant "f:FindTaskCommand" as FindTaskCommand LOGIC_COLOR
+participant "r:CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box TaskList MODEL_COLOR_T1
+participant "taskList:TaskList" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("findt demo")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("findt demo")
+activate AddressBookParser
+
+create FindTaskCommandParser
+AddressBookParser -> FindTaskCommandParser
+activate FindTaskCommandParser
+
+create FindTaskCommand
+FindTaskCommandParser -> FindTaskCommand
+activate FindTaskCommand
+
+FindTaskCommandParser --> AddressBookParser
+deactivate FindTaskCommand
+
+FindTaskCommand --> FindTaskCommandParser
+deactivate FindTaskCommandParser
+
+FindTaskCommandParser -[hidden]-> AddressBookParser
+destroy FindTaskCommandParser
+
+AddressBookParser --> LogicManager
+deactivate FindTaskCommandParser
+
+LogicManager -> FindTaskCommand: execute(model)
+activate FindTaskCommand
+
+FindTaskCommand -> Model : findTask(demo)
+activate Model
+
+Model --> FindTaskCommand
+deactivate Model
+
+create CommandResult
+FindTaskCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> FindTaskCommand
+deactivate CommandResult
+
+FindTaskCommand --> LogicManager : result
+deactivate FindTaskCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml
index 4439108973a..4372c29639b 100644
--- a/docs/diagrams/ModelClassDiagram.puml
+++ b/docs/diagrams/ModelClassDiagram.puml
@@ -12,6 +12,8 @@ Class AddressBook
Class ModelManager
Class UserPrefs
+Class TaskList
+Class Task
Class UniquePersonList
Class Person
Class Address
@@ -34,6 +36,9 @@ ModelManager -left-> "1" AddressBook
ModelManager -right-> "1" UserPrefs
UserPrefs .up.|> ReadOnlyUserPrefs
+
+AddressBook *--> "1" TaskList
+TaskList *--> "~* all" Task
AddressBook *--> "1" UniquePersonList
UniquePersonList --> "~* all" Person
Person *--> Name
diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml
index 760305e0e58..07d7ae74cc9 100644
--- a/docs/diagrams/StorageClassDiagram.puml
+++ b/docs/diagrams/StorageClassDiagram.puml
@@ -22,6 +22,13 @@ Class JsonAdaptedPerson
Class JsonAdaptedTag
}
+package "TaskList Storage" #F4F6F6{
+Class "<>\nTaskListStorage" as TaskListStorage
+Class JsonTaskListStorage
+Class JsonSerializableTaskList
+Class JsonAdaptedTask
+}
+
}
Class HiddenOutside #FFFFFF
@@ -30,14 +37,19 @@ HiddenOutside ..> Storage
StorageManager .up.|> Storage
StorageManager -up-> "1" UserPrefsStorage
StorageManager -up-> "1" AddressBookStorage
+StorageManager -up-> "1" TaskListStorage
-Storage -left-|> UserPrefsStorage
+Storage -right-|> UserPrefsStorage
Storage -right-|> AddressBookStorage
+Storage -right-|> TaskListStorage
JsonUserPrefsStorage .up.|> UserPrefsStorage
JsonAddressBookStorage .up.|> AddressBookStorage
JsonAddressBookStorage ..> JsonSerializableAddressBook
JsonSerializableAddressBook --> "*" JsonAdaptedPerson
JsonAdaptedPerson --> "*" JsonAdaptedTag
+JsonTaskListStorage .up.|> TaskListStorage
+JsonTaskListStorage ..> JsonSerializableTaskList
+JsonSerializableTaskList --> "*" JsonAdaptedTask
@enduml
diff --git a/docs/diagrams/TaskListStorageActivityDiagram.xml b/docs/diagrams/TaskListStorageActivityDiagram.xml
new file mode 100644
index 00000000000..289ccbeba76
--- /dev/null
+++ b/docs/diagrams/TaskListStorageActivityDiagram.xml
@@ -0,0 +1 @@
+7Vrdd+IoFP9rfNwekxirj1o7nYfOnJlx5+z2kSbUMENCDmDV/esXDPkgEE2rMW3P+NCGyw2E3/3kwsC7ibd3FKTRFxJCPHCH4XbgLQau6/gjT/yTlF1GmY4mGWFFUaiYSsIS/QcVcaioaxRCpjFyQjBHqU4MSJLAgGs0QCnZ6GxPBOuzpmAFDcIyANik/oNCHmXUiXtd0j9DtIrymZ3xNOuJQc6sVsIiEJJNheTdDrwbSgjPnuLtDcQSvByX7L1PDb3Fh1GY8DYvPKLnr982wff13/PNYrZ8uInn93+pUZ4BXqsFq4/luxwBMYoAWzTmmwhxuExBIHs2Qt6CFvEYi5YjHgFLMwk8oS0Uk86fEMY3BBO6H8gb7n+Cbn55/hmQcritkNRK7iCJIac7wZL3ThWqSq3GqrkpZZTjHlXEk9OA0opVMXAJnHhQ2L0AR7cFjkk4kwopWgEGjKFAh4+SdRJK3BYSI7hF/F/5fOWr1oPik8+LbYVtscsbiVhK5SXZfKj2la/tW/l7jfJgZE0DeFx9OKArqF79PGPgxzWdzO9jer+d3gU7d5fzwVAzNlO6FfH5FvHlNAox4OhZN1GbTNUM3wgSKyuV53qsKY9QTX2IbN3qrapJ1QZyh7oWOqPaQBkwxkB7FSuW3UrrrLC64xZqJzBfqiahPCIrkgB8W1Jrilfy3BOSKp37BTnfKecM1pzommsV7usUymupUK5vV6DWmnGStXsG7D8ZpBKDLQzWXMQrMel+Gez3PjbFMUhC8UQyJsQ1KlsHAWTsaY3xzpBfKR3nuAuuedxP+995PG5d1/2xY7hcx+Zzx5353OFx7V8J+NL2qy/yCPCYjzA8iIrv11CZ9B2IWkSiBlAOg/x6qPqCwpm08I5nSW4SksghUkiR+HRI5TAoWWnu8TjSbgPSbwjR6aUQraaLIaKCEZFE0EUAkSvvAFPV6115+k8z74beqguss7imfJp4TpGXNUp6fWQDl89cX5p5HEpRj2ceF0o8Dn2kmXg8ym2kO5Ruffj15zKIYLjGsqe7bMISEQzcm/dvo1rm7Jlxs7NswgrtqE9bcSqWUtrNMVvRLKU0nO5txW1pK+MTbcW+6/JcXXfc68vuupxeNlnv1K22VRWnwV9cyK9OexXpx7R+pxvzL0JFvuXy25n/jFKwq7ClkoE1zzOye5mmWlCNfTQd1rQym/+89Z/j6TiNSPy4Zv0G+3ENSkuwn1hi/aSrWF+U5N+PB3d6M/dxv+ZeaEFRd+rI3P0eDNis386klChkkA+y+iFGjF/9YmL3K+1lP+QjlNUXCgEX6ihmkF0h4LLiKM+33nDa79WObQqv2Vvaf226UH/+BTEm6zd1CfgLA1mBBdfhY5yS37BWGrJUiwBGK1nSCAR2QmTeXCKLAoBnqiNGYYibCim6NzqDaFy/hWg8i2jqhyfn89J/tmTtvfTE9NLNRxd9pdlmQVarVQwxAaEyOnmE8kRJbPGC78nf9XhmYpWApYDrzw2AWR6E9gEyO6TKhcI4oWXY2Z9kCTiyMcXUH9lF1ncEtprVRT1kfkmmC3Gi5OMLtLhllJf33d4l6vcZ895Zbclpff3k1K3JaTI1q/YzaWwwTvmuOcunMMVS/QVDJP8K9Bm3ZaXvKiLWC/+e5ebWZUNioW5/TK6FybWtBkxOtLhX7eGNOyqjwzW76UH2brb8Tos7W2+xaFfUR3or2k36tNOXbAfPaG8n33OzK75xbWlaG6Lri4u+YQT1mLaC2YF2mFW5apJ/pV3oUfAMVuKNL7fDE83ytngmifLOvXf7Pw==
diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml
index 95473d5aa19..08bf65d917a 100644
--- a/docs/diagrams/UiClassDiagram.puml
+++ b/docs/diagrams/UiClassDiagram.puml
@@ -4,7 +4,7 @@ skinparam arrowThickness 1.1
skinparam arrowColor UI_COLOR_T4
skinparam classBackgroundColor UI_COLOR
-package UI <>{
+package UI <> #DBEBF9 {
Class "<>\nUi" as Ui
Class "{abstract}\nUiPart" as UiPart
Class UiManager
@@ -13,6 +13,8 @@ Class HelpWindow
Class ResultDisplay
Class PersonListPanel
Class PersonCard
+Class TaskListPanel
+Class TaskCard
Class StatusBarFooter
Class CommandBox
}
@@ -33,11 +35,14 @@ UiManager -down-> "1" MainWindow
MainWindow *-down-> "1" CommandBox
MainWindow *-down-> "1" ResultDisplay
MainWindow *-down-> "1" PersonListPanel
+MainWindow *-down-> "1" TaskListPanel
MainWindow *-down-> "1" StatusBarFooter
MainWindow --> "0..1" HelpWindow
PersonListPanel -down-> "*" PersonCard
+TaskListPanel -down-> "*" TaskCard
+
MainWindow -left-|> UiPart
ResultDisplay --|> UiPart
diff --git a/docs/diagrams/UpdateTaskCommandActivityDiagram.puml b/docs/diagrams/UpdateTaskCommandActivityDiagram.puml
new file mode 100644
index 00000000000..87a129791ff
--- /dev/null
+++ b/docs/diagrams/UpdateTaskCommandActivityDiagram.puml
@@ -0,0 +1,18 @@
+@startuml
+start
+:User types in the
+update task command;
+
+'Since the beta syntax does not support placing the condition outside the
+'diamond we place it as the true branch instead.
+
+if () then ([Valid format and/or input])
+ :Corresponding task is
+ updated in the Tasklist;
+ :Prompt user that the task
+ has been successfully updated;
+else ([Invalid Format Used])
+ -Prompt user with the command's format
+endif
+stop
+@enduml
diff --git a/docs/diagrams/UpdateTaskCommandSequenceDiagram.puml b/docs/diagrams/UpdateTaskCommandSequenceDiagram.puml
new file mode 100644
index 00000000000..d6bce4aae11
--- /dev/null
+++ b/docs/diagrams/UpdateTaskCommandSequenceDiagram.puml
@@ -0,0 +1,63 @@
+@startuml
+!include style.puml
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":UpdateTaskCommandParser" as UpdateTaskCommandParser LOGIC_COLOR
+participant "u:UpdateTaskCommand" as UpdateTaskCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Tasklist MODEL_COLOR_T1
+participant "taskList:Tasklist" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("updt 1 d\demo")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("updt 1 d\demo")
+activate AddressBookParser
+
+create UpdateTaskCommandParser
+AddressBookParser -> UpdateTaskCommandParser
+activate UpdateTaskCommandParser
+
+create UpdateTaskCommand
+UpdateTaskCommandParser -> UpdateTaskCommand
+activate UpdateTaskCommand
+
+UpdateTaskCommand --> UpdateTaskCommandParser : u
+deactivate UpdateTaskCommand
+
+UpdateTaskCommandParser --> AddressBookParser : u
+deactivate UpdateTaskCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+UpdateTaskCommandParser -[hidden]-> AddressBookParser
+destroy UpdateTaskCommandParser
+
+AddressBookParser --> LogicManager : u
+deactivate AddressBookParser
+
+LogicManager -> UpdateTaskCommand : execute(model)
+activate UpdateTaskCommand
+
+UpdateTaskCommand -> Model : updateTask(task, 1)
+activate Model
+
+Model --> UpdateTaskCommand
+deactivate Model
+
+create CommandResult
+UpdateTaskCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> UpdateTaskCommand
+deactivate CommandResult
+
+UpdateTaskCommand --> LogicManager : result
+deactivate UpdateTaskCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/images/AddTaskCommandActivityDiagram.png b/docs/images/AddTaskCommandActivityDiagram.png
new file mode 100644
index 00000000000..480182ba400
Binary files /dev/null and b/docs/images/AddTaskCommandActivityDiagram.png differ
diff --git a/docs/images/AddTaskCommandSequenceDiagram.png b/docs/images/AddTaskCommandSequenceDiagram.png
new file mode 100644
index 00000000000..247ce1b8fa8
Binary files /dev/null and b/docs/images/AddTaskCommandSequenceDiagram.png differ
diff --git a/docs/images/DeleteTaskActivityDiagram.png b/docs/images/DeleteTaskActivityDiagram.png
new file mode 100644
index 00000000000..abf1fe638e2
Binary files /dev/null and b/docs/images/DeleteTaskActivityDiagram.png differ
diff --git a/docs/images/DeleteTaskSequenceDiagram.png b/docs/images/DeleteTaskSequenceDiagram.png
new file mode 100644
index 00000000000..ff7bd66d77d
Binary files /dev/null and b/docs/images/DeleteTaskSequenceDiagram.png differ
diff --git a/docs/images/FindTaskActivityDiagram.png b/docs/images/FindTaskActivityDiagram.png
new file mode 100644
index 00000000000..2369f06e3fc
Binary files /dev/null and b/docs/images/FindTaskActivityDiagram.png differ
diff --git a/docs/images/FindTaskSequenceDiagram.png b/docs/images/FindTaskSequenceDiagram.png
new file mode 100644
index 00000000000..74002d6effe
Binary files /dev/null and b/docs/images/FindTaskSequenceDiagram.png differ
diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png
index 04070af60d8..8ec1f8f910c 100644
Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ
diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png
index 2533a5c1af0..a6b9ed2e7af 100644
Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ
diff --git a/docs/images/TaskListStorageActivityDiagram.png b/docs/images/TaskListStorageActivityDiagram.png
new file mode 100644
index 00000000000..a2b22f34650
Binary files /dev/null and b/docs/images/TaskListStorageActivityDiagram.png differ
diff --git a/docs/images/Ui.png b/docs/images/Ui.png
index 5bd77847aa2..c6c5f27b552 100644
Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ
diff --git a/docs/images/UiAddPerson.png b/docs/images/UiAddPerson.png
new file mode 100644
index 00000000000..b957511ea4c
Binary files /dev/null and b/docs/images/UiAddPerson.png differ
diff --git a/docs/images/UiAddTask.png b/docs/images/UiAddTask.png
new file mode 100644
index 00000000000..fa0a0b2adf1
Binary files /dev/null and b/docs/images/UiAddTask.png differ
diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png
index 785e04dbab4..d441027d4cb 100644
Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ
diff --git a/docs/images/UiClearPerson.png b/docs/images/UiClearPerson.png
new file mode 100644
index 00000000000..3cb28c960ed
Binary files /dev/null and b/docs/images/UiClearPerson.png differ
diff --git a/docs/images/UiDeletePerson.png b/docs/images/UiDeletePerson.png
new file mode 100644
index 00000000000..7bc565d3f18
Binary files /dev/null and b/docs/images/UiDeletePerson.png differ
diff --git a/docs/images/UiDeleteTask.png b/docs/images/UiDeleteTask.png
new file mode 100644
index 00000000000..4a9afeff796
Binary files /dev/null and b/docs/images/UiDeleteTask.png differ
diff --git a/docs/images/UiEditPerson.png b/docs/images/UiEditPerson.png
new file mode 100644
index 00000000000..eaea4a05dab
Binary files /dev/null and b/docs/images/UiEditPerson.png differ
diff --git a/docs/images/UiFindPerson.png b/docs/images/UiFindPerson.png
new file mode 100644
index 00000000000..2ba2c46cc9c
Binary files /dev/null and b/docs/images/UiFindPerson.png differ
diff --git a/docs/images/UiFindTask.png b/docs/images/UiFindTask.png
new file mode 100644
index 00000000000..9c2af042948
Binary files /dev/null and b/docs/images/UiFindTask.png differ
diff --git a/docs/images/UiUpdateTask.png b/docs/images/UiUpdateTask.png
new file mode 100644
index 00000000000..5de238863ad
Binary files /dev/null and b/docs/images/UiUpdateTask.png differ
diff --git a/docs/images/UiViewTask.png b/docs/images/UiViewTask.png
new file mode 100644
index 00000000000..db651653937
Binary files /dev/null and b/docs/images/UiViewTask.png differ
diff --git a/docs/images/UpdateTaskCommandActivityDiagram.png b/docs/images/UpdateTaskCommandActivityDiagram.png
new file mode 100644
index 00000000000..a38f7253928
Binary files /dev/null and b/docs/images/UpdateTaskCommandActivityDiagram.png differ
diff --git a/docs/images/UpdateTaskCommandSequenceDiagram.png b/docs/images/UpdateTaskCommandSequenceDiagram.png
new file mode 100644
index 00000000000..307d101c470
Binary files /dev/null and b/docs/images/UpdateTaskCommandSequenceDiagram.png differ
diff --git a/docs/images/castryl.png b/docs/images/castryl.png
new file mode 100644
index 00000000000..9bd5274a824
Binary files /dev/null and b/docs/images/castryl.png differ
diff --git a/docs/images/ivorcmx.png b/docs/images/ivorcmx.png
new file mode 100644
index 00000000000..d08b591eab0
Binary files /dev/null and b/docs/images/ivorcmx.png differ
diff --git a/docs/images/putaojuice.png b/docs/images/putaojuice.png
new file mode 100644
index 00000000000..d374b470c24
Binary files /dev/null and b/docs/images/putaojuice.png differ
diff --git a/docs/images/yumengtan.png b/docs/images/yumengtan.png
new file mode 100644
index 00000000000..f06209d3f08
Binary files /dev/null and b/docs/images/yumengtan.png differ
diff --git a/docs/index.md b/docs/index.md
index 7601dbaad0d..a446738f1b4 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,17 +1,20 @@
---
layout: page
-title: AddressBook Level-3
+title: NUScheduler
---
-[](https://github.com/se-edu/addressbook-level3/actions)
-[](https://codecov.io/gh/se-edu/addressbook-level3)
+[](https://github.com/AY2122S2-CS2103-F11-4/tp/actions)
+[](https://codecov.io/gh/AY2122S2-CS2103-F11-4/tp)

-**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).
+**NUScheduler is a desktop app for Year 1 Computing students to assist with more efficient management of tasks and 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, NUScheduler can schedule your tasks faster than traditional GUI apps.
-* 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 NUScheduler, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start).
+* If you are interested about developing NUScheduler, the [**Developer Guide**](DeveloperGuide.html) is a good place to start.
**Acknowledgements**
diff --git a/docs/team/castryl.md b/docs/team/castryl.md
new file mode 100644
index 00000000000..f8d3bccc41b
--- /dev/null
+++ b/docs/team/castryl.md
@@ -0,0 +1,38 @@
+---
+layout: page
+title: Ian's Project Portfolio Page
+---
+### Project: NUScheduler
+NUScheduler is a desktop app for Year 1 Computing students to assist with more efficient management of tasks and 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, NUScheduler can schedule your tasks faster than traditional GUI apps.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Implemented view task feature
+ * What it does: Allow the user to view all tasks in the Tasklist
+ * Justification: Allows the user the view all tasks at once in a quick overview
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=castryl&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2022-02-18)
+
+* **Project management**:
+ * Participated in weekly project meetings
+
+* **Enhancements to existing features**:
+ * to be added soon
+
+* **Documentation**:
+ * User Guide:
+ * Added documention for User Stories.
+ * Added documentation for Use Cases.
+ * Added documentation for non-functional requirements.
+ * Proofread entire User Guide for grammatical and spelling errors.
+ * Developer Guide:
+ * Proofread entire Developer Guide for grammatical and spelling errors.
+
+* **Statistics**:
+ * Reviewed PRs: [View](https://github.com/AY2122S2-CS2103-F11-4/tp/pulls?q=is%3Apr+is%3Aclosed+reviewed-by%3Acastryl)
+ * Raised PRs: [View](https://github.com/AY2122S2-CS2103-F11-4/tp/pulls?q=is%3Apr+is%3Aclosed+author%3Acastryl)
+ * Issues Taken: [View](https://github.com/AY2122S2-CS2103-F11-4/tp/issues?q=is%3Aissue+is%3Aclosed+assignee%3Acastryl)
+
+
diff --git a/docs/team/ivorcmx.md b/docs/team/ivorcmx.md
new file mode 100644
index 00000000000..df3b3dd925a
--- /dev/null
+++ b/docs/team/ivorcmx.md
@@ -0,0 +1,76 @@
+---
+layout: page
+title: Ivor's Project Portfolio Page
+---
+
+### Project: NUScheduler
+
+NUScheduler is a desktop app for Year 1 Computing students to assist with more efficient management of tasks and 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, NUScheduler can schedule your tasks faster than traditional GUI apps.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Implemented `DeleteTask` command.
+ * What it does: Allow the user to delete a particular task based on its ID in the Tasklist
+ * Justification: Our target audience is year 1 computing students. It helps them to remove tasks that are not relevant anymore,
+ which helps to keep the program neat.
+ * **Classes created**:
+ * `DeleteTaskCommand`
+ * `DeleteTaskCommandParser`
+ * **Tests Written**:
+ * `DeleteTaskCommandTest`
+ * `DeleteTaskCommandParserTest`
+
+ * **Additional info**:
+ * Updated ParserUtil method of parseNumber to work with DeleteTaskCommand
+ * Added relevant activity and sequence diagrams into Developer Guide
+
+* **New Feature**: Implemented `UpdateTask` command.
+ * What it does: Allow the user to update a particular task attributes based on its ID in the Tasklist
+ * Justification: Our target audience is year 1 computing students. It helps them to update task that details have changed to ensure
+ the program always reflects the latest details.
+ * **Classes created**:
+ * `UpdateTaskCommand`
+ * `UpdateTaskCommandParser`
+ * **Tests Written**:
+ * `UpdateTaskCommandTest`
+ * `UpdateTaskCommandParserTest`
+ * **Additional info**:
+ * Added additional class `UpdateTaskDescriptor` in `UpdateTaskCommand` to assist functionality
+ * Added relevant activity and sequence diagrams into Developer Guide
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=Ivor&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2022-02-18)
+
+* **Project management**:
+ * Managed release `v1.1`, `v1.2`, `v1.3`, `v1.4`.
+ * Participated in weekly project meetings and set up weekly milestones with the team.
+ * Reviewed pull requests.
+
+* **Enhancements to existing features**:
+ * Wrote additional tests to increase coverage for `DeleteTask`
+
+* **Documentation**:
+ * User Guide:
+ * Added documentation for the feature `DeleteTask`.
+ * Added documentation for the feature `UpdateTask`.
+ * Added visuals in UG to enhance readability and understandability.
+ * Fixed User Guide bugs.
+ * Developer Guide:
+ * Contribute to glossary in the developer guide.
+ * Added user story for `DeleteTask` feature.
+ * Added MSS for `DeleteTask`.
+ * Add sequence diagram for the feature `DeleteTask`.
+ * Add activity diagram for the feature `DeleteTask`.
+ * Added user story for `UpdateTask` feature.
+ * Added MSS for `UpdateTask`.
+ * Add sequence diagram for the feature `UpdateTask`.
+ * Add activity diagram for the feature `UpdateTask`.
+
+* **Community**:
+ * PRs reviewed with comments to submitted PRs
+ * Reported 9 bugs during [PE-D](https://github.com/ivorcmx/ped/issues)
+ * Submitted forum post [#240](https://github.com/nus-cs2103-AY2122S2/forum/issues/240).
+
+* **Statistics**:
+ * Reviewed PRs: [24](https://github.com/AY2122S2-CS2103-F11-4/tp/pulls?q=type%3Apr+reviewed-by%3Aivorcmx)
+ * Raised PRs: [20](https://github.com/AY2122S2-CS2103-F11-4/tp/pulls/@me)
+ * Issues Taken: [45](https://github.com/AY2122S2-CS2103-F11-4/tp/issues?q=assignee%3A%40me+is%3Aclosed)
diff --git a/docs/team/putaojuice.md b/docs/team/putaojuice.md
new file mode 100644
index 00000000000..63f63bef549
--- /dev/null
+++ b/docs/team/putaojuice.md
@@ -0,0 +1,96 @@
+---
+layout: page
+title: Chen Yu An's Project Portfolio Page
+---
+### Project: NUScheduler
+NUScheduler is a desktop app for Year 1 Computing students to assist with more efficient management of tasks and 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, NUScheduler can schedule your tasks faster than traditional GUI apps.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Implemented `AddTask` command ([#26](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/26)).
+ * What it does: Allow the user to add a task to the application with description and deadline (optional)
+ * Justification: Our target audience is year 1 computing students. It helps them to keep track of their assignments
+ and any other daily tasks.
+ * **Classes created**:
+ * `AddTaskCommand`
+ * `AddTaskCommandParser`
+ * `TaskList`
+ * `Task`
+ * **Tests written**:
+ * `AddTaskCommandTest`
+ * `AddTaskCommandParserTest`
+ * `TaskListTest`
+ * `TaskTest`
+ * **Additional info**:
+ * `Task` and `TaskList` classes are created to capture the information of tasks and store them as an `ArrayList` in
+ `ModelManager`.
+
+* **New Feature**: Implemented `TaskListStorage` ([#74](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/74)).
+ * What it does: The system will write the task list to user's local folder in Json format whenever there is a change
+ in the task list. The system will read from the saved task list from user's local folder in Json format when starting up
+ to load the stored task list from the last access.
+ * Justification: This feature allows the user to save their task list and continue where it was left from.
+ * **Classes created**:
+ * `ReadOnlyTaskList`
+ * `JsonAdaptedTask`
+ * `JsonSerializableTaskList`
+ * `JsonTaskListStorage`
+ * `TaskListStorage`
+ * **Tests written**:
+ * `JsonAdaptedTaskTest`
+ * `JsonSerializableTaskListTest`
+ * `JsonTaskListStorageTest`
+ * `TypicalTask`
+ * **Additional info**:
+ * This feature requires a good understand of how the application is designed and involves modification to
+ `Logic`, `Model` and `Storage`.
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=putaojuice&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2022-02-18&tabOpen=true&tabType=authorship&tabAuthor=putaojuice&tabRepo=AY2122S2-CS2103-F11-4%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false).
+
+* **Project management**:
+ * Managed release `v1.1`, `v1.2`, `v1.3`, `v1.4`.
+ * Organised weekly project meeting and set up weekly milestone and direction of project.
+ * Reviewed team's pull requests.
+ * Wrap up every milestone.
+ * Complete administrative matters for the team.
+ * Viewed through all PE-D issues and assigned relevant issues to the team members.
+ * Released `v1.2`: [View](https://github.com/AY2122S2-CS2103-F11-4/tp/releases/tag/v1.2).
+ * Released `v1.3 Trial`: [View](https://github.com/AY2122S2-CS2103-F11-4/tp/releases/tag/v1.3.trial).
+ * Released `v1.3`: [View](https://github.com/AY2122S2-CS2103-F11-4/tp/releases/tag/v1.3)
+ * Released `v1.4`: [View](https://github.com/AY2122S2-CS2103-F11-4/tp/releases/tag/v1.4)
+
+* **Enhancements to existing features:**
+ * Added `tasklist.json`, a separate JSON file to store the tasks added by user so that it does not interfere with
+ the original `addressbook.json`
+ * Wrote tests for all new features implemented.
+
+* **Documentation**:
+ * User Guide:
+ * Added documentation for the feature `AddTask`.
+ * Help in proofreading user guide.
+ * Developer Guide:
+ * Contribute non-functional requirement in the developer guide.
+ * Contribute to glossary in the developer guide.
+ * Added MSS for `AddTask`.
+ * Added information about the implementation of the `AddTask` command ([#60](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/60)).
+ * Added 2 UML diagrams to aid the explanations.
+ * Help in proofreading developer guide.
+ * Weekly Team Meeting Minutes:
+ * Help in recording meeting minutes.
+ * Screenshot `v1.2` and `v1.3` demo.
+
+* **Community**:
+ * Participate in forum ([#166](https://github.com/nus-cs2103-AY2122S2/forum/issues/166), [#240](https://github.com/nus-cs2103-AY2122S2/forum/issues/240), [#244](https://github.com/nus-cs2103-AY2122S2/forum/issues/244)).
+ * Reported 13 bugs for other team during PE-D: [View](https://github.com/putaojuice/ped/issues).
+
+* **Tools**:
+ * Set up the team's Github group and repo.
+ * Added CodeCov integration to the repo.
+ * Enabled assertions for the project ([#61](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/61)).
+
+* **Statistics**:
+ * Reviewed PRs: [View](https://github.com/AY2122S2-CS2103-F11-4/tp/pulls?q=is%3Apr+is%3Aclosed+reviewed-by%3Aputaojuice)
+ * Raised PRs: [View](https://github.com/AY2122S2-CS2103-F11-4/tp/pulls?q=is%3Apr+is%3Aclosed+author%3Aputaojuice)
+ * Issues Taken: [View](https://github.com/AY2122S2-CS2103-F11-4/tp/issues?q=is%3Aissue+is%3Aclosed+assignee%3Aputaojuice)
diff --git a/docs/team/yumengtan.md b/docs/team/yumengtan.md
new file mode 100644
index 00000000000..7db4ecb7a74
--- /dev/null
+++ b/docs/team/yumengtan.md
@@ -0,0 +1,67 @@
+---
+layout: page
+title: Tan Yu Meng's Project Portfolio Page
+---
+### Project: NUScheduler
+NUScheduler is a desktop app for Year 1 Computing students to assist with more efficient management of tasks and 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, NUScheduler can schedule your tasks faster than traditional GUI apps.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Implemented `FindTask` command. (Pull Requests [#33](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/33), [#59](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/59), [#84](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/84))
+ * What it does: Allow the user to find matching tasks in task list based on a keyword or multiple keywords
+ * Justification: Our target audience is year 1 computing students. It helps them easily find any tasks such as assignments based on a keyword or multiple keywords.
+ * **Classes created**:
+ * `FindTaskCommand`
+ * `FindTaskCommandParser`
+ * **Tests Written**:
+ * `FindTaskCommandTest`
+ * `FindTaskCommandParserTest`
+ * **Additional info**:
+ * Used regex to filter task list.
+ * Credits: The regex method to implement `FindTask` feature was referenced and adapted from [stackoverflow](https://stackoverflow.com/questions/25483114/regex-to-find-whole-word-in-text-but-case-insensitive).
+
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=yumengtan&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2022-02-18&tabOpen=false)
+
+
+* **Enhancements to existing features**:
+ * Updated GUI layout and components (Pull requests [#64](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/64), [#65](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/65), [#137](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/137), [#81](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/81))
+ * Updated GUI to display task list, added `TaskCard`, `TaskListPanel` and `UniqueTaskList` classes. (Pull request [#78](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/78))
+ * Wrote additional tests to increase coverage (Pull request [#59](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/59))
+
+
+
+* **Documentation**:
+ * User Guide:
+ * Added documentation for the feature `FindTask`. (Pull request [#38](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/38/files))
+ * Fixed User Guide bugs. (Pull requests [#82](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/82), [#135](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/135))
+ * Developer Guide:
+ * Contribute to glossary in the developer guide. (Pull request [#62](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/62))
+ * Added user story for find task feature. (Pull request [#62](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/62))
+ * Added MSS for `FindTask`. (Pull request [#38](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/38/files))
+ * Updated Ui component to match project details. (Pull request [#144](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/144))
+ * Diagrams
+ * Added Sequence Diagram for `FindTask`. (Pull request [#62](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/62))
+ * Added Activity Diagram for `FindTask`. (Pull request [#62](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/62))
+ * Updated Ui Class Diagram to match project details (Pull request [#144](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/144))
+ *README and GitHub Pages:
+ * Refactored README and GitHub pages to match project description. (Pull Requests [#9](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/9), [##47](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/47))
+
+
+* **Contributions to team-based tasks**:
+ * Designed application logo and Ui mockup.
+ * Helped make the demo video.
+
+
+* **Community**:
+ * PRs reviewed (with non-trivial review comments): (Pull Request [#136](https://github.com/AY2122S2-CS2103-F11-4/tp/pull/136))
+ * Reported 12 bugs for other teams during [PE Dry Run](https://github.com/yumengtan/ped/issues)
+ * Participate in forum [#211](https://github.com/nus-cs2103-AY2122S2/forum/issues/211)
+
+
+* **Statistics**:
+ * Reviewed PRs: [view](https://github.com/AY2122S2-CS2103-F11-4/tp/pulls?q=is%3Apr+is%3Aclosed+reviewed-by%3A%40me)
+ * Raised PRs: [view](https://github.com/AY2122S2-CS2103-F11-4/tp/pulls?q=is%3Apr+is%3Aclosed+author%3Ayumengtan+review%3Aapproved)
+ * Issues Taken: [view](https://github.com/AY2122S2-CS2103-F11-4/tp/issues?q=assignee%3Ayumengtan+is%3Aclosed)
diff --git a/dummy.txt b/dummy.txt
new file mode 100644
index 00000000000..7db933aafa3
--- /dev/null
+++ b/dummy.txt
@@ -0,0 +1 @@
+This is for a dummy commit to trigger CI.
diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java
index 4133aaa0151..a305070348b 100644
--- a/src/main/java/seedu/address/MainApp.java
+++ b/src/main/java/seedu/address/MainApp.java
@@ -19,14 +19,18 @@
import seedu.address.model.Model;
import seedu.address.model.ModelManager;
import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyTaskList;
import seedu.address.model.ReadOnlyUserPrefs;
+import seedu.address.model.TaskList;
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.JsonTaskListStorage;
import seedu.address.storage.JsonUserPrefsStorage;
import seedu.address.storage.Storage;
import seedu.address.storage.StorageManager;
+import seedu.address.storage.TaskListStorage;
import seedu.address.storage.UserPrefsStorage;
import seedu.address.ui.Ui;
import seedu.address.ui.UiManager;
@@ -36,7 +40,7 @@
*/
public class MainApp extends Application {
- public static final Version VERSION = new Version(0, 2, 0, true);
+ public static final Version VERSION = new Version(1, 3, 0, true);
private static final Logger logger = LogsCenter.getLogger(MainApp.class);
@@ -48,7 +52,7 @@ public class MainApp extends Application {
@Override
public void init() throws Exception {
- logger.info("=============================[ Initializing AddressBook ]===========================");
+ logger.info("=============================[ Initializing NUScheduler ]===========================");
super.init();
AppParameters appParameters = AppParameters.parse(getParameters());
@@ -57,7 +61,8 @@ public void init() throws Exception {
UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath());
UserPrefs userPrefs = initPrefs(userPrefsStorage);
AddressBookStorage addressBookStorage = new JsonAddressBookStorage(userPrefs.getAddressBookFilePath());
- storage = new StorageManager(addressBookStorage, userPrefsStorage);
+ TaskListStorage taskListStorage = new JsonTaskListStorage(userPrefs.getTaskListFilePath());
+ storage = new StorageManager(addressBookStorage, userPrefsStorage, taskListStorage);
initLogging(config);
@@ -90,7 +95,23 @@ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) {
initialData = new AddressBook();
}
- return new ModelManager(initialData, userPrefs);
+ Optional taskListOptional;
+ ReadOnlyTaskList initialDataTaskList;
+ try {
+ taskListOptional = storage.readTaskList();
+ if (!taskListOptional.isPresent()) {
+ logger.info("Data file not found. Will be starting with an empty TaskList");
+ }
+ initialDataTaskList = taskListOptional.orElseGet(SampleDataUtil::getSampleTaskList);
+ } catch (DataConversionException e) {
+ logger.warning("Data file not in the correct format. Will be starting with an empty TaskList");
+ initialDataTaskList = new TaskList();
+ } catch (IOException e) {
+ logger.warning("Problem while reading from the file. Will be starting with an empty TaskList");
+ initialDataTaskList = new TaskList();
+ }
+
+ return new ModelManager(initialData, userPrefs, initialDataTaskList);
}
private void initLogging(Config config) {
@@ -151,7 +172,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 NUScheduler");
initializedPrefs = new UserPrefs();
}
@@ -167,13 +188,13 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) {
@Override
public void start(Stage primaryStage) {
- logger.info("Starting AddressBook " + MainApp.VERSION);
+ logger.info("Starting NUScheduler " + MainApp.VERSION);
ui.start(primaryStage);
}
@Override
public void stop() {
- logger.info("============================ [ Stopping Address Book ] =============================");
+ logger.info("============================ [ Stopping NUScheduler ] =============================");
try {
storage.saveUserPrefs(model.getUserPrefs());
} catch (IOException e) {
diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/address/commons/core/GuiSettings.java
index ba33653be67..861f3063729 100644
--- a/src/main/java/seedu/address/commons/core/GuiSettings.java
+++ b/src/main/java/seedu/address/commons/core/GuiSettings.java
@@ -11,7 +11,7 @@
public class GuiSettings implements Serializable {
private static final double DEFAULT_HEIGHT = 600;
- private static final double DEFAULT_WIDTH = 740;
+ private static final double DEFAULT_WIDTH = 800;
private final double windowWidth;
private final double windowHeight;
diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java
index 92cd8fa605a..5e1f29acc2b 100644
--- a/src/main/java/seedu/address/logic/Logic.java
+++ b/src/main/java/seedu/address/logic/Logic.java
@@ -9,6 +9,7 @@
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.ReadOnlyAddressBook;
import seedu.address.model.person.Person;
+import seedu.address.model.task.Task;
/**
* API of the Logic component
@@ -33,6 +34,9 @@ public interface Logic {
/** Returns an unmodifiable view of the filtered list of persons */
ObservableList getFilteredPersonList();
+ /** Returns an unmodifiable view of the filtered list of tasks */
+ ObservableList getFilteredTaskList();
+
/**
* Returns the user prefs' address book file path.
*/
diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java
index 9d9c6d15bdc..9dad16689a6 100644
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ b/src/main/java/seedu/address/logic/LogicManager.java
@@ -15,6 +15,7 @@
import seedu.address.model.Model;
import seedu.address.model.ReadOnlyAddressBook;
import seedu.address.model.person.Person;
+import seedu.address.model.task.Task;
import seedu.address.storage.Storage;
/**
@@ -46,6 +47,8 @@ public CommandResult execute(String commandText) throws CommandException, ParseE
commandResult = command.execute(model);
try {
+ //TODO saveTaskList
+ storage.saveTaskList(model.getReadOnlyTaskList());
storage.saveAddressBook(model.getAddressBook());
} catch (IOException ioe) {
throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe);
@@ -64,6 +67,11 @@ public ObservableList getFilteredPersonList() {
return model.getFilteredPersonList();
}
+ @Override
+ public ObservableList getFilteredTaskList() {
+ return model.getFilteredTaskList();
+ }
+
@Override
public Path getAddressBookFilePath() {
return model.getAddressBookFilePath();
diff --git a/src/main/java/seedu/address/logic/commands/AddTaskCommand.java b/src/main/java/seedu/address/logic/commands/AddTaskCommand.java
new file mode 100644
index 00000000000..bb9bf6aa204
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/AddTaskCommand.java
@@ -0,0 +1,83 @@
+package seedu.address.logic.commands;
+
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADD_TASK_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADD_TASK_DESCRIPTION;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.task.Task;
+
+/**
+ * Add a task to the system.
+ */
+public class AddTaskCommand extends Command {
+ public static final String COMMAND_WORD = "addt";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Add a task with description and/or deadline.\n"
+ + "Format: addt d/DESCRIPTION [t/DEADLINE (dd/mm/yyyy)]\n"
+ + "Example: " + COMMAND_WORD + " " + PREFIX_ADD_TASK_DESCRIPTION
+ + "Swimming lesson " + PREFIX_ADD_TASK_DEADLINE + "03/05/2022";
+
+ public static final String MESSAGE_ARGUMENTS = "Task added.\n" + "Description: %1$s\n" + "Deadline: %2$s";
+
+ public static final String MESSAGE_NO_DESCRIPTION = "Task description not found!";
+
+ private final String description;
+ private final String deadline;
+
+ /**
+ * Initializes a AddTaskCommand with the given description. The deadline is not set.
+ */
+ public AddTaskCommand(String description) {
+ requireAllNonNull(description);
+
+ this.description = description;
+ this.deadline = "No deadline set";
+ }
+
+ /**
+ * Initializes a AddTaskCommand with the given description and deadline.
+ */
+ public AddTaskCommand(String description, String deadline) {
+ requireAllNonNull(description, deadline);
+
+ this.description = description;
+ this.deadline = deadline;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireAllNonNull(model);
+
+ if (this.description.equals("")) {
+ // missing description, prompt the user with the format
+ throw new CommandException(MESSAGE_NO_DESCRIPTION + "\n" + MESSAGE_USAGE);
+ }
+
+ model.addTask(new Task(description, deadline));
+
+ // Printing out the current items in task list to make sure the method work, should be removed later on
+ System.out.println(model.getTaskList().getTaskList().toString());
+
+ return new CommandResult(String.format(MESSAGE_ARGUMENTS, description, deadline));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // short circuit if same object
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof AddTaskCommand)) {
+ return false;
+ }
+
+ // state check
+ AddTaskCommand e = (AddTaskCommand) other;
+ return description.equals(e.description);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteTaskCommand.java b/src/main/java/seedu/address/logic/commands/DeleteTaskCommand.java
new file mode 100644
index 00000000000..5c4be7fe5f6
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/DeleteTaskCommand.java
@@ -0,0 +1,72 @@
+package seedu.address.logic.commands;
+
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+
+/**
+ * Delete a task from the system
+ */
+public class DeleteTaskCommand extends Command {
+ public static final String COMMAND_WORD = "delt";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Delete a task number from current tasklist using given index. \n"
+ + "Format: delt . \n"
+ + "Example: " + COMMAND_WORD + " " + "1";
+
+ public static final String MESSAGE_ARGUMENTS = "Task %1$s deleted";
+
+ public static final String MESSAGE_NO_INDEX = "Task number not found!";
+
+ public static final String MESSAGE_INDEX_OUT_OF_BOUNDS = "Task number does not exist in current task list!";
+
+ private final Integer taskNumber;
+
+ /**
+ * Initializes a DeleteTaskCommand with the given taskNumber
+ * @param taskNumber index to be deleted
+ */
+ public DeleteTaskCommand(Integer taskNumber) {
+ requireAllNonNull(taskNumber);
+ this.taskNumber = taskNumber;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireAllNonNull(model);
+
+ if (this.taskNumber <= 0 || this.taskNumber > model.getTaskList().size()) {
+ throw new CommandException(MESSAGE_INDEX_OUT_OF_BOUNDS + "\n" + MESSAGE_USAGE);
+ }
+
+ model.deleteTask(taskNumber);
+
+ // Printing out current items in list to ensure item is deleted
+ System.out.println(model.getTaskList().size());
+
+ return new CommandResult(String.format(MESSAGE_ARGUMENTS, taskNumber));
+
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // short circuit if same object
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof DeleteTaskCommand)) {
+ return false;
+ }
+
+ //state check
+ DeleteTaskCommand e = (DeleteTaskCommand) other;
+ return taskNumber.equals(e.taskNumber);
+ }
+
+
+
+}
diff --git a/src/main/java/seedu/address/logic/commands/FindTaskCommand.java b/src/main/java/seedu/address/logic/commands/FindTaskCommand.java
new file mode 100644
index 00000000000..5f3092471c0
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/FindTaskCommand.java
@@ -0,0 +1,73 @@
+package seedu.address.logic.commands;
+
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+
+/**
+ * finds a task to the system based on user input.
+ */
+public class FindTaskCommand extends Command {
+
+ public static final String COMMAND_WORD = "findt";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": finds a task based on keywords. \n"
+ + "Format: findt [INPUT] \n"
+ + "Example: " + COMMAND_WORD + " " + "Swimming lessons";
+
+ public static final String MESSAGE_ARGUMENTS = "Here are the matching tasks in the list:\n" + "%1$s";
+
+ public static final String MESSAGE_NO_DESCRIPTION = "Unable to find any matching tasks due to empty keyword!";
+
+ public static final String MESSAGE_NO_MATCHING_TASK = "Unable to find any matching tasks based on input!";
+
+ private final String input;
+
+ /**
+ * Initializes a FindTaskCommand with the given input.
+ */
+ public FindTaskCommand(String input) {
+ requireNonNull(input);
+ this.input = input;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ if (this.input.equals("")) {
+ throw new CommandException(MESSAGE_NO_DESCRIPTION + "\n" + MESSAGE_USAGE);
+ }
+ try {
+ String orderedList = model.findTask(input);
+
+ if (orderedList.isEmpty()) {
+ throw new CommandException(MESSAGE_NO_MATCHING_TASK);
+ }
+ return new CommandResult(String.format(MESSAGE_ARGUMENTS, orderedList));
+ } catch (CommandException e) {
+ throw new CommandException(MESSAGE_NO_MATCHING_TASK);
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof FindTaskCommand)) {
+ return false;
+ }
+
+ //state check
+ FindTaskCommand e = (FindTaskCommand) other;
+ return input.equals(e.input);
+ }
+
+
+}
+
diff --git a/src/main/java/seedu/address/logic/commands/UpdateTaskCommand.java b/src/main/java/seedu/address/logic/commands/UpdateTaskCommand.java
new file mode 100644
index 00000000000..6f041129209
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/UpdateTaskCommand.java
@@ -0,0 +1,164 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADD_TASK_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADD_TASK_DESCRIPTION;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.TaskList;
+import seedu.address.model.task.Task;
+
+
+public class UpdateTaskCommand extends Command {
+ public static final String COMMAND_WORD = "updt";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Updates the details of the chosen task"
+ + " by the task ID used in the displayed task list. "
+ + "Existing values will be overwritten by the input values.\n"
+ + "At least one field must be updated "
+ + "Parameters: Task ID (must be a positive integer) "
+ + "[" + PREFIX_ADD_TASK_DESCRIPTION + "DESCRIPTION] "
+ + "[" + PREFIX_ADD_TASK_DEADLINE + "DEADLINE]...\n"
+ + "Example: " + COMMAND_WORD + " 1 "
+ + PREFIX_ADD_TASK_DESCRIPTION + "Running Lesson "
+ + PREFIX_ADD_TASK_DEADLINE + "04/05/2022";
+
+ public static final String MESSAGE_EMPTY_PARAMETERS = "Parameter given cannot be blank";
+ public static final String MESSAGE_UPDATE_TASK_SUCCESS = "Updated Task: %1$s";
+ public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided.";
+ public static final String MESSAGE_INVALID_TASK_DISPLAYED_ID = "The task id provided is invalid";
+
+ private final Integer taskId;
+ private final UpdateTaskDescriptor updateTaskDescriptor;
+
+ /**
+ * @param taskId of the person in the filtered person list to edit
+ * @param updateTaskDescriptor details to edit the person with
+ */
+ public UpdateTaskCommand(Integer taskId, UpdateTaskDescriptor updateTaskDescriptor) {
+ requireNonNull(taskId);
+ requireNonNull(updateTaskDescriptor);
+
+ this.taskId = taskId;
+ this.updateTaskDescriptor = new UpdateTaskDescriptor(updateTaskDescriptor);
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ TaskList lastShownList = model.getTaskList();
+
+ if (taskId > lastShownList.size()) {
+ throw new CommandException(MESSAGE_INVALID_TASK_DISPLAYED_ID);
+ }
+
+ Task taskToUpdate = lastShownList.getTask(taskId);
+ Task updatedTask = createUpdatedTask(taskToUpdate, updateTaskDescriptor);
+
+ model.updateTask(updatedTask, taskId);
+
+ return new CommandResult(String.format(MESSAGE_UPDATE_TASK_SUCCESS, updatedTask));
+ }
+
+ /**
+ * Creates and returns a {@code Task} with the details of {@code taskToEdit}
+ * edited with {@code updateTaskDescriptor}.
+ */
+ public static Task createUpdatedTask(Task taskToUpdate, UpdateTaskDescriptor updateTaskDescriptor) {
+ assert taskToUpdate != null;
+
+ String updatedDescription = "";
+ String updatedDeadline = "";
+
+ if (updateTaskDescriptor.getDescription() == null) {
+ updatedDescription = taskToUpdate.getDescription();
+ } else {
+ updatedDescription = updateTaskDescriptor.getDescription();
+ }
+
+ if (updateTaskDescriptor.getDeadline() == null) {
+ updatedDeadline = taskToUpdate.getDeadline();
+ } else {
+ updatedDeadline = updateTaskDescriptor.getDeadline();
+ }
+
+ return new Task(updatedDescription, updatedDeadline);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // short circuit if same object
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof UpdateTaskCommand)) {
+ return false;
+ }
+
+ // state check
+ UpdateTaskCommand e = (UpdateTaskCommand) other;
+ return taskId.equals(e.taskId)
+ && updateTaskDescriptor.equals(e.updateTaskDescriptor);
+ }
+
+ /**
+ * 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 UpdateTaskDescriptor {
+ private String description;
+ private String deadline;
+
+ public UpdateTaskDescriptor() {
+
+ }
+
+ /**
+ * Constructor for updateTaskDescriptor object
+ *
+ */
+ public UpdateTaskDescriptor(UpdateTaskDescriptor updateTaskDescriptor) {
+ setDeadline(updateTaskDescriptor.deadline);
+ setDescription(updateTaskDescriptor.description);
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getDescription() {
+ return this.description;
+ }
+
+ public void setDeadline(String deadline) {
+ this.deadline = deadline;
+ }
+
+ public String getDeadline() {
+ return this.deadline;
+ }
+
+
+ @Override
+ public boolean equals(Object other) {
+ // short circuit if same object
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof UpdateTaskDescriptor)) {
+ return false;
+ }
+
+ // state check
+ UpdateTaskDescriptor e = (UpdateTaskDescriptor) other;
+
+ return getDescription().equals(e.getDescription())
+ && getDeadline().equals(e.getDeadline());
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ViewTaskCommand.java b/src/main/java/seedu/address/logic/commands/ViewTaskCommand.java
new file mode 100644
index 00000000000..ea1edef845a
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ViewTaskCommand.java
@@ -0,0 +1,30 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+
+/**
+ * Lists all persons in the address book to the user.
+ */
+public class ViewTaskCommand extends Command {
+
+ public static final String COMMAND_WORD = "viewt";
+
+ public static final String MESSAGE_SUCCESS = "Listed all tasks";
+
+ public static final String MESSAGE_NO_TASK = "No tasks found!";
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ if (model.getTaskList().size() == 0) {
+ // No tasks
+ throw new CommandException(MESSAGE_NO_TASK);
+ }
+
+ String message = model.viewTask();
+ return new CommandResult(MESSAGE_SUCCESS + "\n" + message);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddTaskCommandParser.java b/src/main/java/seedu/address/logic/parser/AddTaskCommandParser.java
new file mode 100644
index 00000000000..122a1b27eab
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/AddTaskCommandParser.java
@@ -0,0 +1,172 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADD_TASK_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADD_TASK_DESCRIPTION;
+
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.List;
+
+import seedu.address.logic.commands.AddTaskCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new {@code AddTaskCommand} object
+ */
+public class AddTaskCommandParser implements Parser {
+ /**
+ * Parses the given {@code String} of arguments in the context of the {@code AddTaskCommand}
+ * and returns a {@code AddTaskCommand} object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public AddTaskCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_ADD_TASK_DESCRIPTION,
+ PREFIX_ADD_TASK_DEADLINE);
+
+ // Throw exception if more than 1 description or deadline prefix exists
+ checkMultiplePrefixTokens(argMultimap);
+
+ // Throw exception if user input does not contain any description prefix
+ checkDescriptionPrefixEmpty(argMultimap);
+
+ // Throw exception if deadline does not adhere to specified format
+ checkDeadlineFormat(argMultimap);
+
+ // Throw exception if deadline is invalid
+ checkDeadlineValidity(argMultimap);
+
+ // Throw exception if deadline is before today's date
+ checkDeadlineIsBeforeToday(argMultimap);
+
+ // Throw exception if description contains deadline prefix
+ checkDeadlinePrefixInDescription(argMultimap);
+
+ String description = argMultimap.getValue(PREFIX_ADD_TASK_DESCRIPTION).orElse("");
+ String deadline = argMultimap.getValue(PREFIX_ADD_TASK_DEADLINE).orElse("");
+
+ if (deadline.equals("")) {
+ // task without deadline set
+ return new AddTaskCommand(description);
+ } else {
+ // task with deadline set
+ return new AddTaskCommand(description, deadline);
+ }
+ }
+
+ /**
+ * Check if more than 1 description or deadline prefix token exists in user input.
+ *
+ * @param argMultimap Tokenized user input
+ * @throws ParseException Throw exception if more than 1 of the same prefix exists
+ */
+ private void checkMultiplePrefixTokens(ArgumentMultimap argMultimap) throws ParseException {
+ List description = argMultimap.getAllValues(PREFIX_ADD_TASK_DESCRIPTION);
+ List deadline = argMultimap.getAllValues(PREFIX_ADD_TASK_DEADLINE);
+
+ // Throw exception if more than 1 description or deadline token is used
+ if (description.size() > 1 || deadline.size() > 1) {
+ // more than 1 "d/" or "t/" prefix were used, meaning that it is wrong format
+ throw new ParseException("Duplicated prefix detected in input!\n" + AddTaskCommand.MESSAGE_USAGE);
+ }
+ }
+
+ /**
+ * Check if description prefix token exists in user input.
+ *
+ * @param argMultimap Tokenized user input
+ * @throws ParseException Throw exception if description prefix token does not exist
+ */
+ private void checkDescriptionPrefixEmpty(ArgumentMultimap argMultimap) throws ParseException {
+ String description = argMultimap.getValue(PREFIX_ADD_TASK_DESCRIPTION).orElse("");
+ if (description.equals("")) {
+ throw new ParseException("Description is compulsory!\n" + AddTaskCommand.MESSAGE_USAGE);
+ }
+ }
+
+ /**
+ * Check if user's deadline input follows dd/MM/yyyy format.
+ *
+ * @param argMultimap Tokenized user input
+ * @throws ParseException Throw exception if deadline does not follow the specified format
+ */
+ private void checkDeadlineFormat(ArgumentMultimap argMultimap) throws ParseException {
+ String deadline = argMultimap.getValue(PREFIX_ADD_TASK_DEADLINE).orElse("");
+ DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
+
+ // If deadline is empty, return immediately without checking the format
+ if (deadline.equals("")) {
+ return;
+ }
+
+ try {
+ dateTimeFormatter.parse(deadline);
+ } catch (DateTimeParseException e) {
+ throw new ParseException("Deadline is not in dd/mm/yyyy!\n" + AddTaskCommand.MESSAGE_USAGE);
+ }
+ }
+
+ /**
+ * Check if user's deadline is a valid date.
+ *
+ * @param argMultimap Tokenized user input
+ * @throws ParseException Throw exception if deadline is not a valid date
+ */
+ private void checkDeadlineValidity(ArgumentMultimap argMultimap) throws ParseException {
+ String deadline = argMultimap.getValue(PREFIX_ADD_TASK_DEADLINE).orElse("");
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy");
+
+ if (deadline.equals("")) {
+ return;
+ }
+
+ try {
+ // Check if user's deadline input is a valid date
+ simpleDateFormat.setLenient(false);
+ simpleDateFormat.parse(deadline);
+ } catch (java.text.ParseException e) {
+ throw new ParseException("Invalid date input!\n" + AddTaskCommand.MESSAGE_USAGE);
+ }
+ }
+
+ /**
+ * Check if user's deadline is before today's date.
+ *
+ * @param argMultimap Tokenized user input
+ * @throws ParseException Throw exception if deadline is before today's date
+ */
+ private void checkDeadlineIsBeforeToday(ArgumentMultimap argMultimap) throws ParseException {
+ String deadline = argMultimap.getValue(PREFIX_ADD_TASK_DEADLINE).orElse("");
+
+ if (deadline.equals("")) {
+ return;
+ }
+
+ DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
+ LocalDate today = LocalDate.now(ZoneId.systemDefault());
+ LocalDate date = LocalDate.parse(deadline, dateTimeFormatter);
+ if (date.isBefore(today)) {
+ throw new ParseException("Deadline is before today's date!\n" + AddTaskCommand.MESSAGE_USAGE);
+ }
+ }
+
+ /**
+ * Check if user's description contains deadline prefix.
+ *
+ * @param argMultimap Tokenized user input
+ * @throws ParseException Throw exception if description contains deadline prefix
+ */
+ private void checkDeadlinePrefixInDescription(ArgumentMultimap argMultimap) throws ParseException {
+ String description = argMultimap.getValue(PREFIX_ADD_TASK_DESCRIPTION).orElse("");
+
+ if (description.contains("t/") || description.contains("d/")) {
+ // if deadline or description token is used in the description
+ throw new ParseException("You cannot have 't/' or 'd/' prefix in the description!\n"
+ + AddTaskCommand.MESSAGE_USAGE);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
index 1e466792b46..e77889a71a5 100644
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
@@ -7,14 +7,19 @@
import java.util.regex.Pattern;
import seedu.address.logic.commands.AddCommand;
+import seedu.address.logic.commands.AddTaskCommand;
import seedu.address.logic.commands.ClearCommand;
import seedu.address.logic.commands.Command;
import seedu.address.logic.commands.DeleteCommand;
+import seedu.address.logic.commands.DeleteTaskCommand;
import seedu.address.logic.commands.EditCommand;
import seedu.address.logic.commands.ExitCommand;
import seedu.address.logic.commands.FindCommand;
+import seedu.address.logic.commands.FindTaskCommand;
import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.UpdateTaskCommand;
+import seedu.address.logic.commands.ViewTaskCommand;
import seedu.address.logic.parser.exceptions.ParseException;
/**
@@ -68,6 +73,21 @@ public Command parseCommand(String userInput) throws ParseException {
case HelpCommand.COMMAND_WORD:
return new HelpCommand();
+ case AddTaskCommand.COMMAND_WORD:
+ return new AddTaskCommandParser().parse(arguments);
+
+ case DeleteTaskCommand.COMMAND_WORD:
+ return new DeleteTaskCommandParser().parse(arguments);
+
+ case FindTaskCommand.COMMAND_WORD:
+ return new FindTaskCommandParser().parse(arguments);
+
+ case ViewTaskCommand.COMMAND_WORD:
+ return new ViewTaskCommand();
+
+ case UpdateTaskCommand.COMMAND_WORD:
+ return new UpdateTaskCommandParser().parse(arguments);
+
default:
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
}
diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java
index 75b1a9bf119..63981444232 100644
--- a/src/main/java/seedu/address/logic/parser/CliSyntax.java
+++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java
@@ -11,5 +11,7 @@ public class CliSyntax {
public static final Prefix PREFIX_EMAIL = new Prefix("e/");
public static final Prefix PREFIX_ADDRESS = new Prefix("a/");
public static final Prefix PREFIX_TAG = new Prefix("t/");
+ public static final Prefix PREFIX_ADD_TASK_DESCRIPTION = new Prefix("d/");
+ public static final Prefix PREFIX_ADD_TASK_DEADLINE = new Prefix("t/");
}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteTaskCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteTaskCommandParser.java
new file mode 100644
index 00000000000..3f9713109d9
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/DeleteTaskCommandParser.java
@@ -0,0 +1,30 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.logic.commands.DeleteTaskCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new DeleteTaskCommand object
+ */
+public class DeleteTaskCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the DeleteTaskCommand
+ * and returns a DeleteTaskCommand object for execution.
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+ public DeleteTaskCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ try {
+ Integer taskNumber = ParserUtil.parseNumber(args);
+ return new DeleteTaskCommand(taskNumber);
+ } catch (ParseException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTaskCommand.MESSAGE_USAGE), pe);
+ }
+
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/FindTaskCommandParser.java b/src/main/java/seedu/address/logic/parser/FindTaskCommandParser.java
new file mode 100644
index 00000000000..1b20cdd9ecb
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/FindTaskCommandParser.java
@@ -0,0 +1,26 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.logic.commands.FindTaskCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+public class FindTaskCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of argument in the context of the FindTaskCommand
+ * and returns a FindTaskCommand object for execution.
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+ public FindTaskCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ String trimmedArgs = args.trim();
+ if (trimmedArgs.isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindTaskCommand.MESSAGE_USAGE));
+ }
+ return new FindTaskCommand(args);
+ }
+}
+
diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java
index b117acb9c55..98019ec1498 100644
--- a/src/main/java/seedu/address/logic/parser/ParserUtil.java
+++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java
@@ -1,6 +1,7 @@
package seedu.address.logic.parser;
import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import java.util.Collection;
import java.util.HashSet;
@@ -8,6 +9,7 @@
import seedu.address.commons.core.index.Index;
import seedu.address.commons.util.StringUtil;
+import seedu.address.logic.commands.UpdateTaskCommand;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.person.Address;
import seedu.address.model.person.Email;
@@ -20,7 +22,9 @@
*/
public class ParserUtil {
- public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer.";
+ public static final String MESSAGE_INVALID_INDEX = "Index is not valid.";
+
+ public static final String MESSAGE_INVALID_FIRSTCHAR = "Index starts with empty whitespace";
/**
* Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be
@@ -121,4 +125,18 @@ public static Set parseTags(Collection tags) throws ParseException
}
return tagSet;
}
+
+ /**
+ * Parses {@code oneBasedIndex} into an Integer 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 Integer parseNumber(String oneBasedIndex) throws ParseException {
+ String trimmedIndex = oneBasedIndex.trim();
+ if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UpdateTaskCommand.MESSAGE_USAGE));
+ }
+
+ return Integer.parseInt(trimmedIndex);
+ }
}
diff --git a/src/main/java/seedu/address/logic/parser/UpdateTaskCommandParser.java b/src/main/java/seedu/address/logic/parser/UpdateTaskCommandParser.java
new file mode 100644
index 00000000000..426fbe98dbb
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/UpdateTaskCommandParser.java
@@ -0,0 +1,212 @@
+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_ADD_TASK_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADD_TASK_DESCRIPTION;
+
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.List;
+
+import seedu.address.logic.commands.UpdateTaskCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+public class UpdateTaskCommandParser implements Parser {
+ /**
+ * Parses the given {@code String} of arguments in the context of the UpdateTaskCommand
+ * and returns an UpdateTask object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public UpdateTaskCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_ADD_TASK_DESCRIPTION, PREFIX_ADD_TASK_DEADLINE);
+
+ Integer taskId;
+
+ taskId = ParserUtil.parseNumber(argMultimap.getPreamble());
+
+ // Throw exception if more than 1 description or deadline prefix exists
+ checkMultiplePrefixTokens(argMultimap);
+
+ // Throw exception if no prefix exist
+ checkMinimumOnePrefix(argMultimap);
+
+ // Throw exception if deadline does not adhere to specified format
+ checkDeadlineFormat(argMultimap);
+
+ // Throw exception if empty prefix tokens are found
+ checkEmptyPrefix(argMultimap);
+
+ // Throw exception if deadline is invalid
+ checkDeadlineValidity(argMultimap);
+
+ // Throw exception if deadline is before today's date
+ checkDeadlineIsBeforeToday(argMultimap);
+
+ // Throw exception if description contains deadline prefix
+ checkDeadlinePrefixInDescription(argMultimap);
+
+ UpdateTaskCommand.UpdateTaskDescriptor updateTaskDescriptor = new UpdateTaskCommand.UpdateTaskDescriptor();
+
+ if (argMultimap.getValue(PREFIX_ADD_TASK_DESCRIPTION).isPresent()) {
+ updateTaskDescriptor.setDescription(argMultimap.getValue(PREFIX_ADD_TASK_DESCRIPTION).get());
+ }
+ if (argMultimap.getValue(PREFIX_ADD_TASK_DEADLINE).isPresent()) {
+ updateTaskDescriptor.setDeadline(argMultimap.getValue(PREFIX_ADD_TASK_DEADLINE).get());
+ }
+
+ return new UpdateTaskCommand(taskId, updateTaskDescriptor);
+ }
+
+ /**
+ * Check if more than 1 description or deadline prefix token exists in user input.
+ * Adapted from AddTaskCommandParser
+ *
+ * @param argMultimap Tokenized user input
+ * @throws ParseException Throw exception if more than 1 of the same prefix exists
+ */
+ private void checkMultiplePrefixTokens(ArgumentMultimap argMultimap) throws ParseException {
+ List description = argMultimap.getAllValues(PREFIX_ADD_TASK_DESCRIPTION);
+ List deadline = argMultimap.getAllValues(PREFIX_ADD_TASK_DEADLINE);
+
+ // Throw exception if more than 1 description or deadline token is used
+ if (description.size() > 1 || deadline.size() > 1) {
+ // more than 1 "d/" or "t/" prefix were used, meaning that it is wrong format
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UpdateTaskCommand.MESSAGE_USAGE));
+ }
+ }
+
+ /**
+ * Check if description prefix token exists in user input.
+ * Adapted from AddTaskCommandParser
+ *
+ * @param argMultimap Tokenized user input
+ * @throws ParseException Throw exception if prefix token does not exist
+ */
+ private void checkMinimumOnePrefix(ArgumentMultimap argMultimap) throws ParseException {
+ List description = argMultimap.getAllValues(PREFIX_ADD_TASK_DESCRIPTION);
+ List deadline = argMultimap.getAllValues(PREFIX_ADD_TASK_DEADLINE);
+
+ if (description.size() + deadline.size() < 1) {
+ // no prefix
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ UpdateTaskCommand.MESSAGE_NOT_EDITED));
+ }
+ }
+
+ /**
+ * Check if prefix token values are empty.
+ * Adapted from AddTaskCommandParser
+ *
+ * @param argMultimap Tokenized user input
+ * @throws ParseException Throw exception if either token prefix value is empty
+ */
+ private void checkEmptyPrefix(ArgumentMultimap argMultimap) throws ParseException {
+ if (argMultimap.getValue(PREFIX_ADD_TASK_DESCRIPTION).isPresent()) {
+ String description = argMultimap.getValue(PREFIX_ADD_TASK_DESCRIPTION).orElse("");
+ if (description.equals("")) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ UpdateTaskCommand.MESSAGE_EMPTY_PARAMETERS));
+ }
+ }
+ if (argMultimap.getValue(PREFIX_ADD_TASK_DEADLINE).isPresent()) {
+ String deadline = argMultimap.getValue(PREFIX_ADD_TASK_DEADLINE).orElse("");
+ if (deadline.equals("")) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ UpdateTaskCommand.MESSAGE_EMPTY_PARAMETERS));
+ }
+ }
+ }
+
+ /**
+ * Check if user's deadline input follows dd/MM/yyyy format.
+ * Adapted from AddTaskCommandParser
+ *
+ * @param argMultimap Tokenized user input
+ * @throws ParseException Throw exception if deadline does not follow the specified format
+ */
+ private void checkDeadlineFormat(ArgumentMultimap argMultimap) throws ParseException {
+ String deadline = argMultimap.getValue(PREFIX_ADD_TASK_DEADLINE).orElse("");
+ DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
+
+ // If deadline is empty, return immediately without checking the format
+ if (deadline.equals("")) {
+ return;
+ }
+
+ try {
+ dateTimeFormatter.parse(deadline);
+ } catch (DateTimeParseException e) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UpdateTaskCommand.MESSAGE_USAGE));
+ }
+ }
+
+ /**
+ * Check if user's deadline is a valid date.
+ * Adapated from AddTaskCommandParser
+ *
+ * @param argMultimap Tokenized user input
+ * @throws ParseException Throw exception if deadline is not a valid date
+ */
+ private void checkDeadlineValidity(ArgumentMultimap argMultimap) throws ParseException {
+ String deadline = argMultimap.getValue(PREFIX_ADD_TASK_DEADLINE).orElse("");
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy");
+
+ if (deadline.equals("")) {
+ return;
+ }
+
+ try {
+ // Check if user's deadline input is a valid date
+ simpleDateFormat.setLenient(false);
+ simpleDateFormat.parse(deadline);
+ } catch (java.text.ParseException e) {
+ throw new ParseException("Invalid date input!\n" + UpdateTaskCommand.MESSAGE_USAGE);
+ }
+ }
+
+ /**
+ * Check if user's deadline is before today's date.
+ * Adapted from AddTaskCommandParser
+ *
+ * @param argMultimap Tokenized user input
+ * @throws ParseException Throw exception if deadline is before today's date
+ */
+ private void checkDeadlineIsBeforeToday(ArgumentMultimap argMultimap) throws ParseException {
+ String deadline = argMultimap.getValue(PREFIX_ADD_TASK_DEADLINE).orElse("");
+
+ if (deadline.equals("")) {
+ return;
+ }
+
+ DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
+ LocalDate today = LocalDate.now(ZoneId.systemDefault());
+ LocalDate date = LocalDate.parse(deadline, dateTimeFormatter);
+ if (date.isBefore(today)) {
+ throw new ParseException("Deadline is before today's date!\n" + UpdateTaskCommand.MESSAGE_USAGE);
+ }
+ }
+
+ /**
+ * Check if user's description contains deadline prefix.
+ * Adapted from AddTaskCommandParser
+ *
+ * @param argMultimap Tokenized user input
+ * @throws ParseException Throw exception if description contains deadline prefix
+ */
+ private void checkDeadlinePrefixInDescription(ArgumentMultimap argMultimap) throws ParseException {
+ String description = argMultimap.getValue(PREFIX_ADD_TASK_DESCRIPTION).orElse("");
+
+ if (description.contains("t/")) {
+ // if deadline token is used in the description
+ throw new ParseException("You cannot have 't/' prefix in the description!\n"
+ + UpdateTaskCommand.MESSAGE_USAGE);
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java
index 1a943a0781a..cfd161c78fd 100644
--- a/src/main/java/seedu/address/model/AddressBook.java
+++ b/src/main/java/seedu/address/model/AddressBook.java
@@ -7,6 +7,7 @@
import javafx.collections.ObservableList;
import seedu.address.model.person.Person;
import seedu.address.model.person.UniquePersonList;
+import seedu.address.model.task.Task;
/**
* Wraps all data at the address-book level
@@ -93,6 +94,17 @@ public void removePerson(Person key) {
persons.remove(key);
}
+ /**
+ * Check if the task is a valid task that conforms to description and deadline format.
+ *
+ * @param task a Task object.
+ * @return true if it is valid, false otherwise.
+ */
+ public boolean validTask(Task task) {
+ requireNonNull(task);
+ return task.isValidTask(task);
+ }
+
//// util methods
@Override
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
index d54df471c1f..416f5a4eb55 100644
--- a/src/main/java/seedu/address/model/Model.java
+++ b/src/main/java/seedu/address/model/Model.java
@@ -5,7 +5,9 @@
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
+import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.person.Person;
+import seedu.address.model.task.Task;
/**
* The API of the Model component.
@@ -84,4 +86,33 @@ public interface Model {
* @throws NullPointerException if {@code predicate} is null.
*/
void updateFilteredPersonList(Predicate predicate);
+
+ /** Returns an unmodifiable view of the filtered tasks list */
+ ObservableList getFilteredTaskList();
+
+ void addTask(Task task);
+
+ void addTask(Task task, Integer taskId) throws CommandException;
+
+ TaskList getTaskList();
+
+ void deleteTask(Integer taskNumber);
+
+ /**
+ * finds tasks based on keyword.
+ * filters task list based on matching keyword {@code input}.
+ */
+ String findTask(String input);
+
+ String viewTask();
+
+
+ Path getTaskListFilePath();
+
+ void setTaskListFilePath(Path taskListFilePath);
+
+ ReadOnlyTaskList getReadOnlyTaskList();
+
+ void updateTask(Task updatedTask, Integer taskId) throws CommandException;
+
}
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
index 86c1df298d7..1699a9dcd5d 100644
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ b/src/main/java/seedu/address/model/ModelManager.java
@@ -11,7 +11,9 @@
import javafx.collections.transformation.FilteredList;
import seedu.address.commons.core.GuiSettings;
import seedu.address.commons.core.LogsCenter;
+import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.person.Person;
+import seedu.address.model.task.Task;
/**
* Represents the in-memory model of the address book data.
@@ -22,11 +24,13 @@ public class ModelManager implements Model {
private final AddressBook addressBook;
private final UserPrefs userPrefs;
private final FilteredList filteredPersons;
+ private final FilteredList filteredTasks;
+ private final TaskList taskList;
/**
* Initializes a ModelManager with the given addressBook and userPrefs.
*/
- public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) {
+ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs, ReadOnlyTaskList taskList) {
requireAllNonNull(addressBook, userPrefs);
logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs);
@@ -34,10 +38,12 @@ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs
this.addressBook = new AddressBook(addressBook);
this.userPrefs = new UserPrefs(userPrefs);
filteredPersons = new FilteredList<>(this.addressBook.getPersonList());
+ this.taskList = new TaskList(taskList);
+ filteredTasks = new FilteredList<>(this.taskList.getObservableTaskList());
}
public ModelManager() {
- this(new AddressBook(), new UserPrefs());
+ this(new AddressBook(), new UserPrefs(), new TaskList());
}
//=========== UserPrefs ==================================================================================
@@ -75,6 +81,17 @@ public void setAddressBookFilePath(Path addressBookFilePath) {
userPrefs.setAddressBookFilePath(addressBookFilePath);
}
+ @Override
+ public Path getTaskListFilePath() {
+ return userPrefs.getTaskListFilePath();
+ }
+
+ @Override
+ public void setTaskListFilePath(Path taskListFilePath) {
+ requireNonNull(taskListFilePath);
+ userPrefs.setTaskListFilePath(taskListFilePath);
+ }
+
//=========== AddressBook ================================================================================
@Override
@@ -144,7 +161,66 @@ public boolean equals(Object obj) {
ModelManager other = (ModelManager) obj;
return addressBook.equals(other.addressBook)
&& userPrefs.equals(other.userPrefs)
- && filteredPersons.equals(other.filteredPersons);
+ && filteredPersons.equals(other.filteredPersons)
+ && filteredTasks.equals(other.filteredTasks);
+ }
+ //=========== Filtered Task List Accessors =============================================================
+ /**
+ * Returns an unmodifiable view of the list of {@code Task} backed by the internal list of
+ * {@code versionedAddressBook}
+ * @return filteredTasks
+ */
+ @Override
+ public ObservableList getFilteredTaskList() {
+ return filteredTasks;
+ }
+
+ //=========== TaskList ==================================================================================
+
+ @Override
+ public void addTask(Task task) {
+ taskList.addTask(task);
+ filteredTasks.setPredicate(unused -> true);
+ }
+
+ @Override
+ public void addTask(Task task, Integer taskId) throws CommandException {
+ taskList.addTask(task, taskId);
}
+ @Override
+ public TaskList getTaskList() {
+ return this.taskList;
+ }
+
+ public ReadOnlyTaskList getReadOnlyTaskList() {
+ return this.taskList;
+ }
+
+ //=========== Delete Task ==================================================================================
+
+ @Override
+ public void deleteTask(Integer taskNumber) {
+ taskList.deleteTask(taskNumber);
+ }
+
+ //=========== Find Task ==================================================================================
+
+ @Override
+ public String findTask(String input) {
+ return taskList.findTask(input);
+ }
+
+ //=========== View Task ===============
+ @Override
+ public String viewTask() {
+ return taskList.viewTask();
+ }
+
+ //=========== Update Task ===============
+ @Override
+ public void updateTask(Task updatedTask, Integer taskId) throws CommandException {
+ taskList.deleteTask(taskId);
+ taskList.addTask(updatedTask, taskId);
+ }
}
diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
index 6ddc2cd9a29..f0673ed3838 100644
--- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
+++ b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
@@ -13,5 +13,5 @@ public interface ReadOnlyAddressBook {
* This list will not contain any duplicate persons.
*/
ObservableList getPersonList();
-
}
+
diff --git a/src/main/java/seedu/address/model/ReadOnlyTaskList.java b/src/main/java/seedu/address/model/ReadOnlyTaskList.java
new file mode 100644
index 00000000000..ff0df71db4e
--- /dev/null
+++ b/src/main/java/seedu/address/model/ReadOnlyTaskList.java
@@ -0,0 +1,16 @@
+package seedu.address.model;
+
+import javafx.collections.ObservableList;
+import seedu.address.model.task.Task;
+
+/**
+ * Unmodifiable view of a task list
+ */
+public interface ReadOnlyTaskList {
+
+ /**
+ * Returns an unmodifiable view of the task list.
+ */
+ ObservableList getObservableTaskList();
+
+}
diff --git a/src/main/java/seedu/address/model/TaskList.java b/src/main/java/seedu/address/model/TaskList.java
new file mode 100644
index 00000000000..3e9b23541f9
--- /dev/null
+++ b/src/main/java/seedu/address/model/TaskList.java
@@ -0,0 +1,173 @@
+package seedu.address.model;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javafx.collections.ObservableList;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.task.Task;
+import seedu.address.model.task.UniqueTaskList;
+
+public class TaskList implements ReadOnlyTaskList {
+ private final ArrayList taskList;
+ private final UniqueTaskList uniqueTaskList;
+
+ /*
+ * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication
+ * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html
+ *
+ * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication
+ * among constructors.
+ */
+ {
+ taskList = new ArrayList<>();
+ uniqueTaskList = new UniqueTaskList();
+ }
+
+ public TaskList() {}
+
+ /**
+ * Creates a TaskList using the Tasks in the {@code toBeCopied}
+ */
+ public TaskList(ReadOnlyTaskList toBeCopied) {
+ this();
+ resetData(toBeCopied);
+ }
+
+ /**
+ * Resets the existing data of this {@code TaskList} with {@code newData}.
+ */
+ public void resetData(ReadOnlyTaskList newData) {
+ requireNonNull(newData);
+
+ setTaskList(newData.getObservableTaskList());
+ }
+
+ public void setTaskList(List tasks) {
+ requireNonNull(tasks);
+ this.taskList.addAll(tasks);
+ this.uniqueTaskList.addAll(tasks);
+ }
+
+ /**
+ * Add a task to the taskList.
+ *
+ * @param task the Task to be added which must not be null
+ */
+ public void addTask(Task task) {
+ requireNonNull(task);
+ this.taskList.add(task);
+ this.uniqueTaskList.addTask(task);
+ }
+
+ /**
+ * Add a task to the taskList at a specific index.
+ *
+ * @param task the Task to be added which must not be null
+ * @param taskId the location to be added in which must not be null
+ */
+ public void addTask(Task task, Integer taskId) throws CommandException {
+ uniqueTaskList.addTask(task, taskId); //throws an exception if adding duplicate task
+ requireNonNull(task);
+ requireNonNull(taskId);
+ this.taskList.add(taskId - 1, task); // to convert to zero-based indexing
+ }
+
+ /**
+ * Returns the taskList.
+ *
+ * @return the taskList with all the tasks contained
+ */
+ public ArrayList getTaskList() {
+ return this.taskList;
+ }
+
+ public int size() {
+ return this.taskList.size();
+ }
+
+ /**
+ * Deletes a task from the tasklist in the specified index.
+ *
+ * @param taskNumber the index of the task to be deleted
+ */
+ public void deleteTask(Integer taskNumber) {
+ taskList.remove(taskNumber - 1); // to convert to zero-based indexing
+ uniqueTaskList.deleteTask(taskNumber);
+ }
+
+ /**
+ * Returns an ArrayList of matching tasks based on keyword.
+ *
+ * @param input the keyword input by user
+ * @return the list of ordered tasks that contain the keyword
+ */
+ public String findTask(String input) {
+ ArrayList matchingTasks = new ArrayList<>();
+ for (int i = 0; i < this.taskList.size(); i++) {
+ Task curr = this.taskList.get(i);
+ // Solution below adapted from
+ // https://stackoverflow.com/questions/25483114/regex-to-find-whole-word-in-text-but-case-insensitive
+ String pattern = "(?i)(?<=|^|\\.)"
+ + Pattern.quote(input) + "(?=\\s|$|\\.)"; //bypass case sensitivity with regex
+ if (Pattern.compile(pattern).matcher(curr.toString()).find()) {
+ matchingTasks.add(curr);
+ }
+ }
+ String orderedList = getOutput(matchingTasks);
+ return orderedList;
+ }
+
+ /**
+ * Returns a String of all tasks.
+ *
+ * @return the list of ordered tasks
+ */
+ public String viewTask() {
+ String orderedList = getOutput(taskList);
+ return orderedList;
+ }
+
+ /**
+ * Provides an ordered list of tasks that contains keyword.
+ *
+ * @param list - the list of tasks that matches keyword
+ * @return String of ordered list of tasks.
+ */
+ private String getOutput(ArrayList list) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < list.size(); i++) {
+ int order = i + 1;
+ if (i == list.size() - 1) {
+ sb.append(order).append(". ").append(list.get(i).toString());
+ } else {
+ sb.append(order).append(". ").append(list.get(i).toString()).append("\n");
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Provides the task in the corresponding index
+ * @param taskId task id of the task to be retrieved
+ * @return task with the matching ID in task list
+ */
+ public Task getTask(Integer taskId) {
+ return taskList.get(taskId - 1); // to convert to zero-based
+ }
+
+ /**
+ * Retrieves the observable list of tasks
+ * @return observable list of tasks
+ */
+ @Override
+ public ObservableList getObservableTaskList() {
+ return uniqueTaskList.asUnmodifiableObservableList();
+
+ }
+}
+
+
diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/address/model/UserPrefs.java
index 25a5fd6eab9..3336e1d3879 100644
--- a/src/main/java/seedu/address/model/UserPrefs.java
+++ b/src/main/java/seedu/address/model/UserPrefs.java
@@ -15,6 +15,7 @@ public class UserPrefs implements ReadOnlyUserPrefs {
private GuiSettings guiSettings = new GuiSettings();
private Path addressBookFilePath = Paths.get("data" , "addressbook.json");
+ private Path taskListFilePath = Paths.get("data" , "tasklist.json");
/**
* Creates a {@code UserPrefs} with default values.
@@ -56,6 +57,15 @@ public void setAddressBookFilePath(Path addressBookFilePath) {
this.addressBookFilePath = addressBookFilePath;
}
+ public Path getTaskListFilePath() {
+ return taskListFilePath;
+ }
+
+ public void setTaskListFilePath(Path taskListFilePath) {
+ requireNonNull(taskListFilePath);
+ this.taskListFilePath = taskListFilePath;
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
diff --git a/src/main/java/seedu/address/model/task/Task.java b/src/main/java/seedu/address/model/task/Task.java
new file mode 100644
index 00000000000..a9cf4044003
--- /dev/null
+++ b/src/main/java/seedu/address/model/task/Task.java
@@ -0,0 +1,114 @@
+package seedu.address.model.task;
+
+import static java.util.Objects.requireNonNull;
+
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+
+import seedu.address.logic.commands.AddTaskCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+public class Task {
+
+ private final String description;
+ private final String deadline;
+
+ /**
+ * Initializes a Task with a description and deadline.
+ */
+ public Task(String description, String deadline) {
+ requireNonNull(description);
+ requireNonNull(deadline);
+ this.description = description;
+ this.deadline = deadline;
+ }
+
+ /**
+ * Getter to obtain description of task
+ * @return Description of task
+ */
+ public String getDescription() {
+ return this.description;
+ }
+
+ /**
+ * Getter to obtain deadline of task if any
+ * @return Deadline of task
+ */
+ public String getDeadline() {
+ return this.deadline;
+ }
+
+ /**
+ * Returns true if both task have the same description.
+ * Weaker notion of equality between two tasks
+ * @param otherTask Task object to be compared to
+ * @return boolean if the tasks compared have the same description
+ */
+ public boolean isSameTask(Task otherTask) {
+ if (otherTask == this) {
+ return true;
+ }
+ return otherTask != null && otherTask.getDescription().equals(getDescription());
+ }
+
+ /**
+ * Check if the task has valid description or deadline.
+ *
+ * @param task a Task object
+ * @return true if task has valid description or deadline, false otherwise
+ */
+ public boolean isValidTask(Task task) {
+ String deadline = task.getDeadline();
+ String description = task.getDescription();
+ DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy");
+ try {
+ if (description.contains("t/") || description.contains("d/")) {
+ // if deadline token is used in the description
+ throw new ParseException("You cannot have 't/' prefix in the description!");
+ }
+
+ dateTimeFormatter.parse(deadline);
+
+ simpleDateFormat.setLenient(false);
+ simpleDateFormat.parse(deadline);
+
+ LocalDate today = LocalDate.now(ZoneId.systemDefault());
+ LocalDate date = LocalDate.parse(deadline, dateTimeFormatter);
+ if (date.isBefore(today)) {
+ throw new ParseException("Deadline is before today's date!\n" + AddTaskCommand.MESSAGE_USAGE);
+ }
+ } catch (DateTimeParseException | ParseException | java.text.ParseException e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if both task have the same description and deadline.
+ *
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof Task)) {
+ return false;
+ }
+
+ Task otherTask = (Task) other;
+ return otherTask.getDescription().equals(getDescription())
+ && otherTask.getDeadline().equals(getDeadline());
+ }
+
+ @Override
+ public String toString() {
+ return "Task: " + this.description + " " + deadline;
+ }
+}
diff --git a/src/main/java/seedu/address/model/task/UniqueTaskList.java b/src/main/java/seedu/address/model/task/UniqueTaskList.java
new file mode 100644
index 00000000000..ca1fbcf92ad
--- /dev/null
+++ b/src/main/java/seedu/address/model/task/UniqueTaskList.java
@@ -0,0 +1,84 @@
+package seedu.address.model.task;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import seedu.address.logic.commands.exceptions.CommandException;
+
+/**
+ * A list of tasks that enforces uniqueness between its elements and does not allow nulls.
+ * A person is considered unique by comparing using {@code Task#isSameTask(Task)}. As such, adding and updating of
+ * persons uses Task#isSameTask(Task) for equality so as to ensure that the task being added or updated is
+ * unique in terms of identity in the UniquePTaskList. However, the removal of a person uses Task#equals(Object) so
+ * as to ensure that the task with exactly the same fields will be removed.
+ *
+ * Supports a minimal set of list operations.
+ *
+ */
+public class UniqueTaskList {
+
+ private final ObservableList internalList = FXCollections.observableArrayList();
+ private final ObservableList internalUnmodifiableList =
+ FXCollections.unmodifiableObservableList(internalList);
+
+ /**
+ * Returns true if the list contains an equivalent task as the given argument.
+ */
+ public boolean contains(Task toCheck) {
+ requireNonNull(toCheck);
+ return internalList.stream().anyMatch(toCheck::isSameTask);
+ }
+
+ /**
+ * Add a task to the taskList.
+ *
+ * @param task the Task to be added which must not be null
+ */
+ public void addTask(Task task) {
+ requireNonNull(task);
+ this.internalList.add(task);
+ }
+
+ /**
+ * Adds a task to the list at a specific index.
+ * The task must not already exist in the list.
+ *
+ * @param toAdd the Task to be added which must not be null
+ * @param taskId index of the list the task is to be added
+ */
+
+ public void addTask(Task toAdd, Integer taskId) throws CommandException {
+ requireNonNull(toAdd);
+ requireNonNull(taskId);
+ internalList.add(taskId - 1, toAdd); // to convert to zero-based indexing
+ }
+
+ /**
+ * Sets the observable list of tasks from storage.
+ */
+ public void addAll(List tasks) {
+ requireNonNull(tasks);
+ this.internalList.setAll(tasks);
+ }
+
+ /**
+ * Removes the equivalent task from the list.
+ * The task must exist in the list.
+ */
+
+ public void deleteTask(Integer taskNumber) {
+ requireNonNull(taskNumber);
+ internalList.remove(taskNumber - 1);
+ }
+
+ /**
+ * Returns the backing list as an unmodifiable {@code ObservableList}.
+ */
+ public ObservableList asUnmodifiableObservableList() {
+ return internalUnmodifiableList;
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java
index 1806da4facf..fcefcb0ef2a 100644
--- a/src/main/java/seedu/address/model/util/SampleDataUtil.java
+++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java
@@ -6,12 +6,15 @@
import seedu.address.model.AddressBook;
import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyTaskList;
+import seedu.address.model.TaskList;
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.task.Task;
/**
* Contains utility methods for populating {@code AddressBook} with sample data.
@@ -57,4 +60,17 @@ public static Set getTagSet(String... strings) {
.collect(Collectors.toSet());
}
+ public static Task[] getSampleTask() {
+ return new Task[] {
+ new Task("dummy", "01/01/2023")
+ };
+ }
+
+ public static ReadOnlyTaskList getSampleTaskList() {
+ TaskList sampleTaskList = new TaskList();
+ for (Task sampleTask : getSampleTask()) {
+ sampleTaskList.addTask(sampleTask);
+ }
+ return sampleTaskList;
+ }
}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTask.java b/src/main/java/seedu/address/storage/JsonAdaptedTask.java
new file mode 100644
index 00000000000..fe0bbe9d5f6
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedTask.java
@@ -0,0 +1,52 @@
+package seedu.address.storage;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.task.Task;
+
+public class JsonAdaptedTask {
+
+ public static final String MISSING_FIELD_MESSAGE_FORMAT = "Task's %s field is missing!";
+
+ private final String description;
+ private final String deadline;
+
+ /**
+ * Constructs a {@code JsonAdaptedTask} with the given task details.
+ */
+ @JsonCreator
+ public JsonAdaptedTask(@JsonProperty("description") String description, @JsonProperty("deadline") String deadline) {
+ this.description = description;
+ this.deadline = deadline;
+ }
+ /**
+ * Converts a given {@code Task} into this class for Jackson use.
+ */
+ public JsonAdaptedTask(Task source) {
+ description = source.getDescription();
+ deadline = source.getDeadline();
+ }
+
+ /**
+ * 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 {
+ if (description == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "description"));
+ }
+
+ final String modelDescription = description;
+
+ if (deadline == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "deadline"));
+ }
+
+ final String modelDeadline = deadline;
+
+ return new Task(modelDescription, modelDeadline);
+ }
+}
diff --git a/src/main/java/seedu/address/storage/JsonSerializableTaskList.java b/src/main/java/seedu/address/storage/JsonSerializableTaskList.java
new file mode 100644
index 00000000000..4c31b439f70
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonSerializableTaskList.java
@@ -0,0 +1,51 @@
+package seedu.address.storage;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonRootName;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.AddressBook;
+import seedu.address.model.ReadOnlyTaskList;
+import seedu.address.model.TaskList;
+import seedu.address.model.task.Task;
+
+/**
+ * An Immutable TaskList that is serializable to JSON format.
+ */
+@JsonRootName(value = "tasklist")
+public class JsonSerializableTaskList {
+
+ private final List tasks = new ArrayList<>();
+
+ @JsonCreator
+ public JsonSerializableTaskList(@JsonProperty("tasks") List tasks) {
+ this.tasks.addAll(tasks);
+ }
+
+ public JsonSerializableTaskList(ReadOnlyTaskList source) {
+ tasks.addAll(source.getObservableTaskList().stream().map(JsonAdaptedTask::new).collect(Collectors.toList()));
+ }
+
+ /**
+ * Converts this task list into the model's {@code TaskList} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated.
+ */
+ public TaskList toModelType() throws IllegalValueException {
+ AddressBook addressBook = new AddressBook();
+ TaskList taskList = new TaskList();
+ for (JsonAdaptedTask jsonAdaptedTask : tasks) {
+ Task task = jsonAdaptedTask.toModelType();
+ if (!addressBook.validTask(task)) {
+ throw new IllegalValueException("Invalid task!");
+ }
+ taskList.addTask(task);
+ }
+ return taskList;
+ }
+}
diff --git a/src/main/java/seedu/address/storage/JsonTaskListStorage.java b/src/main/java/seedu/address/storage/JsonTaskListStorage.java
new file mode 100644
index 00000000000..b681b536ace
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonTaskListStorage.java
@@ -0,0 +1,76 @@
+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.ReadOnlyTaskList;
+
+public class JsonTaskListStorage implements TaskListStorage {
+
+ private static final Logger logger = LogsCenter.getLogger(JsonTaskListStorage.class);
+
+ private Path filePath;
+
+ public JsonTaskListStorage(Path filePath) {
+ this.filePath = filePath;
+ }
+
+ public Path getTaskListFilePath() {
+ return filePath;
+ }
+
+ @Override
+ public Optional readTaskList() throws DataConversionException, IOException {
+ return readTaskList(filePath);
+ }
+
+ /**
+ * Similar to {@link #readTaskList()}.
+ *
+ * @param filePath location of the data. Cannot be null.
+ * @throws DataConversionException if the file is not in the correct format.
+ */
+ public Optional readTaskList(Path filePath) throws DataConversionException {
+ requireNonNull(filePath);
+
+ Optional jsonTaskList = JsonUtil.readJsonFile(
+ filePath, JsonSerializableTaskList.class);
+ if (!jsonTaskList.isPresent()) {
+ return Optional.empty();
+ }
+
+ try {
+ return Optional.of(jsonTaskList.get().toModelType());
+ } catch (IllegalValueException ive) {
+ logger.info("Illegal values found in " + filePath + ": " + ive.getMessage());
+ throw new DataConversionException(ive);
+ }
+ }
+
+ @Override
+ public void saveTaskList(ReadOnlyTaskList taskList) throws IOException {
+ saveTaskList(taskList, filePath);
+ }
+
+ /**
+ * Similar to {@link #saveTaskList(ReadOnlyTaskList)}.
+ *
+ * @param filePath location of the data. Cannot be null.
+ */
+ public void saveTaskList(ReadOnlyTaskList taskList, Path filePath) throws IOException {
+ requireNonNull(taskList);
+ requireNonNull(filePath);
+
+ FileUtil.createIfMissing(filePath);
+ JsonUtil.saveJsonFile(new JsonSerializableTaskList(taskList), filePath);
+ }
+}
diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/seedu/address/storage/Storage.java
index beda8bd9f11..bb2fc37cb2a 100644
--- a/src/main/java/seedu/address/storage/Storage.java
+++ b/src/main/java/seedu/address/storage/Storage.java
@@ -6,13 +6,14 @@
import seedu.address.commons.exceptions.DataConversionException;
import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyTaskList;
import seedu.address.model.ReadOnlyUserPrefs;
import seedu.address.model.UserPrefs;
/**
* API of the Storage component
*/
-public interface Storage extends AddressBookStorage, UserPrefsStorage {
+public interface Storage extends AddressBookStorage, UserPrefsStorage, TaskListStorage {
@Override
Optional readUserPrefs() throws DataConversionException, IOException;
@@ -29,4 +30,12 @@ public interface Storage extends AddressBookStorage, UserPrefsStorage {
@Override
void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException;
+ @Override
+ Path getTaskListFilePath();
+
+ @Override
+ Optional readTaskList() throws DataConversionException, IOException;
+
+ @Override
+ void saveTaskList(ReadOnlyTaskList taskList) throws IOException;
}
diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java
index 6cfa0162164..28a5f93fcf1 100644
--- a/src/main/java/seedu/address/storage/StorageManager.java
+++ b/src/main/java/seedu/address/storage/StorageManager.java
@@ -8,6 +8,7 @@
import seedu.address.commons.core.LogsCenter;
import seedu.address.commons.exceptions.DataConversionException;
import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyTaskList;
import seedu.address.model.ReadOnlyUserPrefs;
import seedu.address.model.UserPrefs;
@@ -19,13 +20,16 @@ public class StorageManager implements Storage {
private static final Logger logger = LogsCenter.getLogger(StorageManager.class);
private AddressBookStorage addressBookStorage;
private UserPrefsStorage userPrefsStorage;
+ private TaskListStorage taskListStorage;
/**
* Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}.
*/
- public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) {
+ public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage,
+ TaskListStorage taskListStorage) {
this.addressBookStorage = addressBookStorage;
this.userPrefsStorage = userPrefsStorage;
+ this.taskListStorage = taskListStorage;
}
// ================ UserPrefs methods ==============================
@@ -75,4 +79,31 @@ public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) thro
addressBookStorage.saveAddressBook(addressBook, filePath);
}
+ // ================ TaskList methods ==============================
+ @Override
+ public Path getTaskListFilePath() {
+ return taskListStorage.getTaskListFilePath();
+ }
+
+ @Override
+ public Optional readTaskList() throws DataConversionException, IOException {
+ return readTaskList(taskListStorage.getTaskListFilePath());
+ }
+
+ @Override
+ public Optional readTaskList(Path filePath) throws DataConversionException, IOException {
+ logger.fine("Attempting to read data from file: " + filePath);
+ return taskListStorage.readTaskList(filePath);
+ }
+
+ @Override
+ public void saveTaskList(ReadOnlyTaskList taskList) throws IOException {
+ saveTaskList(taskList, taskListStorage.getTaskListFilePath());
+ }
+
+ @Override
+ public void saveTaskList(ReadOnlyTaskList taskList, Path filePath) throws IOException {
+ logger.fine("Attempting to write to data file: " + filePath);
+ taskListStorage.saveTaskList(taskList, filePath);
+ }
}
diff --git a/src/main/java/seedu/address/storage/TaskListStorage.java b/src/main/java/seedu/address/storage/TaskListStorage.java
new file mode 100644
index 00000000000..227e08a28f1
--- /dev/null
+++ b/src/main/java/seedu/address/storage/TaskListStorage.java
@@ -0,0 +1,41 @@
+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.ReadOnlyTaskList;
+
+public interface TaskListStorage {
+
+ /**
+ * Returns the file path of the data file.
+ */
+ Path getTaskListFilePath();
+
+ /**
+ * Returns TaskList data as a {@link ReadOnlyTaskList}.
+ * 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 readTaskList() throws DataConversionException, IOException;
+
+ /**
+ * @see #getTaskListFilePath()
+ */
+ Optional readTaskList(Path filePath) throws DataConversionException, IOException;
+
+ /**
+ * Saves the given {@link ReadOnlyTaskList} to the storage.
+ * @param taskList cannot be null.
+ * @throws IOException if there was any problem writing to the file.
+ */
+ void saveTaskList(ReadOnlyTaskList taskList) throws IOException;
+
+ /**
+ * @see #saveTaskList(ReadOnlyTaskList)
+ */
+ void saveTaskList(ReadOnlyTaskList taskList, Path filePath) throws IOException;
+}
diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/address/ui/HelpWindow.java
index 9a665915949..aa3bc774d42 100644
--- a/src/main/java/seedu/address/ui/HelpWindow.java
+++ b/src/main/java/seedu/address/ui/HelpWindow.java
@@ -15,7 +15,7 @@
*/
public class HelpWindow extends UiPart {
- public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html";
+ public static final String USERGUIDE_URL = "https://ay2122s2-cs2103-f11-4.github.io/tp/UserGuide.html";
public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL;
private static final Logger logger = LogsCenter.getLogger(HelpWindow.class);
diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java
index 9106c3aa6e5..55cc6bfe2bc 100644
--- a/src/main/java/seedu/address/ui/MainWindow.java
+++ b/src/main/java/seedu/address/ui/MainWindow.java
@@ -34,6 +34,7 @@ public class MainWindow extends UiPart {
private PersonListPanel personListPanel;
private ResultDisplay resultDisplay;
private HelpWindow helpWindow;
+ private TaskListPanel taskListPanel;
@FXML
private StackPane commandBoxPlaceholder;
@@ -44,6 +45,9 @@ public class MainWindow extends UiPart {
@FXML
private StackPane personListPanelPlaceholder;
+ @FXML
+ private StackPane taskListPanelPlaceholder;
+
@FXML
private StackPane resultDisplayPlaceholder;
@@ -113,6 +117,8 @@ 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());
diff --git a/src/main/java/seedu/address/ui/TaskCard.java b/src/main/java/seedu/address/ui/TaskCard.java
new file mode 100644
index 00000000000..1374c4de3c8
--- /dev/null
+++ b/src/main/java/seedu/address/ui/TaskCard.java
@@ -0,0 +1,60 @@
+package seedu.address.ui;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Label;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Region;
+import seedu.address.model.task.Task;
+
+public class TaskCard extends UiPart {
+ private static final String FXML = "TaskCard.fxml";
+
+ /**
+ * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX.
+ * As a consequence, UI elements' variable names cannot be set to such keywords
+ * or an exception will be thrown by JavaFX during runtime.
+ *
+ * @see The issue on AddressBook level 4
+ */
+
+ public final Task task;
+
+ @FXML
+ private HBox cardPane;
+ @FXML
+ private Label taskDescription;
+ @FXML
+ private Label taskDeadline;
+ @FXML
+ private Label taskId;
+
+ /**
+ * Creates a {@code TaskCode} with the given {@code Task} and index to display.
+ */
+ public TaskCard(Task task, int displayedIndex) {
+ super(FXML);
+ this.task = task;
+ taskId.setText(displayedIndex + ". ");
+ taskDescription.setText(task.getDescription());
+ taskDeadline.setText(task.getDeadline());
+ }
+
+ @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 taskId.getText().equals(card.taskId.getText())
+ && task.equals(card.task);
+ }
+}
+
diff --git a/src/main/java/seedu/address/ui/TaskListPanel.java b/src/main/java/seedu/address/ui/TaskListPanel.java
new file mode 100644
index 00000000000..5cb02d1e32f
--- /dev/null
+++ b/src/main/java/seedu/address/ui/TaskListPanel.java
@@ -0,0 +1,45 @@
+package seedu.address.ui;
+
+import java.util.logging.Logger;
+
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.ListCell;
+import javafx.scene.control.ListView;
+import javafx.scene.layout.Region;
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.model.task.Task;
+
+public class TaskListPanel extends UiPart {
+ private static final String FXML = "TaskListPanel.fxml";
+ private final Logger logger = LogsCenter.getLogger(PersonListPanel.class);
+
+ @FXML
+ private ListView taskListView;
+
+ /**
+ * Creates a {@code PersonListPanel} with the given {@code ObservableList}.
+ */
+ public TaskListPanel(ObservableList taskList) {
+ super(FXML);
+ taskListView.setItems(taskList);
+ taskListView.setCellFactory(listView -> new TaskListPanel.TaskListViewCell());
+ }
+
+ /**
+ * Custom {@code ListCell} that displays the graphics of a {@code Person} using a {@code PersonCard}.
+ */
+ 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/UiManager.java b/src/main/java/seedu/address/ui/UiManager.java
index fdf024138bc..8398e9d0c6d 100644
--- a/src/main/java/seedu/address/ui/UiManager.java
+++ b/src/main/java/seedu/address/ui/UiManager.java
@@ -20,7 +20,7 @@ public class UiManager implements Ui {
public static final String ALERT_DIALOG_PANE_FIELD_ID = "alertDialogPane";
private static final Logger logger = LogsCenter.getLogger(UiManager.class);
- private static final String ICON_APPLICATION = "/images/address_book_32.png";
+ private static final String ICON_APPLICATION = "/images/NUScheduler_32.png";
private Logic logic;
private MainWindow mainWindow;
@@ -65,7 +65,7 @@ void showAlertDialogAndWait(Alert.AlertType type, String title, String headerTex
private static void showAlertDialogAndWait(Stage owner, AlertType type, String title, String headerText,
String contentText) {
final Alert alert = new Alert(type);
- alert.getDialogPane().getStylesheets().add("view/DarkTheme.css");
+ alert.getDialogPane().getStylesheets().add("view/ThemePresets.css");
alert.initOwner(owner);
alert.setTitle(title);
alert.setHeaderText(headerText);
diff --git a/src/main/resources/images/NUScheduler_32.png b/src/main/resources/images/NUScheduler_32.png
new file mode 100644
index 00000000000..a8a2ee4f368
Binary files /dev/null and b/src/main/resources/images/NUScheduler_32.png differ
diff --git a/src/main/resources/images/contact.png b/src/main/resources/images/contact.png
new file mode 100644
index 00000000000..2b8eef3892a
Binary files /dev/null and b/src/main/resources/images/contact.png differ
diff --git a/src/main/resources/images/task.png b/src/main/resources/images/task.png
new file mode 100644
index 00000000000..d85bc9e7ab4
Binary files /dev/null and b/src/main/resources/images/task.png differ
diff --git a/src/main/resources/view/CommandBox.fxml b/src/main/resources/view/CommandBox.fxml
index 09f6d6fe9e4..f271f03465c 100644
--- a/src/main/resources/view/CommandBox.fxml
+++ b/src/main/resources/view/CommandBox.fxml
@@ -3,7 +3,6 @@
-
-
+
+
-
diff --git a/src/main/resources/view/HelpWindow.css b/src/main/resources/view/HelpWindow.css
index 17e8a8722cd..bb3ddda2977 100644
--- a/src/main/resources/view/HelpWindow.css
+++ b/src/main/resources/view/HelpWindow.css
@@ -1,5 +1,6 @@
#copyButton, #helpMessage {
-fx-text-fill: white;
+ -fx-font-family: "Verdana";
}
#copyButton {
@@ -15,5 +16,5 @@
}
#helpMessageContainer {
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: derive(#1d1d1d, -10%);
}
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
index a431648f6c0..8f62d592101 100644
--- a/src/main/resources/view/MainWindow.fxml
+++ b/src/main/resources/view/MainWindow.fxml
@@ -10,21 +10,24 @@
+
-
+
+
+
+
-
+
-
-
+
+
@@ -33,25 +36,53 @@
-
+
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
-
+
diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml
index f08ea32ad55..2e0a4e8b0e9 100644
--- a/src/main/resources/view/PersonListCard.fxml
+++ b/src/main/resources/view/PersonListCard.fxml
@@ -28,9 +28,9 @@
-
-
-
+
+
+
diff --git a/src/main/resources/view/TaskCard.fxml b/src/main/resources/view/TaskCard.fxml
new file mode 100644
index 00000000000..ec5d93535d7
--- /dev/null
+++ b/src/main/resources/view/TaskCard.fxml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/TaskListPanel.fxml b/src/main/resources/view/TaskListPanel.fxml
new file mode 100644
index 00000000000..a58887c89d1
--- /dev/null
+++ b/src/main/resources/view/TaskListPanel.fxml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/ThemePresets.css
similarity index 75%
rename from src/main/resources/view/DarkTheme.css
rename to src/main/resources/view/ThemePresets.css
index 36e6b001cd8..c1009935934 100644
--- a/src/main/resources/view/DarkTheme.css
+++ b/src/main/resources/view/ThemePresets.css
@@ -1,5 +1,7 @@
+@import url('https://fonts.googleapis.com/css2?family=Basic&display=swap');
+
.background {
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: derive(#fffff0, 20%);
background-color: #383838; /* Used in the default.html file */
}
@@ -40,9 +42,9 @@
}
.table-view {
- -fx-base: #1d1d1d;
- -fx-control-inner-background: #1d1d1d;
- -fx-background-color: #1d1d1d;
+ -fx-base: #f0ffff;
+ -fx-control-inner-background: #f0ffff;
+ -fx-background-color: #f0ffff;
-fx-table-cell-border-color: transparent;
-fx-table-header-border-color: transparent;
-fx-padding: 5;
@@ -77,47 +79,64 @@
}
.split-pane:horizontal .split-pane-divider {
- -fx-background-color: derive(#1d1d1d, 20%);
- -fx-border-color: transparent transparent transparent #4d4d4d;
+ -fx-background-color: derive(#f0ffff, 20%);
+ -fx-border-color: derive(#f0ffff, 20%);
}
.split-pane {
- -fx-border-radius: 1;
-fx-border-width: 1;
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: derive(#f0ffff, 20%);
+}
+
+.header-icons {
+ -fx-background-color: derive(#f0ffff, 20%);
+}
+
+.icon-text {
+ -fx-background-color: derive(#f0ffff, 20%);
+ -fx-font-size: 22pt;
+ -fx-font-family: 'Basic', sans-serif;
+ -fx-text-fill: black;
+ -fx-opacity: 1;
}
.list-view {
-fx-background-insets: 0;
-fx-padding: 0;
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: derive(#f0ffff, 20%);
}
.list-cell {
-fx-label-padding: 0 0 0 0;
-fx-graphic-text-gap : 0;
- -fx-padding: 0 0 0 0;
+ -fx-background-insets: 3px;
+ -fx-background-color: derive(#f0ffff, 20%);
+
}
.list-cell:filled:even {
- -fx-background-color: #3c3e3f;
+ -fx-background-color: #e8eaf6;
+ -fx-background-radius: 15;
}
.list-cell:filled:odd {
- -fx-background-color: #515658;
+ -fx-background-color: #dadef1;
+ -fx-background-radius: 15;
}
.list-cell:filled:selected {
- -fx-background-color: #424d5f;
+ -fx-background-color: #d2d4e0;
}
.list-cell:filled:selected #cardPane {
- -fx-border-color: #3e7b91;
+ -fx-label-padding: 0 0 0 0;
-fx-border-width: 1;
+ -fx-background-insets: 3px;
+ -fx-border-radius: 15;
}
.list-cell .label {
- -fx-text-fill: white;
+ -fx-text-fill: black;
}
.cell_big_label {
@@ -133,24 +152,25 @@
}
.stack-pane {
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: derive(#f0ffff, 20%);
+ -fx-border-color: derive(#1d1d1d, 40%);
}
.pane-with-border {
- -fx-background-color: derive(#1d1d1d, 20%);
- -fx-border-color: derive(#1d1d1d, 10%);
+ -fx-background-color: derive(#fffff0, 20%);
+ -fx-border-color: derive(#1d1d1d, -10%);
-fx-border-top-width: 1px;
}
.status-bar {
- -fx-background-color: derive(#1d1d1d, 30%);
+ -fx-background-color: #c9f1fd;
}
.result-display {
- -fx-background-color: transparent;
+ -fx-background-color: derive(#fffff0, 20%);
-fx-font-family: "Segoe UI Light";
-fx-font-size: 13pt;
- -fx-text-fill: white;
+ -fx-text-fill: black;
}
.result-display .label {
@@ -159,7 +179,7 @@
.status-bar .label {
-fx-font-family: "Segoe UI Light";
- -fx-text-fill: white;
+ -fx-text-fill: black;
-fx-padding: 4px;
-fx-pref-height: 30px;
}
@@ -181,11 +201,11 @@
}
.grid-pane .stack-pane {
- -fx-background-color: derive(#1d1d1d, 30%);
+ -fx-background-color: derive(#fffff0, 30%);
}
.context-menu {
- -fx-background-color: derive(#1d1d1d, 50%);
+ -fx-background-color: derive(#fffff0, 30%);
}
.context-menu .label {
@@ -199,14 +219,19 @@
.menu-bar .label {
-fx-font-size: 14pt;
-fx-font-family: "Segoe UI Light";
- -fx-text-fill: white;
+ -fx-text-fill: black;
-fx-opacity: 0.9;
}
.menu .left-container {
- -fx-background-color: black;
+ -fx-background-color: #c9f1fd;
}
+.context-menu {
+ -fx-background-color: #c9f1fd;
+ -fx-border-color: #1d1d1d;
+ -fx-border-width: 1;
+}
/*
* Metro style Push Button
* Author: Pedro Duque Vieira
@@ -267,11 +292,11 @@
.dialog-pane > *.label.content {
-fx-font-size: 14px;
-fx-font-weight: bold;
- -fx-text-fill: white;
+ -fx-text-fill: black;
}
.dialog-pane:header *.header-panel {
- -fx-background-color: derive(#1d1d1d, 25%);
+ -fx-background-color: derive(#f0ffff, 20%);
}
.dialog-pane:header *.header-panel *.label {
@@ -282,12 +307,15 @@
}
.scroll-bar {
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: derive(#e6e6e7, 20%);
}
.scroll-bar .thumb {
- -fx-background-color: derive(#1d1d1d, 50%);
+ -fx-background-color: #e6e6e7;
-fx-background-insets: 3;
+ -fx-border-color: #2d4b5a;
+ -fx-background-radius : 1em;
+ -fx-border-radius: 1em;
}
.scroll-bar .increment-button, .scroll-bar .decrement-button {
@@ -318,6 +346,7 @@
}
#commandTextField {
+ -fx-prompt-text-fill: black;
-fx-background-color: transparent #383838 transparent #383838;
-fx-background-insets: 0;
-fx-border-color: #383838 #383838 #ffffff #383838;
@@ -325,7 +354,7 @@
-fx-border-width: 1;
-fx-font-family: "Segoe UI Light";
-fx-font-size: 13pt;
- -fx-text-fill: white;
+ -fx-text-fill: black;
}
#filterField, #personListPanel, #personWebpage {
@@ -333,7 +362,7 @@
}
#resultDisplay .content {
- -fx-background-color: transparent, #383838, transparent, #383838;
+ -fx-background-color: derive(#f0ffff, 20%);
-fx-background-radius: 0;
}
diff --git a/src/test/data/JsonSerializableTaskListTest/invalidTaskTaskList.json b/src/test/data/JsonSerializableTaskListTest/invalidTaskTaskList.json
new file mode 100644
index 00000000000..8115d138a2c
--- /dev/null
+++ b/src/test/data/JsonSerializableTaskListTest/invalidTaskTaskList.json
@@ -0,0 +1,6 @@
+{
+ "tasks" : [ {
+ "description" : null,
+ "deadline" : "01/01/2022"
+ } ]
+}
diff --git a/src/test/data/JsonSerializableTaskListTest/typicalTaskTaskList.json b/src/test/data/JsonSerializableTaskListTest/typicalTaskTaskList.json
new file mode 100644
index 00000000000..666ec16ca0d
--- /dev/null
+++ b/src/test/data/JsonSerializableTaskListTest/typicalTaskTaskList.json
@@ -0,0 +1,6 @@
+{
+ "tasks" : [ {
+ "description" : "dummy",
+ "deadline" : "01/01/2023"
+ } ]
+}
diff --git a/src/test/data/JsonTaskListStorageTest/invalidAndValidTaskTaskList.json b/src/test/data/JsonTaskListStorageTest/invalidAndValidTaskTaskList.json
new file mode 100644
index 00000000000..fc853055425
--- /dev/null
+++ b/src/test/data/JsonTaskListStorageTest/invalidAndValidTaskTaskList.json
@@ -0,0 +1,9 @@
+{
+ "tasks" : [ {
+ "description" : null,
+ "deadline" : "01/01/2022"
+ }, {
+ "description" : "dummy",
+ "deadline" : "01/01/2023"
+ } ]
+}
diff --git a/src/test/data/JsonTaskListStorageTest/invalidTaskTaskList.json b/src/test/data/JsonTaskListStorageTest/invalidTaskTaskList.json
new file mode 100644
index 00000000000..e7b7312004f
--- /dev/null
+++ b/src/test/data/JsonTaskListStorageTest/invalidTaskTaskList.json
@@ -0,0 +1,18 @@
+{
+ "tasks" : [ {
+ "description" : null,
+ "deadline" : "01/01/2023"
+ }, {
+ "description" : "dummy",
+ "deadline" : "01/01/2022"
+ }, {
+ "description" : "dummy",
+ "deadline" : "30/02/2023"
+ }, {
+ "description" : "dummy",
+ "deadline" : "o1/01/2022"
+ }, {
+ "description" : "dummy",
+ "deadline" : "*1/01/2022"
+ } ]
+}
diff --git a/src/test/data/JsonTaskListStorageTest/notJsonFormatAddressBook.json b/src/test/data/JsonTaskListStorageTest/notJsonFormatAddressBook.json
new file mode 100644
index 00000000000..a1097343b5d
--- /dev/null
+++ b/src/test/data/JsonTaskListStorageTest/notJsonFormatAddressBook.json
@@ -0,0 +1 @@
+not json format!
diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/address/logic/LogicManagerTest.java
index ad923ac249a..edb1f52ec3e 100644
--- a/src/test/java/seedu/address/logic/LogicManagerTest.java
+++ b/src/test/java/seedu/address/logic/LogicManagerTest.java
@@ -28,6 +28,7 @@
import seedu.address.model.UserPrefs;
import seedu.address.model.person.Person;
import seedu.address.storage.JsonAddressBookStorage;
+import seedu.address.storage.JsonTaskListStorage;
import seedu.address.storage.JsonUserPrefsStorage;
import seedu.address.storage.StorageManager;
import seedu.address.testutil.PersonBuilder;
@@ -46,7 +47,8 @@ public void setUp() {
JsonAddressBookStorage addressBookStorage =
new JsonAddressBookStorage(temporaryFolder.resolve("addressBook.json"));
JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("userPrefs.json"));
- StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage);
+ JsonTaskListStorage taskListStorage = new JsonTaskListStorage(temporaryFolder.resolve("tasklist.json"));
+ StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage, taskListStorage);
logic = new LogicManager(model, storage);
}
@@ -75,7 +77,8 @@ public void execute_storageThrowsIoException_throwsCommandException() {
new JsonAddressBookIoExceptionThrowingStub(temporaryFolder.resolve("ioExceptionAddressBook.json"));
JsonUserPrefsStorage userPrefsStorage =
new JsonUserPrefsStorage(temporaryFolder.resolve("ioExceptionUserPrefs.json"));
- StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage);
+ JsonTaskListStorage taskListStorage = new JsonTaskListStorage(temporaryFolder.resolve("tasklist.json"));
+ StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage, taskListStorage);
logic = new LogicManager(model, storage);
// Execute add command
@@ -93,6 +96,12 @@ public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException
assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredPersonList().remove(0));
}
+ @Test
+ public void getFilteredTaskList_modifyList_throwsUnsupportedOperationException() {
+ assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredTaskList().remove(0));
+ }
+
+
/**
* Executes the command and confirms that
* - no exceptions are thrown
@@ -129,7 +138,7 @@ private void assertCommandException(String inputCommand, String expectedMessage)
*/
private void assertCommandFailure(String inputCommand, Class extends Throwable> expectedException,
String expectedMessage) {
- Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs(), model.getTaskList());
assertCommandFailure(inputCommand, expectedException, expectedMessage, expectedModel);
}
diff --git a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java b/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java
index cb8714bb055..88112cbe94d 100644
--- a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java
+++ b/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java
@@ -3,6 +3,7 @@
import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import static seedu.address.testutil.TypicalTask.getTypicalTaskList;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -22,14 +23,14 @@ public class AddCommandIntegrationTest {
@BeforeEach
public void setUp() {
- model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTaskList());
}
@Test
public void execute_newPerson_success() {
Person validPerson = new PersonBuilder().build();
- Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs(), model.getTaskList());
expectedModel.addPerson(validPerson);
assertCommandSuccess(new AddCommand(validPerson), model,
diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java
index 5865713d5dd..db736b085f9 100644
--- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/AddCommandTest.java
@@ -19,8 +19,11 @@
import seedu.address.model.AddressBook;
import seedu.address.model.Model;
import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyTaskList;
import seedu.address.model.ReadOnlyUserPrefs;
+import seedu.address.model.TaskList;
import seedu.address.model.person.Person;
+import seedu.address.model.task.Task;
import seedu.address.testutil.PersonBuilder;
public class AddCommandTest {
@@ -147,6 +150,60 @@ public ObservableList getFilteredPersonList() {
public void updateFilteredPersonList(Predicate predicate) {
throw new AssertionError("This method should not be called.");
}
+
+ @Override
+ public ObservableList getFilteredTaskList() {
+ throw new AssertionError("This method should not be called.");
+ }
+ @Override
+ public void addTask(Task task) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void addTask(Task task, Integer taskId) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public TaskList getTaskList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void deleteTask(Integer taskNumber) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public String findTask(String input) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public String viewTask() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public Path getTaskListFilePath() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setTaskListFilePath(Path taskListFilePath) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ReadOnlyTaskList getReadOnlyTaskList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void updateTask(Task updatedTask, Integer taskId) {
+ throw new AssertionError("This method should not be called.");
+ }
}
/**
diff --git a/src/test/java/seedu/address/logic/commands/AddTaskCommandTest.java b/src/test/java/seedu/address/logic/commands/AddTaskCommandTest.java
new file mode 100644
index 00000000000..c5ec080d96f
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/AddTaskCommandTest.java
@@ -0,0 +1,197 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static seedu.address.testutil.Assert.assertThrows;
+
+import java.nio.file.Path;
+import java.util.function.Predicate;
+
+import org.junit.jupiter.api.Test;
+
+import javafx.collections.ObservableList;
+import seedu.address.commons.core.GuiSettings;
+import seedu.address.model.Model;
+import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyTaskList;
+import seedu.address.model.ReadOnlyUserPrefs;
+import seedu.address.model.TaskList;
+import seedu.address.model.person.Person;
+import seedu.address.model.task.Task;
+
+public class AddTaskCommandTest {
+
+ @Test
+ public void constructor_nullDescription_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new AddTaskCommand(null));
+ }
+
+ @Test
+ public void constructor_nullDescriptionAndDeadline_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new AddTaskCommand(null, null));
+ }
+
+ @Test
+ public void execute_addTaskNoDeadline_addSuccessful() throws Exception {
+ ModelStubAcceptTask modelStubAcceptTask = new ModelStubAcceptTask();
+
+ CommandResult commandResult = new AddTaskCommand("description").execute(modelStubAcceptTask);
+ assertEquals(String.format(AddTaskCommand.MESSAGE_ARGUMENTS, "description", "No deadline set"),
+ commandResult.getFeedbackToUser());
+ }
+
+ @Test
+ public void execute_addTaskWithDeadline_addSuccessful() throws Exception {
+ ModelStubAcceptTask modelStubAcceptTask = new ModelStubAcceptTask();
+
+ CommandResult commandResult = new AddTaskCommand("description", "2022")
+ .execute(modelStubAcceptTask);
+ assertEquals(String.format(AddTaskCommand.MESSAGE_ARGUMENTS, "description", "2022"),
+ commandResult.getFeedbackToUser());
+ }
+
+ /**
+ * A default model stub that have all of the methods failing.
+ */
+ private class ModelStub implements Model {
+ @Override
+ public void setUserPrefs(ReadOnlyUserPrefs userPrefs) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ReadOnlyUserPrefs getUserPrefs() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public GuiSettings getGuiSettings() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setGuiSettings(GuiSettings guiSettings) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public Path getAddressBookFilePath() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setAddressBookFilePath(Path addressBookFilePath) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void addPerson(Person person) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setAddressBook(ReadOnlyAddressBook newData) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ReadOnlyAddressBook getAddressBook() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public boolean hasPerson(Person person) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void deletePerson(Person target) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setPerson(Person target, Person editedPerson) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ObservableList getFilteredPersonList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void updateFilteredPersonList(Predicate predicate) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ObservableList getFilteredTaskList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void addTask(Task task) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void addTask(Task task, Integer taskId) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public TaskList getTaskList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void deleteTask(Integer taskNumber) {
+ throw new AssertionError("This method should not be called");
+ }
+
+ @Override
+ public String findTask(String input) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public String viewTask() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public Path getTaskListFilePath() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setTaskListFilePath(Path taskListFilePath) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ReadOnlyTaskList getReadOnlyTaskList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void updateTask(Task updatedTask, Integer taskId) {
+ throw new AssertionError("This method should not be called.");
+ }
+ }
+
+ private class ModelStubAcceptTask extends ModelStub {
+ private TaskList taskList = new TaskList();
+
+ @Override
+ public TaskList getTaskList() {
+ return taskList;
+ }
+
+ @Override
+ public void addTask(Task task) {
+ requireNonNull(task);
+ taskList.addTask(task);
+ }
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java b/src/test/java/seedu/address/logic/commands/ClearCommandTest.java
index 80d9110c03a..ab4935dd9b6 100644
--- a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/ClearCommandTest.java
@@ -2,6 +2,7 @@
import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import static seedu.address.testutil.TypicalTask.getTypicalTaskList;
import org.junit.jupiter.api.Test;
@@ -22,8 +23,8 @@ public void execute_emptyAddressBook_success() {
@Test
public void execute_nonEmptyAddressBook_success() {
- Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
- Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTaskList());
+ Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTaskList());
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
index 643a1d08069..71cd8621d60 100644
--- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
+++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
@@ -3,6 +3,8 @@
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_ADD_TASK_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADD_TASK_DESCRIPTION;
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;
@@ -37,6 +39,15 @@ public class CommandTestUtil {
public static final String VALID_TAG_HUSBAND = "husband";
public static final String VALID_TAG_FRIEND = "friend";
+ public static final String VALID_DESCRIPTION_SWIMMING = "Swimming Lesson";
+ public static final String VALID_DEADLINE_MARCH = "03/03/2023";
+
+ public static final String DESC_SWIMMING = " " + PREFIX_ADD_TASK_DESCRIPTION + VALID_DESCRIPTION_SWIMMING;
+ public static final String DEADLINE_SWIMMING = " " + PREFIX_ADD_TASK_DEADLINE + VALID_DEADLINE_MARCH;
+
+ public static final String INVALID_DEADLINE_YEAR = " " + PREFIX_ADD_TASK_DEADLINE + "2022"; // Wrong format
+ public static final String INVALID_DEADLINE_MONTH = " " + PREFIX_ADD_TASK_DEADLINE + "03/2022"; // Wrong format
+
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;
diff --git a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
index 45a8c910ba1..cbc79b3d9bb 100644
--- a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
@@ -8,6 +8,7 @@
import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON;
import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import static seedu.address.testutil.TypicalTask.getTypicalTaskList;
import org.junit.jupiter.api.Test;
@@ -24,7 +25,7 @@
*/
public class DeleteCommandTest {
- private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTaskList());
@Test
public void execute_validIndexUnfilteredList_success() {
@@ -33,7 +34,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.getAddressBook(), new UserPrefs(), model.getTaskList());
expectedModel.deletePerson(personToDelete);
assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel);
@@ -56,7 +57,7 @@ public void execute_validIndexFilteredList_success() {
String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete);
- Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs(), model.getTaskList());
expectedModel.deletePerson(personToDelete);
showNoPerson(expectedModel);
diff --git a/src/test/java/seedu/address/logic/commands/DeleteTaskCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteTaskCommandTest.java
new file mode 100644
index 00000000000..4e5c0300d66
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/DeleteTaskCommandTest.java
@@ -0,0 +1,216 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import static seedu.address.testutil.TypicalTask.getTypicalTaskList;
+
+import java.nio.file.Path;
+import java.util.function.Predicate;
+
+import org.junit.jupiter.api.Test;
+
+import javafx.collections.ObservableList;
+import seedu.address.commons.core.GuiSettings;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyTaskList;
+import seedu.address.model.ReadOnlyUserPrefs;
+import seedu.address.model.TaskList;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.Person;
+import seedu.address.model.task.Task;
+
+public class DeleteTaskCommandTest {
+
+ private ModelManager model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTaskList());
+
+ @Test
+ public void constructor_validInputSuccess() throws Exception {
+ Integer taskNumberToBeDeleted = 1;
+ DeleteTaskCommand deleteTaskCommand = new DeleteTaskCommand(taskNumberToBeDeleted);
+
+ model.addTask(new Task("test", "2022"));
+
+ CommandResult commandResult = new DeleteTaskCommand(taskNumberToBeDeleted)
+ .execute(model);
+
+ assertEquals(String.format(DeleteTaskCommand.MESSAGE_ARGUMENTS, taskNumberToBeDeleted),
+ commandResult.getFeedbackToUser());
+
+ }
+
+ @Test
+ public void execute_outOfBoundsIndex_throwsCommandException() throws CommandException {
+ Integer outOfBoundIndex = model.getTaskList().size() + 1;
+ DeleteTaskCommand deleteTaskCommand = new DeleteTaskCommand(outOfBoundIndex);
+ String expectedMessage = DeleteTaskCommand.MESSAGE_INDEX_OUT_OF_BOUNDS
+ + "\n" + DeleteTaskCommand.MESSAGE_USAGE;
+
+ assertCommandFailure(deleteTaskCommand, model, expectedMessage);
+ }
+
+ @Test
+ public void constructor_nullDescription_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new DeleteTaskCommand(null));
+ }
+
+ @Test
+ public void equals() {
+ DeleteTaskCommand deleteFirstTaskCommand = new DeleteTaskCommand(1);
+ DeleteTaskCommand deleteSecondTaskCommand = new DeleteTaskCommand(2);
+
+ // same object -> returns true
+ assertTrue(deleteFirstTaskCommand.equals(deleteFirstTaskCommand));
+
+ // same values -> returns true
+ DeleteTaskCommand deleteFirstTaskCommandCopy = new DeleteTaskCommand(1);
+ assertTrue(deleteFirstTaskCommand.equals(deleteFirstTaskCommandCopy));
+
+ // different types -> returns false
+ assertFalse(deleteFirstTaskCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(deleteFirstTaskCommand.equals(null));
+
+ // different person -> returns false
+ assertFalse(deleteFirstTaskCommand.equals(deleteSecondTaskCommand));
+ }
+
+
+
+ /**
+ * A default model stub that have all of the methods failing.
+ */
+ private class ModelStub implements Model {
+ @Override
+ public void setUserPrefs(ReadOnlyUserPrefs userPrefs) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ReadOnlyUserPrefs getUserPrefs() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public GuiSettings getGuiSettings() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setGuiSettings(GuiSettings guiSettings) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public Path getAddressBookFilePath() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setAddressBookFilePath(Path addressBookFilePath) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void addPerson(Person person) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setAddressBook(ReadOnlyAddressBook newData) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ReadOnlyAddressBook getAddressBook() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public boolean hasPerson(Person person) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void deletePerson(Person target) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setPerson(Person target, Person editedPerson) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ObservableList getFilteredPersonList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void updateFilteredPersonList(Predicate predicate) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ObservableList getFilteredTaskList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void addTask(Task task) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void addTask(Task task, Integer taskId) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void deleteTask(Integer taskNumber) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public TaskList getTaskList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public String findTask(String input) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public String viewTask() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public Path getTaskListFilePath() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setTaskListFilePath(Path taskListFilePath) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ReadOnlyTaskList getReadOnlyTaskList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void updateTask(Task updatedTask, Integer taskId) {
+ throw new AssertionError("This method should not be called.");
+ }
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/EditCommandTest.java b/src/test/java/seedu/address/logic/commands/EditCommandTest.java
index 214c6c2507b..d03e30c5e5c 100644
--- a/src/test/java/seedu/address/logic/commands/EditCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/EditCommandTest.java
@@ -13,6 +13,7 @@
import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON;
import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import static seedu.address.testutil.TypicalTask.getTypicalTaskList;
import org.junit.jupiter.api.Test;
@@ -32,7 +33,7 @@
*/
public class EditCommandTest {
- private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTaskList());
@Test
public void execute_allFieldsSpecifiedUnfilteredList_success() {
@@ -42,7 +43,8 @@ public void execute_allFieldsSpecifiedUnfilteredList_success() {
String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson);
- Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(),
+ model.getTaskList());
expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson);
assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
@@ -63,7 +65,8 @@ public void execute_someFieldsSpecifiedUnfilteredList_success() {
String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson);
- Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(),
+ model.getTaskList());
expectedModel.setPerson(lastPerson, editedPerson);
assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
@@ -76,7 +79,8 @@ public void execute_noFieldSpecifiedUnfilteredList_success() {
String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson);
- Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(),
+ model.getTaskList());
assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
}
@@ -92,7 +96,8 @@ public void execute_filteredList_success() {
String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson);
- Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(),
+ model.getTaskList());
expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson);
assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
diff --git a/src/test/java/seedu/address/logic/commands/FindCommandTest.java b/src/test/java/seedu/address/logic/commands/FindCommandTest.java
index 9b15db28bbb..8d43b80dc40 100644
--- a/src/test/java/seedu/address/logic/commands/FindCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/FindCommandTest.java
@@ -9,6 +9,7 @@
import static seedu.address.testutil.TypicalPersons.ELLE;
import static seedu.address.testutil.TypicalPersons.FIONA;
import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import static seedu.address.testutil.TypicalTask.getTypicalTaskList;
import java.util.Arrays;
import java.util.Collections;
@@ -24,8 +25,8 @@
* Contains integration tests (interaction with the Model) for {@code FindCommand}.
*/
public class FindCommandTest {
- private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
- private Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTaskList());
+ private Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTaskList());
@Test
public void equals() {
diff --git a/src/test/java/seedu/address/logic/commands/FindTaskCommandTest.java b/src/test/java/seedu/address/logic/commands/FindTaskCommandTest.java
new file mode 100644
index 00000000000..8718dc9992d
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/FindTaskCommandTest.java
@@ -0,0 +1,216 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import static seedu.address.testutil.TypicalTask.getTypicalTaskList;
+
+import java.nio.file.Path;
+import java.util.function.Predicate;
+
+import org.junit.jupiter.api.Test;
+
+import javafx.collections.ObservableList;
+import seedu.address.commons.core.GuiSettings;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyTaskList;
+import seedu.address.model.ReadOnlyUserPrefs;
+import seedu.address.model.TaskList;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.Person;
+import seedu.address.model.task.Task;
+
+/**
+ * Contains integration tests (interaction with the Model) for {@code FindCommand}.
+ */
+public class FindTaskCommandTest {
+
+ private final ModelManager model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTaskList());
+
+ @Test
+ public void constructor_nullInput_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new FindTaskCommand(null));
+ }
+
+ @Test
+ public void execute_validInputSuccess() throws CommandException {
+ String testInputSuccess = "test";
+ FindTaskCommand findTaskCommand = new FindTaskCommand(testInputSuccess);
+ model.addTask(new Task("test", "2022"));
+ CommandResult commandResult = findTaskCommand
+ .execute(model);
+ String taskSuccess = model.findTask(testInputSuccess);
+ assertEquals(String.format(FindTaskCommand.MESSAGE_ARGUMENTS, taskSuccess),
+ commandResult.getFeedbackToUser());
+ }
+
+ @Test
+ public void execute_validInput_noTaskFound() {
+ String testInputUnsuccessful = "fail";
+ FindTaskCommand findTaskCommand = new FindTaskCommand(testInputUnsuccessful);
+ model.addTask(new Task("test", "2022"));
+ String expectedMessage = FindTaskCommand.MESSAGE_NO_MATCHING_TASK;
+ assertThrows(CommandException.class, expectedMessage, () -> findTaskCommand.execute(model));
+ }
+
+ @Test
+ public void equals() {
+ String firstTest = "test1";
+ String secondTest = "test2";
+ FindTaskCommand findFirstTaskCommand = new FindTaskCommand(firstTest);
+ FindTaskCommand findSecondTaskCommand = new FindTaskCommand(secondTest);
+
+ // same object -> returns true
+ assertTrue(findFirstTaskCommand.equals(findFirstTaskCommand));
+
+ // same values -> returns true
+ FindTaskCommand findFirstTaskCommandCopy = new FindTaskCommand(firstTest);
+ assertTrue(findFirstTaskCommand.equals(findFirstTaskCommandCopy));
+
+ // different types -> returns false
+ assertFalse(findFirstTaskCommand.equals(firstTest));
+
+ // null -> returns false
+ assertFalse(findFirstTaskCommand.equals(null));
+
+ // different person -> returns false
+ assertFalse(findFirstTaskCommand.equals(findSecondTaskCommand));
+ }
+
+
+ /**
+ * A default model stub that have all the methods failing.
+ */
+ private class ModelStub implements Model {
+ @Override
+ public void setUserPrefs(ReadOnlyUserPrefs userPrefs) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ReadOnlyUserPrefs getUserPrefs() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public GuiSettings getGuiSettings() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setGuiSettings(GuiSettings guiSettings) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public Path getAddressBookFilePath() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setAddressBookFilePath(Path addressBookFilePath) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void addPerson(Person person) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setAddressBook(ReadOnlyAddressBook newData) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ReadOnlyAddressBook getAddressBook() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public boolean hasPerson(Person person) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void deletePerson(Person target) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setPerson(Person target, Person editedPerson) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ObservableList getFilteredPersonList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void updateFilteredPersonList(Predicate predicate) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ObservableList getFilteredTaskList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void addTask(Task task) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void addTask(Task task, Integer taskId) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void deleteTask(Integer taskNumber) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public TaskList getTaskList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public String findTask(String input) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public String viewTask() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public Path getTaskListFilePath() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setTaskListFilePath(Path taskListFilePath) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ReadOnlyTaskList getReadOnlyTaskList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void updateTask(Task updatedTask, Integer taskId) {
+ throw new AssertionError("This method should not be called.");
+ }
+ }
+}
+
diff --git a/src/test/java/seedu/address/logic/commands/ListCommandTest.java b/src/test/java/seedu/address/logic/commands/ListCommandTest.java
index 435ff1f7275..d7aa19a9f51 100644
--- a/src/test/java/seedu/address/logic/commands/ListCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/ListCommandTest.java
@@ -4,6 +4,7 @@
import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex;
import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import static seedu.address.testutil.TypicalTask.getTypicalTaskList;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -22,8 +23,8 @@ public class ListCommandTest {
@BeforeEach
public void setUp() {
- model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
- expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTaskList());
+ expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs(), model.getTaskList());
}
@Test
diff --git a/src/test/java/seedu/address/logic/commands/UpdateTaskCommandTest.java b/src/test/java/seedu/address/logic/commands/UpdateTaskCommandTest.java
new file mode 100644
index 00000000000..bfcac13d122
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/UpdateTaskCommandTest.java
@@ -0,0 +1,118 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import static seedu.address.testutil.TypicalTask.getTypicalTaskList;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.AddressBook;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.task.Task;
+
+public class UpdateTaskCommandTest {
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs(), getTypicalTaskList());
+
+ @Test
+ public void execute_allFieldsSpecifiedUnfilteredList_success() throws CommandException {
+ Task updatedTask = new Task("test", "03/03/2022");
+ UpdateTaskCommand.UpdateTaskDescriptor descriptor = new UpdateTaskCommand.UpdateTaskDescriptor();
+ descriptor.setDescription(updatedTask.getDescription());
+ descriptor.setDeadline(updatedTask.getDeadline());
+ UpdateTaskCommand updateTaskCommand = new UpdateTaskCommand(1, descriptor);
+
+ String expectedMessage = String.format(UpdateTaskCommand.MESSAGE_UPDATE_TASK_SUCCESS, updatedTask);
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(),
+ model.getTaskList());
+ expectedModel.updateTask(updatedTask, 1);
+
+ assertCommandSuccess(updateTaskCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void createTask_descriptorWithoutDeadline() {
+ Task updatedTask = new Task("test", "03/03/2022");
+ UpdateTaskCommand.UpdateTaskDescriptor descriptor = new UpdateTaskCommand.UpdateTaskDescriptor();
+ descriptor.setDescription(updatedTask.getDescription());
+ Task newTaskCreated = UpdateTaskCommand.createUpdatedTask(updatedTask, descriptor);
+ assertTrue(newTaskCreated.getDeadline().equals(updatedTask.getDeadline()));
+ }
+
+ @Test
+ public void createTask_descriptorWithoutDescription() {
+ Task updatedTask = new Task("test", "03/03/2022");
+ UpdateTaskCommand.UpdateTaskDescriptor descriptor = new UpdateTaskCommand.UpdateTaskDescriptor();
+ descriptor.setDeadline(updatedTask.getDeadline());
+ Task newTaskCreated = UpdateTaskCommand.createUpdatedTask(updatedTask, descriptor);
+ assertTrue(newTaskCreated.getDescription().equals(updatedTask.getDescription()));
+ }
+
+ @Test
+ public void createTaskWithoutDeadline_descriptorWithoutDeadline() {
+ Task updatedTask = new Task("test", "No deadline set");
+ UpdateTaskCommand.UpdateTaskDescriptor descriptor = new UpdateTaskCommand.UpdateTaskDescriptor();
+ descriptor.setDescription(updatedTask.getDescription());
+ Task newTaskCreated = UpdateTaskCommand.createUpdatedTask(updatedTask, descriptor);
+ assertTrue(newTaskCreated.getDeadline().equals("No deadline set"));
+ }
+
+
+ @Test
+ public void execute_invalidPersonIndexUnfilteredList_failure() {
+ Integer outOfBoundIndex = model.getTaskList().size() + 1;
+ UpdateTaskCommand.UpdateTaskDescriptor descriptor = new UpdateTaskCommand.UpdateTaskDescriptor();
+ descriptor.setDescription("test");
+ descriptor.setDeadline("03/03/2022");
+ UpdateTaskCommand updateTaskCommand = new UpdateTaskCommand(outOfBoundIndex, descriptor);
+
+ assertCommandFailure(updateTaskCommand, model, "The task id provided is invalid");
+ }
+
+ @Test
+ public void equals() {
+ Task updatedTask = new Task("test", "03/03/2022");
+ Task differentTask = new Task("differentTest", "05/05/2022");
+ UpdateTaskCommand.UpdateTaskDescriptor descriptor = new UpdateTaskCommand.UpdateTaskDescriptor();
+ descriptor.setDescription(updatedTask.getDescription());
+ descriptor.setDeadline(updatedTask.getDeadline());
+ final UpdateTaskCommand standardCommand = new UpdateTaskCommand(1, descriptor);
+
+ // same values -> returns true
+ UpdateTaskCommand.UpdateTaskDescriptor copyDescriptor = new UpdateTaskCommand.UpdateTaskDescriptor();
+ copyDescriptor.setDescription(updatedTask.getDescription());
+ copyDescriptor.setDeadline(updatedTask.getDeadline());
+ UpdateTaskCommand commandWithSameValues = new UpdateTaskCommand(1, copyDescriptor);
+ assertTrue(standardCommand.equals(commandWithSameValues));
+
+ // same object -> returns true
+ assertTrue(standardCommand.equals(standardCommand));
+ assertTrue(descriptor.equals(descriptor));
+
+ // null -> returns false
+ assertFalse(standardCommand.equals(null));
+
+ // different types -> returns false
+ assertFalse(standardCommand.equals(new ClearCommand()));
+ assertFalse(descriptor.equals(new ClearCommand()));
+
+ // different index -> returns false
+ assertFalse(standardCommand.equals(new UpdateTaskCommand(2, descriptor)));
+
+ UpdateTaskCommand.UpdateTaskDescriptor differentDescriptor = new UpdateTaskCommand.UpdateTaskDescriptor();
+ descriptor.setDescription(differentTask.getDescription());
+ descriptor.setDeadline(differentTask.getDeadline());
+
+ // different descriptor -> returns false
+ assertFalse(standardCommand.equals(new UpdateTaskCommand(1, differentDescriptor)));
+
+ }
+
+
+}
diff --git a/src/test/java/seedu/address/logic/commands/ViewTaskCommandTest.java b/src/test/java/seedu/address/logic/commands/ViewTaskCommandTest.java
new file mode 100644
index 00000000000..4a069062cef
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/ViewTaskCommandTest.java
@@ -0,0 +1,208 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static seedu.address.logic.commands.ViewTaskCommand.MESSAGE_SUCCESS;
+import static seedu.address.testutil.Assert.assertThrows;
+
+import java.nio.file.Path;
+import java.util.function.Predicate;
+
+import org.junit.jupiter.api.Test;
+
+import javafx.collections.ObservableList;
+import seedu.address.commons.core.GuiSettings;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyTaskList;
+import seedu.address.model.ReadOnlyUserPrefs;
+import seedu.address.model.TaskList;
+import seedu.address.model.person.Person;
+import seedu.address.model.task.Task;
+
+public class ViewTaskCommandTest {
+
+ @Test
+ public void execute_taskListIsEmpty_throwsCommandException() {
+ ModelStubViewTask modelStubViewTask = new ModelStubViewTask();
+ assertThrows(CommandException.class, () -> new ViewTaskCommand().execute(modelStubViewTask));
+ }
+
+ @Test
+ public void execute_nullModel_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new ViewTaskCommand().execute(null));
+ }
+
+ @Test
+ public void execute_taskListIsNotEmpty_viewSuccessful() throws CommandException {
+ ModelStubHasTask modelStubHasTask = new ModelStubHasTask();
+ modelStubHasTask.addTask(new Task("description", "01/01/2022"));
+ String message = modelStubHasTask.viewTask();
+ CommandResult commandResult = new ViewTaskCommand().execute(modelStubHasTask);
+
+ assertEquals(MESSAGE_SUCCESS + "\n" + message, commandResult.getFeedbackToUser());
+ }
+
+ /**
+ * A default model stub that have all of the methods failing.
+ */
+ private class ModelStub implements Model {
+ @Override
+ public void setUserPrefs(ReadOnlyUserPrefs userPrefs) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ReadOnlyUserPrefs getUserPrefs() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public GuiSettings getGuiSettings() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setGuiSettings(GuiSettings guiSettings) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public Path getAddressBookFilePath() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setAddressBookFilePath(Path addressBookFilePath) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void addPerson(Person person) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setAddressBook(ReadOnlyAddressBook newData) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ReadOnlyAddressBook getAddressBook() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public boolean hasPerson(Person person) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void deletePerson(Person target) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setPerson(Person target, Person editedPerson) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ObservableList getFilteredPersonList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void updateFilteredPersonList(Predicate predicate) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ObservableList getFilteredTaskList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void addTask(Task task) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void addTask(Task task, Integer taskId) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public TaskList getTaskList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void deleteTask(Integer taskNumber) {
+ throw new AssertionError("This method should not be called");
+ }
+
+ @Override
+ public String findTask(String input) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public String viewTask() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public Path getTaskListFilePath() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setTaskListFilePath(Path taskListFilePath) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ReadOnlyTaskList getReadOnlyTaskList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void updateTask(Task updatedTask, Integer taskId) {
+ throw new AssertionError("This method should not be called.");
+ }
+ }
+
+ private class ModelStubViewTask extends ModelStub {
+ private TaskList taskList = new TaskList();
+
+ @Override
+ public TaskList getTaskList() {
+ return this.taskList;
+ }
+
+ @Override
+ public String viewTask() {
+ return taskList.viewTask();
+ }
+ }
+
+ private class ModelStubHasTask extends ModelStub {
+ private TaskList taskList = new TaskList();
+
+ @Override
+ public void addTask(Task task) {
+ taskList.addTask(task);
+ }
+
+ @Override
+ public TaskList getTaskList() {
+ return this.taskList;
+ }
+
+ @Override
+ public String viewTask() {
+ return taskList.viewTask();
+ }
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/AddTaskCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddTaskCommandParserTest.java
new file mode 100644
index 00000000000..1c5031a1419
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/AddTaskCommandParserTest.java
@@ -0,0 +1,107 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.AddTaskCommand;
+
+public class AddTaskCommandParserTest {
+ private AddTaskCommandParser parser = new AddTaskCommandParser();
+
+ @Test
+ public void parse_allFieldPresent_success() {
+ assertParseSuccess(parser, "addt d/description t/01/01/2222",
+ new AddTaskCommand("description", "01/01/2222"));
+ }
+
+ @Test
+ public void parse_optionalFieldsMissing_success() {
+ assertParseSuccess(parser, "addt d/description",
+ new AddTaskCommand("description"));
+ }
+
+ @Test
+ public void parse_multipleDescriptions_failure() {
+ String expectedMessage = "Duplicated prefix detected in input!\n" + AddTaskCommand.MESSAGE_USAGE;
+ assertParseFailure(parser, "addt d/abc d/abc", expectedMessage);
+ }
+
+ @Test
+ public void parse_multipleDeadline_failure() {
+ String expectedMessage = "Duplicated prefix detected in input!\n" + AddTaskCommand.MESSAGE_USAGE;
+ assertParseFailure(parser, "addt d/abc t/01/01/2222 t/01/01/2222", expectedMessage);
+ }
+
+ @Test
+ public void parse_compulsoryFieldMissing_failure() {
+ String expectedMessage = "Description is compulsory!\n" + AddTaskCommand.MESSAGE_USAGE;
+
+ // has description prefix
+ assertParseFailure(parser, "addt d/", expectedMessage);
+
+ // has description prefix and deadline prefix
+ assertParseFailure(parser, "addt d/ t/", expectedMessage);
+
+ // no description prefix
+ assertParseFailure(parser, "addt", expectedMessage);
+ }
+
+ @Test
+ public void parse_deadlineFormat_success() {
+ assertParseSuccess(parser, "addt d/description t/01/01/2222",
+ new AddTaskCommand("description", "01/01/2222"));
+ }
+
+ @Test
+ public void parse_deadlineFormat_failure() {
+ String expectedMessage = "Deadline is not in dd/mm/yyyy!\n" + AddTaskCommand.MESSAGE_USAGE;
+
+ // no "/" between date, month, year
+ assertParseFailure(parser, "addt d/description t/01012222", expectedMessage);
+
+ // uses "-" between date, month, year
+ assertParseFailure(parser, "addt d/description t/01-01-2222", expectedMessage);
+ }
+
+ @Test
+ public void parse_deadlineFormatUseAlphabets_failure() {
+ String expectedMessage = "Deadline is not in dd/mm/yyyy!\n" + AddTaskCommand.MESSAGE_USAGE;
+
+ assertParseFailure(parser, "addt d/description t/monday", expectedMessage);
+ assertParseFailure(parser, "addt d/description t/o1/o1/2o22", expectedMessage);
+ }
+
+ @Test
+ public void parse_deadlineFormatUseSymbols_failure() {
+ String expectedMessage = "Deadline is not in dd/mm/yyyy!\n" + AddTaskCommand.MESSAGE_USAGE;
+
+ assertParseFailure(parser, "addt d/description t/!@#$%^&*(){}[]|~`", expectedMessage);
+ assertParseFailure(parser, "addt d/description t/*10!2()22", expectedMessage);
+ }
+
+ @Test
+ public void parse_deadlineFormatIsBeforeTodayDate_failure() {
+ String expectedMessage = "Deadline is before today's date!\n" + AddTaskCommand.MESSAGE_USAGE;
+
+ assertParseFailure(parser, "addt d/description t/01/01/2022", expectedMessage);
+ }
+
+ @Test
+ public void parse_descriptionContainsDeadlinePrefix_failure() {
+ String expectedMessage = "You cannot have 't/' or 'd/' prefix in the description!\n"
+ + AddTaskCommand.MESSAGE_USAGE;
+
+ assertParseFailure(parser, "addt d/descriptiont/01/02/2022 t/01/01/2222", expectedMessage);
+ }
+
+ @Test
+ public void parse_descriptionValidity_failure() {
+ String expectedMessage = "Invalid date input!\n" + AddTaskCommand.MESSAGE_USAGE;
+
+ assertParseFailure(parser, "addt d/description t/31/02/2022", expectedMessage);
+ assertParseFailure(parser, "addt d/description t/30/02/2022", expectedMessage);
+ assertParseFailure(parser, "addt d/description t/29/02/2022", expectedMessage);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/DeleteTaskCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteTaskCommandParserTest.java
new file mode 100644
index 00000000000..00d32adb787
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/DeleteTaskCommandParserTest.java
@@ -0,0 +1,34 @@
+package seedu.address.logic.parser;
+
+
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.DeleteTaskCommand;
+
+
+public class DeleteTaskCommandParserTest {
+
+ private DeleteTaskCommandParser parser = new DeleteTaskCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsDeleteCommand() {
+ assertParseSuccess(parser, "1", new DeleteTaskCommand(1));
+ }
+
+ @Test
+ public void parse_invalidArgs_throwsParseException() {
+ assertParseFailure(parser, "a",
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTaskCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_invalidSymbols_throwsParseException() {
+ assertParseFailure(parser, "$%^%$^%#$",
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTaskCommand.MESSAGE_USAGE));
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/parser/FindTaskCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FindTaskCommandParserTest.java
new file mode 100644
index 00000000000..1fd0128e988
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/FindTaskCommandParserTest.java
@@ -0,0 +1,25 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.FindTaskCommand;
+
+public class FindTaskCommandParserTest {
+
+ private final FindTaskCommandParser parser = new FindTaskCommandParser();
+
+ @Test
+ public void parse_invalidArgs_throwsParseException() {
+ assertParseFailure(parser, "", String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindTaskCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_validArgs_returnsFindCommand() {
+ assertParseSuccess(parser, "test", new FindTaskCommand("test"));
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/parser/UpdateTaskCommandParserTest.java b/src/test/java/seedu/address/logic/parser/UpdateTaskCommandParserTest.java
new file mode 100644
index 00000000000..1db4efe4168
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/UpdateTaskCommandParserTest.java
@@ -0,0 +1,98 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.commands.CommandTestUtil.DEADLINE_SWIMMING;
+import static seedu.address.logic.commands.CommandTestUtil.DESC_SWIMMING;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_DEADLINE_MONTH;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_DEADLINE_YEAR;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_DEADLINE_MARCH;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_DESCRIPTION_SWIMMING;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.UpdateTaskCommand;
+
+public class UpdateTaskCommandParserTest {
+
+ private static final String MESSAGE_INVALID_FORMAT =
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, UpdateTaskCommand.MESSAGE_USAGE);
+
+ private UpdateTaskCommandParser parser = new UpdateTaskCommandParser();
+
+ @Test
+ public void parse_missingParts_failure() {
+ // no taskId specified
+ assertParseFailure(parser, VALID_DESCRIPTION_SWIMMING, String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ UpdateTaskCommand.MESSAGE_USAGE));
+
+ // no field specified
+ assertParseFailure(parser, "1", String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ UpdateTaskCommand.MESSAGE_NOT_EDITED));
+
+ // no taskId and no field specified
+ assertParseFailure(parser, "", String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ UpdateTaskCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_invalidPreamble_failure() {
+ // negative index
+ assertParseFailure(parser, "-5" + DESC_SWIMMING, MESSAGE_INVALID_FORMAT);
+
+ // zero index
+ assertParseFailure(parser, "0" + DESC_SWIMMING, 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_DEADLINE_YEAR, MESSAGE_INVALID_FORMAT); // invalid deadline
+ assertParseFailure(parser, "1" + INVALID_DEADLINE_MONTH, MESSAGE_INVALID_FORMAT); // invalid deadline
+ assertParseFailure(parser, "1" + " d/", String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ UpdateTaskCommand.MESSAGE_EMPTY_PARAMETERS)); // empty description
+ assertParseFailure(parser, "1" + " t/", String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ UpdateTaskCommand.MESSAGE_EMPTY_PARAMETERS)); // empty deadline
+
+ // valid description followed by invalid deadline
+ assertParseFailure(parser, "1" + DESC_SWIMMING + INVALID_DEADLINE_YEAR,
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, UpdateTaskCommand.MESSAGE_USAGE));
+
+ // invalid description followed by valid deadline.
+ assertParseFailure(parser, "1" + " d/" + DEADLINE_SWIMMING,
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, UpdateTaskCommand.MESSAGE_EMPTY_PARAMETERS));
+
+ // multiple invalid values
+ assertParseFailure(parser, "1" + " d/" + INVALID_DEADLINE_YEAR,
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, UpdateTaskCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_allFieldsSpecified_success() {
+ Integer targetTaskId = 1;
+ String userInput = targetTaskId + DESC_SWIMMING + DEADLINE_SWIMMING;
+
+ UpdateTaskCommand.UpdateTaskDescriptor descriptor = new UpdateTaskCommand.UpdateTaskDescriptor();
+ descriptor.setDescription(VALID_DESCRIPTION_SWIMMING);
+ descriptor.setDeadline(VALID_DEADLINE_MARCH);
+
+ UpdateTaskCommand expectedCommand = new UpdateTaskCommand(targetTaskId, descriptor);
+
+ assertParseSuccess(parser, userInput, expectedCommand);
+ }
+
+ @Test
+ public void parse_multipleRepeatedFields_failure() {
+ Integer targetTaskId = 1;
+ String userInput = targetTaskId + DESC_SWIMMING + DESC_SWIMMING;
+
+ assertParseFailure(parser, userInput, MESSAGE_INVALID_FORMAT);
+ }
+
+}
diff --git a/src/test/java/seedu/address/model/AddressBookTest.java b/src/test/java/seedu/address/model/AddressBookTest.java
index 87782528ecd..c63e32e6807 100644
--- a/src/test/java/seedu/address/model/AddressBookTest.java
+++ b/src/test/java/seedu/address/model/AddressBookTest.java
@@ -20,6 +20,7 @@
import javafx.collections.ObservableList;
import seedu.address.model.person.Person;
import seedu.address.model.person.exceptions.DuplicatePersonException;
+import seedu.address.model.task.Task;
import seedu.address.testutil.PersonBuilder;
public class AddressBookTest {
@@ -83,6 +84,32 @@ public void getPersonList_modifyList_throwsUnsupportedOperationException() {
assertThrows(UnsupportedOperationException.class, () -> addressBook.getPersonList().remove(0));
}
+ @Test
+ public void isValidTask_validTask_returnsTrue() {
+ Task task = new Task("dummy", "01/01/2023");
+ assertTrue(addressBook.validTask(task));
+ }
+
+ @Test
+ public void isValidTask_taskInvalidDescription_returnsFalse() {
+ Task task = new Task("dummyt/", "01/01/2023");
+ Task task2 = new Task("dummyd/", "01/01/2023");
+ assertFalse(addressBook.validTask(task));
+ assertFalse(addressBook.validTask(task2));
+ }
+
+ @Test
+ public void isValidTask_taskInvalidDeadline_returnsFalse() {
+ Task task = new Task("dummy", "01/01/2022");
+ Task task2 = new Task("dummy", "30/02/2023");
+ Task task3 = new Task("dummy", "o1/01/2023");
+ Task task4 = new Task("dummy", "01/*1/2023");
+ assertFalse(addressBook.validTask(task));
+ assertFalse(addressBook.validTask(task2));
+ assertFalse(addressBook.validTask(task3));
+ assertFalse(addressBook.validTask(task4));
+ }
+
/**
* A stub ReadOnlyAddressBook whose persons list can violate interface constraints.
*/
diff --git a/src/test/java/seedu/address/model/ModelManagerTest.java b/src/test/java/seedu/address/model/ModelManagerTest.java
index 2cf1418d116..17803dfe657 100644
--- a/src/test/java/seedu/address/model/ModelManagerTest.java
+++ b/src/test/java/seedu/address/model/ModelManagerTest.java
@@ -72,6 +72,18 @@ public void setAddressBookFilePath_validPath_setsAddressBookFilePath() {
assertEquals(path, modelManager.getAddressBookFilePath());
}
+ @Test
+ public void setTaskListFilePath_nullPath_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> modelManager.setTaskListFilePath(null));
+ }
+
+ @Test
+ public void setTaskListFilePath_validPath_setsAddressBookFilePath() {
+ Path path = Paths.get("task/list/file/path");
+ modelManager.setTaskListFilePath(path);
+ assertEquals(path, modelManager.getTaskListFilePath());
+ }
+
@Test
public void hasPerson_nullPerson_throwsNullPointerException() {
assertThrows(NullPointerException.class, () -> modelManager.hasPerson(null));
@@ -93,15 +105,21 @@ public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException
assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredPersonList().remove(0));
}
+ @Test
+ public void getFilteredTaskList_modifyList_throwsUnsupportedOperationException() {
+ assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredTaskList().remove(0));
+ }
+
@Test
public void equals() {
AddressBook addressBook = new AddressBookBuilder().withPerson(ALICE).withPerson(BENSON).build();
AddressBook differentAddressBook = new AddressBook();
UserPrefs userPrefs = new UserPrefs();
+ TaskList taskList = new TaskList();
// same values -> returns true
- modelManager = new ModelManager(addressBook, userPrefs);
- ModelManager modelManagerCopy = new ModelManager(addressBook, userPrefs);
+ modelManager = new ModelManager(addressBook, userPrefs, taskList);
+ ModelManager modelManagerCopy = new ModelManager(addressBook, userPrefs, taskList);
assertTrue(modelManager.equals(modelManagerCopy));
// same object -> returns true
@@ -114,12 +132,12 @@ public void equals() {
assertFalse(modelManager.equals(5));
// different addressBook -> returns false
- assertFalse(modelManager.equals(new ModelManager(differentAddressBook, userPrefs)));
+ assertFalse(modelManager.equals(new ModelManager(differentAddressBook, userPrefs, taskList)));
// 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(addressBook, userPrefs, taskList)));
// resets modelManager to initial state for upcoming tests
modelManager.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
@@ -127,6 +145,6 @@ public void equals() {
// different userPrefs -> returns false
UserPrefs differentUserPrefs = new UserPrefs();
differentUserPrefs.setAddressBookFilePath(Paths.get("differentFilePath"));
- assertFalse(modelManager.equals(new ModelManager(addressBook, differentUserPrefs)));
+ assertFalse(modelManager.equals(new ModelManager(addressBook, differentUserPrefs, taskList)));
}
}
diff --git a/src/test/java/seedu/address/model/TaskListTest.java b/src/test/java/seedu/address/model/TaskListTest.java
new file mode 100644
index 00000000000..b7a0fd392fe
--- /dev/null
+++ b/src/test/java/seedu/address/model/TaskListTest.java
@@ -0,0 +1,53 @@
+package seedu.address.model;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalTask.getTypicalTaskList;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+import org.junit.jupiter.api.Test;
+
+import javafx.collections.ObservableList;
+import seedu.address.model.task.Task;
+
+public class TaskListTest {
+ private final TaskList taskList = new TaskList();
+
+ @Test
+ public void constructor() {
+ assertEquals(Collections.emptyList(), taskList.getTaskList());
+ }
+
+ @Test
+ public void addTask_success() {
+ ArrayList stubTaskList = new ArrayList<>();
+ stubTaskList.add(new Task("description", "2022"));
+ taskList.addTask(new Task("description", "2022"));
+ assertEquals(stubTaskList.toString(), taskList.getTaskList().toString());
+ }
+
+ @Test
+ public void addTask_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> taskList.addTask(null));
+ }
+
+ @Test
+ public void resetData_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> taskList.resetData(null));
+ }
+
+ @Test
+ public void resetData_withValidReadOnlyTaskList_replacesData() {
+ TaskList newData = getTypicalTaskList();
+ taskList.resetData(newData);
+ assertEquals(newData.getTaskList(), taskList.getTaskList());
+ }
+
+ @Test
+ public void getObservableTaskList_success() {
+ ObservableList observableTaskList = taskList.getObservableTaskList();
+ assertEquals(observableTaskList, taskList.getTaskList());
+ }
+}
diff --git a/src/test/java/seedu/address/model/UserPrefsTest.java b/src/test/java/seedu/address/model/UserPrefsTest.java
index b1307a70d52..0bd443c111e 100644
--- a/src/test/java/seedu/address/model/UserPrefsTest.java
+++ b/src/test/java/seedu/address/model/UserPrefsTest.java
@@ -18,4 +18,9 @@ public void setAddressBookFilePath_nullPath_throwsNullPointerException() {
assertThrows(NullPointerException.class, () -> userPrefs.setAddressBookFilePath(null));
}
+ @Test
+ public void setTaskListFilePath_nullPath_throwsNullPointerException() {
+ UserPrefs userPrefs = new UserPrefs();
+ assertThrows(NullPointerException.class, () -> userPrefs.setTaskListFilePath(null));
+ }
}
diff --git a/src/test/java/seedu/address/model/task/TaskTest.java b/src/test/java/seedu/address/model/task/TaskTest.java
new file mode 100644
index 00000000000..3f924fa3a89
--- /dev/null
+++ b/src/test/java/seedu/address/model/task/TaskTest.java
@@ -0,0 +1,42 @@
+package seedu.address.model.task;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static seedu.address.testutil.Assert.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+public class TaskTest {
+
+ @Test
+ public void constructor_allNull_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new Task(null, null));
+ }
+
+ @Test
+ public void constructor_descriptionNull_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new Task(null, "2022"));
+ }
+
+ @Test
+ public void constructor_deadlineNull_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new Task("description", null));
+ }
+
+ @Test
+ public void getDescription_success() {
+ Task task = new Task("dummy", "01/01/2022");
+ assertEquals("dummy", task.getDescription());
+ }
+
+ @Test
+ public void getDeadline_success() {
+ Task task = new Task("dummy", "01/01/2022");
+ assertEquals("01/01/2022", task.getDeadline());
+ }
+
+ @Test
+ public void toString_success() {
+ Task task = new Task("description", "2022");
+ assertEquals(task.toString(), "Task: description 2022");
+ }
+}
diff --git a/src/test/java/seedu/address/model/task/UniqueTaskListTest.java b/src/test/java/seedu/address/model/task/UniqueTaskListTest.java
new file mode 100644
index 00000000000..3d55e27bec9
--- /dev/null
+++ b/src/test/java/seedu/address/model/task/UniqueTaskListTest.java
@@ -0,0 +1,59 @@
+package seedu.address.model.task;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalTask.DUMMY_TASK;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+
+
+public class UniqueTaskListTest {
+
+ private final UniqueTaskList uniqueTaskList = new UniqueTaskList();
+
+ @Test
+ public void contains_nullTask_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueTaskList.contains(null));
+ }
+
+ @Test
+ public void contains_taskNotInList_returnsFalse() {
+ assertFalse(uniqueTaskList.contains(DUMMY_TASK));
+ }
+
+ @Test
+ public void contains_taskInList_returnsTrue() throws CommandException {
+ uniqueTaskList.addTask(DUMMY_TASK, 1);
+ assertTrue(uniqueTaskList.contains(DUMMY_TASK));
+ }
+
+ @Test
+ public void add_nullTask_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueTaskList.addTask(null));
+ }
+
+
+ @Test
+ public void remove_nullTask_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueTaskList.deleteTask(null));
+ }
+
+ @Test
+ public void remove_existingTask_removesTask() {
+ UniqueTaskList expectedUniqueTaskList = uniqueTaskList;
+ Task test = new Task("test", "30/03/2022");
+ uniqueTaskList.addTask(test);
+ uniqueTaskList.deleteTask(1);
+ assertEquals(uniqueTaskList, expectedUniqueTaskList);
+ }
+
+ @Test
+ public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() {
+ assertThrows(UnsupportedOperationException.class, ()
+ -> uniqueTaskList.asUnmodifiableObservableList().remove(0));
+ }
+}
diff --git a/src/test/java/seedu/address/storage/JsonAdaptedTaskTest.java b/src/test/java/seedu/address/storage/JsonAdaptedTaskTest.java
new file mode 100644
index 00000000000..fc7cb54ae53
--- /dev/null
+++ b/src/test/java/seedu/address/storage/JsonAdaptedTaskTest.java
@@ -0,0 +1,33 @@
+package seedu.address.storage;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static seedu.address.storage.JsonAdaptedTask.MISSING_FIELD_MESSAGE_FORMAT;
+import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalTask.DUMMY_TASK;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+
+class JsonAdaptedTaskTest {
+
+ @Test
+ public void toModelType_validTask_returnsTask() throws Exception {
+ JsonAdaptedTask task = new JsonAdaptedTask("dummy", "01/01/2023");
+ assertEquals(DUMMY_TASK.toString(), task.toModelType().toString());
+ }
+
+ @Test
+ void toModelType_nullDescription_throwsIllegalValueException() {
+ JsonAdaptedTask task = new JsonAdaptedTask(null, "01/01/2023");
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, "description");
+ assertThrows(IllegalValueException.class, expectedMessage, task::toModelType);
+ }
+
+ @Test
+ void toModelType_nullDeadline_throwsIllegalValueException() {
+ JsonAdaptedTask task = new JsonAdaptedTask("description", null);
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, "deadline");
+ assertThrows(IllegalValueException.class, expectedMessage, task::toModelType);
+ }
+}
diff --git a/src/test/java/seedu/address/storage/JsonSerializableTaskListTest.java b/src/test/java/seedu/address/storage/JsonSerializableTaskListTest.java
new file mode 100644
index 00000000000..5f484f32da5
--- /dev/null
+++ b/src/test/java/seedu/address/storage/JsonSerializableTaskListTest.java
@@ -0,0 +1,36 @@
+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.TaskList;
+import seedu.address.testutil.TypicalTask;
+
+class JsonSerializableTaskListTest {
+ private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonSerializableTaskListTest");
+ private static final Path TYPICAL_TASK_FILE = TEST_DATA_FOLDER.resolve("typicalTaskTaskList.json");
+ private static final Path INVALID_TASK_FILE = TEST_DATA_FOLDER.resolve("invalidTaskTaskList.json");
+
+ @Test
+ public void toModelType_typicalTaskFile_success() throws Exception {
+ JsonSerializableTaskList dataFromFile = JsonUtil.readJsonFile(TYPICAL_TASK_FILE,
+ JsonSerializableTaskList.class).get();
+ TaskList taskListFromFile = dataFromFile.toModelType();
+ TaskList typicalTaskList = TypicalTask.getTypicalTaskList();
+ assertEquals(taskListFromFile.getTaskList().toString(), typicalTaskList.getTaskList().toString());
+ }
+
+ @Test
+ public void toModelType_invalidTaskFile_throwsIllegalValueException() throws Exception {
+ JsonSerializableTaskList dataFromFile = JsonUtil.readJsonFile(INVALID_TASK_FILE,
+ JsonSerializableTaskList.class).get();
+ assertThrows(IllegalValueException.class, dataFromFile::toModelType);
+ }
+}
diff --git a/src/test/java/seedu/address/storage/JsonTaskListStorageTest.java b/src/test/java/seedu/address/storage/JsonTaskListStorageTest.java
new file mode 100644
index 00000000000..e57c9ccb60d
--- /dev/null
+++ b/src/test/java/seedu/address/storage/JsonTaskListStorageTest.java
@@ -0,0 +1,103 @@
+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.TypicalTask.getTypicalTaskList;
+
+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.ReadOnlyTaskList;
+import seedu.address.model.TaskList;
+import seedu.address.model.task.Task;
+
+class JsonTaskListStorageTest {
+ private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonTaskListStorageTest");
+
+ @TempDir
+ public Path testFolder;
+
+ @Test
+ public void readTaskList_nullFilePath_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> readTaskList(null));
+ }
+
+ private java.util.Optional readTaskList(String filePath) throws Exception {
+ return new JsonTaskListStorage(Paths.get(filePath)).readTaskList(addToTestDataPathIfNotNull(filePath));
+ }
+
+ private Path addToTestDataPathIfNotNull(String prefsFileInTestDataFolder) {
+ return prefsFileInTestDataFolder != null
+ ? TEST_DATA_FOLDER.resolve(prefsFileInTestDataFolder)
+ : null;
+ }
+
+ @Test
+ public void readTaskList_missingFile_emptyResult() throws Exception {
+ assertFalse(readTaskList("NonExistentFile.json").isPresent());
+ }
+
+ @Test
+ public void readTaskList_notJsonFormat_exceptionThrown() {
+ assertThrows(DataConversionException.class, () -> readTaskList("notJsonFormatAddressBook.json"));
+ }
+
+ @Test
+ public void readTaskList_invalidPersonAddressBook_throwDataConversionException() {
+ assertThrows(DataConversionException.class, () -> readTaskList("invalidTaskTaskList.json"));
+ }
+
+ @Test
+ public void readTaskList_invalidAndValidPersonAddressBook_throwDataConversionException() {
+ assertThrows(DataConversionException.class, () -> readTaskList("invalidAndValidTaskTaskList.json"));
+ }
+
+ @Test
+ public void readAndSaveAddressBook_allInOrder_success() throws Exception {
+ Path filePath = testFolder.resolve("TempAddressBook.json");
+ TaskList original = getTypicalTaskList();
+ JsonTaskListStorage jsonTaskListStorage = new JsonTaskListStorage(filePath);
+
+ // Save in new file and read back
+ jsonTaskListStorage.saveTaskList(original, filePath);
+ ReadOnlyTaskList readBack = jsonTaskListStorage.readTaskList(filePath).get();
+ assertEquals(original.getTaskList().toString(), new TaskList(readBack).getTaskList().toString());
+
+ // Modify data, overwrite exiting file, and read back
+ original.addTask(new Task("dummy", "01/01/2023"));
+ original.deleteTask(1);
+ jsonTaskListStorage.saveTaskList(original, filePath);
+ readBack = jsonTaskListStorage.readTaskList(filePath).get();
+ assertEquals(original.getTaskList().toString(), new TaskList(readBack).getTaskList().toString());
+
+ // Save and read without specifying file path
+ original.addTask(new Task("dummy", "01/01/2023"));
+ jsonTaskListStorage.saveTaskList(original); // file path not specified
+ readBack = jsonTaskListStorage.readTaskList().get(); // file path not specified
+ assertEquals(original.getTaskList().toString(), new TaskList(readBack).getTaskList().toString());
+ }
+
+ @Test
+ public void saveTaskList_nullTaskList_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> saveTaskList(null, "SomeFile.json"));
+ }
+
+ private void saveTaskList(ReadOnlyTaskList taskList, String filePath) {
+ try {
+ new JsonTaskListStorage(Paths.get(filePath)).saveTaskList(taskList, addToTestDataPathIfNotNull(filePath));
+ } catch (IOException ioe) {
+ throw new AssertionError("There should not be an error writing to the file.", ioe);
+ }
+ }
+
+ @Test
+ public void saveTaskList_nullFilePath_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> saveTaskList(new TaskList(), null));
+ }
+}
diff --git a/src/test/java/seedu/address/storage/StorageManagerTest.java b/src/test/java/seedu/address/storage/StorageManagerTest.java
index 99a16548970..ca7ffff8383 100644
--- a/src/test/java/seedu/address/storage/StorageManagerTest.java
+++ b/src/test/java/seedu/address/storage/StorageManagerTest.java
@@ -3,6 +3,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import static seedu.address.testutil.TypicalTask.getTypicalTaskList;
import java.nio.file.Path;
@@ -13,6 +14,8 @@
import seedu.address.commons.core.GuiSettings;
import seedu.address.model.AddressBook;
import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyTaskList;
+import seedu.address.model.TaskList;
import seedu.address.model.UserPrefs;
public class StorageManagerTest {
@@ -26,7 +29,8 @@ public class StorageManagerTest {
public void setUp() {
JsonAddressBookStorage addressBookStorage = new JsonAddressBookStorage(getTempFilePath("ab"));
JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(getTempFilePath("prefs"));
- storageManager = new StorageManager(addressBookStorage, userPrefsStorage);
+ JsonTaskListStorage taskListStorage = new JsonTaskListStorage(getTempFilePath("tasklist"));
+ storageManager = new StorageManager(addressBookStorage, userPrefsStorage, taskListStorage);
}
private Path getTempFilePath(String fileName) {
@@ -65,4 +69,16 @@ public void getAddressBookFilePath() {
assertNotNull(storageManager.getAddressBookFilePath());
}
+ @Test
+ public void taskListReadSave() throws Exception {
+ TaskList original = getTypicalTaskList();
+ storageManager.saveTaskList(original);
+ ReadOnlyTaskList retrieved = storageManager.readTaskList().get();
+ assertEquals(original.getTaskList().toString(), new TaskList(retrieved).getTaskList().toString());
+ }
+
+ @Test
+ public void getTaskListFilePath() {
+ assertNotNull(storageManager.getTaskListFilePath());
+ }
}
diff --git a/src/test/java/seedu/address/testutil/TypicalPersons.java b/src/test/java/seedu/address/testutil/TypicalPersons.java
index fec76fb7129..2937d1066ef 100644
--- a/src/test/java/seedu/address/testutil/TypicalPersons.java
+++ b/src/test/java/seedu/address/testutil/TypicalPersons.java
@@ -67,6 +67,7 @@ public static AddressBook getTypicalAddressBook() {
for (Person person : getTypicalPersons()) {
ab.addPerson(person);
}
+
return ab;
}
diff --git a/src/test/java/seedu/address/testutil/TypicalTask.java b/src/test/java/seedu/address/testutil/TypicalTask.java
new file mode 100644
index 00000000000..cf26d910884
--- /dev/null
+++ b/src/test/java/seedu/address/testutil/TypicalTask.java
@@ -0,0 +1,26 @@
+package seedu.address.testutil;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import seedu.address.model.TaskList;
+import seedu.address.model.task.Task;
+
+public class TypicalTask {
+ public static final Task DUMMY_TASK = new Task("dummy", "01/01/2023");
+
+ private TypicalTask() {} // prevent instantiation
+
+ public static TaskList getTypicalTaskList() {
+ TaskList taskList = new TaskList();
+ for (Task task : getTypicalTask()) {
+ taskList.addTask(task);
+ }
+ return taskList;
+ }
+
+ public static List getTypicalTask() {
+ return new ArrayList<>(Arrays.asList(DUMMY_TASK));
+ }
+}