diff --git a/.gitignore b/.gitignore
index 284c4ca7cd9..7257bcb496b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@ src/main/resources/docs/
/data/
/config.json
/preferences.json
+/settings.json
/*.log.*
hs_err_pid[0-9]*.log
@@ -21,3 +22,4 @@ src/test/data/sandbox/
# MacOS custom attributes files created by Finder
.DS_Store
docs/_site/
+bin/
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000000..14c8b89250c
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "java.debug.settings.onBuildFailureProceed": true
+}
diff --git a/README.md b/README.md
index 13f5c77403f..eb15a2abbad 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,17 @@
-[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions)
+[![CI Status](https://github.com/AY2324S1-CS2103T-W15-1/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2324S1-CS2103T-W15-1/tp/actions)
![Ui](docs/images/Ui.png)
-* This is **a sample project for Software Engineering (SE) students**.
+
+Team Members: Raman, Tejas, Jackie, Kiat Win, Yarn Meng
+* This is **a CLI Task Manager for Computer Science professors**.
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.
+ * as a CLI Task Manager for CS professors.
+ * as a grading component for CS2103T students.
+* The project simulates a CLI Task Manager for Professors to easily schedule and manage tasks.
+ * It is **written in OOP fashion**. It provides a **brilliantly well-written** code base **around 6 KLoC.**
+ * It comes with an **excellent level of user and developer documentation**.
+* It is named `ProfPlan` as it helps CS professors plan their tasks and schedules.
+* For the detailed documentation of this project, see the **[ProfPlan Product Website - TO BE UPDATED](https://github.com/AY2324S1-CS2103T-W15-1/tp/blob/master/docs/DeveloperGuide.md)**.
+
+* This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org).
diff --git a/bin/main/images/calendar.png b/bin/main/images/calendar.png
new file mode 100644
index 00000000000..8b2bdf4f1c1
Binary files /dev/null and b/bin/main/images/calendar.png differ
diff --git a/bin/main/images/clock.png b/bin/main/images/clock.png
new file mode 100644
index 00000000000..0807cbf6451
Binary files /dev/null and b/bin/main/images/clock.png differ
diff --git a/bin/main/images/fail.png b/bin/main/images/fail.png
new file mode 100644
index 00000000000..6daf01290dd
Binary files /dev/null and b/bin/main/images/fail.png differ
diff --git a/bin/main/images/help_icon.png b/bin/main/images/help_icon.png
new file mode 100644
index 00000000000..f8e80d6c1c5
Binary files /dev/null and b/bin/main/images/help_icon.png differ
diff --git a/bin/main/images/info_icon.png b/bin/main/images/info_icon.png
new file mode 100644
index 00000000000..f8cef714095
Binary files /dev/null and b/bin/main/images/info_icon.png differ
diff --git a/bin/main/profplan/AppParameters.class b/bin/main/profplan/AppParameters.class
new file mode 100644
index 00000000000..439cc1c6547
Binary files /dev/null and b/bin/main/profplan/AppParameters.class differ
diff --git a/bin/main/profplan/Main.class b/bin/main/profplan/Main.class
new file mode 100644
index 00000000000..a69ed4d421f
Binary files /dev/null and b/bin/main/profplan/Main.class differ
diff --git a/bin/main/profplan/MainApp.class b/bin/main/profplan/MainApp.class
new file mode 100644
index 00000000000..a05e5a4ae8a
Binary files /dev/null and b/bin/main/profplan/MainApp.class differ
diff --git a/bin/main/profplan/commons/core/Config.class b/bin/main/profplan/commons/core/Config.class
new file mode 100644
index 00000000000..ab516899bb4
Binary files /dev/null and b/bin/main/profplan/commons/core/Config.class differ
diff --git a/bin/main/profplan/commons/core/GuiSettings.class b/bin/main/profplan/commons/core/GuiSettings.class
new file mode 100644
index 00000000000..e783d1d40fa
Binary files /dev/null and b/bin/main/profplan/commons/core/GuiSettings.class differ
diff --git a/bin/main/profplan/commons/core/LogsCenter.class b/bin/main/profplan/commons/core/LogsCenter.class
new file mode 100644
index 00000000000..2a61f434678
Binary files /dev/null and b/bin/main/profplan/commons/core/LogsCenter.class differ
diff --git a/bin/main/profplan/commons/core/Version.class b/bin/main/profplan/commons/core/Version.class
new file mode 100644
index 00000000000..29a70dce86b
Binary files /dev/null and b/bin/main/profplan/commons/core/Version.class differ
diff --git a/bin/main/profplan/commons/core/index/Index.class b/bin/main/profplan/commons/core/index/Index.class
new file mode 100644
index 00000000000..4695c91d244
Binary files /dev/null and b/bin/main/profplan/commons/core/index/Index.class differ
diff --git a/bin/main/profplan/commons/exceptions/DataLoadingException.class b/bin/main/profplan/commons/exceptions/DataLoadingException.class
new file mode 100644
index 00000000000..08edc810934
Binary files /dev/null and b/bin/main/profplan/commons/exceptions/DataLoadingException.class differ
diff --git a/bin/main/profplan/commons/exceptions/IllegalValueException.class b/bin/main/profplan/commons/exceptions/IllegalValueException.class
new file mode 100644
index 00000000000..c32c5f27c03
Binary files /dev/null and b/bin/main/profplan/commons/exceptions/IllegalValueException.class differ
diff --git a/bin/main/profplan/commons/util/AppUtil.class b/bin/main/profplan/commons/util/AppUtil.class
new file mode 100644
index 00000000000..ba33fc51daf
Binary files /dev/null and b/bin/main/profplan/commons/util/AppUtil.class differ
diff --git a/bin/main/profplan/commons/util/CollectionUtil.class b/bin/main/profplan/commons/util/CollectionUtil.class
new file mode 100644
index 00000000000..cd444575483
Binary files /dev/null and b/bin/main/profplan/commons/util/CollectionUtil.class differ
diff --git a/bin/main/profplan/commons/util/ConfigUtil.class b/bin/main/profplan/commons/util/ConfigUtil.class
new file mode 100644
index 00000000000..85ad59b3dd0
Binary files /dev/null and b/bin/main/profplan/commons/util/ConfigUtil.class differ
diff --git a/bin/main/profplan/commons/util/FileUtil.class b/bin/main/profplan/commons/util/FileUtil.class
new file mode 100644
index 00000000000..83efff8a03e
Binary files /dev/null and b/bin/main/profplan/commons/util/FileUtil.class differ
diff --git a/bin/main/profplan/commons/util/JsonUtil$LevelDeserializer.class b/bin/main/profplan/commons/util/JsonUtil$LevelDeserializer.class
new file mode 100644
index 00000000000..e227c354815
Binary files /dev/null and b/bin/main/profplan/commons/util/JsonUtil$LevelDeserializer.class differ
diff --git a/bin/main/profplan/commons/util/JsonUtil.class b/bin/main/profplan/commons/util/JsonUtil.class
new file mode 100644
index 00000000000..db34b77ee79
Binary files /dev/null and b/bin/main/profplan/commons/util/JsonUtil.class differ
diff --git a/bin/main/profplan/commons/util/StringUtil.class b/bin/main/profplan/commons/util/StringUtil.class
new file mode 100644
index 00000000000..f9d8e789187
Binary files /dev/null and b/bin/main/profplan/commons/util/StringUtil.class differ
diff --git a/bin/main/profplan/commons/util/ToStringBuilder.class b/bin/main/profplan/commons/util/ToStringBuilder.class
new file mode 100644
index 00000000000..5f53ab1b813
Binary files /dev/null and b/bin/main/profplan/commons/util/ToStringBuilder.class differ
diff --git a/bin/main/profplan/logic/Logic.class b/bin/main/profplan/logic/Logic.class
new file mode 100644
index 00000000000..2f4902d78a9
Binary files /dev/null and b/bin/main/profplan/logic/Logic.class differ
diff --git a/bin/main/profplan/logic/LogicManager.class b/bin/main/profplan/logic/LogicManager.class
new file mode 100644
index 00000000000..826f13ac701
Binary files /dev/null and b/bin/main/profplan/logic/LogicManager.class differ
diff --git a/bin/main/profplan/logic/Messages.class b/bin/main/profplan/logic/Messages.class
new file mode 100644
index 00000000000..cd268d094f7
Binary files /dev/null and b/bin/main/profplan/logic/Messages.class differ
diff --git a/bin/main/profplan/logic/commands/AddCommand.class b/bin/main/profplan/logic/commands/AddCommand.class
new file mode 100644
index 00000000000..5a3025d8a5f
Binary files /dev/null and b/bin/main/profplan/logic/commands/AddCommand.class differ
diff --git a/bin/main/profplan/logic/commands/ClearCommand.class b/bin/main/profplan/logic/commands/ClearCommand.class
new file mode 100644
index 00000000000..1f1826bf519
Binary files /dev/null and b/bin/main/profplan/logic/commands/ClearCommand.class differ
diff --git a/bin/main/profplan/logic/commands/Command.class b/bin/main/profplan/logic/commands/Command.class
new file mode 100644
index 00000000000..d15bfb51848
Binary files /dev/null and b/bin/main/profplan/logic/commands/Command.class differ
diff --git a/bin/main/profplan/logic/commands/CommandResult.class b/bin/main/profplan/logic/commands/CommandResult.class
new file mode 100644
index 00000000000..72f10d2e67d
Binary files /dev/null and b/bin/main/profplan/logic/commands/CommandResult.class differ
diff --git a/bin/main/profplan/logic/commands/DeleteCommand.class b/bin/main/profplan/logic/commands/DeleteCommand.class
new file mode 100644
index 00000000000..bfdf3a97a4d
Binary files /dev/null and b/bin/main/profplan/logic/commands/DeleteCommand.class differ
diff --git a/bin/main/profplan/logic/commands/EditCommand$EditTaskDescriptor.class b/bin/main/profplan/logic/commands/EditCommand$EditTaskDescriptor.class
new file mode 100644
index 00000000000..edfb18189bd
Binary files /dev/null and b/bin/main/profplan/logic/commands/EditCommand$EditTaskDescriptor.class differ
diff --git a/bin/main/profplan/logic/commands/EditCommand.class b/bin/main/profplan/logic/commands/EditCommand.class
new file mode 100644
index 00000000000..47f950028bf
Binary files /dev/null and b/bin/main/profplan/logic/commands/EditCommand.class differ
diff --git a/bin/main/profplan/logic/commands/ExitCommand.class b/bin/main/profplan/logic/commands/ExitCommand.class
new file mode 100644
index 00000000000..c27d4d28b0b
Binary files /dev/null and b/bin/main/profplan/logic/commands/ExitCommand.class differ
diff --git a/bin/main/profplan/logic/commands/FilterCommand.class b/bin/main/profplan/logic/commands/FilterCommand.class
new file mode 100644
index 00000000000..c552ae60c85
Binary files /dev/null and b/bin/main/profplan/logic/commands/FilterCommand.class differ
diff --git a/bin/main/profplan/logic/commands/FindCommand.class b/bin/main/profplan/logic/commands/FindCommand.class
new file mode 100644
index 00000000000..81efb620dfd
Binary files /dev/null and b/bin/main/profplan/logic/commands/FindCommand.class differ
diff --git a/bin/main/profplan/logic/commands/HelpCommand.class b/bin/main/profplan/logic/commands/HelpCommand.class
new file mode 100644
index 00000000000..1323fd2c18a
Binary files /dev/null and b/bin/main/profplan/logic/commands/HelpCommand.class differ
diff --git a/bin/main/profplan/logic/commands/ListCommand.class b/bin/main/profplan/logic/commands/ListCommand.class
new file mode 100644
index 00000000000..c4c9d349e61
Binary files /dev/null and b/bin/main/profplan/logic/commands/ListCommand.class differ
diff --git a/bin/main/profplan/logic/commands/MarkCommand.class b/bin/main/profplan/logic/commands/MarkCommand.class
new file mode 100644
index 00000000000..69ce35dc178
Binary files /dev/null and b/bin/main/profplan/logic/commands/MarkCommand.class differ
diff --git a/bin/main/profplan/logic/commands/UnmarkCommand.class b/bin/main/profplan/logic/commands/UnmarkCommand.class
new file mode 100644
index 00000000000..2fa4a5e51d5
Binary files /dev/null and b/bin/main/profplan/logic/commands/UnmarkCommand.class differ
diff --git a/bin/main/profplan/logic/commands/exceptions/CommandException.class b/bin/main/profplan/logic/commands/exceptions/CommandException.class
new file mode 100644
index 00000000000..9655e79dd13
Binary files /dev/null and b/bin/main/profplan/logic/commands/exceptions/CommandException.class differ
diff --git a/bin/main/profplan/logic/parser/AddCommandParser.class b/bin/main/profplan/logic/parser/AddCommandParser.class
new file mode 100644
index 00000000000..13798e136a9
Binary files /dev/null and b/bin/main/profplan/logic/parser/AddCommandParser.class differ
diff --git a/bin/main/profplan/logic/parser/ArgumentMultimap.class b/bin/main/profplan/logic/parser/ArgumentMultimap.class
new file mode 100644
index 00000000000..8e0d264d395
Binary files /dev/null and b/bin/main/profplan/logic/parser/ArgumentMultimap.class differ
diff --git a/bin/main/profplan/logic/parser/ArgumentTokenizer$PrefixPosition.class b/bin/main/profplan/logic/parser/ArgumentTokenizer$PrefixPosition.class
new file mode 100644
index 00000000000..9667deee74c
Binary files /dev/null and b/bin/main/profplan/logic/parser/ArgumentTokenizer$PrefixPosition.class differ
diff --git a/bin/main/profplan/logic/parser/ArgumentTokenizer.class b/bin/main/profplan/logic/parser/ArgumentTokenizer.class
new file mode 100644
index 00000000000..531d4f7120b
Binary files /dev/null and b/bin/main/profplan/logic/parser/ArgumentTokenizer.class differ
diff --git a/bin/main/profplan/logic/parser/CliSyntax.class b/bin/main/profplan/logic/parser/CliSyntax.class
new file mode 100644
index 00000000000..126b3d6696e
Binary files /dev/null and b/bin/main/profplan/logic/parser/CliSyntax.class differ
diff --git a/bin/main/profplan/logic/parser/DeleteCommandParser.class b/bin/main/profplan/logic/parser/DeleteCommandParser.class
new file mode 100644
index 00000000000..36d04fbe624
Binary files /dev/null and b/bin/main/profplan/logic/parser/DeleteCommandParser.class differ
diff --git a/bin/main/profplan/logic/parser/EditCommandParser.class b/bin/main/profplan/logic/parser/EditCommandParser.class
new file mode 100644
index 00000000000..14edb6215b4
Binary files /dev/null and b/bin/main/profplan/logic/parser/EditCommandParser.class differ
diff --git a/bin/main/profplan/logic/parser/FilterCommandParser.class b/bin/main/profplan/logic/parser/FilterCommandParser.class
new file mode 100644
index 00000000000..81c4928b8b0
Binary files /dev/null and b/bin/main/profplan/logic/parser/FilterCommandParser.class differ
diff --git a/bin/main/profplan/logic/parser/FindCommandParser.class b/bin/main/profplan/logic/parser/FindCommandParser.class
new file mode 100644
index 00000000000..527741fcf07
Binary files /dev/null and b/bin/main/profplan/logic/parser/FindCommandParser.class differ
diff --git a/bin/main/profplan/logic/parser/MarkCommandParser.class b/bin/main/profplan/logic/parser/MarkCommandParser.class
new file mode 100644
index 00000000000..58c1464f9e3
Binary files /dev/null and b/bin/main/profplan/logic/parser/MarkCommandParser.class differ
diff --git a/bin/main/profplan/logic/parser/Parser.class b/bin/main/profplan/logic/parser/Parser.class
new file mode 100644
index 00000000000..8d55927c43b
Binary files /dev/null and b/bin/main/profplan/logic/parser/Parser.class differ
diff --git a/bin/main/profplan/logic/parser/ParserUtil.class b/bin/main/profplan/logic/parser/ParserUtil.class
new file mode 100644
index 00000000000..43f63c51927
Binary files /dev/null and b/bin/main/profplan/logic/parser/ParserUtil.class differ
diff --git a/bin/main/profplan/logic/parser/Prefix.class b/bin/main/profplan/logic/parser/Prefix.class
new file mode 100644
index 00000000000..5f3b8935edc
Binary files /dev/null and b/bin/main/profplan/logic/parser/Prefix.class differ
diff --git a/bin/main/profplan/logic/parser/ProfPlanParser.class b/bin/main/profplan/logic/parser/ProfPlanParser.class
new file mode 100644
index 00000000000..1d7139d250f
Binary files /dev/null and b/bin/main/profplan/logic/parser/ProfPlanParser.class differ
diff --git a/bin/main/profplan/logic/parser/UnmarkCommandParser.class b/bin/main/profplan/logic/parser/UnmarkCommandParser.class
new file mode 100644
index 00000000000..1a2cf9ade53
Binary files /dev/null and b/bin/main/profplan/logic/parser/UnmarkCommandParser.class differ
diff --git a/bin/main/profplan/logic/parser/exceptions/ParseException.class b/bin/main/profplan/logic/parser/exceptions/ParseException.class
new file mode 100644
index 00000000000..aea639efc77
Binary files /dev/null and b/bin/main/profplan/logic/parser/exceptions/ParseException.class differ
diff --git a/bin/main/profplan/model/Model.class b/bin/main/profplan/model/Model.class
new file mode 100644
index 00000000000..4efa7f9017f
Binary files /dev/null and b/bin/main/profplan/model/Model.class differ
diff --git a/bin/main/profplan/model/ModelManager.class b/bin/main/profplan/model/ModelManager.class
new file mode 100644
index 00000000000..b0959d62dfb
Binary files /dev/null and b/bin/main/profplan/model/ModelManager.class differ
diff --git a/bin/main/profplan/model/ProfPlan.class b/bin/main/profplan/model/ProfPlan.class
new file mode 100644
index 00000000000..ad3880d2d91
Binary files /dev/null and b/bin/main/profplan/model/ProfPlan.class differ
diff --git a/bin/main/profplan/model/ReadOnlyProfPlan.class b/bin/main/profplan/model/ReadOnlyProfPlan.class
new file mode 100644
index 00000000000..62eb7644c4b
Binary files /dev/null and b/bin/main/profplan/model/ReadOnlyProfPlan.class differ
diff --git a/bin/main/profplan/model/ReadOnlyUserPrefs.class b/bin/main/profplan/model/ReadOnlyUserPrefs.class
new file mode 100644
index 00000000000..7417db5435c
Binary files /dev/null and b/bin/main/profplan/model/ReadOnlyUserPrefs.class differ
diff --git a/bin/main/profplan/model/UserPrefs.class b/bin/main/profplan/model/UserPrefs.class
new file mode 100644
index 00000000000..f40fae154b7
Binary files /dev/null and b/bin/main/profplan/model/UserPrefs.class differ
diff --git a/bin/main/profplan/model/tag/Tag.class b/bin/main/profplan/model/tag/Tag.class
new file mode 100644
index 00000000000..a526017b358
Binary files /dev/null and b/bin/main/profplan/model/tag/Tag.class differ
diff --git a/bin/main/profplan/model/task/DueDate.class b/bin/main/profplan/model/task/DueDate.class
new file mode 100644
index 00000000000..1eafc8f846d
Binary files /dev/null and b/bin/main/profplan/model/task/DueDate.class differ
diff --git a/bin/main/profplan/model/task/Name.class b/bin/main/profplan/model/task/Name.class
new file mode 100644
index 00000000000..d797f8c69dd
Binary files /dev/null and b/bin/main/profplan/model/task/Name.class differ
diff --git a/bin/main/profplan/model/task/Priority.class b/bin/main/profplan/model/task/Priority.class
new file mode 100644
index 00000000000..84dc145c761
Binary files /dev/null and b/bin/main/profplan/model/task/Priority.class differ
diff --git a/bin/main/profplan/model/task/Status.class b/bin/main/profplan/model/task/Status.class
new file mode 100644
index 00000000000..36d1779b885
Binary files /dev/null and b/bin/main/profplan/model/task/Status.class differ
diff --git a/bin/main/profplan/model/task/Task.class b/bin/main/profplan/model/task/Task.class
new file mode 100644
index 00000000000..14afbf517c5
Binary files /dev/null and b/bin/main/profplan/model/task/Task.class differ
diff --git a/bin/main/profplan/model/task/UniqueTaskList.class b/bin/main/profplan/model/task/UniqueTaskList.class
new file mode 100644
index 00000000000..410b3e78419
Binary files /dev/null and b/bin/main/profplan/model/task/UniqueTaskList.class differ
diff --git a/bin/main/profplan/model/task/exceptions/DuplicateTaskException.class b/bin/main/profplan/model/task/exceptions/DuplicateTaskException.class
new file mode 100644
index 00000000000..1ad88c4772a
Binary files /dev/null and b/bin/main/profplan/model/task/exceptions/DuplicateTaskException.class differ
diff --git a/bin/main/profplan/model/task/exceptions/TaskNotFoundException.class b/bin/main/profplan/model/task/exceptions/TaskNotFoundException.class
new file mode 100644
index 00000000000..32452b99586
Binary files /dev/null and b/bin/main/profplan/model/task/exceptions/TaskNotFoundException.class differ
diff --git a/bin/main/profplan/model/util/SampleDataUtil.class b/bin/main/profplan/model/util/SampleDataUtil.class
new file mode 100644
index 00000000000..3f14e4e8a85
Binary files /dev/null and b/bin/main/profplan/model/util/SampleDataUtil.class differ
diff --git a/bin/main/profplan/storage/JsonAdaptedTag.class b/bin/main/profplan/storage/JsonAdaptedTag.class
new file mode 100644
index 00000000000..56d0cfa5b4d
Binary files /dev/null and b/bin/main/profplan/storage/JsonAdaptedTag.class differ
diff --git a/bin/main/profplan/storage/JsonAdaptedTask.class b/bin/main/profplan/storage/JsonAdaptedTask.class
new file mode 100644
index 00000000000..81f8bf86af1
Binary files /dev/null and b/bin/main/profplan/storage/JsonAdaptedTask.class differ
diff --git a/bin/main/profplan/storage/JsonProfPlanStorage.class b/bin/main/profplan/storage/JsonProfPlanStorage.class
new file mode 100644
index 00000000000..fd754aa48df
Binary files /dev/null and b/bin/main/profplan/storage/JsonProfPlanStorage.class differ
diff --git a/bin/main/profplan/storage/JsonSerializableProfPlan.class b/bin/main/profplan/storage/JsonSerializableProfPlan.class
new file mode 100644
index 00000000000..8cd53ec829f
Binary files /dev/null and b/bin/main/profplan/storage/JsonSerializableProfPlan.class differ
diff --git a/bin/main/profplan/storage/JsonUserPrefsStorage.class b/bin/main/profplan/storage/JsonUserPrefsStorage.class
new file mode 100644
index 00000000000..0ba15e498c3
Binary files /dev/null and b/bin/main/profplan/storage/JsonUserPrefsStorage.class differ
diff --git a/bin/main/profplan/storage/ProfPlanStorage.class b/bin/main/profplan/storage/ProfPlanStorage.class
new file mode 100644
index 00000000000..3b32606089c
Binary files /dev/null and b/bin/main/profplan/storage/ProfPlanStorage.class differ
diff --git a/bin/main/profplan/storage/Storage.class b/bin/main/profplan/storage/Storage.class
new file mode 100644
index 00000000000..d197d61a2be
Binary files /dev/null and b/bin/main/profplan/storage/Storage.class differ
diff --git a/bin/main/profplan/storage/StorageManager.class b/bin/main/profplan/storage/StorageManager.class
new file mode 100644
index 00000000000..04c8c3f5847
Binary files /dev/null and b/bin/main/profplan/storage/StorageManager.class differ
diff --git a/bin/main/profplan/storage/UserPrefsStorage.class b/bin/main/profplan/storage/UserPrefsStorage.class
new file mode 100644
index 00000000000..2445f6c8f36
Binary files /dev/null and b/bin/main/profplan/storage/UserPrefsStorage.class differ
diff --git a/bin/main/profplan/ui/CommandBox$CommandExecutor.class b/bin/main/profplan/ui/CommandBox$CommandExecutor.class
new file mode 100644
index 00000000000..1db3d3e1d39
Binary files /dev/null and b/bin/main/profplan/ui/CommandBox$CommandExecutor.class differ
diff --git a/bin/main/profplan/ui/CommandBox.class b/bin/main/profplan/ui/CommandBox.class
new file mode 100644
index 00000000000..6a69c4aea5e
Binary files /dev/null and b/bin/main/profplan/ui/CommandBox.class differ
diff --git a/bin/main/profplan/ui/HelpWindow.class b/bin/main/profplan/ui/HelpWindow.class
new file mode 100644
index 00000000000..bc6ff8eb5f1
Binary files /dev/null and b/bin/main/profplan/ui/HelpWindow.class differ
diff --git a/bin/main/profplan/ui/MainWindow.class b/bin/main/profplan/ui/MainWindow.class
new file mode 100644
index 00000000000..0b95ac2922a
Binary files /dev/null and b/bin/main/profplan/ui/MainWindow.class differ
diff --git a/bin/main/profplan/ui/ResultDisplay.class b/bin/main/profplan/ui/ResultDisplay.class
new file mode 100644
index 00000000000..20bfc14f66e
Binary files /dev/null and b/bin/main/profplan/ui/ResultDisplay.class differ
diff --git a/bin/main/profplan/ui/StatusBarFooter.class b/bin/main/profplan/ui/StatusBarFooter.class
new file mode 100644
index 00000000000..050b9aa99e4
Binary files /dev/null and b/bin/main/profplan/ui/StatusBarFooter.class differ
diff --git a/bin/main/profplan/ui/TaskCard.class b/bin/main/profplan/ui/TaskCard.class
new file mode 100644
index 00000000000..d46cd3703ed
Binary files /dev/null and b/bin/main/profplan/ui/TaskCard.class differ
diff --git a/bin/main/profplan/ui/TaskListPanel$TaskListViewCell.class b/bin/main/profplan/ui/TaskListPanel$TaskListViewCell.class
new file mode 100644
index 00000000000..8f389538e2d
Binary files /dev/null and b/bin/main/profplan/ui/TaskListPanel$TaskListViewCell.class differ
diff --git a/bin/main/profplan/ui/TaskListPanel.class b/bin/main/profplan/ui/TaskListPanel.class
new file mode 100644
index 00000000000..48caa1a3520
Binary files /dev/null and b/bin/main/profplan/ui/TaskListPanel.class differ
diff --git a/bin/main/profplan/ui/Ui.class b/bin/main/profplan/ui/Ui.class
new file mode 100644
index 00000000000..af77cbe5806
Binary files /dev/null and b/bin/main/profplan/ui/Ui.class differ
diff --git a/bin/main/profplan/ui/UiManager.class b/bin/main/profplan/ui/UiManager.class
new file mode 100644
index 00000000000..db2a2989b5e
Binary files /dev/null and b/bin/main/profplan/ui/UiManager.class differ
diff --git a/bin/main/profplan/ui/UiPart.class b/bin/main/profplan/ui/UiPart.class
new file mode 100644
index 00000000000..cf686e36ee7
Binary files /dev/null and b/bin/main/profplan/ui/UiPart.class differ
diff --git a/bin/main/view/CommandBox.fxml b/bin/main/view/CommandBox.fxml
new file mode 100644
index 00000000000..124283a392e
--- /dev/null
+++ b/bin/main/view/CommandBox.fxml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/bin/main/view/DarkTheme.css b/bin/main/view/DarkTheme.css
new file mode 100644
index 00000000000..273bf6edfbf
--- /dev/null
+++ b/bin/main/view/DarkTheme.css
@@ -0,0 +1,408 @@
+.background {
+ -fx-background-color: derive(#1d1d1d, 20%);
+ background-color: #383838; /* Used in the default.html file */
+}
+
+.label {
+ -fx-font-size: 11pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: #555555;
+ -fx-opacity: 0.9;
+}
+
+.label-bright {
+ -fx-font-size: 11pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
+}
+
+.label-header {
+ -fx-font-size: 32pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
+}
+
+.text-field {
+ -fx-font-size: 12pt;
+ -fx-font-family: "Segoe UI Semibold";
+}
+
+.tab-pane {
+ -fx-padding: 0 0 0 1;
+}
+
+.tab-pane .tab-header-area {
+ -fx-padding: 0 0 0 0;
+ -fx-min-height: 0;
+ -fx-max-height: 0;
+}
+
+
+.table-view {
+ -fx-base: #1d1d1d;
+ -fx-control-inner-background: #1d1d1d;
+ -fx-background-color: #1d1d1d;
+ -fx-table-cell-border-color: transparent;
+ -fx-table-header-border-color: transparent;
+ -fx-padding: 10;
+}
+
+
+.table-view .column-header-background {
+ -fx-background-color: #44106B;
+}
+
+.table-view .column-header, .table-view .filler {
+ -fx-size: 35;
+ -fx-border-width: 0 0 1 0;
+ -fx-background-color: transparent;
+ -fx-border-color:
+ transparent
+ transparent
+ derive(-fx-base, 80%)
+ transparent;
+ -fx-border-insets: 0 10 1 0;
+}
+
+.table-view .column-header .label {
+ -fx-font-size: 20pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: white;
+ -fx-alignment: center-left;
+ -fx-opacity: 1;
+}
+
+.table-view:focused .table-row-cell.list-cell {
+ -fx-background-color: #DC4247;
+}
+
+.table-view:focused .table-row-cell:filled:focused:selected {
+ -fx-background-color: transparent;
+}
+
+.table-view .table-row-cell:focused {
+ -fx-background-color: transparent;
+}
+
+
+.split-pane:horizontal .split-pane-divider {
+ -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-border-color: transparent transparent transparent #4d4d4d;
+}
+
+.split-pane {
+ -fx-border-radius: 1;
+ -fx-border-width: 1;
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
+
+.list-view {
+ -fx-background-insets: 0;
+ -fx-padding: 0;
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
+
+.list-cell {
+ -fx-label-padding: 0 0 0 0;
+ -fx-graphic-text-gap : 0;
+ -fx-padding: 0 0 0 0;
+}
+
+.list-cell:filled:even {
+ -fx-background-color: #32337D;
+}
+
+.list-cell:filled:odd {
+ -fx-background-color: #4C3C78;
+}
+
+.list-cell:filled:selected {
+ -fx-background-color: #424d5f;
+}
+
+.list-cell:filled:selected #cardPane {
+ -fx-border-color: #3e7b91;
+ -fx-border-width: 1;
+}
+
+.list-cell .label {
+ -fx-text-fill: white;
+}
+
+.cell_big_label {
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-font-size: 16px;
+ -fx-text-fill: #010504;
+}
+
+.cell_small_label {
+ -fx-font-family: "Segoe UI";
+ -fx-font-size: 13px;
+ -fx-text-fill: #010504;
+}
+
+.stack-pane {
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
+
+.pane-with-border {
+ -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-border-color: derive(#1d1d1d, 10%);
+ -fx-border-top-width: 1px;
+}
+
+.status-bar {
+ -fx-background-color: derive(#1d1d1d, 30%);
+}
+
+.table-view {
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
+
+.table-view .column-header-background .label {
+ -fx-font-family: "Segoe UI Light";
+ -fx-font-size: 10pt;
+ -fx-text-fill: white;
+ -fx-background-color: transparent;
+ -fx-alignment: CENTER;
+}
+
+.matrix-display {
+ -fx-background-color: transparent;
+ -fx-font-family: "Segoe UI Light";
+ -fx-font-size: 13pt;
+ -fx-text-fill: white;
+}
+.matrix-display .table-row-cell {
+ -fx-background-color: #794ECC;
+}
+
+.matrix-display .list-cell {
+ -fx-background-color: #3c3e3f;
+ -fx-font-size: 11pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
+}
+
+.result-display .label {
+ -fx-text-fill: white !important;
+}
+
+.status-bar .label {
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-padding: 4px;
+ -fx-pref-height: 30px;
+}
+
+.status-bar-with-border {
+ -fx-background-color: derive(#1d1d1d, 30%);
+ -fx-border-color: derive(#1d1d1d, 25%);
+ -fx-border-width: 1px;
+}
+
+.status-bar-with-border .label {
+ -fx-text-fill: white;
+}
+
+.grid-pane {
+ -fx-background-color: derive(#1d1d1d, 30%);
+ -fx-border-color: derive(#1d1d1d, 30%);
+ -fx-border-width: 1px;
+}
+
+.grid-pane .stack-pane {
+ -fx-background-color: derive(#1d1d1d, 30%);
+}
+
+.context-menu {
+ -fx-background-color: derive(#1d1d1d, 50%);
+}
+
+.context-menu .label {
+ -fx-text-fill: white;
+}
+
+.menu-bar {
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
+
+.menu-bar .label {
+ -fx-font-size: 14pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-opacity: 0.9;
+}
+
+.menu .left-container {
+ -fx-background-color: black;
+}
+
+/*
+ * Metro style Push Button
+ * Author: Pedro Duque Vieira
+ * http://pixelduke.wordpress.com/2012/10/23/jmetro-windows-8-controls-on-java/
+ */
+.button {
+ -fx-padding: 5 22 5 22;
+ -fx-border-color: #e2e2e2;
+ -fx-border-width: 2;
+ -fx-background-radius: 0;
+ -fx-background-color: #1d1d1d;
+ -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
+ -fx-font-size: 11pt;
+ -fx-text-fill: #d8d8d8;
+ -fx-background-insets: 0 0 0 0, 0, 1, 2;
+}
+
+.button:hover {
+ -fx-background-color: #3a3a3a;
+}
+
+.button:pressed, .button:default:hover:pressed {
+ -fx-background-color: white;
+ -fx-text-fill: #1d1d1d;
+}
+
+.button:focused {
+ -fx-border-color: white, white;
+ -fx-border-width: 1, 1;
+ -fx-border-style: solid, segments(1, 1);
+ -fx-border-radius: 0, 0;
+ -fx-border-insets: 1 1 1 1, 0;
+}
+
+.button:disabled, .button:default:disabled {
+ -fx-opacity: 0.4;
+ -fx-background-color: #1d1d1d;
+ -fx-text-fill: white;
+}
+
+.button:default {
+ -fx-background-color: -fx-focus-color;
+ -fx-text-fill: #ffffff;
+}
+
+.button:default:hover {
+ -fx-background-color: derive(-fx-focus-color, 30%);
+}
+
+.dialog-pane {
+ -fx-background-color: #1d1d1d;
+}
+
+.dialog-pane > *.button-bar > *.container {
+ -fx-background-color: #1d1d1d;
+}
+
+.dialog-pane > *.label.content {
+ -fx-font-size: 14px;
+ -fx-font-weight: bold;
+ -fx-text-fill: white;
+}
+
+.dialog-pane:header *.header-panel {
+ -fx-background-color: derive(#1d1d1d, 25%);
+}
+
+.dialog-pane:header *.header-panel *.label {
+ -fx-font-size: 18px;
+ -fx-font-style: italic;
+ -fx-fill: white;
+ -fx-text-fill: white;
+}
+
+.scroll-bar {
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
+
+.scroll-bar .thumb {
+ -fx-background-color: derive(#1d1d1d, 50%);
+ -fx-background-insets: 3;
+}
+
+.scroll-bar .increment-button, .scroll-bar .decrement-button {
+ -fx-background-color: transparent;
+ -fx-padding: 0 0 0 0;
+}
+
+.scroll-bar .increment-arrow, .scroll-bar .decrement-arrow {
+ -fx-shape: " ";
+}
+
+.scroll-bar:vertical .increment-arrow, .scroll-bar:vertical .decrement-arrow {
+ -fx-padding: 1 8 1 8;
+}
+
+.scroll-bar:horizontal .increment-arrow, .scroll-bar:horizontal .decrement-arrow {
+ -fx-padding: 8 1 8 1;
+}
+
+#cardPane {
+ -fx-background-color: transparent;
+ -fx-border-width: 0;
+}
+
+#commandTypeLabel {
+ -fx-font-size: 11px;
+ -fx-text-fill: #F70D1A;
+}
+
+#commandTextField {
+ -fx-background-color: #ADDBB6;
+ -fx-background-insets: 0;
+ -fx-border-color: #383838 #383838 #50AB64 #383838;
+ -fx-border-insets: 0;
+ -fx-border-width: 3;
+ -fx-font-family: "Segoe UI Bold";
+ -fx-font-size: 13pt;
+ -fx-text-fill: black;
+ -fx-prompt-text-fill: #545454;
+}
+
+#filterField, #taskListPanel, #taskWebpage {
+ -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0);
+}
+
+#resultDisplay {
+
+}
+#resultDisplay .content {
+ -fx-background-color: black;
+ -fx-background-radius: 0;
+ -fx-border-width: 3;
+ -fx-border-color: black;
+}
+
+#tags {
+ -fx-hgap: 7;
+ -fx-vgap: 3;
+}
+
+#tags .label {
+ -fx-text-fill: white;
+ -fx-background-color: #D97930;
+ -fx-padding: 1 3 1 3;
+ -fx-border-radius: 2;
+ -fx-background-radius: 2;
+ -fx-font-size: 11;
+}
+
+.list-view {
+ -fx-padding: 5px ;
+}
+
+.list-cell {
+ -fx-padding: 5px ;
+ -fx-background-color: transparent;
+ -fx-background-insets: 0px, 5px ;
+}
+
+.list-cell:empty {
+ -fx-padding: 5px ;
+ -fx-background-color: transparent ;
+ -fx-background-insets: 0 ;
+}
diff --git a/bin/main/view/Extensions.css b/bin/main/view/Extensions.css
new file mode 100644
index 00000000000..bfe82a85964
--- /dev/null
+++ b/bin/main/view/Extensions.css
@@ -0,0 +1,20 @@
+
+.error {
+ -fx-text-fill: #d06651 !important; /* The error class should always override the default text-fill style */
+}
+
+.list-cell:empty {
+ /* Empty cells will not have alternating colours */
+ -fx-background: #383838;
+}
+
+.tag-selector {
+ -fx-border-width: 1;
+ -fx-border-color: white;
+ -fx-border-radius: 3;
+ -fx-background-radius: 3;
+}
+
+.tooltip-text {
+ -fx-text-fill: white;
+}
diff --git a/bin/main/view/HelpWindow.css b/bin/main/view/HelpWindow.css
new file mode 100644
index 00000000000..17e8a8722cd
--- /dev/null
+++ b/bin/main/view/HelpWindow.css
@@ -0,0 +1,19 @@
+#copyButton, #helpMessage {
+ -fx-text-fill: white;
+}
+
+#copyButton {
+ -fx-background-color: dimgray;
+}
+
+#copyButton:hover {
+ -fx-background-color: gray;
+}
+
+#copyButton:armed {
+ -fx-background-color: darkgray;
+}
+
+#helpMessageContainer {
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
diff --git a/bin/main/view/HelpWindow.fxml b/bin/main/view/HelpWindow.fxml
new file mode 100644
index 00000000000..e01f330de33
--- /dev/null
+++ b/bin/main/view/HelpWindow.fxml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bin/main/view/MainWindow.fxml b/bin/main/view/MainWindow.fxml
new file mode 100644
index 00000000000..a357c5414bb
--- /dev/null
+++ b/bin/main/view/MainWindow.fxml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bin/main/view/ResultDisplay.fxml b/bin/main/view/ResultDisplay.fxml
new file mode 100644
index 00000000000..e8f3d0dfecc
--- /dev/null
+++ b/bin/main/view/ResultDisplay.fxml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/bin/main/view/StatusBarFooter.fxml b/bin/main/view/StatusBarFooter.fxml
new file mode 100644
index 00000000000..7b430f9c6a2
--- /dev/null
+++ b/bin/main/view/StatusBarFooter.fxml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/PersonListCard.fxml b/bin/main/view/TaskListCard.fxml
similarity index 72%
rename from src/main/resources/view/PersonListCard.fxml
rename to bin/main/view/TaskListCard.fxml
index f5e812e25e6..9564c3e0f0f 100644
--- a/src/main/resources/view/PersonListCard.fxml
+++ b/bin/main/view/TaskListCard.fxml
@@ -28,9 +28,11 @@
-
-
-
+
+
+
+
+
diff --git a/src/main/resources/view/PersonListPanel.fxml b/bin/main/view/TaskListPanel.fxml
similarity index 77%
rename from src/main/resources/view/PersonListPanel.fxml
rename to bin/main/view/TaskListPanel.fxml
index a1bb6bbace8..096ccf83953 100644
--- a/src/main/resources/view/PersonListPanel.fxml
+++ b/bin/main/view/TaskListPanel.fxml
@@ -4,5 +4,5 @@
-
+
diff --git a/bin/test/profplan/AppParametersTest$ParametersStub.class b/bin/test/profplan/AppParametersTest$ParametersStub.class
new file mode 100644
index 00000000000..2e3892f1f17
Binary files /dev/null and b/bin/test/profplan/AppParametersTest$ParametersStub.class differ
diff --git a/bin/test/profplan/AppParametersTest.class b/bin/test/profplan/AppParametersTest.class
new file mode 100644
index 00000000000..b4ac4c7321e
Binary files /dev/null and b/bin/test/profplan/AppParametersTest.class differ
diff --git a/bin/test/profplan/commons/core/ConfigTest.class b/bin/test/profplan/commons/core/ConfigTest.class
new file mode 100644
index 00000000000..9c2db997888
Binary files /dev/null and b/bin/test/profplan/commons/core/ConfigTest.class differ
diff --git a/bin/test/profplan/commons/core/GuiSettingsTest.class b/bin/test/profplan/commons/core/GuiSettingsTest.class
new file mode 100644
index 00000000000..15576ea03fa
Binary files /dev/null and b/bin/test/profplan/commons/core/GuiSettingsTest.class differ
diff --git a/bin/test/profplan/commons/core/VersionTest.class b/bin/test/profplan/commons/core/VersionTest.class
new file mode 100644
index 00000000000..b8f83de778b
Binary files /dev/null and b/bin/test/profplan/commons/core/VersionTest.class differ
diff --git a/bin/test/profplan/commons/core/index/IndexTest.class b/bin/test/profplan/commons/core/index/IndexTest.class
new file mode 100644
index 00000000000..a404e8d2616
Binary files /dev/null and b/bin/test/profplan/commons/core/index/IndexTest.class differ
diff --git a/bin/test/profplan/commons/util/AppUtilTest.class b/bin/test/profplan/commons/util/AppUtilTest.class
new file mode 100644
index 00000000000..e763a2cc6f8
Binary files /dev/null and b/bin/test/profplan/commons/util/AppUtilTest.class differ
diff --git a/bin/test/profplan/commons/util/CollectionUtilTest.class b/bin/test/profplan/commons/util/CollectionUtilTest.class
new file mode 100644
index 00000000000..2082289b11b
Binary files /dev/null and b/bin/test/profplan/commons/util/CollectionUtilTest.class differ
diff --git a/bin/test/profplan/commons/util/ConfigUtilTest.class b/bin/test/profplan/commons/util/ConfigUtilTest.class
new file mode 100644
index 00000000000..276fbf4b32b
Binary files /dev/null and b/bin/test/profplan/commons/util/ConfigUtilTest.class differ
diff --git a/bin/test/profplan/commons/util/FileUtilTest.class b/bin/test/profplan/commons/util/FileUtilTest.class
new file mode 100644
index 00000000000..22fe0a93b14
Binary files /dev/null and b/bin/test/profplan/commons/util/FileUtilTest.class differ
diff --git a/bin/test/profplan/commons/util/JsonUtilTest.class b/bin/test/profplan/commons/util/JsonUtilTest.class
new file mode 100644
index 00000000000..c2e1a54a960
Binary files /dev/null and b/bin/test/profplan/commons/util/JsonUtilTest.class differ
diff --git a/bin/test/profplan/commons/util/SampleDataUtilTest.class b/bin/test/profplan/commons/util/SampleDataUtilTest.class
new file mode 100644
index 00000000000..a0ed304abe6
Binary files /dev/null and b/bin/test/profplan/commons/util/SampleDataUtilTest.class differ
diff --git a/bin/test/profplan/commons/util/StringUtilTest.class b/bin/test/profplan/commons/util/StringUtilTest.class
new file mode 100644
index 00000000000..2e270c73dc4
Binary files /dev/null and b/bin/test/profplan/commons/util/StringUtilTest.class differ
diff --git a/bin/test/profplan/logic/LogicManagerTest.class b/bin/test/profplan/logic/LogicManagerTest.class
new file mode 100644
index 00000000000..3b5ab2ed4ff
Binary files /dev/null and b/bin/test/profplan/logic/LogicManagerTest.class differ
diff --git a/bin/test/profplan/logic/commands/AddCommandIntegrationTest.class b/bin/test/profplan/logic/commands/AddCommandIntegrationTest.class
new file mode 100644
index 00000000000..50b02999899
Binary files /dev/null and b/bin/test/profplan/logic/commands/AddCommandIntegrationTest.class differ
diff --git a/bin/test/profplan/logic/commands/AddCommandTest$ModelStub.class b/bin/test/profplan/logic/commands/AddCommandTest$ModelStub.class
new file mode 100644
index 00000000000..0a9fdb49371
Binary files /dev/null and b/bin/test/profplan/logic/commands/AddCommandTest$ModelStub.class differ
diff --git a/bin/test/profplan/logic/commands/AddCommandTest$ModelStubAcceptingTaskAdded.class b/bin/test/profplan/logic/commands/AddCommandTest$ModelStubAcceptingTaskAdded.class
new file mode 100644
index 00000000000..7bc14ed933d
Binary files /dev/null and b/bin/test/profplan/logic/commands/AddCommandTest$ModelStubAcceptingTaskAdded.class differ
diff --git a/bin/test/profplan/logic/commands/AddCommandTest$ModelStubWithTask.class b/bin/test/profplan/logic/commands/AddCommandTest$ModelStubWithTask.class
new file mode 100644
index 00000000000..033e7b036fd
Binary files /dev/null and b/bin/test/profplan/logic/commands/AddCommandTest$ModelStubWithTask.class differ
diff --git a/bin/test/profplan/logic/commands/AddCommandTest.class b/bin/test/profplan/logic/commands/AddCommandTest.class
new file mode 100644
index 00000000000..75c3b049027
Binary files /dev/null and b/bin/test/profplan/logic/commands/AddCommandTest.class differ
diff --git a/bin/test/profplan/logic/commands/ClearCommandTest.class b/bin/test/profplan/logic/commands/ClearCommandTest.class
new file mode 100644
index 00000000000..4047044579b
Binary files /dev/null and b/bin/test/profplan/logic/commands/ClearCommandTest.class differ
diff --git a/bin/test/profplan/logic/commands/CommandResultTest.class b/bin/test/profplan/logic/commands/CommandResultTest.class
new file mode 100644
index 00000000000..9c3eae1c14e
Binary files /dev/null and b/bin/test/profplan/logic/commands/CommandResultTest.class differ
diff --git a/bin/test/profplan/logic/commands/CommandTestUtil.class b/bin/test/profplan/logic/commands/CommandTestUtil.class
new file mode 100644
index 00000000000..4e5cb3b742b
Binary files /dev/null and b/bin/test/profplan/logic/commands/CommandTestUtil.class differ
diff --git a/bin/test/profplan/logic/commands/DeleteCommandTest.class b/bin/test/profplan/logic/commands/DeleteCommandTest.class
new file mode 100644
index 00000000000..acd562db079
Binary files /dev/null and b/bin/test/profplan/logic/commands/DeleteCommandTest.class differ
diff --git a/bin/test/profplan/logic/commands/EditCommandTest.class b/bin/test/profplan/logic/commands/EditCommandTest.class
new file mode 100644
index 00000000000..e3c03bd556e
Binary files /dev/null and b/bin/test/profplan/logic/commands/EditCommandTest.class differ
diff --git a/bin/test/profplan/logic/commands/EditTaskDescriptorTest.class b/bin/test/profplan/logic/commands/EditTaskDescriptorTest.class
new file mode 100644
index 00000000000..2991110d26e
Binary files /dev/null and b/bin/test/profplan/logic/commands/EditTaskDescriptorTest.class differ
diff --git a/bin/test/profplan/logic/commands/ExitCommandTest.class b/bin/test/profplan/logic/commands/ExitCommandTest.class
new file mode 100644
index 00000000000..7f38cb36548
Binary files /dev/null and b/bin/test/profplan/logic/commands/ExitCommandTest.class differ
diff --git a/bin/test/profplan/logic/commands/FilterCommandTest.class b/bin/test/profplan/logic/commands/FilterCommandTest.class
new file mode 100644
index 00000000000..5baa9d42176
Binary files /dev/null and b/bin/test/profplan/logic/commands/FilterCommandTest.class differ
diff --git a/bin/test/profplan/logic/commands/FindCommandTest.class b/bin/test/profplan/logic/commands/FindCommandTest.class
new file mode 100644
index 00000000000..b569e67cd46
Binary files /dev/null and b/bin/test/profplan/logic/commands/FindCommandTest.class differ
diff --git a/bin/test/profplan/logic/commands/HelpCommandTest.class b/bin/test/profplan/logic/commands/HelpCommandTest.class
new file mode 100644
index 00000000000..bf16f11aff2
Binary files /dev/null and b/bin/test/profplan/logic/commands/HelpCommandTest.class differ
diff --git a/bin/test/profplan/logic/commands/ListCommandTest.class b/bin/test/profplan/logic/commands/ListCommandTest.class
new file mode 100644
index 00000000000..6cc6166937a
Binary files /dev/null and b/bin/test/profplan/logic/commands/ListCommandTest.class differ
diff --git a/bin/test/profplan/logic/parser/AddCommandParserTest.class b/bin/test/profplan/logic/parser/AddCommandParserTest.class
new file mode 100644
index 00000000000..bbd690d4fa6
Binary files /dev/null and b/bin/test/profplan/logic/parser/AddCommandParserTest.class differ
diff --git a/bin/test/profplan/logic/parser/ArgumentTokenizerTest.class b/bin/test/profplan/logic/parser/ArgumentTokenizerTest.class
new file mode 100644
index 00000000000..3032ca7bfca
Binary files /dev/null and b/bin/test/profplan/logic/parser/ArgumentTokenizerTest.class differ
diff --git a/bin/test/profplan/logic/parser/CommandParserTestUtil.class b/bin/test/profplan/logic/parser/CommandParserTestUtil.class
new file mode 100644
index 00000000000..fde6b61e2bb
Binary files /dev/null and b/bin/test/profplan/logic/parser/CommandParserTestUtil.class differ
diff --git a/bin/test/profplan/logic/parser/DeleteCommandParserTest.class b/bin/test/profplan/logic/parser/DeleteCommandParserTest.class
new file mode 100644
index 00000000000..9bc145a7aab
Binary files /dev/null and b/bin/test/profplan/logic/parser/DeleteCommandParserTest.class differ
diff --git a/bin/test/profplan/logic/parser/EditCommandParserTest.class b/bin/test/profplan/logic/parser/EditCommandParserTest.class
new file mode 100644
index 00000000000..308345b8bbb
Binary files /dev/null and b/bin/test/profplan/logic/parser/EditCommandParserTest.class differ
diff --git a/bin/test/profplan/logic/parser/FilterCommandParserTest.class b/bin/test/profplan/logic/parser/FilterCommandParserTest.class
new file mode 100644
index 00000000000..04c48738339
Binary files /dev/null and b/bin/test/profplan/logic/parser/FilterCommandParserTest.class differ
diff --git a/bin/test/profplan/logic/parser/FindCommandParserTest.class b/bin/test/profplan/logic/parser/FindCommandParserTest.class
new file mode 100644
index 00000000000..cb928aef474
Binary files /dev/null and b/bin/test/profplan/logic/parser/FindCommandParserTest.class differ
diff --git a/bin/test/profplan/logic/parser/ParserUtilTest.class b/bin/test/profplan/logic/parser/ParserUtilTest.class
new file mode 100644
index 00000000000..b7e896f9855
Binary files /dev/null and b/bin/test/profplan/logic/parser/ParserUtilTest.class differ
diff --git a/bin/test/profplan/logic/parser/ProfPlanParserTest.class b/bin/test/profplan/logic/parser/ProfPlanParserTest.class
new file mode 100644
index 00000000000..3f9378aa899
Binary files /dev/null and b/bin/test/profplan/logic/parser/ProfPlanParserTest.class differ
diff --git a/bin/test/profplan/model/ModelManagerTest.class b/bin/test/profplan/model/ModelManagerTest.class
new file mode 100644
index 00000000000..76f136f8434
Binary files /dev/null and b/bin/test/profplan/model/ModelManagerTest.class differ
diff --git a/bin/test/profplan/model/ProfPlanTest$ProfPlanStub.class b/bin/test/profplan/model/ProfPlanTest$ProfPlanStub.class
new file mode 100644
index 00000000000..d5a95e1dcc3
Binary files /dev/null and b/bin/test/profplan/model/ProfPlanTest$ProfPlanStub.class differ
diff --git a/bin/test/profplan/model/ProfPlanTest.class b/bin/test/profplan/model/ProfPlanTest.class
new file mode 100644
index 00000000000..bb871b49b8b
Binary files /dev/null and b/bin/test/profplan/model/ProfPlanTest.class differ
diff --git a/bin/test/profplan/model/UserPrefsTest.class b/bin/test/profplan/model/UserPrefsTest.class
new file mode 100644
index 00000000000..403a7e70078
Binary files /dev/null and b/bin/test/profplan/model/UserPrefsTest.class differ
diff --git a/bin/test/profplan/model/tag/TagTest.class b/bin/test/profplan/model/tag/TagTest.class
new file mode 100644
index 00000000000..6a74183f5e0
Binary files /dev/null and b/bin/test/profplan/model/tag/TagTest.class differ
diff --git a/bin/test/profplan/model/task/DueDateTest.class b/bin/test/profplan/model/task/DueDateTest.class
new file mode 100644
index 00000000000..9042d9d46f2
Binary files /dev/null and b/bin/test/profplan/model/task/DueDateTest.class differ
diff --git a/bin/test/profplan/model/task/NameContainsKeywordsPredicateTest.class b/bin/test/profplan/model/task/NameContainsKeywordsPredicateTest.class
new file mode 100644
index 00000000000..67240da7f54
Binary files /dev/null and b/bin/test/profplan/model/task/NameContainsKeywordsPredicateTest.class differ
diff --git a/bin/test/profplan/model/task/NameTest.class b/bin/test/profplan/model/task/NameTest.class
new file mode 100644
index 00000000000..7bf24eec3d2
Binary files /dev/null and b/bin/test/profplan/model/task/NameTest.class differ
diff --git a/bin/test/profplan/model/task/PriorityTest.class b/bin/test/profplan/model/task/PriorityTest.class
new file mode 100644
index 00000000000..5a60837bfc2
Binary files /dev/null and b/bin/test/profplan/model/task/PriorityTest.class differ
diff --git a/bin/test/profplan/model/task/TaskTest.class b/bin/test/profplan/model/task/TaskTest.class
new file mode 100644
index 00000000000..d994ff0b1f1
Binary files /dev/null and b/bin/test/profplan/model/task/TaskTest.class differ
diff --git a/bin/test/profplan/model/task/UniqueTaskListTest.class b/bin/test/profplan/model/task/UniqueTaskListTest.class
new file mode 100644
index 00000000000..a8e5168fac2
Binary files /dev/null and b/bin/test/profplan/model/task/UniqueTaskListTest.class differ
diff --git a/bin/test/profplan/storage/JsonAdaptedTaskTest.class b/bin/test/profplan/storage/JsonAdaptedTaskTest.class
new file mode 100644
index 00000000000..8cdf5011199
Binary files /dev/null and b/bin/test/profplan/storage/JsonAdaptedTaskTest.class differ
diff --git a/bin/test/profplan/storage/JsonProfPlanStorageTest.class b/bin/test/profplan/storage/JsonProfPlanStorageTest.class
new file mode 100644
index 00000000000..c8f8458ce90
Binary files /dev/null and b/bin/test/profplan/storage/JsonProfPlanStorageTest.class differ
diff --git a/bin/test/profplan/storage/JsonSerializableProfPlanTest.class b/bin/test/profplan/storage/JsonSerializableProfPlanTest.class
new file mode 100644
index 00000000000..244e934cb97
Binary files /dev/null and b/bin/test/profplan/storage/JsonSerializableProfPlanTest.class differ
diff --git a/bin/test/profplan/storage/JsonUserPrefsStorageTest.class b/bin/test/profplan/storage/JsonUserPrefsStorageTest.class
new file mode 100644
index 00000000000..bec51a3c7c6
Binary files /dev/null and b/bin/test/profplan/storage/JsonUserPrefsStorageTest.class differ
diff --git a/bin/test/profplan/storage/StorageManagerTest.class b/bin/test/profplan/storage/StorageManagerTest.class
new file mode 100644
index 00000000000..5a472d9bd8e
Binary files /dev/null and b/bin/test/profplan/storage/StorageManagerTest.class differ
diff --git a/bin/test/profplan/testutil/Assert.class b/bin/test/profplan/testutil/Assert.class
new file mode 100644
index 00000000000..e1d9ba4f7e3
Binary files /dev/null and b/bin/test/profplan/testutil/Assert.class differ
diff --git a/bin/test/profplan/testutil/EditTaskDescriptorBuilder.class b/bin/test/profplan/testutil/EditTaskDescriptorBuilder.class
new file mode 100644
index 00000000000..3320b2bf052
Binary files /dev/null and b/bin/test/profplan/testutil/EditTaskDescriptorBuilder.class differ
diff --git a/bin/test/profplan/testutil/ProfPlanBuilder.class b/bin/test/profplan/testutil/ProfPlanBuilder.class
new file mode 100644
index 00000000000..9f0e7d8c96b
Binary files /dev/null and b/bin/test/profplan/testutil/ProfPlanBuilder.class differ
diff --git a/bin/test/profplan/testutil/SerializableTestClass.class b/bin/test/profplan/testutil/SerializableTestClass.class
new file mode 100644
index 00000000000..4b62cd8112e
Binary files /dev/null and b/bin/test/profplan/testutil/SerializableTestClass.class differ
diff --git a/bin/test/profplan/testutil/TaskBuilder.class b/bin/test/profplan/testutil/TaskBuilder.class
new file mode 100644
index 00000000000..a7df47fd108
Binary files /dev/null and b/bin/test/profplan/testutil/TaskBuilder.class differ
diff --git a/bin/test/profplan/testutil/TaskUtil.class b/bin/test/profplan/testutil/TaskUtil.class
new file mode 100644
index 00000000000..6fa8870cfd3
Binary files /dev/null and b/bin/test/profplan/testutil/TaskUtil.class differ
diff --git a/bin/test/profplan/testutil/TestUtil.class b/bin/test/profplan/testutil/TestUtil.class
new file mode 100644
index 00000000000..c4b9077812f
Binary files /dev/null and b/bin/test/profplan/testutil/TestUtil.class differ
diff --git a/bin/test/profplan/testutil/TypicalIndexes.class b/bin/test/profplan/testutil/TypicalIndexes.class
new file mode 100644
index 00000000000..2a1b79ff80f
Binary files /dev/null and b/bin/test/profplan/testutil/TypicalIndexes.class differ
diff --git a/bin/test/profplan/testutil/TypicalTasks.class b/bin/test/profplan/testutil/TypicalTasks.class
new file mode 100644
index 00000000000..4db281c3a86
Binary files /dev/null and b/bin/test/profplan/testutil/TypicalTasks.class differ
diff --git a/bin/test/profplan/ui/TestFxmlObject.class b/bin/test/profplan/ui/TestFxmlObject.class
new file mode 100644
index 00000000000..90df3e0fa66
Binary files /dev/null and b/bin/test/profplan/ui/TestFxmlObject.class differ
diff --git a/bin/test/profplan/ui/UiPartTest$TestUiPart.class b/bin/test/profplan/ui/UiPartTest$TestUiPart.class
new file mode 100644
index 00000000000..cd118386aaa
Binary files /dev/null and b/bin/test/profplan/ui/UiPartTest$TestUiPart.class differ
diff --git a/bin/test/profplan/ui/UiPartTest.class b/bin/test/profplan/ui/UiPartTest.class
new file mode 100644
index 00000000000..f085d4e8723
Binary files /dev/null and b/bin/test/profplan/ui/UiPartTest.class differ
diff --git a/bin/test/view/UiPartTest/invalidFile.fxml b/bin/test/view/UiPartTest/invalidFile.fxml
new file mode 100644
index 00000000000..67680946732
--- /dev/null
+++ b/bin/test/view/UiPartTest/invalidFile.fxml
@@ -0,0 +1 @@
+Not a valid FXML file
diff --git a/bin/test/view/UiPartTest/validFile.fxml b/bin/test/view/UiPartTest/validFile.fxml
new file mode 100644
index 00000000000..c3e4113e309
--- /dev/null
+++ b/bin/test/view/UiPartTest/validFile.fxml
@@ -0,0 +1,4 @@
+
+
+
+Hello World!
diff --git a/bin/test/view/UiPartTest/validFileWithFxRoot.fxml b/bin/test/view/UiPartTest/validFileWithFxRoot.fxml
new file mode 100644
index 00000000000..c5b35d26797
--- /dev/null
+++ b/bin/test/view/UiPartTest/validFileWithFxRoot.fxml
@@ -0,0 +1,7 @@
+
+
+
+
+ Hello World!
+
diff --git a/build.gradle b/build.gradle
index a2951cc709e..bd3ec1affcb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -6,7 +6,7 @@ plugins {
id 'jacoco'
}
-mainClassName = 'seedu.address.Main'
+mainClassName = 'profplan.Main'
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
@@ -66,7 +66,11 @@ dependencies {
}
shadowJar {
- archiveFileName = 'addressbook.jar'
+ archiveFileName = 'profplanv14.jar'
+}
+
+run {
+ enableAssertions = true;
}
defaultTasks 'clean', 'test'
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 1c9514e966a..24e763e9fd6 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -5,55 +5,51 @@ 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`
## Project team
-### John Doe
+### Tejas Gandhi
-
+
-[[homepage](http://www.comp.nus.edu.sg/~damithch)]
-[[github](https://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/newway1814)]
+[[portfolio](team/newway1814.md)]
-* Role: Project Advisor
+* Role: Developer
-### Jane Doe
+### Guo Jiaqi Jackie
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/jack1e0)] [[portfolio](team/jack1e0.md)]
-* Role: Team Lead
-* Responsibilities: UI
+* Role: Developer
-### Johnny Doe
+### Kiat Win
-
+
-[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)]
+[[github](http://github.com/kiatkat)]
+[[portfolio](team/kiatkat.md)]
* Role: Developer
-* Responsibilities: Data
-### Jean Doe
+### Yarn Meng
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/yarnmengnus)]
+[[portfolio](team/yarnmengnus.md)]
* Role: Developer
-* Responsibilities: Dev Ops + Threading
-### James Doe
+### Raman Gupta
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/eyelessrhyme7)]
+[[portfolio](team/eyelessrhyme7.md)]
* Role: Developer
-* Responsibilities: UI
+
+
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 8a861859bfd..635e718f555 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -6,10 +6,11 @@ title: Developer Guide
{:toc}
--------------------------------------------------------------------------------------------------------------------
+
## **Acknowledgements**
-* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+* This project is built on top of the codebase of [AddressBook Level-3 (AB3)](https://github.com/se-edu/addressbook-level3).
--------------------------------------------------------------------------------------------------------------------
@@ -26,6 +27,8 @@ Refer to the guide [_Setting up and getting started_](SettingUp.md).
:bulb: **Tip:** The `.puml` files used to create diagrams in this document `docs/diagrams` folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams.
+
+
### Architecture
@@ -36,7 +39,7 @@ Given below is a quick overview of main components and how they interact with ea
**Main components of the architecture**
-**`Main`** (consisting of classes [`Main`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java)) is in charge of the app launch and shut down.
+**`Main`** (consisting of classes [`Main`](https://github.com/AY2324S1-CS2103T-W15-1/tp/blob/master/src/main/java/profplan/Main.java) and [`MainApp`](https://github.com/AY2324S1-CS2103T-W15-1/tp/blob/master/src/main/java/profplan/MainApp.java)) is in charge of the app launch and shut down.
* At app launch, it initializes the other components in the correct sequence, and connects them up with each other.
* At shut down, it shuts down the other components and invokes cleanup methods where necessary.
@@ -58,7 +61,9 @@ 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.
@@ -66,31 +71,36 @@ 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 **API** of this component is specified in [`Ui.java`](https://github.com/AY2324S1-CS2103T-W15-1/tp/blob/master/src/main/java/profplan/ui/Ui.java)
![Structure of the UI Component](images/UiClassDiagram.png)
-The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI.
+The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `TaskListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI.
-The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml)
+The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/AY2324S1-CS2103T-W15-1/tp/blob/master/src/main/java/profplan/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/AY2324S1-CS2103T-W15-1/tp/tree/master/src/main/resources/view/MainWindow.fxml)
The `UI` component,
* executes user commands using the `Logic` component.
* listens for changes to `Model` data so that the UI can be updated with the modified data.
* keeps a reference to the `Logic` component, because the `UI` relies on the `Logic` to execute commands.
-* depends on some classes in the `Model` component, as it displays `Person` object residing in the `Model`.
+* depends on some classes in the `Model` component, as it displays `Task` object residing in the `Model`.
+
+
### Logic component
-**API** : [`Logic.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/logic/Logic.java)
+**API** : [`Logic.java`](https://github.com/AY2324S1-CS2103T-W15-1/tp/blob/master/src/main/java/profplan/logic/Logic.java)
Here's a (partial) class diagram of the `Logic` component:
+
The sequence diagram below illustrates the interactions within the `Logic` component, taking `execute("delete 1")` API call as an example.
![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png)
@@ -98,11 +108,12 @@ The sequence diagram below illustrates the interactions within the `Logic` compo
:information_source: **Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
+
How the `Logic` component works:
-1. When `Logic` is called upon to execute a command, it is passed to an `AddressBookParser` object which in turn creates a parser that matches the command (e.g., `DeleteCommandParser`) and uses it to parse the command.
+1. When `Logic` is called upon to execute a command, it is passed to an `ProfPlanParser` object which in turn creates a parser that matches the command (e.g., `DeleteCommandParser`) and uses it to parse the command.
1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `DeleteCommand`) which is executed by the `LogicManager`.
-1. The command can communicate with the `Model` when it is executed (e.g. to delete a person).
+1. The command can communicate with the `Model` when it is executed (e.g. to delete a task).
1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`.
Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command:
@@ -110,86 +121,399 @@ Here are the other classes in `Logic` (omitted from the class diagram above) tha
How the parsing works:
-* When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `AddressBookParser` returns back as a `Command` object.
+* When called upon to parse a user command, the `ProfPlanParser` 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 `ProfPlanParser` 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)
+**API** : [`Model.java`](https://github.com/AY2324S1-CS2103T-W15-1/tp/blob/master/src/main/java/profplan/model/Model.java)
-
+
The `Model` component,
-* stores the address book data i.e., all `Person` objects (which are contained in a `UniquePersonList` object).
-* stores the currently 'selected' `Person` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
+* stores the task list data i.e., all `Task` objects (which are contained in a `UniqueTaskList` object).
+* stores the currently 'selected' `Task` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
* stores a `UserPref` object that represents the user’s preferences. This is exposed to the outside as a `ReadOnlyUserPref` objects.
* does not depend on any of the other three components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components)
-
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Person` references. This allows `AddressBook` to only require one `Tag` object per unique tag, instead of each `Person` needing their own `Tag` objects.
+
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `ProfPlan`, which `Task` references. This allows `ProfPlan` to only require one `Tag` object per unique tag, instead of each `Task` needing their own `Tag` objects.
+
+
+
### Storage component
-**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java)
+**API** : [`Storage.java`](https://github.com/AY2324S1-CS2103T-W15-1/tp/blob/master/src/main/java/profplan/storage/Storage.java)
The `Storage` component,
-* can save both address book data and user preference data in JSON format, and read them back into corresponding objects.
-* inherits from both `AddressBookStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed).
+* can save both tasklist data and user preference data in JSON format, and read them back into corresponding objects.
+* inherits from both `ProfPlanStorage` and `UserPrefsStorage`, which means it can be treated as either one (if only the functionality of only one is needed).
* depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`)
+
+
### Common classes
-Classes used by multiple components are in the `seedu.addressbook.commons` package.
+Classes used by multiple components are in the `profplan.commons` package.
--------------------------------------------------------------------------------------------------------------------
+
## **Implementation**
This section describes some noteworthy details on how certain features are implemented.
-### \[Proposed\] Undo/redo feature
-#### Proposed Implementation
+## DoNext feature
+### Actual implementation
+- The DoNext feature in ProfPlan allows users to generate a recommendation of which task to do next.
+- We compute (priority) / (number of days to due date) for every task, and select the task with the highest computed value as the recommendation.
+- This is because a task is recommended if it has higher priority and lower number of days left to due date.
+- Below, we describe the implementation details for this feature through a (partial) UML class and state diagram:
+
+
+The `DoNextCommand` is a part of the ProfPlan application, a task management tool. It allows users to generate next task recommendation. It is one of the standout features of ProfPlan, as it generates smart recommendations for professors.
+
+### Code Structure
+The code structure for the `DoNextCommand` is well-organized. It consists of the following components:
+- `DoNextCommand` class: Represents the command itself.
+- `ProfPlanParser` class: Responsible for parsing user input and creating `DoNextCommand` instances.
+- `Priority` class: Represents the priority of a task (from 1 to 10).
+- `DueDate` class: Represents the deadline of a task (in dd-MM-yyyy format, e.g. '01-02-2023').
+- `Task` class: Represents a task, and it contains the priority and dueDate that `DoNextCommand` processes.
+- `ModelManager` class: contains the `getDoNextTask()` function to generate recommended task.
+
+### Class Details
+### `DoNextCommand`
+- Purpose: Represents the `do_next` command that generates next task recommendation.
+- Key Methods:
+ - `execute(Model model)`: Executes the `DoNextCommand` by computing and output the next task to do.
+
+### `Priority`
+- Purpose: Represents the priority of a task (from 1 to 10).
+- Key Methods:
+ - `isValidPriority(String test)`: Checks if a given priority is valid according to the predefined regex.
+
+### `DueDate`
+- Purpose: Represents the deadline of a task (in dd-MM-yyyy format or '01-02-2023').
+- Key Methods:
+ - `isValidDate(String test)`: Checks if a given date is valid according to the predefined regex.
+ - `isIncludedorBefore(DueDate otherDate)`: Checks whether the current date is before, on equals the given date.
+
+### `Task`
+- Purpose: Represents a task, and it contains the priority and dueDate.
+- Key Methods:
+ - `getPriority()`: returns the priority of the task.
+ - `getDueDate()`: returns the dueDate of the task.
+
+### `ModelManager`
+- Purpose: Represents a model manager, implements the Model class and carries out core functionality for commands.
+- Key Methods:
+ - `getDoNextTask()`: returns the recommended task after processing priority/#daysTodueDate for every task.
+
+### Sequence Diagram
+The sequence diagram provides an overview of how the `DoNext` is executed and interacts with other components.
+
+[//]: # ()
+[//]: # (![Sequence Diagram](images/DoNextCommandSequenceDiagram.png))
+
+Here's a breakdown of the sequence:
+1. The `LogicManager` receives the command "do_next" from the user.
+2. The `ProfPlanParser` parses the command and creates a `DoNextCommand`.
+4. The `DoNextCommand` is executed.
+5. The `ModelManager` executes the function `getDoNextTask`
+6. A `CommandResult` is created to provide feedback to the user, containing the recommended task as a String.
+7. The result is returned to the `LogicManager`.
+
+### Usage
+To use the `DoNext` in the ProfPlan application, you can execute the following steps:
+
+1. Enter the "do_next" command.
+
+2. The `DoNext` will handle the task recommendation generation.
+
+3. The application will provide feedback, and output the task generated in the message window.
+
+
+
+## Mark feature
+### Actual implementation
+The Mark feature in ProfPlan allows users to set a task as done. Below, we describe the implementation details for this feature through a (partial) UML class diagram:
+
+
+
+
+
+
+
+
+The `MarkCommand` is a part of the ProfPlan application, a task management tool. The `MarkCommand` allows users to mark a task as done by specifying the task index. It is a critical part of the application's functionality, as it helps users manage and track their tasks efficiently.
+
+### Code Structure
+The code structure for the `MarkCommand` is well-organized. It consists of the following components:
+- `MarkCommand` class: Represents the command itself.
+- `MarkCommandParser` class: Responsible for parsing user input and creating `MarkCommand` instances.
+- `Status` class: Represents the status of a task (either "done" or "undone").
+- `Task` class: Represents a task, and it contains the status that the `MarkCommand` changes.
+
+### Class Details
+### `MarkCommand`
+- Purpose: Represents the `mark` command that allows users to mark a task as done.
+- Key Methods:
+ - `execute(Model model)`: Executes the `MarkCommand` by marking the task as done in the model.
+ - `equals(Object other)`: Compares two `MarkCommand` objects for equality.
+ - `toString()`: Returns a string representation of the `MarkCommand`.
+
+### `MarkCommandParser`
+- Purpose: Parses user input to create `MarkCommand` instances.
+- Key Methods:
+ - `parse(String args)`: Parses the user input and returns a `MarkCommand` if the input is valid.
+
+### `Status`
+- Purpose: Represents the status of a task. It can be either "done" or "undone."
+- Key Methods:
+ - `isValidStatus(String test)`: Checks if a given status is valid according to the predefined regex.
+
+### `Task`
+- Purpose: Represents a task, and it contains the status that the `MarkCommand` changes.
+- Key Methods:
+ - `setStatus(Status status)`: Sets the status of the task.
+ - `Task(Task task)`: Constructor for creating a new task as a copy of an existing task.
+
+### Sequence Diagram
+The sequence diagram below provides an overview of how the `MarkCommand` is executed and interacts with other components.
+
+![Sequence Diagram](images/MarkCommandSequenceDiagram.png)
+
+Here's a breakdown of the sequence:
+1. The `LogicManager` receives the command "mark 1" from the user.
+2. The `ProfPlanParser` parses the command and recognizes it as a `MarkCommand`.
+3. The `MarkCommandParser` parses the argument "1" and creates a `MarkCommand`.
+4. The `MarkCommand` is executed.
+5. The `Model` is updated by marking the task as done.
+6. A `CommandResult` is created to provide feedback to the user.
+7. The result is returned to the `LogicManager`.
+
+### Usage
+To use the `MarkCommand` in the ProfPlan application, you can execute the following steps:
+
+1. Enter the "mark" command followed by the task index you want to mark as done. For example, "mark 1" marks the task with index 1 as done.
+
+2. The `MarkCommand` will handle the task marking process and update the task status in the model.
+
+3. The application will provide feedback, indicating the successful marking of the task as done.
+
+
+
+## Filter feature
+### Actual implementation
+The Filter feature in ProfPlan allows users to filter according to one or more of the following criteria:
+- `DueDate`: displays tasks before given due date
+- `Priority`: displays tasks of given priority
+- `Status`: displays tasks of given status
+- `RecurringType`: displays tasks of given recurring type
+
+Below, we describe the implementation details for this feature through a (partial )UML class diagram:
+
+
+
+
+
+
+
+
+The `FilterCommand` is a part of the ProfPlan application, a task management tool. The `FilterCommand` allows users to filter tasks by due date, priority, and status. This enables clearer visualisation of tasks.
+
+### Code Structure
+The code structure for the `FilterCommand` is well-organized. It consists of the following components:
+- `FilterCommand` class: Represents the command itself.
+- `FilterCommandParser` class: Responsible for parsing user input and creating `FilterCommand` instances.
+- `TaskDueDatePredicate` class: Represents the DueDate predicate.
+- `TaskPriorityPredicate` class: Represents the Priority predicate.
+- `TaskStatusPredicate` class: Represents the Status predicate.
+- `TaskRecurringTypePredicate` class: Represents the RecurringType predicate
+- `CombinedPredicate` class: Represents the combination of multiple predicates
+
+### Class Details
+### `FilterCommand`
+- Purpose: Represents the `filter` command that allows users to filter by DueDate, Priority, Status, and/or RecurringType.
+- Key Methods:
+ - `execute(Model model)`: Executes the `FilterCommand` by updating the filtered task list in model.
+ - `equals(Object other)`: Compares two `FilterCommand` objects for equality.
+ - `toString()`: Returns a string representation of the `FilterCommand`.
+
+### `FilterCommandParser`
+- Purpose: Parses user input to create `FilterCommand` instances.
+- Key Methods:
+ - `parse(String args)`: Parses the user input using the `ArgumentMultimap` and returns a `FilterCommand` if the input is valid.
-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:
+### `TaskDueDatePredicate`
+- Purpose: Represents the DueDate predicate. It contains a `DueDate`, whose value is determined by the user input.
+- Key Methods:
+ - `getDueDate()`: Returns the `DueDate` predicate.
+ - `test(Task task)`: Returns true if a given Task falls before or on the `DueDate`.
+ - `equals(Object other)`: Compares two `TaskDueDatePredicate` objects for equality.
+ - `toString()`: Returns a string representation of the `TaskDueDatePredicate`.
-* `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.
+### `TaskPriorityPredicate`
+- Purpose: Represents the Priority predicate. It contains a `Priority`, whose value is determined by the user input.
+- Key Methods:
+ - `getPriority()`: Returns the `Priority` predicate.
+ - `test(Task task)`: Returns true if given Task has the same `Priority` as the predicate.
+ - `equals(Object other)`: Compares two `TaskPriorityPredicate` objects for equality.
+ - `toString()`: Returns a string representation of the `TaskPriorityPredicate`.
-These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively.
+### `TaskStatusPredicate`
+- Purpose: Represents the Status predicate. It contains a `Status`, whose value is determined by the user input.
+- Key Methods:
+ - `getStatus()`: Returns the `Status` predicate.
+ - `test(Task task)`: Returns true if given Task has the same `Status` as the predicate.
+ - `equals(Object other)`: Compares two `TaskStatusPredicate` objects for equality.
+ - `toString()`: Returns a string representation of the `TaskStatusPredicate`.
+
+### `TaskRecurringTypePredicate`
+- Purpose: Represents the RecurringType predicate. It contains a `RecurringType`, whose value is determined by the user input.
+- Key Methods:
+ - `getRecurringType()`: Returns the `RecurringType` predicate.
+ - `test(Task task)`: Returns true if given Task has the same `RecurringType` as the predicate.
+ - `equals(Object other)`: Compares two `TaskRecurringTypePredicate` objects for equality.
+ - `toString()`: Returns a string representation of the `TaskRecurringTypePredicate`.
+
+### `CombinedPredicate`
+- Purpose: Represents the combination of multiple predicates. It contains an `ArrayList>`, which contains the predicates it considers.
+- Key Methods:
+ - `test(Task task)`: Returns true if given Task fulfills all the predicates in the ArrayList of predicates.
+ - `equals(Object other)`: Compares two `CombinedPredicate` objects for equality.
+ - `toString()`: Returns a string representation of the `CombinedPredicate`.
+
+### Usage
+To use the `FilterCommand` in the ProfPlan application, you can execute the following steps:
+
+1. Enter the "filter" command followed by "d/", "p/", "s/" and/or "recur/", for DueDate, Priority, Status and RecurringType respectively. Then, input the value of predicate, which must follow the formats of the criteria:
+ - DueDate: a date in the format dd-MM-yyyy
+ - Priority: an Integer from 1 to 10 inclusive
+ - Status: "done" or "undone
+ - RecurringType: "none", "daily", "weekly", "monthly", or "semesterly"
+For example, "filter d/01-01-2023 s/undone" filters for tasks before and on 01-01-2023 that are undone.
+
+2. The `FilterCommand` will filter the task list in the model according to the predicate(s) applied.
+
+3. The application will provide feedback, indicating the successful filtering, and display the relevant tasks.
+
+
+
+## Help for Commands feature
+
+The detail listing mechanism is facilitated by the `HelpCommandParser` class, which implements the `Parser` interface. This `HelpCommandParser` overrides the `parse()` method in `Parser` and creates either an empty `HelpCommand`, or a `HelpCommand` with a `COMMAND_WORD` as a String. For example `help` or `help delete`.
+
+Hence, this will be facilitated by overloading the constructor in `HelpCommand` to either create an empty `HelpCommand`, or a `HelpCommand` with a `COMMAND_WORD`:
+* When an empty `HelpCommand` executes `execute()`, a `CommandResult` with `MESSAGE_USAGE` of all commands will be created.
+* When a `HelpCommand` with a `COMMAND_WORD` executes `execute()`, a `CommandResult` with the `MESSAGE_DETAIL` of the `Command` specified by the `COMMAND_WORD` will be created.
+
+The following activity diagram shows how the Help Command works:
+
+![HelpActivityDiagram](images/HelpActivity.png)
+
+### Design considerations:
+
+**Aspect: How to store `Command` details:**
+
+* **Alternative 1 (current choice): Store Command Details in each command.**
+
+ * Pros: Follows OOP principles.
+
+ * Cons: Changes will need to be propagated in Command Usage and Details format to all Commands individually.
+
+* **Alternative 2: Store all Command Details in a separate class.**
+
+ * Pros: Centralised area to view all Messages.
+
+ * Cons: Does not follow OOP principles. Future implementations that tap on these Details will take longer to implement.
+
+
+
+## Displaying statistics
+
+The statistics mechanism requires support from `Model`, which will provide `StatsCommand` the required statistics. Currently, this is done using a public getter `getCompletionRate()`.
+
+When `StatsCommand` executes `execute()`, it calls this getter and returns a new `CommandResult` containing a formatted `String`, with the statistics retrieved.
+
+The following sequence diagram shows how the stats command works:
+
+![StatsSequenceDiagram](images/StatsSequenceDiagram.png)
+
+Here's a breakdown of the sequence:
+1. The `LogicManager` receives the command "stats" from the user.
+1. The `ProfPlanParser` parses the command and creates a `StatsCommand`.
+1. The `StatsCommand` is executed by `LogicManager`.
+1. The getter, `getCompletionRate()`, is called on `Model`.
+1. `Model` returns a `double` containing the completion rate.
+1. A `CommandResult` is created and formatted to provide feedback to the user.
+1. The result is returned to the `LogicManager`.
+
+### Design considerations:
+
+**Aspect: How to retrieve statistics:**
+
+* **Alternative 1 (current choice): Use a public getter in Model.**
+
+ * Pros: Simple and readable.
+
+ * Cons: Exposes more of Model, which may result in unintended use by developers.
+
+* **Alternative 2: Retrieve a copy of the task list and calculate statistics as such.**
+
+ * Pros: List is not mutated. All calculations of Statistics is centralised under StatsCommand.
+
+ * Cons: Does not follow OOP principles. Operations regarding the list should be handled by the list.
+
+
+
+## \[Proposed\] Undo/redo feature
+
+### Proposed Implementation
+
+The proposed undo/redo mechanism is facilitated by `VersionedProfPlan`. It extends `ProfPlan` with an undo/redo history, stored internally as an `profPlanStateList` and `currentStatePointer`. Additionally, it implements the following operations:
+
+* `VersionedProfPlan#commit()` — Saves the current tasklist state in its history.
+* `VersionedProfPlan#undo()` — Restores the previous tasklist state from its history.
+* `VersionedProfPlan#redo()` — Restores a previously undone tasklist state from its history.
+
+These operations are exposed in the `Model` interface as `Model#commitProfPlan()`, `Model#undoProfPlan()` and `Model#redoProfPlan()` respectively.
Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.
-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.
+Step 1. The user launches the application for the first time. The `VersionedProfPlan` will be initialized with the initial task list state, and the `currentStatePointer` pointing to that single task list state.
![UndoRedoState0](images/UndoRedoState0.png)
-Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state.
+Step 2. The user executes `delete 5` command to delete the 5th task in the tasklist. The `delete` command calls `Model#commitProfPlan()`, causing the modified state of the task list after the `delete 5` command executes to be saved in the `profPlanStateList`, and the `currentStatePointer` is shifted to the newly inserted tasklist state.
![UndoRedoState1](images/UndoRedoState1.png)
-Step 3. The user executes `add n/David …` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`.
+Step 3. The user executes `add n/Grade assignment …` to add a new task. The `add` command also calls `Model#commitProfPlan()`, causing another modified tasklist state to be saved into the `ProfPlanStateList`.
![UndoRedoState2](images/UndoRedoState2.png)
-
: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`.
+
:information_source: **Note:** If a command fails its execution, it will not call `Model#commitProfPlan()`, so the tasklist state will not be saved into the `profPlanStateList`.
-Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state.
+Step 4. The user now decides that adding the task was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoProfPlan()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous tasklist state, and restores the tasklist to that state.
![UndoRedoState3](images/UndoRedoState3.png)
-
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather
-than attempting to perform the undo.
+
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial ProfPlan state, then there are no previous ProfPlan states to restore. The `undo` command uses `Model#canUndoProfPlan()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the undo.
@@ -201,17 +525,17 @@ The following sequence diagram shows how the undo operation works:
-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.
+The `redo` command does the opposite — it calls `Model#redoProfPlan()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the tasklist 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.
+
:information_source: **Note:** If the `currentStatePointer` is at index `profPlanStateList.size() - 1`, pointing to the latest tasklist state, then there are no undone ProfPlan states to restore. The `redo` command uses `Model#canRedoProfPlan()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
-Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged.
+Step 5. The user then decides to execute the command `list`. Commands that do not modify the tasklist, such as `list`, will usually not call `Model#commitProfPlan()`, `Model#undoProfPlan()` or `Model#redoProfPlan()`. Thus, the `profPlanStateList` remains unchanged.
![UndoRedoState4](images/UndoRedoState4.png)
-Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …` command. This is the behavior that most modern desktop applications follow.
+Step 6. The user executes `clear`, which calls `Model#commitProfPlan()`. Since the `currentStatePointer` is not pointing at the end of the `profPlanStateList`, all tasklist states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/Grade assignment …` command. This is the behavior that most modern desktop applications follow.
![UndoRedoState5](images/UndoRedoState5.png)
@@ -219,27 +543,112 @@ The following activity diagram summarizes what happens when a user executes a ne
-#### Design considerations:
+### Design considerations:
**Aspect: How undo & redo executes:**
-* **Alternative 1 (current choice):** Saves the entire address book.
+* **Alternative 1 (current choice):** Saves the entire tasklist.
* Pros: Easy to implement.
* Cons: May have performance issues in terms of memory usage.
* **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).
+ * Pros: Will use less memory (e.g. for `delete`, just save the task being deleted).
* Cons: We must ensure that the implementation of each individual command are correct.
-_{more aspects and alternatives to be added}_
+
+
+## \[Proposed\] Task Archiving feature
+
+### Proposed Implementation
+
+The proposed task archiving function makes use of the `ArchivedProfPlan` class. It extends `ProfPlan`, storing a separate list of tasks that are archived.
+
+In addition, the `ModelManager`, which implements `Model`, has to include `ArchivedProfPlan`, in addition to `ProfPlan`, as one of its fields.
+
+
+#### 1. Archiving tasks
+
+This is supported by the `ArchiveCommand` that extends `Command`, and implements this main operation:
+
+* `ArchiveCommand#execute(Model model)` — Archives the selected task, i.e. deleting it from ProfPlan and adding it to ArchivedProfPlan.
+
+This operation invodes a method in the `Model` interface, namely `Model#archiveTask(Task target)`.
-### \[Proposed\] Data archiving
+To parse the Archive Command, there is the `ArchiveCommandParser` that implements `Parser`, which supports this main operation:
-_{Explain here how the data archiving feature will be implemented}_
+* `ArchiveCommandParser#parse(String args)` — Parses the user input to return an `ArchiveCommand`.
+ * Valid arguments: Integers (task index), or "all"
+
+
+The sequence diagram below shows how archiving a task works.
+
+![ArchiveSequenceDiagram](images/ArchiveSequenceDiagram.png)
+
+
+#### 2. List archived tasks
+
+This is supported by the `ListArchivesCommand` that extends `Command`, and implements this main operation:
+
+* `ListArchivesCommand#execute(Model model)` — Displays all archived tasks.
+
+This operation invodes a method in the `Model` interface, namely `Model#getArchivedTasks()`.
+
+
+The sequence diagram below shows how displaying archived tasks works.
+
+![ListArchivesSequenceDiagram](images/ListArchivesSequenceDiagram.png)
+
+
+#### 3. Delete archived tasks
+
+This uses similar implementation as the delete function on ProfPlan, but acting on ArchivedProfPlan instead.
+
+It is supported by the `DeleteArchiveCommand` that extends `Command`, and implements this main operation:
+
+* `DeleteArchiveCommand#execute(Model model)` — Deletes the selected task from ArchivedProfPlan.
+
+This operation invodes a method in the `Model` interface, namely `Model#deleteArchive(Task target)`.
+
+To parse the DeleteArchive Command, there is the `DeleteArchiveCommandParser` that implements `Parser`, which supports this main operation:
+
+* `DeleteArchiveCommandParser#parse(String args)` — Parses the user input to return an `DeleteArchiveCommand`.
+ * Valid arguments: Integers (task index), or "all"
+
+
+#### 4. Restore archived tasks
+
+This uses similar implementation as the archive function, but reversing ProfPlan and ArchivedProfPlan.
+
+It is supported by the `RestoreArchiveCommand` that extends `Command`, and implements this main operation:
+
+* `RestoreArchiveCommand#execute(Model model)` — Restores the selected archived task, i.e. deleting it from ArchivedProfPlan and adding it to ProfPlan.
+
+This operation invodes a method in the `Model` interface, namely `Model#restoreArchiveTask(Task target)`.
+
+To parse the Archive Command, there is the `RestoreArchiveCommandParser` that implements `Parser`, which supports this main operation:
+
+* `RestoreArchiveCommandParser#parse(String args)` — Parses the user input to return an `RestoreArchiveCommand`.
+ * Valid arguments: Integers (task index), or "all"
+
+
+### Design considerations:
+
+**Aspect: How to store archived tasks:**
+
+* **Alternative 1 (current choice):** Saves archived tasks in a separate tasklist from the main ProfPlan, named ArchivedProfPlan.
+ * Pros:
+ * Better performance if many tasks are archived, and methods invoked on ProfPlan will not affect tasks in ArchivedProfPlan.
+ * Clear segregation between archived and normal tasks.
+ * Cons: Harder to implement.
+
+* **Alternative 2:** Each task have an archive flag, which indicates whether they are archived. Archived tasks are not shown in the main ProfPlan.
+ * Pros: Easier to implement as it is similar to tagging tasks.
+ * Cons: Performance issues in the main ProfPlan if many tasks are archived.
--------------------------------------------------------------------------------------------------------------------
+
## **Documentation, logging, testing, configuration, dev-ops**
@@ -250,6 +659,7 @@ _{Explain here how the data archiving feature will be implemented}_
* [DevOps guide](DevOps.md)
--------------------------------------------------------------------------------------------------------------------
+
## **Appendix: Requirements**
@@ -257,74 +667,370 @@ _{Explain here how the data archiving feature will be implemented}_
**Target user profile**:
-* has a need to manage a significant number of contacts
+* needs to manage a significant number of tasks
+* prefer to organise and classify tasks
+* want to visualise relationships between tasks
* prefer desktop apps over other types
* can type fast
* prefers typing to mouse interactions
* is reasonably comfortable using CLI apps
-**Value proposition**: manage contacts faster than a typical mouse/GUI driven app
+**Value proposition**: Professors often need to plan and organise their course modules, including lecture materials, and research. ProfPlan can help them create and manage their academic tasks seamlessly through the CLI, which is optimised and helps in faster task management. It is tailored to professors’ requirements and task categories, helping them prioritise.
+
### User stories
-Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*`
+**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… |
+|----------|-----------|-------------------------------------------------------------|-----------------------------------------|
+| `* * *` | professor | be able to create a new task | keep track of them in a list |
+| `* * *` | professor | assign priorities to tasks in my list | gauge task urgency |
+| `* * *` | professor | categorise/tag my tasks | segregate and classify tasks |
+| `* * *` | professor | mark tasks as done/undone | keep track of what has been completed |
+| `* * *` | professor | delete tasks | clear up clutter |
+| `* *` | professor | filter tasks by due date | manage deadlines effectively |
+| `* *` | professor | search for tasks | find the task I want to work on quickly |
+| `*` | professor | attach reference materials (filepath) or links to each task | access reference material easily |
+| `*` | professor | create subtasks within the task list | have better organisation |
+
+
+
### 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 `ProfPlan` and the **Actor** is the `user`, unless specified otherwise.
-**Use case: Delete a person**
+
+**Use Case: Create a New Task**
**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 create a new task with specified details.
+2. ProfPlan validates the input format for the given fields.
+3. ProfPlan creates the new task and adds it to the task list.
+4. ProfPlan displays a confirmation message with the updated task list.
- Use case ends.
+ Use case ends.
**Extensions**
-* 2a. The list is empty.
+* **2a.** User enters a field with invalid format.
- Use case ends.
+ * **2a1.** ProfPlan displays an error message and provides guidance on the correct format.
-* 3a. The given index is invalid.
+ *Use case ends.*
- * 3a1. AddressBook shows an error message.
+* **3a.** ProfPlan encounters an internal error while creating the task.
- Use case resumes at step 2.
+ * **3a1.** ProfPlan displays an error message indicating the task creation failed.
-*{More to be added}*
+ *Use case ends.*
+
+
+**Use Case: Edit a Task**
+
+**MSS**
+
+1. User requests to edit a task.
+2. ProfPlan validates the input format for the task number and the specified field(s).
+3. ProfPlan assigns new value(s) to the specified field(s) of the task.
+4. ProfPlan displays a confirmation message with the updated task.
+
+
+ Use case ends.
+
+**Extensions**
+
+* 2a. User specifies a task number that is outside the list indices or provides an invalid input.
+
+ * 2a1. ProfPlan displays an error message to indicate invalid input.
+
+ Use case ends.
+
+* 2b. User requests to edit a task when there are no tasks in the list.
+
+ * 2b1. ProfPlan displays an error message to indicate that there are no tasks.
+
+ Use case ends.
+
+* 3a. ProfPlan encounters an internal error while updating new values.
+
+ * 3a1. ProfPlan displays an error message to indicate edit task failed.
+
+ Use case ends.
+
+
+**Use case: Find a Task**
+
+**MSS**
+
+1. User requests to find tasks matching certain search term(s).
+2. ProfPlan displays a list of tasks that contain the search term(s).
+
+ Use case ends.
+
+**Extensions**
+
+* 2a. There are no tasks that match the search term(s).
+
+ * 2a1. ProfPlan displays an empty list, and informs the user that the search failed to find matches.
+
+ *Use case ends.*
+
+**Use Case: Mark Task as Done/Undone**
+
+*Preconditions: There is at least one task in the task list.*
+
+**MSS**
+
+1. User requests to list tasks.
+2. ProfPlan shows a list of tasks.
+3. User selects a task to mark as done or undone by specifying its number in the list.
+4. ProfPlan updates the task's completion status accordingly.
+
+ Use case ends.
+
+**Extensions**
+
+* 3a. User specifies a task number that is outside the list indices or provides an invalid input.
+
+ * 3a1. ProfPlan displays an error message.
+
+ Use case ends.
+
+* 3b. User attempts to mark a task as done/undone when there are no tasks in the list.
+
+ * 3b1. ProfPlan displays an error message.
+
+ Use case ends.
+
+
+**Use Case: Delete Task/Delete All Tasks**
+
+*Preconditions: There is at least one task in the task list.*
+
+**MSS**
+
+1. User requests to list tasks.
+2. ProfPlan shows a list of tasks.
+3. User chooses to delete a specific task by specifying its number in the list or requests to delete all tasks.
+4. ProfPlan performs the deletion as per the user's request.
+
+ Use case ends.
+
+**Extensions**
+
+* 3a. User specifies a task number that is outside the list indices or provides an invalid input.
+
+ * 3a1. ProfPlan displays an error message.
+
+ Use case ends.
+
+* 3b. User requests to delete a task when there are no tasks in the list.
+
+ * 3b1. ProfPlan displays an error message.
+
+ Use case ends.
+
+
+**Use Case: Filter Tasks based on Due Date**
+
+**MSS**
+
+1. User requests to filter task based on due date.
+2. ProfPlan validates the input format for the date.
+3. ProfPlan performs the filter and display tasks that fall before and on the specified date.
+
+
+ Use case ends.
+
+**Extensions**
+
+
+* 2a. User provides an invalid date input.
+
+ * 2a1. ProfPlan displays an error message to indicate invalid input.
+
+ Use case ends.
+
+* 3a. User requests to filter tasks when there are no tasks in the list.
+
+ * 3a1. ProfPlan displays an empty list.
+
+ Use case ends.
+
+* 3b. There are tasks in the list but no tasks before the specified date.
+
+ * 3b1. ProfPlan displays an empty list.
+
+ Use case ends.
+
+
+**Use Case: Attaching a link to a Task**
+
+*Preconditions: There is at least one task in the task list.*
+
+**MSS**
+
+1. User requests to list tasks.
+2. ProfPlan shows a list of tasks.
+3. User requests to attach a link to a task.
+4. ProfPlan attaches the link as per the user's request.
+
+* 3a. User specifies a task number that is outside the list indices or provides an invalid input.
+
+ * 3a1. ProfPlan displays an error message.
+
+ Use case ends.
+
+* 3b. User requests to attach a link to a task when there are no tasks in the list.
+
+ * 3b1. ProfPlan displays an error message.
+
+ Use case ends.
+
+* 3c. User requests to attach an invalid link to a task.
+
+ * 3c1. ProfPlan displays an error message.
+
+ Use case ends.
+
+**Use Case: Generating a Task Recommendation with `do_next` Command**
+
+*Preconditions: The app is launched and at least one task in task list.*
+
+**MSS**
+
+1. User requests a task recommendation using the `do_next` command.
+2. The system analyzes the task list based on proximity to the deadline and priority.
+3. ProfPlan recommends the most important task to the user.
+
+ - 3a. If there are multiple tasks with the same priority, ProfPlan selects the one closest to the deadline.
+
+ Use case ends.
+
+ - 3b. If no tasks are present in the list.
+
+ - 3b1. ProfPlan displays a completion message.
+
+ Use case ends.
+
+ - 3c. If the user executes `delete all` and then uses the `do_next` command.
+
+ - 3c1. No tasks are present. ProfPlan displays a completion message.
+
+ Use case ends.
+
+ - 3d. If the user inputs incorrect `do_next` commands (e.g., `doNext`, `do_next x`, `...`)
+
+ - 3d1. ProfPlan displays an error message.
+
+ Use case ends.
+
+
+**Use Case: Changing a setting**
+
+**MSS**
+
+1. User requests to update a setting with a new value.
+2. ProfPlan updates the specified setting with the provided value.
+
+ Use case ends.
+
+**Extensions:**
+
+* 1a. User specifies a nonexistent setting, or an invalid value.
+
+ * 1a1. ProfPlan displays an error message.
+
+ Use case ends.
+
+**Use Case: Sorting Tasks based on priority**
+
+**MSS**
+
+1. User requests to sort tasks based on descending order of priority.
+2. ProfPlan validates the input.
+3. ProfPlan sorts the tasks based on descending order of priority.
+
+ Use case ends.
+
+**Extensions:**
+
+* 1a. User requests to sort the tasks when there are no tasks displayed in the Ui.
+
+ * 1a1. ProfPlan displays an error message.
+
+ Use case ends.
+
+**Use Case: Sorting Tasks based on nearest duedate**
+
+**MSS**
+
+1. User requests to sort tasks based on nearest duedate.
+2. ProfPlan validates the input.
+3. ProfPlan sorts the tasks based on nearest duedate.
+
+ Use case ends.
+
+**Extensions:**
+
+* 1a. User requests to sort the tasks when there are no tasks displayed in the Ui.
+
+ * 1a1. ProfPlan displays an error message.
+
+ Use case ends.
+
+
+
### Non-Functional Requirements
+#### Performance
+
+- **Response Time:** ProfPlan shall respond to user commands within 2 seconds under normal system load conditions. This response time includes task creation, task modification, and task retrieval operations.
+
+- **Scalability:** The application should be able to handle a growing number of tasks and users efficiently. It should scale gracefully without significant performance degradation.
+
+#### Reliability
+
+- **Availability:** ProfPlan should be available for use 24/7, with planned maintenance windows communicated to users in advance. The application should have an uptime of at least 99.5%.
+
+- **Error Handling:** The application should provide informative error messages to users when they input incorrect or invalid commands. It should gracefully handle errors and exceptions without crashing.
+
+#### Security
-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.
+- **Data Protection:** ProfPlan should ensure the security and confidentiality of user data. User passwords and sensitive information should be stored securely using industry-standard encryption techniques.
+
+- **Authentication:** Users should be required to log in with a valid username and password to access their task lists. Unauthorized access to user data should be prevented.
+
+#### Compatibility
+
+- **Java Version:** ProfPlan should be compatible with Java JDK 11 or higher to ensure optimal performance and support for the latest features and security updates.
+
+#### Usability
+
+- **User-Friendly Interface:** The CLI interface should be intuitive and easy to use, with clear and concise prompts, error messages, and help documentation.
+
+- **Accessibility:** The application should be designed to be accessible to users with disabilities, following best practices for accessibility standards.
*{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
--------------------------------------------------------------------------------------------------------------------
+
+
## **Appendix: Instructions for manual testing**
Given below are instructions to test the app manually.
@@ -340,38 +1046,286 @@ testers are expected to do more *exploratory* testing.
1. Download the jar file and copy into an empty folder
- 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
+ 1. Type the following command in your terminal
+
+ `java -jar ProfPlan.jar`
+
+ Expected: Shows the Graphical User Interface with a set of sample tasks.
+
1. Saving window preferences
1. Resize the window to an optimum size. Move the window to a different location. Close the window.
- 1. Re-launch the app by double-clicking the jar file.
- Expected: The most recent window size and location is retained.
+ 1. Re-launch the app by typing the command below in your terminal.
-1. _{ more test cases … }_
+ `java -jar ProfPlan.jar`
-### Deleting a person
+ Expected: The most recent window size and location is retained.
-1. Deleting a person while all persons are being shown
+### Adding a task
- 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list.
+1. Adding a task
- 1. Test case: `delete 1`
- Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated.
+ 1. Test case: `add n/Attend seminar p/1 d/01-01-2023`
+ Expected: Added task into list. Details of the task is shown in the status message.
+ 1. Test case: `add n/Grade assignments p/1 d/01-01-2023 recur/weekly t/grading t/assignment l/www.example.com des/for cs0000`
+ Expected: Same as above
+
+ 1. Test case: `add n/task p/3`
+ Expected: Compulsory DueDate field missing, task is not added. Error details is shown in the status message.
+
+ 1. Other incorrect add commands to try: `add n/task 1 p/1 d/`, `add n/task 2 n/task 2`
+ Expected: Task is not added. Error details is shown in the status message.
+
+
+### Editing a task
+
+1. Editing a task
+
+ 1. Prerequisites: There must be at least one task in list.
+
+ 1. Test case: `edit 1 n/Attend networking event`
+ Expected: Name of the first task is edited. Details of the updated task is shown in the status message.
+
+ 1. Test case: `edit 1 t/`
+ Expected: Tags of the first task is removed. Details of the updated task is shown in the status message.
+
+ 1. Test case: `edit 1`
+ Expected: At least one field must be edited. Error details is shown in the status message.
+
+ 1. Other incorrect edit commands to try: `edit`, `edit x`, `...` (where x is larger than the list size)
+ Expected: Task is not edited. Error details is shown in the status message.
+
+
+### Finding tasks from keywords
+
+1. Finding a task
+
+ 1. Test case: `find event`
+ Expected: Task with name containing "event" is displayed.
+
+ 1. Test case: `find event event`
+ Expected: Same as above.
+
+ 1. Test case" `find event fsfkjsnfk`
+ Expected: Same as above. If task containing "fsfkjsnfk" exists, that is displayed too.
+
+ 1. Other incorrect find commands to try: `find`
+ Expected: Error details is shown in the status message.
+
+
+### Marking a `Task` as done
+
+1. Marking a task while particular tasks are being shown
+
+ 1. Prerequisites: Launch the app.
+
+ 1. Test case: `mark 1`
+ Expected: The status of first `Task` is marked as `done`.
+ 1. Test case: `mark 0`
+ Expected: No `Task` is marked as `done`. An error message is displayed.
+
+ 1. Other incorrect delete commands to try: `mark`, `mark x`, `...` (where x is larger than the list size)
+ Expected: Similar to previous.
+
+### Marking a `Task` as undone
+
+1. Marking a task while particular tasks are being shown
+
+ 1. Prerequisites: Launch the app.
+ 1. Test case: `unmark 1`
+ Expected: The status of first `Task` is marked as `undone`.
+ 1. Test case: `unmark 0`
+ Expected: No `Task` is marked as `undone`. An error message is displayed.
+
+ 1. Other incorrect delete commands to try: `unmark`, `unmark x`, `...` (where x is larger than the list size)
+ Expected: Similar to previous.
+
+### Deleting a `Task`
+
+1. Deleting a task while all tasks are being shown
+
+ 1. Prerequisites: Launch the app.
+
+ 1. Test case: `delete 1`
+ Expected: First `Task` is deleted from the list.
1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.
+ Expected: No `Task` is deleted. An error message is displayed.
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 … }_
+
+### List tasks within week/month from now
+
+1. Listing tasks within week/month from now
+
+ 1. Test case: `list_week`
+ Expected: All tasks that are due within a week from the current date is displayed.
+
+ 1. Test case: `list_month`
+ Expected: All tasks that are due within a month from the current date is displayed.
+
+ 1. Other incorrect commands to try: `listweek`, `list month`
+ Expected: Error details is shown in the status message.
+
+### Generate Recommendation for next task to do
+
+1. Generating a task using `do_next` command
+
+ 1. Prerequisites: Launch the app.
+
+ 1. Test case: `do_next`
+ Expected: Most Important `Task`, based on proximity to deadline, as well as higher priority is selected to be recommended.
+ 1. Test case: `delete all` then `do_next`
+ Expected: No `Task` is present. `No more tasks to do Prof! Skies are clear ahead :)` message is displayed
+
+ 1. Other incorrect do_next commands to try: `doNext`, `do_next x`, `...` (where x is any character)
+ Expected: `Unknown Command`
+
+### Editing priority of a task
+
+1. Editing task priority using the `edit` command
+
+ 1. Prerequisites: Launch the app.
+
+ 1. Test case: `edit 1 p/8`
+ Expected: First `Task` priority is changed to 8.
+ 1. Test case: `edit 1 p/12`
+ Expected: `Priority should only contain numbers between 1-10` feedback message is displayed
+
+ 1. Other incorrect priority commands to try: `edit 1 p/0`, `edit 1 p/-4`, `edit 1 p/d`
+ Expected: `Priority should only contain numbers between 1-10`
+
+### Filtering for tasks
+
+1. Filtering tasks
+
+ 1. Test case: `filter d/01-01-2024`
+ Expected: All tasks that are due before or on 01-01-2024 is displayed. Details of filter criteria is shown in the status message.
+
+ 1. Test case: `filter d/01-01-2024 p/1`
+ Expected: All tasks that are due before or on 01-01-2024, and of priority 1 is displayed. Details of filter criteria is shown in the status message.
+
+ 1. Test case: `filter d/01-01-2024 p/1 s/undone`
+ Expected: All undone tasks that are due before or on 01-01-2024, and of priority 1 is displayed. Details of filter criteria is shown in the status message.
+
+ 1. Test case: `filter d/01-01-2024 p/1 s/undone recur/weekly`
+ Expected: All undone tasks that are due before or on 01-01-2024, of priority 1, and recurring weekly is displayed. Details of filter criteria is shown in the status message.
+
+ 1. Other incorrect filter commands to try: `filter d/`, `filter p/0`, `filter s/done s/undone`, `filter recur/weekly s/`
+ Expected: Error details is shown in the status message.
+
+
+
+### Sorting `Tasks` based on Descending Order of Priority
+1. Sorting Tasks based on descending order of priority while all tasks are being shown
+
+ 1. Prerequisites: Launch the app.
+
+ 1. Test case: `sort_priority`
+ Expected: Sorts the Tasks that are displayed currently based on the descending order of Priority.
+
+ 1. Test case: `sortpriority`
+ Expected: No Sorting is done, an error message is displayed.
+ 1. Other incorrect delete commands to try: `sort_`, `sort priority`, `...` (where ... any combination of words related sort priority.)
+ Expected: Similar to previous.
+
+### Sorting `Tasks` based on Nearest DueDate
+1. Sorting Tasks while all tasks are being shown
+
+ 1. Prerequisites: Launch the app.
+
+ 1. Test case: `sort_duedate`
+ Expected: Sorts the Tasks that are displayed currently based on the descending order of Priority.
+
+ 1. Test case: `sortduedate`
+ Expected: No Sorting is done, an error message is displayed.
+ 1. Other incorrect delete commands to try: `sort_`, `sort duedate`, `...` (where ... any combination of words related sort priority.)
+ Expected: Similar to previous.
+
+### Getting help
+1. Showing the list and usage of all command
+ 1. Test case: `help`
+ Expected: Show a list of all the command words and the usage for each command.
+1. Showing more detailed usage about a particular command
+ 1. Test case: `help add`
+ Expected: Shows the description, parameters and an example of a proper use of the `add` command. No change to the list occurs.
+
+ 1. Test case: `help sort_duedate`
+ Expected: Shows the description of the `sort_duedate` command. No change to the list occurs.
+
+### Showing stats
+1. Showing statistics of the whole list
+ 1. Test case: `stats`
+ Expected: Shows the completion rate of all tasks in the task list, regardless of filter.
### Saving data
1. Dealing with missing/corrupted data files
- 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_
+Note: The data file refer to the /data/profplan.json file.
+
+ 1. Missing data file
+ - Delete the /data folder (if any), and start the application.
+ Expected: ProfPlan will create a new data file, and start from an empty task list.
+ 2. Corrupted data file
+ - Open the data file, and type in a string of random characters.
+ Expected: ProfPlan will delete the current data file and create a new one, starting from an empty task list.
+
+
+
+## Appendix: Effort
+
+### Overview
+
+ProfPlan is an advanced task management application tailored specifically for computer science professors. This project extends the functionalities of AB3, introducing unique features that cater to the needs of the target audience. The development of ProfPlan posed significant challenges due to its extensive and dynamic features, necessitating a dedicated effort from our team.
+
+### Challenges and Efforts
+
+**1. Better task organisation**
+
+To represent the complex tasks faced by professors, we added many additional parameters to a Task, building upon the foundation provided by AB3. Examples include due date, recurring type, and links. These features introduces further complexity in tracking and organisating tasks, and require an enhanced understanding of data structures and optimised coding designs.
+
+It also serves as a basis for more sophisticated task organisation functions. One example is our robust system of sort and filter mechanisms. To ensure that tasks are presented to the user in an organized and meaningful way, we introduced a variety of commands and criteria that a task can be sort/filtered by. This required a thorough understanding of sorting mechanisms and backend activity.
+
+**2. Custom algorithms**
+
+The "Recommend next task" feature intelligently analyses tasks based on their priorities and due dates, to determine which task should be completed next. This necessitates the creation of a custom algorithm that compares and weighs the importance of tasks, while resolving ties and conflicts in a sophisticated manner.
+
+In addition, the "View Task statistics" feature required intensive calculation and aggregation of data related to Tasks, namely their Status, in order to generate a meaningful task completion rate for users.
+
+
+**3. UI Visualisation**
+
+ProfPlan introduces the "Urgency-Priority Matrix", a novel visualization tool, to organise tasks based on its priority, and due date. Developing an intuitive and comprehensive UI display required a deep understanding of JavaFX and frontend development, as well as a keen eye for user experience design.
+
+
+### Effort Saved Through Reuse
+
+We reused components from AB3, particularly the foundational architecture and some basic UI elements. This reuse saved approximately 10% of our total development effort.
+
+
+### Achievements
+
+Despite the challenges, our team successfully delivered a fully functional and user-friendly application. Novel features like the Urgency-Priority Matrix and intelligent task recommendation have been well-received by our user base, demonstrating the software's practical utility for our target audience.
+
+In summary, the development of ProfPlan was a challenging yet rewarding endeavor. The application's success stands as a testament to our team's dedication in software development.
+
+
+
+## **Appendix: Planned Enhancements**
+
+### Recommend next task:
+
+More sophisticated tie-breaker mechanism can be implemented with Natural Language Processing. When tasks have the same priority and due date, the preferences of the user can be considered in determining which task is of higher importance.
+
+### Link:
+
+User input for the Link parameter of a Task should be verified for its validity as a URL. Links that are displayed in the UI should also redirect users to the specified URL upon clicking, instead of just being regular text.
+
+### Visual indicators:
-1. _{ more test cases … }_
+There can be colour-coding for tasks based on their priority, or tasks that are overdue. These visual aids will be helpful in alerting professors to more important, and overdue tasks.
diff --git a/docs/SettingUp.md b/docs/SettingUp.md
index 275445bd551..ca9e3d75717 100644
--- a/docs/SettingUp.md
+++ b/docs/SettingUp.md
@@ -23,7 +23,7 @@ If you plan to use Intellij IDEA (highly recommended):
1. **Import the project as a Gradle project**: Follow the guide [_[se-edu/guides] IDEA: Importing a Gradle project_](https://se-education.org/guides/tutorials/intellijImportGradleProject.html) to import the project into IDEA.
:exclamation: Note: Importing a Gradle project is slightly different from importing a normal Java project.
1. **Verify the setup**:
- 1. Run the `seedu.address.Main` and try a few commands.
+ 1. Run the `profplan.Main` and try a few commands.
1. [Run the tests](Testing.md) to ensure they all pass.
--------------------------------------------------------------------------------------------------------------------
diff --git a/docs/Testing.md b/docs/Testing.md
index 8a99e82438a..c05c02d23d4 100644
--- a/docs/Testing.md
+++ b/docs/Testing.md
@@ -31,6 +31,6 @@ This project has three types of tests:
1. *Unit tests* targeting the lowest level methods/classes.
e.g. `seedu.address.commons.StringUtilTest`
1. *Integration tests* that are checking the integration of multiple code units (those code units are assumed to be working).
- e.g. `seedu.address.storage.StorageManagerTest`
+ e.g. `storage.profplan.StorageManagerTest`
1. Hybrids of unit and integration tests. These test are checking multiple code units as well as how the are connected together.
- e.g. `seedu.address.logic.LogicManagerTest`
+ e.g. `logic.profplan.LogicManagerTest`
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index 57437026c7b..02093cbc56f 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -1,197 +1,790 @@
---
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.
+
+
+#
User Guide - ProfPlan
+
+
+![TeamProfPlan](images/profplan_32.png)
+
-* Table of Contents
-{:toc}
+## Overview
---------------------------------------------------------------------------------------------------------------------
+We believe that no Computer Science (CS) professor should have to wrestle and wrangle with redundant features and complicated interfaces. Built upon hours of research, ProfPlan offers a fast, efficient and seamless user experience for you to resolve your schedules in minimal time.
-## Quick start
+ProfPlan is a **desktop app for managing tasks, 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, ProfPlan can get your contact management tasks done faster than traditional GUI apps.
-1. Ensure you have Java `11` or above installed in your Computer.
+
-1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases).
+## What It Does
-1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook.
+ProfPlan can help you manage tasks seamlessly. Create, edit, delete tasks with ease. Our specialised tasks help you assign priorities, deadlines, status and much more to enhance your task management experience.
-1. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar addressbook.jar` command to run the application.
- A GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
- ![Ui](images/Ui.png)
+Our **smart recommendation** and **visualisation** features help you decide what to do next, and rank tasks based on several factors. You can also manage **recurring tasks** smoothly using ProfPlan’s recurring task functionality.
-1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
- Some example commands you can try:
+## Target Users
+
+This product is specifically designed for **CS professors** who prefer **CLI** over GUI and have a **variety of tasks** such as module management (lecture, assessments, readings), research tracking (deadlines, publications, collaborations, papers), admin tasks (department, paperwork, meetings, budget) and so on.
+
+## Team Behind ProfPlan
+
+![TeamProfPlan](images/TeamProfPlan.png)
+Contact us using this [Google Form](https://forms.gle/Dzb12Re4MYJxzf8w6) to connect with us, leave feedback, and more!
+
+
+---
+
+## Contents
+
+- [Overview](#overview)
+- [A Task in ProfPlan](#a-task-in-profplan)
+- [What can ProfPlan do for you?](#what-can-profplan-do-for-you)
+- [Quick Start](#quick-start)
+- [Basic Features](#basic-features)
+ 1. [Viewing help : help](#viewing-help--help)
+ a. [Viewing help for a specific command](#a-viewing-help-for-a-specific-command)
+ 1. [Create a new task : add](#create-a-new-task--add)
+ 1. [Edit existing tasks : edit](#edit-existing-tasks--edit)
+ 1. [Delete tasks and delete all tasks : delete](#delete-tasks-and-delete-all-tasks--delete)
+ 1. [List all Tasks : list](#list-all-tasks--list)
+ 1. [List Tasks due within week : list week](#list-tasks-within-a-week-from-now-list_week)
+ 1. [List Tasks due within month: list month](#list-tasks-within-a-month-from-now-list_month)
+ 1. [Locating Tasks by name: find](#locating-tasks-by-name-find)
+ 1. [Setting Configurations](#configuring-settings--set)
+ 1. [Editing the data file](#editing-the-data-file)
+ 1. [Saving the data](#saving-the-data)
+ 1. [Exiting the program : exit](#exiting-the-program--exit)
+- [Task Management Features](#task-management-features)
+ 1. [Parameters](#parameters)
+ 1. [Description](#description)
+ 1. [Priority](#priority)
+ 1. [Status](#status)
+ 1. [Due Date](#due-date)
+ 1. [Tags](#tags)
+ 1. [Link](#link)
+ 1. [Recurrence](#recurrence)
+ 1. [Management Commands](#management-commands)
+ 1. [Mark task as done/undone : mark / unmark](#mark-task-as-done--undone--mark--unmark)
+ 1. [Filter tasks : filter](#filter-tasks--filter)
+
+
+
+
+- [Advanced Features](#advanced-features)
+ 1. [Recommend next task](#recommend-next-task-)
+ 1. [Urgency-Importance Matrix: Visualise tasks](#visualise-important-and-urgent-tasks-)
+ 1. [Create A Recurring task](#create-a-recurring-task)
+ 1. [Sort Tasks based on Priority and DueDate](#sort-tasks-based-on-priority-)
+ 1. [View Task statistics](#view-task-statistics-stats)
+- [FAQ](#faq)
+- [Known Issues](#known-issues)
+- [Supported flags](#supported-flags)
+- [Command summary](#command-summary)
+- [Supported Setting Parameters](#supported-setting-parameters)
+- [Glossary](#glossary)
+- [Contact Us for Feedback & More](#contact-us-for-feedback--more)
+
+---
+
+
+## A Task in ProfPlan
- * `list` : Lists all contacts.
+Your central interaction with ProfPlan is through the creation and management of Tasks.
- * `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.
+:question: So, what is a Task in ProfPlan?
- * `delete 3` : Deletes the 3rd contact shown in the current list.
+A Task contains all the information relevant to a piece of work that needs to be done. This work could be anything: academia-related, like research projects and papers, teaching-related, like grading and lecturing, or even personal tasks, like a daily brew of coffee.
- * `clear` : Deletes all contacts.
+:question: What information does a Task contain?
- * `exit` : Exits the app.
+A Task must contain, minimally, a Name. Optionally, you may also choose to add a Priority, a Link, a Due Date, and/or a Description to it. Refer to the add and edit commands below for details on how to achieve this.
-1. Refer to the [Features](#features) below for details of each command.
+:question: What are all those underlined terms?
---------------------------------------------------------------------------------------------------------------------
+These are the various attributes of a Task:
-## Features
+- Name: A short summary of the work. Consider this akin to the title of a book, or an abstract of a paper - concise, yet descriptive.
+- Priority: An integer from 1 to 10, indicative of the importance of the task. The higher the number, the more important the task.
+- Status: Represents the current completion state of a task. (Done or Undone)
+- Link: A field to store reference material.
+- Due Date: The date by which the task should be completed by. Presented in the dd-MM-yyyy format.
+- Description: Further details about the task. Any information that is too long to be put into the name should go here.
-
+:question: What else can I do with Tasks?
-**:information_source: Notes about the command format:**
+Just like on a checklist, Tasks can be [marked](#mark-task-as-doneundone--mark--unmark) as done or undone, or you can create associations between tasks by [setting](#set-other-tasks-as-parent--set) a Task as a parent of another. To better navigate your Task list, you may also [filter](#filter-tasks--filter) or [search](#locating-tasks-by-name-find) for Tasks.
-* 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`.
+---
+
+
+## What can ProfPlan do for you?
+
+:question: What really makes ProfPlan standout?
+This is but the tip of the iceberg of the things you can do working with ProfPlan.
+
+**User-Friendly Interface:** Tired of memorizing steps and commands? Fret not, with our specialized `help` functionality, simply type `help` and the command name, and instantly get guided on its usage and format.
+
+**Recurring Tasks:** ProfPlan allows you to create recurring tasks with frequency of a day, week or month. With automatically shifting deadlines, you never have to spend time setting up repetitive tasks again.
+
+**Task Recommendations:** Puzzled about which task to do next? Worry not, ProfPlan has you covered. Using urgency (proximity to deadline) and priority (task importance set by you), ProfPlan generates a smart recommendation of which task to do next.
-* 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.
+**Smart Analysis and Filters** Having trouble sorting through and filering through the right lists of tasks? Use our seamless sort and filter features and find just the right sub-list of tasks to tackle at a time!
+
+**Urgency-Priority Matrix Visualization:** Need a 2D Interactive visualization of all your tasks? Look no further, here comes the dynamic urgency-priority matrix of profplan, a novel feature to help you gain a birdseye view of all your tasks!
+
+To explore more, visit [this section](#advanced-features) for more advanced tips.
+
+---
+
+
+## Quick start
-* 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.
+1. Ensure you have `Java 11` or above installed in your Computer.
-* 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`.
+1. Download the latest `profplan.jar` from [here](https://github.com/AY2324S1-CS2103T-W15-1/tp/releases).
+
+1. Copy the file to the folder you want to use as the home folder for your ProfPlan.
+1. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar profplan.jar` command to run the application.
+ A GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
+ ![NewLaunch](images/Ui.png)
+
+1. Type the command in the command box and press Enter to execute it. e.g. typing `help` and pressing Enter will open the help window.
+ Some example commands you can try:
+ - `list` : Lists all tasks.
+ - `add n/submit application p/2 d/01-01-2023 t/quiz t/graded l/www.quiz.com des/for mod cs0000` : Adds a task named `submit application` to ProfPlan.
+ - `delete 3` : Deletes the 3rd task shown in the current list.
+ - `delete all` : Deletes all tasks. \* `exit` : Exits the app.
+1. Refer to the [Features](#basic-features) below for details of each command.
+
+---
+
+
+## Before you start
+
+
+:blue_book: Notes about the command format:
+
+- Words in square brackets are the parameters you should supply to ProfPlan.
+ - Example: `task [taskToDo] /by [deadline]`
+- Commands specified under **Compulsory Commands** are mandatory for the function to run. The rest are optional.
+- Items with `…` after them can accept multiple parameters.
+ - Example: `find [keywords…]`
+- If you are using a PDF version of this document, be careful when copying and pasting commands that span multiple lines, as space characters surrounding line-breaks may be omitted when copied over to the application.
-* If you are using a PDF version of this document, be careful when copying and pasting commands that span multiple lines as space characters surrounding line-breaks may be omitted when copied over to the application.
+
+
+## Basic Features
+
+Simple yet essential features for you to get started.
+
### Viewing help : `help`
-Shows a message explaning how to access the help page.
+#### Viewing help for all commands:
+
+ Shows the list of usage of all commands.
+ **Command Format:** `help`
+
+#### a. Viewing help for a specific command:
+
+ Shows the description and usage of the command you specified.
+ **Command Format:** `help [commandName]`
+** Acceptable Values for each Parameter:**
+ `[taskName]` - String of len > 0
+ **Example Command:** `help add`
+
+### Create a new task : `add`
+
+Creates a new task and adds it to your task list. You may specify the name and deadline for the task when creating it.
+
+**Command Format:** `add n/[taskName] p/[priority] d/[dueDate] recur/[recur] t/[tag...] l/[link] des/[description]`
+**Compulsory Parameters:** `[taskname]`, `[priority]`, `[dueDate]`
+
+**Acceptable Values for each Parameter:**
+ `[taskName]` - String of len > 0
+ `[priority]` - Integer between 1 and 10
+ `[dueDate]` - Format: dd-MM-yyyy
+ `[recur]` - 'daily', 'weekly', 'monthly', 'semesterly' (short forms also work: 'd', 'w', 'm', 's')
+ `[tag]` - String of len > 0
+ `[link]` - String of len > 0
+ `[description]` - String of len > 0
+
+**Example Commands:** `add n/submit quiz p/2 d/01-01-2023 recur/weekly t/quiz t/graded l/www.quiz.com des/for mod cs0000`
+
+**Precise Expected Outputs when the command succeeds:**
+
+```
+New task added: Draft assignments; Priority: 1; Status: undone; Tags: [assignment][grade]; DueDate: 01-01-2023; Link: www.example.com
+```
+
+**Precise Expected Outputs when the command fails:**
+
+```
+Invalid command format!
+add: Adds a task to the task list.
+Compulsory Parameters: n/[name] p/[priority] d/[dueDate]
+Optional Parameters: recur/[recur] t/[tag...] l/[link] des/[description]
+Example: add n/Grade assignments p/1 t/assignment t/grade d/01-01-2023 l/www.example.com
+```
-![help message](images/helpMessage.png)
+### Edit existing tasks : `edit`
-Format: `help`
+Edit the aspects of selected existing task, as specified by the user.
+**Command Format:** `edit [index] [prefix]/[newValue]`
-### Adding a person: `add`
+**Acceptable Values for each Parameter:**
+ `[index]` - integer
+ `[taskName]` - String of len > 0
+ `[priority]` - Integer between 1 and 10
+ `[dueDate]` - Format: dd-MM-yyyy
+ `[tag]` - String of len > 0
+ `[link]` - String of len > 0
-Adds a person to the address book.
+**Example Commands:** `edit 2 n/Updated task p/3 d/01-01-2023 t/newTag l/www.newlink.com`
-Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…`
+
+
+:blue_book: Note that:
+- Inputting `t/` will result in all tags being erased.
+- Recurrence cannot be edited.
+- To edit description, use `description [index] des/[description]`
-
:bulb: **Tip:**
-A person can have any number of tags (including 0)
-Examples:
-* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01`
-* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal`
+
-### Listing all persons : `list`
+**Precise Expected Outputs when the command succeeds:**
-Shows a list of all persons in the address book.
+```
+Edited Task: Grant Application; Priority: 10; Status: undone; Tags: [grant][research]; DueDate: 20-12-2023; Link: -
+```
-Format: `list`
+**Precise Expected Outputs when the command fails:**
-### Editing a person : `edit`
+```
+Invalid command format!
+edit: Edits the details of the task identified by the index number used in the displayed task list.
+Existing values will be overwritten by the input values.
+Parameters: [index] n/[name] p/[priority] d/[dueDate] t/[tag...] l/[link]
+Example: edit 1 p/4
+```
-Edits an existing person in the address book.
+### Delete tasks and delete all tasks : `delete`
-Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…`
+The Delete Task feature allows you to remove a specific task from your task list when it is no longer relevant or even remove all the tasks if required.
-* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …
-* At least one of the optional fields must be provided.
-* Existing values will be updated to the input values.
-* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative.
-* You can remove all the person’s tags by typing `t/` without
- specifying any tags after it.
+**Command Format:** `delete [taskNumber]`
+**Command Format:** `delete all` or `clear` (for delete all)
-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.
+**Example Commands:**
+ `delete 2` (To delete a task present at index number 2)
+ `delete all` (To delete all the tasks present in the list)
+ `clear` (To delete all the tasks present in the list)
-### Locating persons by name: `find`
+**Acceptable Values for each Parameter:**
+ `[taskNumber]` - Integer, that is a valid task number in the list
+ `“all”` - A keyword (type String) to delete all the tasks present in the list.
-Finds persons whose names contain any of the given keywords.
+**Precise Expected Outputs when the command succeeds:**
-Format: `find KEYWORD [MORE_KEYWORDS]`
+(For Deleting a particular Task)
-* The search is case-insensitive. e.g `hans` will match `Hans`
-* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans`
-* Only the name is searched.
-* Only full words will be matched e.g. `Han` will not match `Hans`
-* Persons matching at least one keyword will be returned (i.e. `OR` search).
- e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang`
+```
+Deleted Task: Grant Application; Priority: 10; Status: undone; Tags: [grant][research]; DueDate: 20-12-2023; Link: -
+```
-Examples:
-* `find John` returns `john` and `John Doe`
-* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png)
+(For Deleting all the Tasks in the list)
-### Deleting a person : `delete`
+```
+All Tasks Deleted Successfully Prof!
+```
-Deletes the specified person from the address book.
+**Precise Expected Outputs when the command fails:**
+(If provided with an invalid tasknumber which is not in the list or even a negative index number
+the following message will be printed)
-Format: `delete INDEX`
+```
+The task index provided is invalid
+```
-* 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, …
+**Precise Expected Outputs when the command fails: (for delete all command)**
-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.
+```
+Can not delete all tasks in empty Task List
+```
-### Clearing all entries : `clear`
+### List all Tasks : `list`
-Clears all entries from the address book.
+Shows a list of all tasks in ProfPlan. Displays done and undone tasks.
-Format: `clear`
+**Command Format:** `list`
+**Precise Expected Outputs when the command succeeds:**
-### Exiting the program : `exit`
+```
+Listed all tasks
+```
-Exits the program.
+### List Tasks within a week from now: `list_week`
-Format: `exit`
+Shows a list of tasks within a week from now in ProfPlan. Displays done and undone tasks.
-### Saving the data
+**Command Format:** `list_week`
+**Precise Expected Outputs when the command succeeds:**
+
+```
+Here are your tasks within a week Prof!
+```
+
+### List Tasks within a month from now: `list_month`
+
+Shows a list of tasks within a month from now in ProfPlan. Displays done and undone tasks.
+**Command Format:** `list_month`
+**Precise Expected Outputs when the command succeeds:**
+
+```
+Here are your tasks within a month Prof!
+```
+
+### Locating Tasks by name: `find`
+
+Finding a task is user-friendly and efficient. It's case-insensitive, allowing "task" to match "Task." Keyword order is
+flexible,
+and only the task name is considered. Full words are matched, so "Tas" won't match "Task." The search operates on an OR logic, returning tasks matching at least one keyword. This ensures a simple and effective task-finding process.
+
+**Command Format:** `find [keywords]...`
+
+**Acceptable Values for each Parameter:**
+ `[TaskName]` - A task name that matches in the Task list.
+
+**Example Commands:**
+ `find quiz` (returns quiz grading task and make a quiz)
+ `find project` (returns project grading)
-AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
+**Precise Expected Outputs when the command succeeds:**
+(For Finding a particular Task)
+
+```
+3 tasks listed!
+```
+
+**Precise Expected Outputs when the command fails:**
+(If provided with an invalid taskNumber which is not in the list or even a negative index number
+the following message will be printed)
+
+```
+0 tasks listed!
+```
+
+### Configuring Settings : `set`
+
+Allows you to configure certain parameters as you see fit.
+
+**Command Format:** `set [parameterName] [value]`
+
+For a list of the currently supported parameters, click [here](#supported-setting-parameters).
+To make ProfPlan more customizable, we are looking to add more in the future, so stay tuned!
### Editing the data file
-AddressBook data are saved automatically as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file.
+ProfPlan data is saved automatically as a JSON file `[JAR file location]/data/profplan.json`. Advanced users are welcome to update data directly by editing that data file
-
:exclamation: **Caution:**
-If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it.
+
:warning: Caution: If your changes to the data file makes its format invalid, ProfPlan will discard all data and start with an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it
-### Archiving data files `[coming in v2.0]`
+### Saving the data
+
+ProfPlan data is saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
+
+### Exiting the program : `exit`
+
+Exits the program.
+
+**Command Format:** `exit`
+
+
+
+## Task Management Features
+
+Recommended features for you to get more out of ProfPlan
+
+## Parameters
+
+### Description
+
+You can insert a description for each task, to note down additional details.
+**Valid format:** `des/[description]`
+**Acceptable Parameter Values**: `description`: Any text input.
+
+### Priority
+
+All tasks must have a priority level specified on creation, which can also be edited.
+**Valid format:** `p/[priority]`
+**Acceptable Parameter Values**: `priority`: Integer from 1 to 10, inclusive.
+
+### Status
+
+Indicates the current completion state of a task. It is crucial for tracking the progress and managing the workload.
+
+- **Valid States**:
+ - `done`: This status is set when all objectives of the task are met and no further action is required.
+ - `undone`: This status is used for tasks that are still in progress or have not been started. It helps in identifying tasks that need attention.
+
+Note: Status is set as undone by default as soon as a task is added. This parameter is not available to be directly set on the creation of a task.
+
+### Due Date
+
+All tasks must have a due date specified on creation, which can also be edited. If user inputs impossible dates (eg. 30-02-2023), the date is autocorrected to last date of the month.
+**Valid format:** `d/[dueDate]`
+**Acceptable Parameter Values**: `dueDate`: A date of the format dd-MM-yyyy.
+
+### Tags
+
+You can assign tags to a task, to further segregate and classify them.
+**Valid format:** `t/[tag]`
+**Acceptable Parameter Values**: `tag`: Any alphanumeric value, without spaces.
+
+### Link
+
+You can assign a link to a task, to access the reference easily.
+**Valid format:** `l/[link]`
+**Acceptable Parameter Values**: `link`: Any text input.
+
+### Recurrence
+
+You can specify whether the task recurs on a daily, weekly, monthly, or semesterly basis.
+If a task does recur, marking the task as `done` will refresh its due date, and its Status will always be `undone`.
+Tasks with unassigned recurrence will not recur.
+**Valid format:** `recur/[recurrenceType]`
+**Acceptable Parameter Values**: `recurrenceType`: Any of the following words, case-insensitive: `daily`, `weekly`, `monthly`, `semesterly`. Alternatively, any of the short-forms `d`, `w`, `m`, `s`, also case-insensitive.
+
+
+
+### Management Commands
-_Details coming soon ..._
+### Mark task as done / undone : `mark / unmark`
---------------------------------------------------------------------------------------------------------------------
+**What it does:**
+Allows you to easily track the completion status of your tasks. You can mark a task as done when you have completed it, and mark it as undone if you need to revisit or revise the same task.
+
+**Command Format:**
+ To mark Task as done: `mark [taskNumber]`
+ To mark Task as undone: `unmark [taskNumber]`
+
+**Acceptable Values for each Parameter:**
+ `[taskNumber]` - Integer representing the task number in your task list (1, 2, 3, ...)
+
+**Example Commands:**
+ `mark 5`
+ `unmark 4`
+
+**Precise Expected Outputs when marking a task as done:**
+When you mark Task as done, you will receive the following confirmation message:
+
+```
+Task successfully marked as done, Prof! Here is your updated task list
+```
+
+**Precise Expected Outputs when marking a task as undone:**
+When you mark a task as undone, you'll receive the following confirmation message:
+
+```
+Task successfully marked as undone, Prof! Here is your updated task list
+```
+
+![markCommandExample](images/markCommandAVD.png)
+
+**Precise Expected Outputs when the command fails:**
+If you provide an invalid task number (e.g., a task number that does not exist in your task list),
+you will receive the following error message: (both for mark and unmark command)
+
+```
+Task not found please enter a valid Task Number.
+```
+
+### Filter tasks : `filter`
+
+#### a. Due Date:
+
+ **What it does:**
+ All tasks that fall before the given due date is displayed
+
+ **Command Format:** `filter d/[date]`
+
+ **Acceptable Values for each Parameter:**
+ `[date]` - In dd-MM-yyyy format.
+
+ **Example Commands:** `filter d/01-01-2023`
+
+ **Precise Expected Outputs when the command succeeds:**
+
+```
+Here are your tasks that are:
+Due before: 13-12-2023
+```
+
+#### b. Priority
+
+ **What it does:**
+ All tasks of the given priority is displayed.
+
+ **Command Format:** `filter p/[priority]`
+
+ **Acceptable Values for each Parameter:**
+ `[priority]` - Integer from 1 to 10 inclusive.
+
+ **Example Commands:** `filter p/3`
+
+ **Precise Expected Outputs when the command succeeds:**
+
+```
+Here are your tasks that are:
+Priority: 3
+```
+
+#### c. Status
+
+ **What it does:**
+ All tasks of the given status is displayed.
+
+ **Command Format:** `filter s/[status]`
+
+ **Acceptable Values for each Parameter:**
+ `[status]`- done or undone
+
+ **Example Commands:** `filter s/done`
+
+ **Precise Expected Outputs when the command succeeds:**
+
+```
+Here are your tasks that are:
+Status: done
+```
+
+#### d. Recurrence
+
+ **What it does:**
+ All tasks of the given recurring type is displayed.
+
+ **Command Format:** `filter recur/[recurringType]`
+
+ **Acceptable Values for each Parameter:**
+ `[recurringType]`- none, daily, weekly, monthly, semesterly
+
+ **Example Commands:** `filter recur/weekly`
+
+ **Precise Expected Outputs when the command succeeds:**
+
+```
+Here are your tasks that are:
+Recurring: WEEKLY
+```
+
+#### e. Combination of the above
+
+ **What it does:**
+ All tasks meeting the specified criteria is displayed.
+
+ **Command Format:** `filter d/[dueDate] p/[priority] s[status] recur/[recurringType]`
+
+ **Example Commands:** `filter p/3 recur/weekly`
+
+ **Precise Expected Outputs when the command succeeds:**
+
+```
+Here are your tasks that are:
+Priority: 3
+Recurring: WEEKLY
+```
+
+ **Precise Expected Outputs when the command fails:**
+
+```
+Invalid command format!
+filter: Filters for tasks with one or more criteria and displays them as a list with index numbers.
+Parameters: d/[dueDate] p/[priority] recur/[recur] s/[status]
+Example: filter d/01-01-2024 s/done
+```
+
+
+
+## Advanced Features
+
+Psst! Were the features mentioned in the introduction not quite enough to satiate your craving for productivity? Fret not! ProfPlan comes with novel capabilities that will reinvent the way you approach tasks! Before we end this guide, we’ll show you some ways our veteran users use ProfPlan to make themselves more productive than ever before!
+
+### Recommend next task :
+
+Ever felt overwhelmed by a mountain of work, and lost on what to do next? Many tasks, each with slightly different deadlines and priorities, can come together to overload our capacities for good planning, and make it impossible for us to choose the optimal task to work on next.
+With our customised algorithm, ProfPlan takes this cognitive load off your shoulders. Simply use the command do_next, and ProfPlan will identify the ideal task for you to work on next!
+**Valid Format:** `do_next`
+**Expected Output:**
+```
+Here is the next task you need to do Prof:
+Grade assignments, Priority: 10, DueDate: 02-11-2023
+```
+
+![do_nextCommand](images/donextADV.png)
+
+
+
+### Visualise important and urgent tasks :
+
+Sometimes, we might have 20, 30, 40 or more tasks in our lists!
+It can be extremely difficult to prioritize and visualize them based on what to do next.
+Fret not! With the Urgency-Priority Matrix, you can constantly visualize your tasks with ease.
+The matrix is updated automatically whenever there's any change to your taskList.
+
+The columns represent the different Urgency Levels (How close the deadline is): `1-10`
+The rows represent the different Priority Levels (How important the task is): `1-10`
+
+The higher the urgency and the priority, the task will appear further towards the top right of the matrix.
+The task(s) in the top right of the matrix should be done first.
+![img.png](matrix.png)
+
+
+
+### Create A Recurring Task
+
+You can specify whether the task recurs on a daily, weekly, monthly, or semesterly basis.
+If a task does recur, marking the task as `done` will refresh its due date, and its Status will always be `undone`.
+Tasks with unassigned recurrence will not recur.
+**Valid format:** `recur/[recurrenceType]`
+**Acceptable Parameter Values**: `recurrenceType`: Any of the following words, case-insensitive: `daily`, `weekly`, `monthly`, `semesterly`. Alternatively, any of the short-forms `d`, `w`, `m`, `s`, also case-insensitive.
+
+### Sort Tasks based on Priority :
+
+
+Struggling to decide which task to tackle first in your busy schedule? Our 'Sort by Priority' feature simplifies this by neatly organizing your tasks in order of importance. The most critical tasks rise to the top, allowing you to address the most urgent matters first and efficiently manage your workload
+
+**Valid Format:** `sort_priority`
+**Expected Output:**
+` Here is your task list Prof, sorted based on priority`
+
+![sortPriority](images/sortADV.png)
+
+**Note:** ProfPlan will sort the tasks that is displayed in the UI at the time when the command is run.
+
+
+
+### Sort Tasks based on DueDate:
+
+Overwhelmed by deadlines? The 'Sort by Due Date' function brings clarity to your schedule. It arranges your tasks starting with those that have the nearest deadlines. This ensures you never miss an important date and can methodically work through your tasks in a timely manner.
+
+**Valid Format:** `sort_duedate`
+**Expected Output:**
+` Here is your task list Prof, sorted based on nearest due date.`
+
+**Note:** ProfPlan will sort the tasks that is displayed in the UI at the time when the command is run.
+
+
+### View Task statistics: `stats`
+
+Retrieves a list of statistics about your tasks in ProfPlan. As of now, Completion Rate of Tasks is available.
+
+**Valid Format:** `stats`
+
+**Precise Expected Outputs when the command succeeds:**
+
+```
+Here are your statistics Prof!
+Completion Rate: 38.5%
+```
+
+---
+
## 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 in the previous ProfPlan home folder.
---------------------------------------------------------------------------------------------------------------------
+---
+
## Known issues
1. **When using multiple screens**, if you move the application to a secondary screen, and later switch to using only the primary screen, the GUI will open off-screen. The remedy is to delete the `preferences.json` file created by the application before running the application again.
+1. **When scrolling through Urgency-Priority Matrix**, if you scroll too quickly, the cells take some time to render and display the tasks.
+1. **After using filter command**, use `list` to list all tasks so that all operations are done on the correct index number. The filtered list doesn't revert back when doing next command, so user has to manually enter `list` to avoid any discrepancies.
+
+---
+
+
+## Supported flags
---------------------------------------------------------------------------------------------------------------------
+| **Name of flag** | **Flag in command** | **Description** |
+|-----------------------------------------| ------------------- | ------------------------------ |
+| Name | n/ | The name of the task |
+| Due Date | d/ | The due date of the task |
+| Priority | p/ | The priority of the task |
+| Tag | t/ | The tag that the task is under |
+| Link | l/ | The link of the task |
+| Recur (not editable) | recur/ | The recurring type of the task |
+| Description (not editable through `edit`) | des/ | The description of the task |
+
+
## 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** |
+| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
+| **Add** | `add n/[taskName] p/[priority] d/[dueDate] recur[recur] t/[tags...] l/[link] des/[description]`, e.g. `add n/Task p/1 d/01-01-2023 recur/weekly` |
+| **Edit** | `edit [INDEX] n/[name] p/[priority] d/[dueDate] t/[tags...] l/[link]`, e.g. `edit 1 n/Task p/1 d/01-01-2023` |
+| **Find** | `find [keywords...]`, e.g. `find canvas quiz` |
+| **List Week** | `list_week` |
+| **List Month** | `list_month` |
+| **Mark** | `mark [INDEX]`, e.g. `mark 2` |
+| **Unmark** | `unmark [INDEX]`, e.g. `unmark 2` |
+| **Delete** | `delete [INDEX]`, e.g. `delete 2` |
+| **Delete All** | `delete all` |
+| **Sort Priority** | `sort_priority` |
+| **Sort Duedate** | `sort_duedate` |
+| **Filter** | `filter d/[duedate] s/[status]...`, e.g. `filter s/done p/4` |
+| **Help** | `help` |
+
+
+
+## Supported Setting Parameters
+
+| **Parameter** | **Description** | **Valid values** | **Default value** |
+| ---------------- | --------------------------------- | ------------------------ | ----------------- |
+| **semesterDays** | The number of days in a semester. | Any non-negative integer | 180 |
+
+## Glossary
+
+| **Word** | **Definition** |
+| ------------ | ----------------------------------------------------------------------------------------- |
+| **Syntax** | The way that you should format your commands such that ProfPlan can understand them. |
+| **Status** | Whether a task is done or undone |
+| **Priority** | Importance of a task (different from due date) |
+
+## Contact Us for Feedback & More!
+
+We're delighted to hear from you! At ProfPlan, we value your thoughts, inquiries, and suggestions. Your feedback helps us continually improve and provide you with the best possible experience. Whether you have a question, need assistance, or want to share your insights, we're here to assist.
+
+Kindly fill in the form below and we will get back to you as soon as possible.
+
+[Form Link](https://forms.gle/Dzb12Re4MYJxzf8w6)
diff --git a/docs/_config.yml b/docs/_config.yml
index 6bd245d8f4e..f6ad6eeef91 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -1,4 +1,4 @@
-title: "AB-3"
+title: "ProfPlan"
theme: minima
header_pages:
@@ -8,7 +8,7 @@ header_pages:
markdown: kramdown
-repository: "se-edu/addressbook-level3"
+repository: "AY2324S1-CS2103T-W15-1/tp"
github_icon: "images/github-icon.png"
plugins:
diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss
index 0d3f6e80ced..b60f9438e77 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: "ProfPlan";
font-size: 32px;
}
}
diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml
index 48b6cc4333c..1910251c5c5 100644
--- a/docs/diagrams/ArchitectureSequenceDiagram.puml
+++ b/docs/diagrams/ArchitectureSequenceDiagram.puml
@@ -14,13 +14,13 @@ activate ui UI_COLOR
ui -[UI_COLOR]> logic : execute("delete 1")
activate logic LOGIC_COLOR
-logic -[LOGIC_COLOR]> model : deletePerson(p)
+logic -[LOGIC_COLOR]> model : deleteTask(p)
activate model MODEL_COLOR
model -[MODEL_COLOR]-> logic
deactivate model
-logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook)
+logic -[LOGIC_COLOR]> storage : saveProfPlan(profPlan)
activate storage STORAGE_COLOR
storage -[STORAGE_COLOR]> storage : Save to file
diff --git a/docs/diagrams/ArchiveSequenceDiagram.puml b/docs/diagrams/ArchiveSequenceDiagram.puml
new file mode 100644
index 00000000000..c3bf0834196
--- /dev/null
+++ b/docs/diagrams/ArchiveSequenceDiagram.puml
@@ -0,0 +1,73 @@
+@startuml
+
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":ProfPlanParser" as ProfPlanParser LOGIC_COLOR
+participant ":ArchiveCommandParser" as ArchiveCommandParser LOGIC_COLOR
+participant "d:ArchiveCommand" as ArchiveCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("archive 1")
+activate LogicManager
+
+LogicManager -> ProfPlanParser : parseCommand("archive 1")
+activate ProfPlanParser
+
+create ArchiveCommandParser
+ProfPlanParser -> ArchiveCommandParser
+activate ArchiveCommandParser
+
+ArchiveCommandParser --> ProfPlanParser
+deactivate ArchiveCommandParser
+
+ProfPlanParser -> ArchiveCommandParser : parse("1")
+activate ArchiveCommandParser
+
+create ArchiveCommand
+ArchiveCommandParser -> ArchiveCommand
+activate ArchiveCommand
+
+ArchiveCommand --> ArchiveCommandParser : d
+deactivate ArchiveCommand
+
+ArchiveCommandParser --> ProfPlanParser : d
+deactivate ArchiveCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+ArchiveCommandParser -[hidden]-> ProfPlanParser
+destroy ArchiveCommandParser
+
+ProfPlanParser --> LogicManager : d
+deactivate ProfPlanParser
+
+LogicManager -> ArchiveCommand : execute()
+activate ArchiveCommand
+
+ArchiveCommand -> Model : archiveTask(1)
+activate Model
+
+Model --> ArchiveCommand
+deactivate Model
+
+create CommandResult
+ArchiveCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> ArchiveCommand
+deactivate CommandResult
+
+ArchiveCommand --> LogicManager : result
+deactivate ArchiveCommand
+
+[<--LogicManager
+deactivate LogicManager
+
+
+@enduml
diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml
index 598474a5c82..175e3821ce7 100644
--- a/docs/diagrams/BetterModelClassDiagram.puml
+++ b/docs/diagrams/BetterModelClassDiagram.puml
@@ -4,18 +4,23 @@ skinparam arrowThickness 1.1
skinparam arrowColor MODEL_COLOR
skinparam classBackgroundColor MODEL_COLOR
-AddressBook *-right-> "1" UniquePersonList
-AddressBook *-right-> "1" UniqueTagList
-UniqueTagList -[hidden]down- UniquePersonList
-UniqueTagList -[hidden]down- UniquePersonList
+ProfPlan *-right-> "1" UniqueTaskList
+ProfPlan *-right-> "1" UniqueTagList
+UniqueTagList -[hidden]down- UniqueTaskList
+UniqueTagList -[hidden]down- UniqueTaskList
UniqueTagList -right-> "*" Tag
-UniquePersonList -right-> Person
+UniqueTaskList -right-> Task
-Person -up-> "*" Tag
+Task -up-> "*" Tag
-Person *--> Name
-Person *--> Phone
-Person *--> Email
-Person *--> Address
+
+Task *-- Name
+Task *-- Priority
+Task *-- RecurringType
+Task *-- Link
+Task *-- Status
+Task *-- DueDate
+Task *-- Description
+Task *-- "*" Tag
@enduml
diff --git a/docs/diagrams/CommitActivityDiagram.puml b/docs/diagrams/CommitActivityDiagram.puml
index 8c0892d6a70..07e238e94b5 100644
--- a/docs/diagrams/CommitActivityDiagram.puml
+++ b/docs/diagrams/CommitActivityDiagram.puml
@@ -8,10 +8,10 @@ start
'Since the beta syntax does not support placing the condition outside the
'diamond we place it as the true branch instead.
-if () then ([command commits AddressBook])
+if () then ([command commits ProfPlan])
:Purge redundant states;
- :Save AddressBook to
- addressBookStateList;
+ :Save ProfPlan to
+ profPlanStateList;
else ([else])
endif
stop
diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml
index 40ea6c9dc4c..51caa28ce53 100644
--- a/docs/diagrams/DeleteSequenceDiagram.puml
+++ b/docs/diagrams/DeleteSequenceDiagram.puml
@@ -4,7 +4,7 @@ skinparam ArrowFontStyle plain
box Logic LOGIC_COLOR_T1
participant ":LogicManager" as LogicManager LOGIC_COLOR
-participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":ProfPlanParser" as ProfPlanParser LOGIC_COLOR
participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR
participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR
participant ":CommandResult" as CommandResult LOGIC_COLOR
@@ -17,17 +17,17 @@ end box
[-> LogicManager : execute("delete 1")
activate LogicManager
-LogicManager -> AddressBookParser : parseCommand("delete 1")
-activate AddressBookParser
+LogicManager -> ProfPlanParser : parseCommand("delete 1")
+activate ProfPlanParser
create DeleteCommandParser
-AddressBookParser -> DeleteCommandParser
+ProfPlanParser -> DeleteCommandParser
activate DeleteCommandParser
-DeleteCommandParser --> AddressBookParser
+DeleteCommandParser --> ProfPlanParser
deactivate DeleteCommandParser
-AddressBookParser -> DeleteCommandParser : parse("1")
+ProfPlanParser -> DeleteCommandParser : parse("1")
activate DeleteCommandParser
create DeleteCommand
@@ -37,19 +37,19 @@ activate DeleteCommand
DeleteCommand --> DeleteCommandParser : d
deactivate DeleteCommand
-DeleteCommandParser --> AddressBookParser : d
+DeleteCommandParser --> ProfPlanParser : d
deactivate DeleteCommandParser
'Hidden arrow to position the destroy marker below the end of the activation bar.
-DeleteCommandParser -[hidden]-> AddressBookParser
+DeleteCommandParser -[hidden]-> ProfPlanParser
destroy DeleteCommandParser
-AddressBookParser --> LogicManager : d
-deactivate AddressBookParser
+ProfPlanParser --> LogicManager : d
+deactivate ProfPlanParser
LogicManager -> DeleteCommand : execute()
activate DeleteCommand
-DeleteCommand -> Model : deletePerson(1)
+DeleteCommand -> Model : deleteTask(1)
activate Model
Model --> DeleteCommand
diff --git a/docs/diagrams/FilterCommandClassDiagram.puml b/docs/diagrams/FilterCommandClassDiagram.puml
new file mode 100644
index 00000000000..95ed8035b75
--- /dev/null
+++ b/docs/diagrams/FilterCommandClassDiagram.puml
@@ -0,0 +1,44 @@
+@startuml
+
+class FilterCommandParser {
+ + parse: FilterCommand
+}
+
+class FilterCommand {
+ - COMMAND_WORD: String
+ - MESSAGE_USAGE: String
+ - MESSAGE_DETAILS: String
+ - messageSuccess: String
+ + FilterCommand(predicate: Predicate)
+ + execute(model: Model): CommandResult
+ + equals(other: Object): boolean
+ + toString(): String
+ + getSuccessMessage(): String
+}
+
+class TaskDueDatePredicate
+class TaskPriorityPredicate
+class TaskStatusPredicate
+class TaskRecurringTypePredicate
+class CombinedPredicate
+
+class "<>\nPredicate" as Predicate
+
+FilterCommand <.. Predicate
+Predicate <-- TaskDueDatePredicate
+Predicate <-- TaskPriorityPredicate
+Predicate <-- TaskStatusPredicate
+Predicate <-- TaskRecurringTypePredicate
+Predicate <-- CombinedPredicate
+
+FilterCommandParser <.. TaskDueDatePredicate
+FilterCommandParser <.. TaskPriorityPredicate
+FilterCommandParser <.. TaskStatusPredicate
+FilterCommandParser <.. TaskRecurringTypePredicate
+FilterCommandParser <.. CombinedPredicate
+
+
+
+FilterCommandParser .left.> FilterCommand : parse >
+
+@enduml
diff --git a/docs/diagrams/ListArchivesSequenceDiagram.puml b/docs/diagrams/ListArchivesSequenceDiagram.puml
new file mode 100644
index 00000000000..0e27f1775d7
--- /dev/null
+++ b/docs/diagrams/ListArchivesSequenceDiagram.puml
@@ -0,0 +1,57 @@
+@startuml
+
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":ProfPlanParser" as ProfPlanParser LOGIC_COLOR
+participant "d:ListArchivesCommand" as ListArchivesCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("list archives")
+activate LogicManager
+
+LogicManager -> ProfPlanParser : parseCommand("list archives")
+activate ProfPlanParser
+
+
+create ListArchivesCommand
+ProfPlanParser -> ListArchivesCommand
+activate ListArchivesCommand
+
+ListArchivesCommand --> ProfPlanParser : d
+deactivate ListArchivesCommand
+
+ProfPlanParser --> LogicManager : d
+deactivate ProfPlanParser
+
+LogicManager -> ListArchivesCommand : execute()
+activate ListArchivesCommand
+
+ListArchivesCommand -> Model : listArchives()
+activate Model
+
+Model --> ListArchivesCommand
+deactivate Model
+
+create CommandResult
+ListArchivesCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> ListArchivesCommand
+deactivate CommandResult
+
+ListArchivesCommand --> LogicManager : result
+deactivate ListArchivesCommand
+
+[<--LogicManager
+deactivate LogicManager
+
+
+@enduml
diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml
index a57720890ee..04abb3f52ee 100644
--- a/docs/diagrams/LogicClassDiagram.puml
+++ b/docs/diagrams/LogicClassDiagram.puml
@@ -6,7 +6,7 @@ skinparam classBackgroundColor LOGIC_COLOR
package Logic as LogicPackage {
-Class AddressBookParser
+Class ProfPlanParser
Class XYZCommand
Class CommandResult
Class "{abstract}\nCommand" as Command
@@ -27,8 +27,8 @@ Class HiddenOutside #FFFFFF
HiddenOutside ..> Logic
LogicManager .right.|> Logic
-LogicManager -right->"1" AddressBookParser
-AddressBookParser ..> XYZCommand : creates >
+LogicManager -right->"1" ProfPlanParser
+ProfPlanParser ..> XYZCommand : creates >
XYZCommand -up-|> Command
LogicManager .left.> Command : executes >
diff --git a/docs/diagrams/MarkCommandClassDiagram.puml b/docs/diagrams/MarkCommandClassDiagram.puml
new file mode 100644
index 00000000000..f2553c13d1b
--- /dev/null
+++ b/docs/diagrams/MarkCommandClassDiagram.puml
@@ -0,0 +1,23 @@
+@startuml MarkCommand
+
+!define classFontStyle bold
+
+skinparam class {
+ FontStyle classFontStyle
+}
+
+
+class MarkCommand {
+ - COMMAND_WORD: String
+ - MESSAGE_USAGE: String
+ - MESSAGE_SUCCESS: String
+ - MESSAGE_INVALID_NUMBER: String
+ - MESSAGE_ALREADY_DONE: String
+ - taskNumber: int
+ + MarkCommand(number: int)
+ + execute(model: Model): CommandResult
+ + equals(other: Object): boolean
+ + toString(): String
+}
+
+@enduml
diff --git a/docs/diagrams/MarkcommandSequencediagram.puml b/docs/diagrams/MarkcommandSequencediagram.puml
new file mode 100644
index 00000000000..ffc560620bf
--- /dev/null
+++ b/docs/diagrams/MarkcommandSequencediagram.puml
@@ -0,0 +1,73 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic #BADA55
+
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":ProfPlanParser" as ProfPlanParser LOGIC_COLOR
+participant ":MarkCommandParser" as MarkCommandParser LOGIC_COLOR
+participant "d:MarkCommand" as MarkCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("mark 1")
+activate LogicManager
+
+LogicManager -> ProfPlanParser : parseCommand("mark 1")
+activate ProfPlanParser
+
+create MarkCommandParser
+ProfPlanParser -> MarkCommandParser
+activate MarkCommandParser
+
+MarkCommandParser --> ProfPlanParser
+deactivate MarkCommandParser
+
+ProfPlanParser -> MarkCommandParser : parse("1")
+activate MarkCommandParser
+
+create MarkCommand
+MarkCommandParser -> MarkCommand
+activate MarkCommand
+
+MarkCommand --> MarkCommandParser : d
+deactivate MarkCommand
+
+MarkCommandParser --> ProfPlanParser : d
+deactivate MarkCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+MarkCommandParser -[hidden]-> ProfPlanParser
+destroy MarkCommandParser
+
+ProfPlanParser --> LogicManager : d
+deactivate ProfPlanParser
+
+LogicManager -> MarkCommand : execute()
+activate MarkCommand
+
+MarkCommand -> Model : markTask(1)
+activate Model
+
+Model --> MarkCommand
+deactivate Model
+
+create CommandResult
+MarkCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> MarkCommand
+deactivate CommandResult
+
+MarkCommand --> LogicManager : result
+deactivate MarkCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml
index 0de5673070d..9816021c608 100644
--- a/docs/diagrams/ModelClassDiagram.puml
+++ b/docs/diagrams/ModelClassDiagram.puml
@@ -1,54 +1,63 @@
@startuml
-!include style.puml
+!define MODEL_COLOR #0055CC
+!define BACKGROUND_COLOR #D0E4FF
+
skinparam arrowThickness 1.1
skinparam arrowColor MODEL_COLOR
-skinparam classBackgroundColor MODEL_COLOR
-
-Package Model as ModelPackage <>{
-Class "<>\nReadOnlyAddressBook" as ReadOnlyAddressBook
-Class "<>\nReadOnlyUserPrefs" as ReadOnlyUserPrefs
-Class "<>\nModel" as Model
-Class AddressBook
-Class ModelManager
-Class UserPrefs
-
-Class UniquePersonList
-Class Person
-Class Address
-Class Email
-Class Name
-Class Phone
-Class Tag
-
-Class I #FFFFFF
+skinparam classBackgroundColor BACKGROUND_COLOR
+
+Package Model as ModelPackage <> {
+ Class "<>\nReadOnlyProfPlan" as ReadOnlyProfPlan
+ Class "<>\nReadOnlyUserPrefs" as ReadOnlyUserPrefs
+ Class "<>\nReadOnlyUserConfigs" as ReadOnlyUserConfigs
+ Class "<>\nModel" as Model
+ Class ProfPlan
+ Class ModelManager
+ Class UserPrefs
+ Class UserConfigs
+
+ Class UniqueTaskList
+ Class Task {
+ - Name name
+ - Priority priority
+ - RecurringType recurringType
+ - Link link
+ - Status status
+ - DueDate dueDate
+ - Description description
+ }
+
+ Class Name
+ Class Priority
+ Class RecurringType
+ Class Link
+ Class Status
+ Class DueDate
+ Class Description
+ Class Tag
}
-Class HiddenOutside #FFFFFF
-HiddenOutside ..> Model
-AddressBook .up.|> ReadOnlyAddressBook
+ReadOnlyProfPlan <|.. ProfPlan
+Model <|.. ModelManager
+ReadOnlyUserPrefs <|.. UserPrefs
+ReadOnlyUserConfigs <|.. UserConfigs
-ModelManager .up.|> Model
-Model .right.> ReadOnlyUserPrefs
-Model .left.> ReadOnlyAddressBook
-ModelManager -left-> "1" AddressBook
+ModelManager -left-> "1" ProfPlan
ModelManager -right-> "1" UserPrefs
-UserPrefs .up.|> ReadOnlyUserPrefs
-
-AddressBook *--> "1" UniquePersonList
-UniquePersonList --> "~* all" Person
-Person *--> Name
-Person *--> Phone
-Person *--> Email
-Person *--> Address
-Person *--> "*" Tag
+ModelManager -right-> "1" UserConfigs
+ModelManager --> "~* filtered" Task
-Person -[hidden]up--> I
-UniquePersonList -[hidden]right-> I
+ProfPlan *--> "1" UniqueTaskList
+UniqueTaskList --> "~* all " Task
-Name -[hidden]right-> Phone
-Phone -[hidden]right-> Address
-Address -[hidden]right-> Email
+Task *-- Name
+Task *-- Priority
+Task *-- RecurringType
+Task *-- Link
+Task *-- Status
+Task *-- DueDate
+Task *-- Description
+Task *-- "*" Tag
-ModelManager --> "~* filtered" Person
@enduml
diff --git a/docs/diagrams/ParserClasses.puml b/docs/diagrams/ParserClasses.puml
index 0c7424de6e0..5e2807e6a4b 100644
--- a/docs/diagrams/ParserClasses.puml
+++ b/docs/diagrams/ParserClasses.puml
@@ -9,7 +9,7 @@ Class XYZCommand
package "Parser classes"{
Class "<>\nParser" as Parser
-Class AddressBookParser
+Class ProfPlanParser
Class XYZCommandParser
Class CliSyntax
Class ParserUtil
@@ -19,12 +19,12 @@ Class Prefix
}
Class HiddenOutside #FFFFFF
-HiddenOutside ..> AddressBookParser
+HiddenOutside ..> ProfPlanParser
-AddressBookParser .down.> XYZCommandParser: creates >
+ProfPlanParser .down.> XYZCommandParser: creates >
XYZCommandParser ..> XYZCommand : creates >
-AddressBookParser ..> Command : returns >
+ProfPlanParser ..> Command : returns >
XYZCommandParser .up.|> Parser
XYZCommandParser ..> ArgumentMultimap
XYZCommandParser ..> ArgumentTokenizer
diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml
index a821e06458c..f64c1ca709d 100644
--- a/docs/diagrams/StorageClassDiagram.puml
+++ b/docs/diagrams/StorageClassDiagram.puml
@@ -14,11 +14,11 @@ Class JsonUserPrefsStorage
Class "<>\nStorage" as Storage
Class StorageManager
-package "AddressBook Storage" #F4F6F6{
-Class "<>\nAddressBookStorage" as AddressBookStorage
-Class JsonAddressBookStorage
-Class JsonSerializableAddressBook
-Class JsonAdaptedPerson
+package "ProfPlan Storage" #F4F6F6{
+Class "<>\nProfPlanStorage" as ProfPlanStorage
+Class JsonProfPlanStorage
+Class JsonSerializableProfPlan
+Class JsonAdaptedTask
Class JsonAdaptedTag
}
@@ -29,15 +29,15 @@ HiddenOutside ..> Storage
StorageManager .up.|> Storage
StorageManager -up-> "1" UserPrefsStorage
-StorageManager -up-> "1" AddressBookStorage
+StorageManager -up-> "1" ProfPlanStorage
Storage -left-|> UserPrefsStorage
-Storage -right-|> AddressBookStorage
+Storage -right-|> ProfPlanStorage
JsonUserPrefsStorage .up.|> UserPrefsStorage
-JsonAddressBookStorage .up.|> AddressBookStorage
-JsonAddressBookStorage ..> JsonSerializableAddressBook
-JsonSerializableAddressBook --> "*" JsonAdaptedPerson
-JsonAdaptedPerson --> "*" JsonAdaptedTag
+JsonProfPlanStorage .up.|> ProfPlanStorage
+JsonProfPlanStorage ..> JsonSerializableProfPlan
+JsonSerializableProfPlan --> "*" JsonAdaptedTask
+JsonAdaptedTask --> "*" JsonAdaptedTag
@enduml
diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml
index 95473d5aa19..a3508d17859 100644
--- a/docs/diagrams/UiClassDiagram.puml
+++ b/docs/diagrams/UiClassDiagram.puml
@@ -11,8 +11,8 @@ Class UiManager
Class MainWindow
Class HelpWindow
Class ResultDisplay
-Class PersonListPanel
-Class PersonCard
+Class TaskListPanel
+Class TaskCard
Class StatusBarFooter
Class CommandBox
}
@@ -32,26 +32,26 @@ UiManager .left.|> Ui
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
CommandBox --|> UiPart
-PersonListPanel --|> UiPart
-PersonCard --|> UiPart
+TaskListPanel --|> UiPart
+TaskCard --|> UiPart
StatusBarFooter --|> UiPart
HelpWindow --|> UiPart
-PersonCard ..> Model
+TaskCard ..> Model
UiManager -right-> Logic
MainWindow -left-> Logic
-PersonListPanel -[hidden]left- HelpWindow
+TaskListPanel -[hidden]left- HelpWindow
HelpWindow -[hidden]left- CommandBox
CommandBox -[hidden]left- ResultDisplay
ResultDisplay -[hidden]left- StatusBarFooter
diff --git a/docs/diagrams/UndoRedoState0.puml b/docs/diagrams/UndoRedoState0.puml
index 43a45903ac9..bf51f1892c1 100644
--- a/docs/diagrams/UndoRedoState0.puml
+++ b/docs/diagrams/UndoRedoState0.puml
@@ -7,9 +7,9 @@ skinparam ClassBackgroundColor #FFFFAA
title Initial state
package States {
- class State1 as "ab0:AddressBook"
- class State2 as "ab1:AddressBook"
- class State3 as "ab2:AddressBook"
+ class State1 as "pp0:ProfPlan"
+ class State2 as "pp1:ProfPlan"
+ class State3 as "pp2:ProfPlan"
}
State1 -[hidden]right-> State2
State2 -[hidden]right-> State3
diff --git a/docs/diagrams/UndoRedoState1.puml b/docs/diagrams/UndoRedoState1.puml
index 5a41e9e1651..472388e47ef 100644
--- a/docs/diagrams/UndoRedoState1.puml
+++ b/docs/diagrams/UndoRedoState1.puml
@@ -7,9 +7,9 @@ skinparam ClassBackgroundColor #FFFFAA
title After command "delete 5"
package States <> {
- class State1 as "ab0:AddressBook"
- class State2 as "ab1:AddressBook"
- class State3 as "ab2:AddressBook"
+ class State1 as "pp0:ProfPlan"
+ class State2 as "pp1:ProfPlan"
+ class State3 as "pp2:ProfPlan"
}
State1 -[hidden]right-> State2
diff --git a/docs/diagrams/UndoRedoState2.puml b/docs/diagrams/UndoRedoState2.puml
index ad32fce1b0b..4b7a0200399 100644
--- a/docs/diagrams/UndoRedoState2.puml
+++ b/docs/diagrams/UndoRedoState2.puml
@@ -7,9 +7,9 @@ skinparam ClassBackgroundColor #FFFFAA
title After command "add n/David"
package States <> {
- class State1 as "ab0:AddressBook"
- class State2 as "ab1:AddressBook"
- class State3 as "ab2:AddressBook"
+ class State1 as "pp0:ProfPlan"
+ class State2 as "pp1:ProfPlan"
+ class State3 as "pp2:ProfPlan"
}
State1 -[hidden]right-> State2
diff --git a/docs/diagrams/UndoRedoState3.puml b/docs/diagrams/UndoRedoState3.puml
index 9187a690036..adc5648995e 100644
--- a/docs/diagrams/UndoRedoState3.puml
+++ b/docs/diagrams/UndoRedoState3.puml
@@ -7,9 +7,9 @@ skinparam ClassBackgroundColor #FFFFAA
title After command "undo"
package States <> {
- class State1 as "ab0:AddressBook"
- class State2 as "ab1:AddressBook"
- class State3 as "ab2:AddressBook"
+ class State1 as "pp0:ProfPlan"
+ class State2 as "pp1:ProfPlan"
+ class State3 as "pp2:ProfPlan"
}
State1 -[hidden]right-> State2
diff --git a/docs/diagrams/UndoRedoState4.puml b/docs/diagrams/UndoRedoState4.puml
index 2bc631ffcd0..99e6a7ffe0d 100644
--- a/docs/diagrams/UndoRedoState4.puml
+++ b/docs/diagrams/UndoRedoState4.puml
@@ -7,9 +7,9 @@ skinparam ClassBackgroundColor #FFFFAA
title After command "list"
package States <> {
- class State1 as "ab0:AddressBook"
- class State2 as "ab1:AddressBook"
- class State3 as "ab2:AddressBook"
+ class State1 as "pp0:ProfPlan"
+ class State2 as "pp1:ProfPlan"
+ class State3 as "pp2:ProfPlan"
}
State1 -[hidden]right-> State2
diff --git a/docs/diagrams/UndoRedoState5.puml b/docs/diagrams/UndoRedoState5.puml
index e77b04104aa..b6e002e01e2 100644
--- a/docs/diagrams/UndoRedoState5.puml
+++ b/docs/diagrams/UndoRedoState5.puml
@@ -7,9 +7,9 @@ skinparam ClassBackgroundColor #FFFFAA
title After command "clear"
package States <> {
- class State1 as "ab0:AddressBook"
- class State2 as "ab1:AddressBook"
- class State3 as "ab3:AddressBook"
+ class State1 as "pp0:ProfPlan"
+ class State2 as "pp1:ProfPlan"
+ class State3 as "pp3:ProfPlan"
}
State1 -[hidden]right-> State2
@@ -18,5 +18,5 @@ State2 -[hidden]right-> State3
class Pointer as "Current State" #FFFFFF
Pointer -up-> State3
-note right on link: State ab2 deleted.
+note right on link: State pp2 deleted.
@end
diff --git a/docs/diagrams/UndoSequenceDiagram.puml b/docs/diagrams/UndoSequenceDiagram.puml
index 87ff3e9237e..e4226ee1181 100644
--- a/docs/diagrams/UndoSequenceDiagram.puml
+++ b/docs/diagrams/UndoSequenceDiagram.puml
@@ -4,42 +4,42 @@ skinparam ArrowFontStyle plain
box Logic LOGIC_COLOR_T1
participant ":LogicManager" as LogicManager LOGIC_COLOR
-participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":ProfPlanParser" as ProfPlanParser LOGIC_COLOR
participant "u:UndoCommand" as UndoCommand LOGIC_COLOR
end box
box Model MODEL_COLOR_T1
participant ":Model" as Model MODEL_COLOR
-participant ":VersionedAddressBook" as VersionedAddressBook MODEL_COLOR
+participant ":VersionedProfPlan" as VersionedProfPlan MODEL_COLOR
end box
[-> LogicManager : execute(undo)
activate LogicManager
-LogicManager -> AddressBookParser : parseCommand(undo)
-activate AddressBookParser
+LogicManager -> ProfPlanParser : parseCommand(undo)
+activate ProfPlanParser
create UndoCommand
-AddressBookParser -> UndoCommand
+ProfPlanParser -> UndoCommand
activate UndoCommand
-UndoCommand --> AddressBookParser
+UndoCommand --> ProfPlanParser
deactivate UndoCommand
-AddressBookParser --> LogicManager : u
-deactivate AddressBookParser
+ProfPlanParser --> LogicManager : u
+deactivate ProfPlanParser
LogicManager -> UndoCommand : execute()
activate UndoCommand
-UndoCommand -> Model : undoAddressBook()
+UndoCommand -> Model : undoProfPlan()
activate Model
-Model -> VersionedAddressBook : undo()
-activate VersionedAddressBook
+Model -> VersionedProfPlan : undo()
+activate VersionedProfPlan
-VersionedAddressBook -> VersionedAddressBook :resetData(ReadOnlyAddressBook)
-VersionedAddressBook --> Model :
-deactivate VersionedAddressBook
+VersionedProfPlan -> VersionedProfPlan :resetData(ReadOnlyProfPlan)
+VersionedProfPlan --> Model :
+deactivate VersionedProfPlan
Model --> UndoCommand
deactivate Model
diff --git a/docs/diagrams/UnmarkCommandClassDiagram.puml b/docs/diagrams/UnmarkCommandClassDiagram.puml
new file mode 100644
index 00000000000..dca0ba3b629
--- /dev/null
+++ b/docs/diagrams/UnmarkCommandClassDiagram.puml
@@ -0,0 +1,23 @@
+@startuml UnmarkCommand
+!define classBackgroundColor #ff023134
+!define classFontStyle bold
+
+skinparam class {
+ BackgroundColor classBackgroundColor
+ FontStyle classFontStyle
+}
+
+class UnmarkCommand {
+ - COMMAND_WORD: String
+ - MESSAGE_USAGE: String
+ - MESSAGE_SUCCESS: String
+ - MESSAGE_INVALID_NUMBER: String
+ - MESSAGE_ALREADY_UNDONE: String
+ - taskNumber: int
+ + UnmarkCommand(number: int)
+ + execute(model: Model): CommandResult
+ + equals(other: Object): boolean
+ + toString(): String
+}
+
+@enduml
diff --git a/docs/diagrams/tracing/LogicSequenceDiagram.puml b/docs/diagrams/tracing/LogicSequenceDiagram.puml
index 42bf46d3ce8..6cd65bad819 100644
--- a/docs/diagrams/tracing/LogicSequenceDiagram.puml
+++ b/docs/diagrams/tracing/LogicSequenceDiagram.puml
@@ -3,7 +3,7 @@
skinparam ArrowFontStyle plain
Participant ":LogicManager" as logic LOGIC_COLOR
-Participant ":AddressBookParser" as abp LOGIC_COLOR
+Participant ":ProfPlanParser" as abp LOGIC_COLOR
Participant ":EditCommandParser" as ecp LOGIC_COLOR
Participant "command:EditCommand" as ec LOGIC_COLOR
@@ -14,7 +14,7 @@ create ecp
abp -> ecp
abp -> ecp ++: parse(arguments)
create ec
-ecp -> ec ++: index, editPersonDescriptor
+ecp -> ec ++: index, editTaskDescriptor
ec --> ecp --
ecp --> abp --: command
abp --> logic --: command
diff --git a/docs/images/AddCommand.png b/docs/images/AddCommand.png
new file mode 100644
index 00000000000..317ca871a6f
Binary files /dev/null and b/docs/images/AddCommand.png differ
diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png
index 37ad06a2803..6105cc060b4 100644
Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ
diff --git a/docs/images/ArchiveSequenceDiagram.png b/docs/images/ArchiveSequenceDiagram.png
new file mode 100644
index 00000000000..8d33a620744
Binary files /dev/null and b/docs/images/ArchiveSequenceDiagram.png differ
diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png
index 02a42e35e76..7f7eaa9b28b 100644
Binary files a/docs/images/BetterModelClassDiagram.png and b/docs/images/BetterModelClassDiagram.png differ
diff --git a/docs/images/CommitActivityDiagram.png b/docs/images/CommitActivityDiagram.png
index 5b464126b35..aab387303f9 100644
Binary files a/docs/images/CommitActivityDiagram.png and b/docs/images/CommitActivityDiagram.png differ
diff --git a/docs/images/DeleteCommand.png b/docs/images/DeleteCommand.png
new file mode 100644
index 00000000000..d34a95d36aa
Binary files /dev/null and b/docs/images/DeleteCommand.png differ
diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png
index e186f7ba096..4329d4aec03 100644
Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ
diff --git a/docs/images/EditCommand.png b/docs/images/EditCommand.png
new file mode 100644
index 00000000000..a365aa8e35b
Binary files /dev/null and b/docs/images/EditCommand.png differ
diff --git a/docs/images/FilterCommand.png b/docs/images/FilterCommand.png
new file mode 100644
index 00000000000..4fb216ed51d
Binary files /dev/null and b/docs/images/FilterCommand.png differ
diff --git a/docs/images/FilterCommandClassDiagram.png b/docs/images/FilterCommandClassDiagram.png
new file mode 100644
index 00000000000..c18224987e7
Binary files /dev/null and b/docs/images/FilterCommandClassDiagram.png differ
diff --git a/docs/images/HelpActivity.png b/docs/images/HelpActivity.png
new file mode 100644
index 00000000000..bb841f030f4
Binary files /dev/null and b/docs/images/HelpActivity.png differ
diff --git a/docs/images/ListArchivesSequenceDiagram.png b/docs/images/ListArchivesSequenceDiagram.png
new file mode 100644
index 00000000000..e4de3b6dd6c
Binary files /dev/null and b/docs/images/ListArchivesSequenceDiagram.png differ
diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png
index e3b784310fe..28a8a087b49 100644
Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ
diff --git a/docs/images/MarkCommand.png b/docs/images/MarkCommand.png
new file mode 100644
index 00000000000..819eb372be4
Binary files /dev/null and b/docs/images/MarkCommand.png differ
diff --git a/docs/images/MarkCommandClassDiagram.png b/docs/images/MarkCommandClassDiagram.png
new file mode 100644
index 00000000000..01b83bd1491
Binary files /dev/null and b/docs/images/MarkCommandClassDiagram.png differ
diff --git a/docs/images/MarkCommandSequenceDiagram.png b/docs/images/MarkCommandSequenceDiagram.png
new file mode 100644
index 00000000000..ea7924f7f1f
Binary files /dev/null and b/docs/images/MarkCommandSequenceDiagram.png differ
diff --git a/docs/images/NewLaunch.png b/docs/images/NewLaunch.png
new file mode 100644
index 00000000000..4a27f1bd10d
Binary files /dev/null and b/docs/images/NewLaunch.png differ
diff --git a/docs/images/ParserClasses.png b/docs/images/ParserClasses.png
index edfd1ff7897..d9dcc8d7171 100644
Binary files a/docs/images/ParserClasses.png and b/docs/images/ParserClasses.png differ
diff --git a/docs/images/PriorityCommand.png b/docs/images/PriorityCommand.png
new file mode 100644
index 00000000000..c1f728aae45
Binary files /dev/null and b/docs/images/PriorityCommand.png differ
diff --git a/docs/images/StatsSequenceDiagram.png b/docs/images/StatsSequenceDiagram.png
new file mode 100644
index 00000000000..f945d6c98ad
Binary files /dev/null and b/docs/images/StatsSequenceDiagram.png differ
diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png
index 18fa4d0d51f..d57e8f6b83c 100644
Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ
diff --git a/docs/images/TeamProfPlan.png b/docs/images/TeamProfPlan.png
new file mode 100644
index 00000000000..c4384615661
Binary files /dev/null and b/docs/images/TeamProfPlan.png differ
diff --git a/docs/images/Ui.png b/docs/images/Ui.png
index 5bd77847aa2..b8984854e51 100644
Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ
diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png
index 11f06d68671..e44334da154 100644
Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ
diff --git a/docs/images/Uif.png b/docs/images/Uif.png
new file mode 100644
index 00000000000..c6e738a3d43
Binary files /dev/null and b/docs/images/Uif.png differ
diff --git a/docs/images/UmlForModel.png b/docs/images/UmlForModel.png
new file mode 100644
index 00000000000..065b4463f5b
Binary files /dev/null and b/docs/images/UmlForModel.png differ
diff --git a/docs/images/UndoRedoState0.png b/docs/images/UndoRedoState0.png
index c5f91b58533..3ff8009f89b 100644
Binary files a/docs/images/UndoRedoState0.png and b/docs/images/UndoRedoState0.png differ
diff --git a/docs/images/UndoRedoState1.png b/docs/images/UndoRedoState1.png
index 2d3ad09c047..5ba4e0ebc97 100644
Binary files a/docs/images/UndoRedoState1.png and b/docs/images/UndoRedoState1.png differ
diff --git a/docs/images/UndoRedoState2.png b/docs/images/UndoRedoState2.png
index 20853694e03..72e1d6a4708 100644
Binary files a/docs/images/UndoRedoState2.png and b/docs/images/UndoRedoState2.png differ
diff --git a/docs/images/UndoRedoState3.png b/docs/images/UndoRedoState3.png
index 1a9551b31be..8ecdc0815fe 100644
Binary files a/docs/images/UndoRedoState3.png and b/docs/images/UndoRedoState3.png differ
diff --git a/docs/images/UndoRedoState4.png b/docs/images/UndoRedoState4.png
index 46dfae78c94..47a1a624fc9 100644
Binary files a/docs/images/UndoRedoState4.png and b/docs/images/UndoRedoState4.png differ
diff --git a/docs/images/UndoRedoState5.png b/docs/images/UndoRedoState5.png
index f45889b5fdf..3edcf2eb1eb 100644
Binary files a/docs/images/UndoRedoState5.png and b/docs/images/UndoRedoState5.png differ
diff --git a/docs/images/UndoSequenceDiagram.png b/docs/images/UndoSequenceDiagram.png
index c7a7e637266..d231e71a9f8 100644
Binary files a/docs/images/UndoSequenceDiagram.png and b/docs/images/UndoSequenceDiagram.png differ
diff --git a/docs/images/UnmarkCommandClassDiagram.png b/docs/images/UnmarkCommandClassDiagram.png
new file mode 100644
index 00000000000..e401cb4aead
Binary files /dev/null and b/docs/images/UnmarkCommandClassDiagram.png differ
diff --git a/docs/images/catergoriseTwoCatQuizResult.png b/docs/images/catergoriseTwoCatQuizResult.png
new file mode 100644
index 00000000000..b1f43eb55b5
Binary files /dev/null and b/docs/images/catergoriseTwoCatQuizResult.png differ
diff --git a/docs/images/donextADV.png b/docs/images/donextADV.png
new file mode 100644
index 00000000000..eda2322f936
Binary files /dev/null and b/docs/images/donextADV.png differ
diff --git a/docs/images/eyelessrhyme7.png b/docs/images/eyelessrhyme7.png
new file mode 100644
index 00000000000..b552546528d
Binary files /dev/null and b/docs/images/eyelessrhyme7.png differ
diff --git a/docs/images/findAlexDavidResult.png b/docs/images/findAlexDavidResult.png
deleted file mode 100644
index 235da1c273e..00000000000
Binary files a/docs/images/findAlexDavidResult.png and /dev/null differ
diff --git a/docs/images/findQuizGradesResult.png b/docs/images/findQuizGradesResult.png
new file mode 100644
index 00000000000..5846c8a4bab
Binary files /dev/null and b/docs/images/findQuizGradesResult.png differ
diff --git a/docs/images/jack1e0.png b/docs/images/jack1e0.png
new file mode 100644
index 00000000000..40738d02689
Binary files /dev/null and b/docs/images/jack1e0.png differ
diff --git a/docs/images/kiatkat.png b/docs/images/kiatkat.png
new file mode 100644
index 00000000000..531809b263a
Binary files /dev/null and b/docs/images/kiatkat.png differ
diff --git a/docs/images/linkOneLinkWwwDotExampleDotComResult.png b/docs/images/linkOneLinkWwwDotExampleDotComResult.png
new file mode 100644
index 00000000000..4301ebb4a16
Binary files /dev/null and b/docs/images/linkOneLinkWwwDotExampleDotComResult.png differ
diff --git a/docs/images/markCommandAVD.png b/docs/images/markCommandAVD.png
new file mode 100644
index 00000000000..9eb99234083
Binary files /dev/null and b/docs/images/markCommandAVD.png differ
diff --git a/docs/images/matrixADV.png b/docs/images/matrixADV.png
new file mode 100644
index 00000000000..e3dc1548f94
Binary files /dev/null and b/docs/images/matrixADV.png differ
diff --git a/docs/images/newway1814.png b/docs/images/newway1814.png
new file mode 100644
index 00000000000..60c326a6566
Binary files /dev/null and b/docs/images/newway1814.png differ
diff --git a/docs/images/profplan_32.png b/docs/images/profplan_32.png
new file mode 100644
index 00000000000..9ef091702ea
Binary files /dev/null and b/docs/images/profplan_32.png differ
diff --git a/docs/images/setOneTwoResult.png b/docs/images/setOneTwoResult.png
new file mode 100644
index 00000000000..d3f54dc38a5
Binary files /dev/null and b/docs/images/setOneTwoResult.png differ
diff --git a/docs/images/sortADV.png b/docs/images/sortADV.png
new file mode 100644
index 00000000000..33a04763500
Binary files /dev/null and b/docs/images/sortADV.png differ
diff --git a/docs/images/yarnmengnus.png b/docs/images/yarnmengnus.png
new file mode 100644
index 00000000000..db9bed820c5
Binary files /dev/null and b/docs/images/yarnmengnus.png differ
diff --git a/docs/index.md b/docs/index.md
index 7601dbaad0d..f3861c25b89 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,6 +1,6 @@
---
layout: page
-title: AddressBook Level-3
+title: ProfPlan
---
[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions)
@@ -8,10 +8,11 @@ title: AddressBook Level-3
![Ui](images/Ui.png)
-**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface).
+**ProfPlan 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).
-* 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 ProfPlan, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start).
+* If you are interested about developing ProfPlan, the [**Developer Guide**](DeveloperGuide.html) is a good place to start.
**Acknowledgements**
diff --git a/docs/matrix.png b/docs/matrix.png
new file mode 100644
index 00000000000..b0d65222d27
Binary files /dev/null and b/docs/matrix.png differ
diff --git a/docs/team/eyelessrhyme7.md b/docs/team/eyelessrhyme7.md
new file mode 100644
index 00000000000..951f9f7926a
--- /dev/null
+++ b/docs/team/eyelessrhyme7.md
@@ -0,0 +1,75 @@
+---
+layout: page
+title: Raman Gupta's Project Portfolio Page
+---
+
+## Overview
+ProfPlan is a CLI-based task management tool tailored to university professors, offering features like task prioritization, categorization, research tracking, and team coordination to enhance productivity and organization in academia.
+
+## Summary of Contributions
+### Code contributed
+- **[Link to my code on tP Code Dashboard](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=eyelessrhyme7&breakdown=true)**.
+
+### Enhancements implemented
+- Changed the README.md to reflect the ProfPlan project.
+- Developed the add-task command, the base command to add tasks.
+- Developed the priority feature, based of which do_next and matrix is implemented.
+- Developed the do_next command, which generates smart task recommendation on what to do next.
+- Co-developed the Urgency-Priority Matrix functionality, specifically the backend.
+- Added matrix UI refinements.
+- Designed the taskCard UI component.
+- Added Product UI changes and enhanced design.
+
+### Contributions to team-based tasks
+- Set up all meetings and independently led discussion on approach & implementation.
+- Responsible for merging almost ALL the PRs to the master branch. Resolved merged conflicts for almost ALL PRs.
+- Tested all release versions before wrapping up milestones.
+- Released v1.2, v1.3.Trial and v1.3.
+- Fixed severe bugs in v1.2, v1.3.Trial and v1.3 before release.
+- Fixed severe command bugs, date bugs and math errors (division by 0) in v1.3.
+- Created the structure for the User Guide.
+- Created the structure for the product demo and pitch in CS2101.
+- Collated and submitted the User Guide. Designed UI mockups for the UG.
+- Created the Team Organization.
+- Forked the tp repo to the Team Organization.
+- Updated site-wide settings.
+- Contributed to morphing of addressbook to profplan.
+- Morphed add-person to add-task, contributing to morphing of addressbook.
+- Updated the index.md page.
+- Led the fight against bugs, divided bugs according to scope/severity and assigned accordingly.
+- Fixed 4 severe functionality bugs that were discovered during the PE-Dry Run.
+
+## Contributions to the Developer Guide
+- Updated the Delete command section of the Developer Guide
+- Added the DoNext Command feature section the Developer Guide.
+- Added use cases for the do_next command.
+- Added Manual Testing instructions for do_next command and priority feature.
+- Ideated the 'Recommend next task' Planned Enhancement.
+
+
+## Contributions to the User Guide
+- Developed the UI Mockups for all team members to adapt to their features.
+- Created the core presentation format for each command in UG Draft.
+- Created the index, outline and structure plan for the UG.
+- Wrote the 'Overview', 'What It Does', 'Target Users', 'What can ProfPlan do for you?' sections.
+- Designed graphic for the 'Team Behind ProfPlan' section.
+- Created the "Adding a Task" section of the User Guide.
+- Created the "Assigning Priority to a Task" feature of the User Guide.
+- Wrote the 'Recommend next task' and 'Urgency-Importance Matrix' sections under 'Advanced Features'.
+- Produced expected outputs for failure and success for ALL commands and features. Responsible for ensuring consistency with latest working version.
+
+### Review/mentoring contributions
+- Merged almost 100 PRs to the master branch of the team project. Effectively 'officially' reviewed 25 PRs.
+- Helped guide implementation and approach for certain issues, such as dueDate and link.
+
+### Contributions beyond the project team
+- Evidence of helping others
+ - Helped resolve someone's task file saving error in the iP. (https://github.com/nus-cs2103-AY2324S1/forum/issues/32#issuecomment-1705649996)
+ - Contributed constructively with a thorough post of decoupling the Storage class, analyzing trade-offs in effort. (https://github.com/nus-cs2103-AY2324S1/forum/issues/30#issuecomment-1705655827)
+ - Offered suggestion on how to make up for missed commit in iP. (https://github.com/nus-cs2103-AY2324S1/forum/issues/74#issuecomment-1705651700)
+ - Shared my iP UI screenshots among peers, gave feedback to other UIs. (https://github.com/nus-cs2103-AY2324S1/forum/issues/101#issuecomment-1729970744)
+- Evidence of technical leadership
+ - Contributed constructively with a thorough post of decoupling the Storage class, analyzing trade-offs in effort. (https://github.com/nus-cs2103-AY2324S1/forum/issues/30#issuecomment-1705655827)
+ - Asked insightful question about scoping restrictions for different versions. (https://github.com/nus-cs2103-AY2324S1/forum/issues/281#issuecomment-1791745304)
+ - Shared my candid and honest feedback about the course website with Prof. Damith.
+ - Successfully reported 6 comprehensive and detailed true bugs in another team's project. (https://github.com/eyelessrhyme7/ped/issues)
diff --git a/docs/team/jack1e0.md b/docs/team/jack1e0.md
new file mode 100644
index 00000000000..098bdf4973b
--- /dev/null
+++ b/docs/team/jack1e0.md
@@ -0,0 +1,62 @@
+---
+layout: page
+title: Jackie's Project Portfolio Page
+---
+
+# Project: ProfPlan
+
+ProfPlan is a CLI-based task management tool tailored to university professors, offering features like task prioritization, categorization, research tracking, and team coordination to enhance productivity and organization in academia.
+
+## Contributions
+
+**Code contributed:** [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=jack1e0&breakdown=true)
+
+### 1. Features implemented
+
+* **Added the due date field to tasks**
+ * What it does: All tasks now have a due date, which signifies by when they should be complete by.
+ * Justification: Due dates are integral part of tasks, and as a task management tool, implementing this increases the functionality of the application. It also acts as a basis to support other features, such as recurring tasks and the Urgency-Priority matrix.
+ * Credits: Original code
+
+* **Filter tasks function**
+ * What it does: Users can filter their tasklist by one or more of the following criteria, namely priority, due date, status, and recurring type.
+ * Justification: This will enable users to better categorize and manage their tasks, by grouping them together under common criteria.
+ * Credits: Original code
+
+* **List tasks within a week/month from now function**
+ * What it does: Users can view what tasks are within a week, or month from the current date.
+ * Justification: This will enable users to get a better sense of what tasks are more urgent/coming up.
+ * Credits: Original code
+
+### 2. Documentation
+
+**User Guide**
+* Added documentation for the `filter`, `list_week` and `list_month` commands.
+* Added documentation for the `DueDate` parameter.
+* Updated the previous version of other commands in AddressBook, such as `add`, `edit`, etc.
+* Added information in the Supported Flags, and Command Summary tables, such that it is up-to-date with the current implementation.
+* Contributed to formatting the UG and fixing errors.
+
+**Developer Guide**
+* Added detailed documentation for the `filter` command, including an UML class diagram, and code design explanation.
+* Added documentation for the Proposed Task Archiving feature, including several sequence diagrams, and proposed implementation for related functions (such as display and restore archived tasks)
+* Updated the previous version of the Proposed Undo/Redo feature, such that it is now specific to ProfPlan.
+* Added Use Case for filtering tasks.
+* In charge of the final formatting and cleaning up.
+
+
+### 3. Contributions to team-based tasks
+* Responsible for creating and setting deadlines for all milestones
+* Responsible for merging few PRs to the master branch.
+* Contributed to the renaming of AddressBook to ProfPlan.
+* Tested all release versions before wrapping up milestones.
+* Fixed severe bugs.
+* Helped in giving inputs regarding the UI Color Scheme.
+
+
+### 4. Community
+* Reviewed most PRs from teammates
+* Reported bugs and suggestions for other teams in the class (during [PE-D](https://github.com/jack1e0/ped/issues))
+
+
+
diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md
deleted file mode 100644
index 773a07794e2..00000000000
--- a/docs/team/johndoe.md
+++ /dev/null
@@ -1,46 +0,0 @@
----
-layout: page
-title: John Doe's Project Portfolio Page
----
-
-### Project: AddressBook Level 3
-
-AddressBook - Level 3 is a desktop address book application used for teaching Software Engineering principles. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
-
-Given below are my contributions to the project.
-
-* **New Feature**: Added the ability to undo/redo previous commands.
- * What it does: allows the user to undo all previous commands one at a time. Preceding undo commands can be reversed by using the redo command.
- * Justification: This feature improves the product significantly because a user can make mistakes in commands and the app should provide a convenient way to rectify them.
- * Highlights: This enhancement affects existing commands and commands to be added in future. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing commands.
- * Credits: *{mention here if you reused any code/ideas from elsewhere or if a third-party library is heavily used in the feature so that a reader can make a more accurate judgement of how much effort went into the feature}*
-
-* **New Feature**: Added a history command that allows the user to navigate to previous commands using up/down keys.
-
-* **Code contributed**: [RepoSense link]()
-
-* **Project management**:
- * Managed releases `v1.3` - `v1.5rc` (3 releases) on GitHub
-
-* **Enhancements to existing features**:
- * Updated the GUI color scheme (Pull requests [\#33](), [\#34]())
- * Wrote additional tests for existing features to increase coverage from 88% to 92% (Pull requests [\#36](), [\#38]())
-
-* **Documentation**:
- * User Guide:
- * Added documentation for the features `delete` and `find` [\#72]()
- * Did cosmetic tweaks to existing documentation of features `clear`, `exit`: [\#74]()
- * Developer Guide:
- * Added implementation details of the `delete` feature.
-
-* **Community**:
- * PRs reviewed (with non-trivial review comments): [\#12](), [\#32](), [\#19](), [\#42]()
- * Contributed to forum discussions (examples: [1](), [2](), [3](), [4]())
- * Reported bugs and suggestions for other teams in the class (examples: [1](), [2](), [3]())
- * Some parts of the history feature I added was adopted by several other class mates ([1](), [2]())
-
-* **Tools**:
- * Integrated a third party library (Natty) to the project ([\#42]())
- * Integrated a new Github plugin (CircleCI) to the team repo
-
-* _{you can add/remove categories in the list above}_
diff --git a/docs/team/kiatkat.md b/docs/team/kiatkat.md
new file mode 100644
index 00000000000..730356fb510
--- /dev/null
+++ b/docs/team/kiatkat.md
@@ -0,0 +1,73 @@
+---
+layout: page
+title: Kiat Win's Project Portfolio Page
+---
+
+### Project: ProfPlan
+
+ProfPlan is a desktop productivity application written in Java, used for task management and organization. The user
+interacts with it using a CLI, and it has a GUI created with JavaFX.
+
+Listed below are my contributions to the project, with some relevant pull requests linked where possible. ([RepoSense
+link to code contributed](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=kiatkat&breakdown=true))
+
+### New Features
+* Added the ability to create recurring tasks. [#130](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/130)
+ * What it does: Allows the user to create a new type of task that postpones its due date when marked as completed.
+ * Justification: A large number of tasks are cyclical in nature. The application can now better model these tasks
+ and provide a more natural means of management.
+ * Highlights: This enhancement directly integrates into the `add` command. A thorough consideration of design
+ options was necessary to find a solution which does so with minimal overhead while still providing extensibility
+ and flexibility.
+ * Credits: Code written is original.
+
+* Added the ability for the user to modify settings. [#138](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/138)
+ * What it does: Allows the user to change certain global parameters that affect the behaviour of other commands
+ via the `set` command.
+ * Justification: Different users may desire different parameters for some commands and wish to customize the
+ application to suit their unique needs.
+ * Highlights: This feature required changes across the codebase in multiple regions of the architecture. A
+ file is created to maintain user configurations between sessions, and the user must be able to modify the
+ values in this file, with any command also being able to read these settings. Careful thought had to be given to
+ the structure and design of this system to minimize dependencies.
+ * Credits: Code written is original.
+ * Note: There was a `set` feature of the same name implemented in versions pre-v1.3, which is completely different
+ in specification, and has since been removed. All mentions of `set` in this document refer to the implementation
+ described above.
+
+* Added the ability for the user to add descriptions. [#105](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/105)
+ * What it does: Allows the user to add an associated description field to a task and edit it with the
+ `description` command.
+ * Justification: Users may want to store information related to the task that is not expressible by the limited
+ values of the other fields.
+ * Highlights: This feature required good planning to integrate well into the existing structure of a task, while
+ maintaining flexibility and minimizing coupling.
+ * Credits: Code written is original.
+
+### Project management and team-based tasks
+* Responsible for creating and setting deadlines for all milestones
+* Performed most of the renaming required for the initial morph of AB3 into ProfPlan
+* Responsible for removing redundancies and cleaning up the codebase [#121](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/121)
+ [#137](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/137)
+* Consolidated features and divided work into issues for delegation
+* Created and enforced early milestones for `v1.2` and `v1.3` to prevent deadline overrun
+* Contributed to all team planning, scoping out features to be implemented
+
+### User Guide
+
+* Added documentation for the `find` and `set` features ([#22](https://github.com/AY2324S1-CS2103T-W15-1/tp/issues/22)),
+ the `recur/` flag and corresponding mentions of recurring tasks, as well as supported setting parameters
+* Clarified the acceptable values for the parameters that the user can input
+* Wrote the section "What can ProfPlan do for you?" and part of "Advanced Features"
+* Made some tweaks to fix language errors
+
+### Developer Guide
+
+* Modified UML diagrams to reflect the addition of the `UserConfigs` system
+* Added use cases specific to the features implemented above
+* Made tweaks to the design and layout and corrected some language errors
+
+### Community
+
+* Reviewed PRs (examples: [#104](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/104), [#133](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/133))
+* Reported bugs and suggestions for other teams in the class (examples can be found in the [PE-D](https://github.com/kiatkat/ped/issues))
diff --git a/docs/team/newway1814.md b/docs/team/newway1814.md
new file mode 100644
index 00000000000..eb9cef856c4
--- /dev/null
+++ b/docs/team/newway1814.md
@@ -0,0 +1,113 @@
+---
+layout: page
+title: Tejas Gandhi's Project Portfolio Page
+---
+
+## Overview
+ProfPlan is a CLI-based task management tool tailored to university professors, offering features like task prioritization, categorization, research tracking, and team coordination to enhance productivity and organization in academia.
+
+## Summary of Contributions
+### Code contributed
+- **[Link to my code on tP Code Dashboard](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=newway1814&breakdown=true)**.
+
+### Enhancements implemented Summary
+- Changed all the Person instances in the Code base to Task.
+- Refactored the left over instances of AddressBook to profPlan.
+- Developed the Mark command feature, the main command to mark a task as done.
+- Developed the Unmark command feature, the main command to unmark the task from done to undone.
+- Developed the delete all command, to delete all the tasks.
+- Developed sort_priority command, to sort the tasks based on decreasing order of the priority.
+- Developed sort_duedate command, to sort the tasks based on nearest duedate.
+- Co-developed the Urgency-Priority Matrix functionality, Focussed on frontend while also implemented the algorithm at the backend.
+- Added test cases for extensive testing and for improving code-coverage.
+- Helped in choosing the colour scheme for the UI.
+
+
+### Contributions to the User Guide
+- Added the description of ProfPlan.
+- Created the "delete and delete all tasks" section of the User Guide.
+- Edited the "list" section to make it adaptable to ProfPlan.
+- Created the "Mark Task as Done" feature of the User Guide.
+- Created the "Unmark a Task" feature of the User Guide.
+- Collated the "Mark task as Done" and "Unmark a Task" feature together.
+- Created the "Sort Tasks based on priority" feature of the User Guide.
+- Created the "Sort Tasks according to deadline" feature of the User Guide.
+- Updated the "Urgency-Priority Matrix" feature of the User Guide.
+- Created the "Contact Us" section of the User Guide.
+- Created the "Feedback" section of the User Guide.
+- Added the form link for feedback/issues/Contact-Us in the User Guide.
+
+### Contributions to the Developer Guide
+- Updated the Delete command section of the Developer Guide
+- Updated the Model sequence diagram to make it fit to our implementation.
+- Updated the UI class diagram to make it fit to our implementation.
+- Updated the Logic class diagram to make it fit to our implementation.
+- Updated the Delete Command sequence diagram to make it fit to our implementation.
+- Added the Model Uml diagram with clear colours to represent the Model implementation.
+- Added the Mark Command feature section the Developer Guide.
+- Added the Mark Command Sequence Diagram depicting the logical flow.
+- Added the Mark Command class Diagram.
+- Added Manual Testing instructions for mark command.
+- Added Manual Testing instructions for unmark feature.
+- Added Manual Testing instructions for sort_priority feature.
+- Added Manual Testing instructions for sort_duedate feature.
+
+
+### Contributions to team-based tasks
+- Set up few weekly meetings and lead some discussions based on the flow of the project.
+- Responsible for merging few PRs to the master branch.
+- Tested all release versions before wrapping up milestones.
+- Responsible for releasing v1.1 jar file.
+- Responsible for assigning most Issues created on our team repo.
+- Helped Teammates in fixing severe bugs.
+- Responisble for assigning reviewers for most PR's
+- Fixed severe command bugs.
+- Initiated the creation of jekyll docs for User Guide.
+- Contributed to morphing of addressbook to profplan.
+- Morphed add-person to add-task, contributing to morphing of addressbook.
+- Helped in giving inputs regarding the UI Color Scheme.
+- Tested the product extensively.
+- Gave constant updates regarding the project flow and timeline on the telegram group of our team.
+
+
+
+### Review/mentoring contributions
+
+ * PRs reviewed : [#73](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/73),
+ [#74](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/74),
+ [#75](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/75),
+ [#77](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/77),
+ [#100](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/100),
+ [#102](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/102),
+ [#105](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/105),
+ [#106](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/106),
+ [#107](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/107),
+ [#110](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/110),
+ [#113](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/113),
+ [#118](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/118),
+ [#121](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/121),
+ [#130](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/130),
+ [#137](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/137),
+ [#138](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/138),
+ [#140](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/140),
+ [#143](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/143),
+ [#144](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/144),
+ [#146](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/146),
+ [#149](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/149)
+ * Effectively reviewed 20+ PR's.
+
+
+
+### Contributions beyond the project team (community)
+- Evidence of helping others
+ - Successfully reported 9 bugs in another team's project [#1](https://github.com/newway1814/ped/issues/1),
+ [#2](https://github.com/newway1814/ped/issues/2), [#3](https://github.com/newway1814/ped/issues/3),
+ [#4](https://github.com/newway1814/ped/issues/4), [#5](https://github.com/newway1814/ped/issues/5),
+ [#6](https://github.com/newway1814/ped/issues/6), [#7](https://github.com/newway1814/ped/issues/7),
+ [#8](https://github.com/newway1814/ped/issues/8), [#9](https://github.com/newway1814/ped/issues/9)
+- Evidence of my participation in forum discussions
+ - Here are some issues from the forum: [#187](https://github.com/nus-cs2103-AY2324S1/forum/issues/187), [#200](https://github.com/nus-cs2103-AY2324S1/forum/issues/200),
+ [#267](https://github.com/nus-cs2103-AY2324S1/forum/issues/267)
+- Evidence of technical leadership
+ - Led the ideation, approach and development for UI Overhaul and Urgency-Priority Matrix.
+ - Checking deadlines, staying vigilant and ensuring pace of work.
diff --git a/docs/team/yarnmengnus.md b/docs/team/yarnmengnus.md
new file mode 100644
index 00000000000..d1e1f514477
--- /dev/null
+++ b/docs/team/yarnmengnus.md
@@ -0,0 +1,62 @@
+---
+layout: page
+title: Yarn Meng's Project Portfolio Page
+---
+
+## Overview
+
+ProfPlan is a CLI-based task management tool tailored to university professors, offering features like task prioritization, categorization, research tracking, and team coordination to enhance productivity and organization in academia.
+
+## Contributions
+**Code contributed**
+**[Link to my code on tP Code Dashboard](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=yarnmengnus&breakdown=false)**.
+
+### 1. Features implemented
+* **New Feature**: Added the `link` field to tasks [#74](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/74)
+ * What it does: Allows the user to include references for the task.
+ * Justification: The user (CS professors) often has the need for references. This feature enables the user to view their references at a glance. This makes ProfPlan better tailored to the user's needs.
+ * Highlights: This enhancement affects existing `add` and `edit` commands. It required much analysis of the user's needs regarding their needs for references. The chosen implementation takes into account the fact that Professors not only use URLs for referenecs, but also books and other hardcopy literature, etc.
+ * Credits: Code written for this feature is original.
+
+* **New Feature**: Added the `stats` command to view statistics [#155](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/155)
+ * What it does: Shows the users statistics about their tasks.
+ * Justification: This feature allows users to view a high-level summary of their tasks, providing motivation for them to complete them.
+ * Credits: Code written for this feature is original.
+
+
+
+* **New Feature**: Added significant capabilities to the `help` command [#127](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/127)
+ * What it does:
+ 1. Lists all commands and their usage when no arguments supplied
+ 1. Shows more details description and usage of commands when a command is supplied as an argument
+ * Justification: This feature guides new users to better navigate ProfPlan, instead of diverting users to the user guide. This saves much time for users.
+ * Credits: Code written for this feature is original.
+
+
+* **Project management**:
+ * Responsible for uniformity of the User Guide.
+ * Contributed to team code reviews while merging ALL PRs.
+ * Tested release versions for bugs.
+ * Set up physical meetings for code reviews.
+ * Assisted in scoping of features in each release.
+ * Contributed to milestone and issue categorisation and management.
+
+### 2. Documentation
+**User Guide**
+ * Added documentation for the `help` and `stats` feature [#114](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/114)
+ * Added docutmentation for the Link parameter.
+ * Fixed link functionality
+ * Fixed visual bugs in the User Guide.
+ * In charge of the final formatting and cleaning up.
+
+**Developer Guide**
+ * Added documentation for the `help` command, including an UML activation diagram. [#110](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/110)
+ * Added detailed documentation for the `stats` command, including an UML sequence diagram. [#234](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/234)
+ * Added Use Case for `help`, `stats` and `link` commands. [#63](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/63)
+
+### Other contributions
+* **Community**:
+ * Reviewed most PRs from teammates
+
+* **Tools**:
+ * Integrated Java Reflections to the project ([#225](https://github.com/AY2324S1-CS2103T-W15-1/tp/pull/225))
diff --git a/docs/tutorials/AddRemark.md b/docs/tutorials/AddRemark.md
index d98f38982e7..73acf7fcee3 100644
--- a/docs/tutorials/AddRemark.md
+++ b/docs/tutorials/AddRemark.md
@@ -5,7 +5,7 @@ title: "Tutorial: Adding a command"
Let's walk you through the implementation of a new command — `remark`.
-This command allows users of the AddressBook application to add optional remarks to people in their address book and edit it if required. The command should have the following format:
+This command allows users of the AddressBook application to add optional remarks to people in their task list and edit it if required. The command should have the following format:
`remark INDEX r/REMARK` (e.g., `remark 2 r/Likes baseball`)
@@ -25,10 +25,10 @@ For now, let’s keep `RemarkCommand` as simple as possible and print some outpu
``` java
package seedu.address.logic.commands;
-import seedu.address.model.Model;
+import model.profplan.Model;
/**
- * Changes the remark of an existing person in the address book.
+ * Changes the remark of an existing task in the task list.
*/
public class RemarkCommand extends Command {
@@ -43,7 +43,7 @@ public class RemarkCommand extends Command {
### Hook `RemarkCommand` into the application
-Now that we have our `RemarkCommand` ready to be executed, we need to update `AddressBookParser#parseCommand()` to recognize the `remark` keyword. Add the new command to the `switch` block by creating a new `case` that returns a new instance of `RemarkCommand`.
+Now that we have our `RemarkCommand` ready to be executed, we need to update `ProfPlanParser#parseCommand()` to recognize the `remark` keyword. Add the new command to the `switch` block by creating a new `case` that returns a new instance of `RemarkCommand`.
You can refer to the changes in this [diff](https://github.com/se-edu/addressbook-level3/commit/35eb7286f18a029d39cb7a29df8f172a001e4fd8#diff-399c284cb892c20b7c04a69116fcff6ccc0666c5230a1db8e4a9145def8fa4ee).
@@ -65,8 +65,8 @@ Following the convention in other commands, we add relevant messages as constant
``` java
public static final String MESSAGE_USAGE = COMMAND_WORD
- + ": Edits the remark of the person identified "
- + "by the index number used in the last person listing. "
+ + ": Edits the remark of the task identified "
+ + "by the index number used in the last task listing. "
+ "Existing remark will be overwritten by the input.\n"
+ "Parameters: INDEX (must be a positive integer) "
+ "r/ [REMARK]\n"
@@ -91,7 +91,7 @@ Let’s change `RemarkCommand` to parse input from the user.
We start by modifying the constructor of `RemarkCommand` to accept an `Index` and a `String`. While we are at it, let’s change the error message to echo the values. While this is not a replacement for tests, it is an obvious way to tell if our code is functioning as intended.
``` java
-import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import static util.commons.profplan.CollectionUtil.requireAllNonNull;
//...
public class RemarkCommand extends Command {
//...
@@ -101,8 +101,8 @@ public class RemarkCommand extends Command {
private final String remark;
/**
- * @param index of the person in the filtered person list to edit the remark
- * @param remark of the person to be updated to
+ * @param index of the task in the filtered task list to edit the remark
+ * @param remark of the task to be updated to
*/
public RemarkCommand(Index index, String remark) {
requireAllNonNull(index, remark);
@@ -214,7 +214,7 @@ public RemarkCommand parse(String args) throws ParseException {
-:information_source: Don’t forget to update `AddressBookParser` to use our new `RemarkCommandParser`!
+:information_source: Don’t forget to update `ProfPlanParser` to use our new `RemarkCommandParser`!
@@ -223,11 +223,11 @@ If you are stuck, check out the sample
## Add `Remark` to the model
-Now that we have all the information that we need, let’s lay the groundwork for propagating the remarks added into the in-memory storage of person data. We achieve that by working with the `Person` model. Each field in a Person is implemented as a separate class (e.g. a `Name` object represents the person’s name). That means we should add a `Remark` class so that we can use a `Remark` object to represent a remark given to a person.
+Now that we have all the information that we need, let’s lay the groundwork for propagating the remarks added into the in-memory storage of task data. We achieve that by working with the `Task` model. Each field in a Task is implemented as a separate class (e.g. a `Name` object represents the task’s name). That means we should add a `Remark` class so that we can use a `Remark` object to represent a remark given to a task.
### Add a new `Remark` class
-Create a new `Remark` in `seedu.address.model.person`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code.
+Create a new `Remark` in `seedu.address.model.task`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code.
A copy-paste and search-replace later, you should have something like [this](https://github.com/se-edu/addressbook-level3/commit/4516e099699baa9e2d51801bd26f016d812dedcc#diff-41bb13c581e280c686198251ad6cc337cd5e27032772f06ed9bf7f1440995ece). Note how `Remark` has no constrains and thus does not require input
validation.
@@ -238,11 +238,11 @@ Let’s change `RemarkCommand` and `RemarkCommandParser` to use the new `Remark`
## Add a placeholder element for remark to the UI
-Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each person.
+Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each task.
-Simply add the following to [`seedu.address.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688).
+Simply add the following to [`ui.profplan.TaskCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688).
-**`PersonCard.java`:**
+**`TaskCard.java`:**
``` java
@FXML
@@ -252,9 +252,9 @@ private Label remark;
`@FXML` is an annotation that marks a private or protected field and makes it accessible to FXML. It might sound like Greek to you right now, don’t worry — we will get back to it later.
-Then insert the following into [`main/resources/view/PersonListCard.fxml`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-d44c4f51c24f6253c277a2bb9bc440b8064d9c15ad7cb7ceda280bca032efce9).
+Then insert the following into [`main/resources/view/TaskListCard.fxml`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-d44c4f51c24f6253c277a2bb9bc440b8064d9c15ad7cb7ceda280bca032efce9).
-**`PersonListCard.fxml`:**
+**`TaskListCard.fxml`:**
``` xml
@@ -264,21 +264,21 @@ That’s it! Fire up the application again and you should see something like thi
![$remark shows up in each entry](../images/add-remark/$Remark.png)
-## Modify `Person` to support a `Remark` field
+## Modify `Task` to support a `Remark` field
-Since `PersonCard` displays data from a `Person`, we need to update `Person` to get our `Remark` displayed!
+Since `TaskCard` displays data from a `Task`, we need to update `Task` to get our `Remark` displayed!
-### Modify `Person`
+### Modify `Task`
-We change the constructor of `Person` to take a `Remark`. We will also need to define new fields and accessors accordingly to store our new addition.
+We change the constructor of `Task` to take a `Remark`. We will also need to define new fields and accessors accordingly to store our new addition.
-### Update other usages of `Person`
+### Update other usages of `Task`
-Unfortunately, a change to `Person` will cause other commands to break, you will have to modify these commands to use the updated `Person`!
+Unfortunately, a change to `Task` will cause other commands to break, you will have to modify these commands to use the updated `Task`!
-:bulb: Use the `Find Usages` feature in IntelliJ IDEA on the `Person` class to find these commands.
+:bulb: Use the `Find Usages` feature in IntelliJ IDEA on the `Task` class to find these commands.
@@ -287,13 +287,13 @@ Refer to [this commit](https://github.com/se-edu/addressbook-level3/commit/ce998
## Updating Storage
-AddressBook stores data by serializing `JsonAdaptedPerson` into `json` with the help of an external library — Jackson. Let’s update `JsonAdaptedPerson` to work with our new `Person`!
+AddressBook stores data by serializing `JsonAdaptedTask` into `json` with the help of an external library — Jackson. Let’s update `JsonAdaptedTask` to work with our new `Task`!
While the changes to code may be minimal, the test data will have to be updated as well.
-:exclamation: You must delete AddressBook’s storage file located at `/data/addressbook.json` before running it! Not doing so will cause AddressBook to default to an empty address book!
+:exclamation: You must delete AddressBook’s storage file located at `/data/addressbook.json` before running it! Not doing so will cause AddressBook to default to an empty task list!
@@ -302,16 +302,16 @@ to see what the changes entail.
## Finalizing the UI
-Now that we have finalized the `Person` class and its dependencies, we can now bind the `Remark` field to the UI.
+Now that we have finalized the `Task` class and its dependencies, we can now bind the `Remark` field to the UI.
Just add [this one line of code!](https://github.com/se-edu/addressbook-level3/commit/5b98fee11b6b3f5749b6b943c4f3bd3aa049b692)
-**`PersonCard.java`:**
+**`TaskCard.java`:**
``` java
-public PersonCard(Person person, int displayedIndex) {
+public TaskCard(Task task, int displayedIndex) {
//...
- remark.setText(person.getRemark().value);
+ remark.setText(task.getRemark().value);
}
```
@@ -323,43 +323,43 @@ After the previous step, we notice a peculiar regression — we went from di
### Update `RemarkCommand` and `RemarkCommandParser`
-In this last step, we modify `RemarkCommand#execute()` to change the `Remark` of a `Person`. Since all fields in a `Person` are immutable, we create a new instance of a `Person` with the values that we want and
-save it with `Model#setPerson()`.
+In this last step, we modify `RemarkCommand#execute()` to change the `Remark` of a `Task`. Since all fields in a `Task` are immutable, we create a new instance of a `Task` with the values that we want and
+save it with `Model#setTask()`.
**`RemarkCommand.java`:**
``` java
//...
- public static final String MESSAGE_ADD_REMARK_SUCCESS = "Added remark to Person: %1$s";
- public static final String MESSAGE_DELETE_REMARK_SUCCESS = "Removed remark from Person: %1$s";
+ public static final String MESSAGE_ADD_REMARK_SUCCESS = "Added remark to Task: %1$s";
+ public static final String MESSAGE_DELETE_REMARK_SUCCESS = "Removed remark from Task: %1$s";
//...
@Override
public CommandResult execute(Model model) throws CommandException {
- List lastShownList = model.getFilteredPersonList();
+ List lastShownList = model.getFilteredTaskList();
if (index.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ throw new CommandException(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
}
- Person personToEdit = lastShownList.get(index.getZeroBased());
- Person editedPerson = new Person(
- personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(),
- personToEdit.getAddress(), remark, personToEdit.getTags());
+ Task taskToEdit = lastShownList.get(index.getZeroBased());
+ Task editedTask = new Task(
+ taskToEdit.getName(), taskToEdit.get(), taskToEdit.getEmail(),
+ taskToEdit.getAddress(), remark, taskToEdit.getTags());
- model.setPerson(personToEdit, editedPerson);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ model.setTask(taskToEdit, editedTask);
+ model.updateFilteredTaskList(PREDICATE_SHOW_ALL_TASKS);
- return new CommandResult(generateSuccessMessage(editedPerson));
+ return new CommandResult(generateSuccessMessage(editedTask));
}
/**
* Generates a command execution success message based on whether
* the remark is added to or removed from
- * {@code personToEdit}.
+ * {@code taskToEdit}.
*/
- private String generateSuccessMessage(Person personToEdit) {
+ private String generateSuccessMessage(Task taskToEdit) {
String message = !remark.value.isEmpty() ? MESSAGE_ADD_REMARK_SUCCESS : MESSAGE_DELETE_REMARK_SUCCESS;
- return String.format(message, personToEdit);
+ return String.format(message, taskToEdit);
}
```
diff --git a/docs/tutorials/RemovingFields.md b/docs/tutorials/RemovingFields.md
index f29169bc924..f26340f592a 100644
--- a/docs/tutorials/RemovingFields.md
+++ b/docs/tutorials/RemovingFields.md
@@ -8,7 +8,7 @@ title: "Tutorial: Removing Fields"
> — Antoine de Saint-Exupery
When working on an existing code base, you will most likely find that some features that are no longer necessary.
-This tutorial aims to give you some practice on such a code 'removal' activity by removing the `address` field from `Person` class.
+This tutorial aims to give you some practice on such a code 'removal' activity by removing the `address` field from `Task` class.
@@ -28,7 +28,7 @@ IntelliJ IDEA provides a refactoring tool that can identify *most* parts of a re
### Assisted refactoring
-The `address` field in `Person` is actually an instance of the `seedu.address.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu.
+The `address` field in `Task` is actually an instance of the `task.model.profplan.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu.
* :bulb: To make things simpler, you can unselect the options `Search in comments and strings` and `Search for text occurrences`
![Usages detected](../images/remove/UnsafeDelete.png)
@@ -37,11 +37,11 @@ Choose to `View Usages` and you should be presented with a list of `Safe Delete
![List of conflicts](../images/remove/SafeDeleteConflicts.png)
-Remove usages of `Address` by performing `Safe Delete`s on each entry i.e., double-click on the entry (which takes you to the code in concern, right-click on that entity, and choose `Refactor` -> `Safe delete` as before). You will need to exercise discretion when removing usages of `Address`. Functions like `ParserUtil#parseAddress()` can be safely removed but its usages must be removed as well. Other usages like in `EditPersonDescriptor` may require more careful inspection.
+Remove usages of `Address` by performing `Safe Delete`s on each entry i.e., double-click on the entry (which takes you to the code in concern, right-click on that entity, and choose `Refactor` -> `Safe delete` as before). You will need to exercise discretion when removing usages of `Address`. Functions like `ParserUtil#parseAddress()` can be safely removed but its usages must be removed as well. Other usages like in `EditTaskDescriptor` may require more careful inspection.
-Let’s try removing references to `Address` in `EditPersonDescriptor`.
+Let’s try removing references to `Address` in `EditTaskDescriptor`.
-1. Safe delete the field `address` in `EditPersonDescriptor`.
+1. Safe delete the field `address` in `EditTaskDescriptor`.
1. Select `Yes` when prompted to remove getters and setters.
@@ -52,7 +52,7 @@ Let’s try removing references to `Address` in `EditPersonDescriptor`.
- :bulb: **Tip:** Removing usages may result in errors. Exercise discretion and fix them. For example, removing the `address` field from the `Person` class will require you to modify its constructor.
+ :bulb: **Tip:** Removing usages may result in errors. Exercise discretion and fix them. For example, removing the `address` field from the `Task` class will require you to modify its constructor.
1. Repeat the steps for the remaining usages of `Address`
@@ -63,13 +63,13 @@ After you are done, verify that the application still works by compiling and run
Unfortunately, there are usages of `Address` that IntelliJ IDEA cannot identify. You can find them by searching for instances of the word `address` in your code (`Edit` \> `Find` \> `Find in path`).
-Places of interest to look out for would be resources used by the application. `main/resources` contains images and `fxml` files used by the application and `test/resources` contains test data. For example, there is a `$address` in each `PersonCard` that has not been removed nor identified.
+Places of interest to look out for would be resources used by the application. `main/resources` contains images and `fxml` files used by the application and `test/resources` contains test data. For example, there is a `$address` in each `TaskCard` that has not been removed nor identified.
![$address](../images/remove/$address.png)
-A quick look at the `PersonCard` class and its `fxml` file quickly reveals why it slipped past the automated refactoring.
+A quick look at the `TaskCard` class and its `fxml` file quickly reveals why it slipped past the automated refactoring.
-**`PersonCard.java`**
+**`TaskCard.java`**
``` java
...
@@ -78,11 +78,11 @@ private Label address;
...
```
-**`PersonCard.fxml`**
+**`TaskCard.fxml`**
``` xml
...
-
+
...
@@ -96,13 +96,13 @@ At this point, your application is working as intended and all your tests are pa
In `src/test/data/`, data meant for testing purposes are stored. While keeping the `address` field in the json files does not cause the tests to fail, it is not good practice to let cruft from old features accumulate.
-**`invalidPersonAddressBook.json`:**
+**`invalidTaskAddressBook.json`:**
```json
{
- "persons": [ {
- "name": "Person with invalid name field: Ha!ns Mu@ster",
- "phone": "9482424",
+ "tasks": [ {
+ "name": "Task with invalid name field: Ha!ns Mu@ster",
+ "priority": "9482424",
"email": "hans@example.com",
"address": "4th street"
} ]
diff --git a/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md
index 4fb62a83ef6..4e83338aad4 100644
--- a/docs/tutorials/TracingCode.md
+++ b/docs/tutorials/TracingCode.md
@@ -39,7 +39,7 @@ In our case, we would want to begin the tracing at the very point where the App
-According to the sequence diagram you saw earlier (and repeated above for reference), the `UI` component yields control to the `Logic` component through a method named `execute`. Searching through the code base for an `execute()` method that belongs to the `Logic` component yields a promising candidate in `seedu.address.logic.Logic`.
+According to the sequence diagram you saw earlier (and repeated above for reference), the `UI` component yields control to the `Logic` component through a method named `execute`. Searching through the code base for an `execute()` method that belongs to the `Logic` component yields a promising candidate in `logic.profplan.Logic`.
@@ -48,7 +48,7 @@ According to the sequence diagram you saw earlier (and repeated above for refere
:bulb: **Intellij Tip:** The ['**Search Everywhere**' feature](https://www.jetbrains.com/help/idea/searching-everywhere.html) can be used here. In particular, the '**Find Symbol**' ('Symbol' here refers to methods, variables, classes etc.) variant of that feature is quite useful here as we are looking for a _method_ named `execute`, not simply the text `execute`.
-A quick look at the `seedu.address.logic.Logic` (an extract given below) confirms that this indeed might be what we’re looking for.
+A quick look at the `logic.profplan.Logic` (an extract given below) confirms that this indeed might be what we’re looking for.
```java
public interface Logic {
@@ -85,7 +85,7 @@ Now let’s set the breakpoint. First, double-click the item to reach the corres
## Tracing the execution path
-Recall from the User Guide that the `edit` command has the format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…` For this tutorial we will be issuing the command `edit 1 n/Alice Yeoh`.
+Recall from the User Guide that the `edit` command has the format: `edit INDEX [n/NAME] [p/PRIORITY] [e/EMAIL] [a/ADDRESS] [t/TAG]…` For this tutorial we will be issuing the command `edit 1 n/Alice Yeoh`.
@@ -120,7 +120,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
CommandResult commandResult;
//Parse user input from String to a Command
- Command command = addressBookParser.parseCommand(commandText);
+ Command command = ProfPlanParser.parseCommand(commandText);
//Executes the Command and stores the result
commandResult = command.execute(model);
@@ -141,7 +141,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
1. _Step over_ the logging code since it is of no interest to us now.
![StepOver](../images/tracing/StepOver.png)
-1. _Step into_ the line where user input in parsed from a String to a Command, which should bring you to the `AddressBookParser#parseCommand()` method (partial code given below):
+1. _Step into_ the line where user input in parsed from a String to a Command, which should bring you to the `ProfPlanParser#parseCommand()` method (partial code given below):
``` java
public Command parseCommand(String userInput) throws ParseException {
...
@@ -171,7 +171,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
1. Stepping through the method shows that it calls `ArgumentTokenizer#tokenize()` and `ParserUtil#parseIndex()` to obtain the arguments and index required.
-1. The rest of the method seems to exhaustively check for the existence of each possible parameter of the `edit` command and store any possible changes in an `EditPersonDescriptor`. Recall that we can verify the contents of `editPersonDesciptor` through the 'Variables' window.
+1. The rest of the method seems to exhaustively check for the existence of each possible parameter of the `edit` command and store any possible changes in an `EditTaskDescriptor`. Recall that we can verify the contents of `editTaskDesciptor` through the 'Variables' window.
![EditCommand](../images/tracing/EditCommand.png)
1. As you just traced through some code involved in parsing a command, you can take a look at this class diagram to see where the various parsing-related classes you encountered fit into the design of the `Logic` component.
@@ -189,22 +189,22 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
@Override
public CommandResult execute(Model model) throws CommandException {
...
- Person personToEdit = lastShownList.get(index.getZeroBased());
- Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor);
- if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) {
- throw new CommandException(MESSAGE_DUPLICATE_PERSON);
+ Task taskToEdit = lastShownList.get(index.getZeroBased());
+ Task editedTask = createEditedTask(taskToEdit, editTaskDescriptor);
+ if (!taskToEdit.isSameTask(editedTask) && model.hasTask(editedTask)) {
+ throw new CommandException(MESSAGE_DUPLICATE_TASK);
}
- model.setPerson(personToEdit, editedPerson);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson));
+ model.setTask(taskToEdit, editedTask);
+ model.updateFilteredTaskList(PREDICATE_SHOW_ALL_TASKS);
+ return new CommandResult(String.format(MESSAGE_EDIT_TASK_SUCCESS, editedTask));
}
```
1. As suspected, `command#execute()` does indeed make changes to the `model` object. Specifically,
- * it uses the `setPerson()` method (defined in the interface `Model` and implemented in `ModelManager` as per the usual pattern) to update the person data.
- * it uses the `updateFilteredPersonList` method to ask the `Model` to populate the 'filtered list' with _all_ persons.
- FYI, The 'filtered list' is the list of persons resulting from the most recent operation that will be shown to the user immediately after. For the `edit` command, we populate it with all the persons so that the user can see the edited person along with all other persons. If this was a `find` command, we would be setting that list to contain the search results instead.
- To provide some context, given below is the class diagram of the `Model` component. See if you can figure out where the 'filtered list' of persons is being tracked.
+ * it uses the `setTask()` method (defined in the interface `Model` and implemented in `ModelManager` as per the usual pattern) to update the task data.
+ * it uses the `updateFilteredTaskList` method to ask the `Model` to populate the 'filtered list' with _all_ tasks.
+ FYI, The 'filtered list' is the list of tasks resulting from the most recent operation that will be shown to the user immediately after. For the `edit` command, we populate it with all the tasks so that the user can see the edited task along with all other tasks. If this was a `find` command, we would be setting that list to contain the search results instead.
+ To provide some context, given below is the class diagram of the `Model` component. See if you can figure out where the 'filtered list' of tasks is being tracked.
* :bulb: This may be a good time to read through the [`Model` component section of the DG](../DeveloperGuide.html#model-component)
@@ -231,15 +231,15 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
* {@code JsonSerializableAddressBook}.
*/
public JsonSerializableAddressBook(ReadOnlyAddressBook source) {
- persons.addAll(
- source.getPersonList()
+ tasks.addAll(
+ source.getTaskList()
.stream()
- .map(JsonAdaptedPerson::new)
+ .map(JsonAdaptedTask::new)
.collect(Collectors.toList()));
}
```
-1. It appears that a `JsonAdaptedPerson` is created for each `Person` and then added to the `JsonSerializableAddressBook`.
+1. It appears that a `JsonAdaptedTask` is created for each `Task` and then added to the `JsonSerializableAddressBook`.
This is because regular Java objects need to go through an _adaptation_ for them to be suitable to be saved in JSON format.
1. While you are stepping through the classes in the `Storage` component, here is the component's class diagram to help you understand how those classes fit into the structure of the component.
@@ -292,10 +292,10 @@ Here are some quick questions you can try to answer based on your execution path
2. Allow `delete` to remove more than one index at a time
- 3. Save the address book in the CSV format instead
+ 3. Save the task list in the CSV format instead
4. Add a new command
- 5. Add a new field to `Person`
+ 5. Add a new field to `Task`
- 6. Add a new entity to the address book
+ 6. Add a new entity to the task list
diff --git a/src/main/java/seedu/address/AppParameters.java b/src/main/java/profplan/AppParameters.java
similarity index 92%
rename from src/main/java/seedu/address/AppParameters.java
rename to src/main/java/profplan/AppParameters.java
index 3d603622d4e..7a9fdbea4b4 100644
--- a/src/main/java/seedu/address/AppParameters.java
+++ b/src/main/java/profplan/AppParameters.java
@@ -1,4 +1,4 @@
-package seedu.address;
+package profplan;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -7,9 +7,9 @@
import java.util.logging.Logger;
import javafx.application.Application;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.commons.util.FileUtil;
-import seedu.address.commons.util.ToStringBuilder;
+import profplan.commons.core.LogsCenter;
+import profplan.commons.util.FileUtil;
+import profplan.commons.util.ToStringBuilder;
/**
* Represents the parsed command-line parameters given to the application.
diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/profplan/Main.java
similarity index 96%
rename from src/main/java/seedu/address/Main.java
rename to src/main/java/profplan/Main.java
index ec1b7958746..d1ef4e37d39 100644
--- a/src/main/java/seedu/address/Main.java
+++ b/src/main/java/profplan/Main.java
@@ -1,9 +1,9 @@
-package seedu.address;
+package profplan;
import java.util.logging.Logger;
import javafx.application.Application;
-import seedu.address.commons.core.LogsCenter;
+import profplan.commons.core.LogsCenter;
/**
* The main entry point to the application.
diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/profplan/MainApp.java
similarity index 50%
rename from src/main/java/seedu/address/MainApp.java
rename to src/main/java/profplan/MainApp.java
index 3d6bd06d5af..cc821230739 100644
--- a/src/main/java/seedu/address/MainApp.java
+++ b/src/main/java/profplan/MainApp.java
@@ -1,4 +1,4 @@
-package seedu.address;
+package profplan;
import java.io.IOException;
import java.nio.file.Path;
@@ -7,36 +7,40 @@
import javafx.application.Application;
import javafx.stage.Stage;
-import seedu.address.commons.core.Config;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.commons.core.Version;
-import seedu.address.commons.exceptions.DataLoadingException;
-import seedu.address.commons.util.ConfigUtil;
-import seedu.address.commons.util.StringUtil;
-import seedu.address.logic.Logic;
-import seedu.address.logic.LogicManager;
-import seedu.address.model.AddressBook;
-import seedu.address.model.Model;
-import seedu.address.model.ModelManager;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.ReadOnlyUserPrefs;
-import seedu.address.model.UserPrefs;
-import seedu.address.model.util.SampleDataUtil;
-import seedu.address.storage.AddressBookStorage;
-import seedu.address.storage.JsonAddressBookStorage;
-import seedu.address.storage.JsonUserPrefsStorage;
-import seedu.address.storage.Storage;
-import seedu.address.storage.StorageManager;
-import seedu.address.storage.UserPrefsStorage;
-import seedu.address.ui.Ui;
-import seedu.address.ui.UiManager;
+import profplan.commons.core.Config;
+import profplan.commons.core.LogsCenter;
+import profplan.commons.core.Version;
+import profplan.commons.exceptions.DataLoadingException;
+import profplan.commons.util.ConfigUtil;
+import profplan.commons.util.StringUtil;
+import profplan.logic.Logic;
+import profplan.logic.LogicManager;
+import profplan.model.Model;
+import profplan.model.ModelManager;
+import profplan.model.ProfPlan;
+import profplan.model.ReadOnlyProfPlan;
+import profplan.model.ReadOnlyUserConfigs;
+import profplan.model.ReadOnlyUserPrefs;
+import profplan.model.UserConfigs;
+import profplan.model.UserPrefs;
+import profplan.model.util.SampleDataUtil;
+import profplan.storage.JsonProfPlanStorage;
+import profplan.storage.JsonUserConfigsStorage;
+import profplan.storage.JsonUserPrefsStorage;
+import profplan.storage.ProfPlanStorage;
+import profplan.storage.Storage;
+import profplan.storage.StorageManager;
+import profplan.storage.UserConfigsStorage;
+import profplan.storage.UserPrefsStorage;
+import profplan.ui.Ui;
+import profplan.ui.UiManager;
/**
* Runs the application.
*/
public class MainApp extends Application {
- public static final Version VERSION = new Version(0, 2, 2, true);
+ public static final Version VERSION = new Version(1, 3, 2, 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 ProfPlan ]===========================");
super.init();
AppParameters appParameters = AppParameters.parse(getParameters());
@@ -57,10 +61,12 @@ 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);
+ UserConfigsStorage userConfigsStorage = new JsonUserConfigsStorage(config.getUserConfigsFilePath());
+ UserConfigs userConfigs = initConfigs(userConfigsStorage);
+ ProfPlanStorage profPlanStorage = new JsonProfPlanStorage(userPrefs.getProfPlanFilePath());
+ storage = new StorageManager(profPlanStorage, userPrefsStorage, userConfigsStorage);
- model = initModelManager(storage, userPrefs);
+ model = initModelManager(storage, userPrefs, userConfigs);
logic = new LogicManager(model, storage);
@@ -68,29 +74,30 @@ public void init() throws Exception {
}
/**
- * Returns a {@code ModelManager} with the data from {@code storage}'s address book and {@code userPrefs}.
- * The data from the sample address book will be used instead if {@code storage}'s address book is not found,
- * or an empty address book will be used instead if errors occur when reading {@code storage}'s address book.
+ * Returns a {@code ModelManager} with the data from {@code storage}'s task list and {@code userPrefs}.
+ * The data from the sample task list will be used instead if {@code storage}'s task list is not found,
+ * or an empty task list will be used instead if errors occur when reading {@code storage}'s task list.
*/
- private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) {
- logger.info("Using data file : " + storage.getAddressBookFilePath());
+ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs,
+ ReadOnlyUserConfigs userConfigs) {
+ logger.info("Using data file : " + storage.getProfPlanFilePath());
- Optional addressBookOptional;
- ReadOnlyAddressBook initialData;
+ Optional profPlanOptional;
+ ReadOnlyProfPlan initialData;
try {
- addressBookOptional = storage.readAddressBook();
- if (!addressBookOptional.isPresent()) {
- logger.info("Creating a new data file " + storage.getAddressBookFilePath()
- + " populated with a sample AddressBook.");
+ profPlanOptional = storage.readProfPlan();
+ if (!profPlanOptional.isPresent()) {
+ logger.info("Creating a new data file " + storage.getProfPlanFilePath()
+ + " populated with a sample ProfPlan.");
}
- initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook);
+ initialData = profPlanOptional.orElseGet(SampleDataUtil::getSampleProfPlan);
} catch (DataLoadingException e) {
- logger.warning("Data file at " + storage.getAddressBookFilePath() + " could not be loaded."
- + " Will be starting with an empty AddressBook.");
- initialData = new AddressBook();
+ logger.warning("Data file at " + storage.getProfPlanFilePath() + " could not be loaded."
+ + " Will be starting with an empty ProfPlan.");
+ initialData = new ProfPlan();
}
- return new ModelManager(initialData, userPrefs);
+ return new ModelManager(initialData, userPrefs, userConfigs);
}
private void initLogging(Config config) {
@@ -168,19 +175,56 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) {
return initializedPrefs;
}
+ /**
+ * Returns a {@code UserConfigs} using the file at {@code storage}'s user settings file path,
+ * or a new {@code UserConfigs} with default configuration if errors occur when
+ * reading from the file.
+ */
+ protected UserConfigs initConfigs(UserConfigsStorage storage) {
+ Path configsFilePath = storage.getUserConfigsFilePath();
+ logger.info("Using settings file : " + configsFilePath);
+
+ UserConfigs initializedConfigs;
+ try {
+ Optional configsOptional = storage.readUserConfigs();
+ if (!configsOptional.isPresent()) {
+ logger.info("Creating new config file " + configsFilePath);
+ }
+ initializedConfigs = configsOptional.orElse(new UserConfigs());
+ } catch (DataLoadingException e) {
+ logger.warning("Settings file at " + configsFilePath + " could not be loaded."
+ + " Using default settings.");
+ initializedConfigs = new UserConfigs();
+ }
+
+ //Update prefs file in case it was missing to begin with or there are new/unused fields
+ try {
+ storage.saveUserConfigs(initializedConfigs);
+ } catch (IOException e) {
+ logger.warning("Failed to save settings file : " + StringUtil.getDetails(e));
+ }
+
+ return initializedConfigs;
+ }
+
@Override
public void start(Stage primaryStage) {
- logger.info("Starting AddressBook " + MainApp.VERSION);
+ logger.info("Starting ProfPlan " + MainApp.VERSION);
ui.start(primaryStage);
}
@Override
public void stop() {
- logger.info("============================ [ Stopping Address Book ] =============================");
+ logger.info("============================ [ Stopping ProfPlan ] =============================");
try {
storage.saveUserPrefs(model.getUserPrefs());
} catch (IOException e) {
- logger.severe("Failed to save preferences " + StringUtil.getDetails(e));
+ logger.severe("Failed to save user preferences " + StringUtil.getDetails(e));
+ }
+ try {
+ storage.saveUserConfigs(ModelManager.getUserConfigs());
+ } catch (IOException e) {
+ logger.severe("Failed to save user configs " + StringUtil.getDetails(e));
}
}
}
diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/profplan/commons/core/Config.java
similarity index 81%
rename from src/main/java/seedu/address/commons/core/Config.java
rename to src/main/java/profplan/commons/core/Config.java
index 485f85a5e05..f7bef90a30a 100644
--- a/src/main/java/seedu/address/commons/core/Config.java
+++ b/src/main/java/profplan/commons/core/Config.java
@@ -1,11 +1,11 @@
-package seedu.address.commons.core;
+package profplan.commons.core;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.logging.Level;
-import seedu.address.commons.util.ToStringBuilder;
+import profplan.commons.util.ToStringBuilder;
/**
* Config values used by the app
@@ -17,6 +17,7 @@ public class Config {
// Config values customizable through config file
private Level logLevel = Level.INFO;
private Path userPrefsFilePath = Paths.get("preferences.json");
+ private Path userConfigsFilePath = Paths.get("settings.json");
public Level getLogLevel() {
return logLevel;
@@ -33,6 +34,12 @@ public Path getUserPrefsFilePath() {
public void setUserPrefsFilePath(Path userPrefsFilePath) {
this.userPrefsFilePath = userPrefsFilePath;
}
+ public Path getUserConfigsFilePath() {
+ return userConfigsFilePath;
+ }
+ public void setUserConfigsFilePath(Path userConfigsFilePath) {
+ this.userConfigsFilePath = userConfigsFilePath;
+ }
@Override
public boolean equals(Object other) {
diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/profplan/commons/core/GuiSettings.java
similarity index 96%
rename from src/main/java/seedu/address/commons/core/GuiSettings.java
rename to src/main/java/profplan/commons/core/GuiSettings.java
index a97a86ee8d7..1e7b1ec3672 100644
--- a/src/main/java/seedu/address/commons/core/GuiSettings.java
+++ b/src/main/java/profplan/commons/core/GuiSettings.java
@@ -1,10 +1,10 @@
-package seedu.address.commons.core;
+package profplan.commons.core;
import java.awt.Point;
import java.io.Serializable;
import java.util.Objects;
-import seedu.address.commons.util.ToStringBuilder;
+import profplan.commons.util.ToStringBuilder;
/**
* A Serializable class that contains the GUI settings.
diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/profplan/commons/core/LogsCenter.java
similarity index 99%
rename from src/main/java/seedu/address/commons/core/LogsCenter.java
rename to src/main/java/profplan/commons/core/LogsCenter.java
index 8cf8e15a0f0..62486d061a5 100644
--- a/src/main/java/seedu/address/commons/core/LogsCenter.java
+++ b/src/main/java/profplan/commons/core/LogsCenter.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.core;
+package profplan.commons.core;
import static java.util.Objects.requireNonNull;
diff --git a/src/main/java/profplan/commons/core/Settings.java b/src/main/java/profplan/commons/core/Settings.java
new file mode 100644
index 00000000000..27987f01505
--- /dev/null
+++ b/src/main/java/profplan/commons/core/Settings.java
@@ -0,0 +1,73 @@
+package profplan.commons.core;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+import profplan.commons.util.ToStringBuilder;
+import profplan.logic.parser.Prefix;
+
+/**
+ * A Serializable class that contains the general settings.
+ * Guarantees: immutable.
+ */
+public class Settings implements Serializable {
+
+ // array containing all keywords used to specify these fields. contain these keywords in Prefixes.
+ public static final Prefix[] KEYWORDS = {new Prefix("semesterDays")};
+
+ // misc.
+ public static final String SEMESTER_DAYS_CONSTRAINTS = "The parameter semesterDays must be a non-negative integer.";
+
+ // default values
+ private static final int DEFAULT_SEMESTER_DAYS = 180;
+
+ // fields
+ private final int semesterDays;
+
+ /**
+ * Constructs a {@code Settings} with default settings.
+ */
+ public Settings() {
+ semesterDays = DEFAULT_SEMESTER_DAYS;
+ }
+
+ /**
+ * Constructs a {@code Settings} with the specified settings.
+ */
+ public Settings(int semesterDays) {
+ this.semesterDays = semesterDays;
+ }
+
+ // getters
+ public int getSemesterDays() {
+ return semesterDays;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Settings)) {
+ return false;
+ }
+
+ Settings otherSettings = (Settings) other;
+ return this.semesterDays == otherSettings.semesterDays;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(semesterDays);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("semesterDays", semesterDays)
+ .toString();
+ }
+
+}
diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/profplan/commons/core/Version.java
similarity index 98%
rename from src/main/java/seedu/address/commons/core/Version.java
rename to src/main/java/profplan/commons/core/Version.java
index 491d24559b4..569f65fa1bc 100644
--- a/src/main/java/seedu/address/commons/core/Version.java
+++ b/src/main/java/profplan/commons/core/Version.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.core;
+package profplan.commons.core;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
diff --git a/src/main/java/seedu/address/commons/core/index/Index.java b/src/main/java/profplan/commons/core/index/Index.java
similarity index 95%
rename from src/main/java/seedu/address/commons/core/index/Index.java
rename to src/main/java/profplan/commons/core/index/Index.java
index dd170d8b68d..97c1b62ea77 100644
--- a/src/main/java/seedu/address/commons/core/index/Index.java
+++ b/src/main/java/profplan/commons/core/index/Index.java
@@ -1,6 +1,6 @@
-package seedu.address.commons.core.index;
+package profplan.commons.core.index;
-import seedu.address.commons.util.ToStringBuilder;
+import profplan.commons.util.ToStringBuilder;
/**
* Represents a zero-based or one-based index.
diff --git a/src/main/java/seedu/address/commons/exceptions/DataLoadingException.java b/src/main/java/profplan/commons/exceptions/DataLoadingException.java
similarity index 82%
rename from src/main/java/seedu/address/commons/exceptions/DataLoadingException.java
rename to src/main/java/profplan/commons/exceptions/DataLoadingException.java
index 9904ba47afe..79c259946b7 100644
--- a/src/main/java/seedu/address/commons/exceptions/DataLoadingException.java
+++ b/src/main/java/profplan/commons/exceptions/DataLoadingException.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.exceptions;
+package profplan.commons.exceptions;
/**
* Represents an error during loading of data from a file.
diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java b/src/main/java/profplan/commons/exceptions/IllegalValueException.java
similarity index 93%
rename from src/main/java/seedu/address/commons/exceptions/IllegalValueException.java
rename to src/main/java/profplan/commons/exceptions/IllegalValueException.java
index 19124db485c..9a21c37732f 100644
--- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java
+++ b/src/main/java/profplan/commons/exceptions/IllegalValueException.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.exceptions;
+package profplan.commons.exceptions;
/**
* Signals that some given data does not fulfill some constraints.
diff --git a/src/main/java/seedu/address/commons/util/AppUtil.java b/src/main/java/profplan/commons/util/AppUtil.java
similarity index 94%
rename from src/main/java/seedu/address/commons/util/AppUtil.java
rename to src/main/java/profplan/commons/util/AppUtil.java
index 87aa89c0326..9240b2567b6 100644
--- a/src/main/java/seedu/address/commons/util/AppUtil.java
+++ b/src/main/java/profplan/commons/util/AppUtil.java
@@ -1,9 +1,9 @@
-package seedu.address.commons.util;
+package profplan.commons.util;
import static java.util.Objects.requireNonNull;
import javafx.scene.image.Image;
-import seedu.address.MainApp;
+import profplan.MainApp;
/**
* A container for App specific utility functions
diff --git a/src/main/java/seedu/address/commons/util/CollectionUtil.java b/src/main/java/profplan/commons/util/CollectionUtil.java
similarity index 96%
rename from src/main/java/seedu/address/commons/util/CollectionUtil.java
rename to src/main/java/profplan/commons/util/CollectionUtil.java
index eafe4dfd681..0d8f33e9fb4 100644
--- a/src/main/java/seedu/address/commons/util/CollectionUtil.java
+++ b/src/main/java/profplan/commons/util/CollectionUtil.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.util;
+package profplan.commons.util;
import static java.util.Objects.requireNonNull;
diff --git a/src/main/java/seedu/address/commons/util/ConfigUtil.java b/src/main/java/profplan/commons/util/ConfigUtil.java
similarity index 77%
rename from src/main/java/seedu/address/commons/util/ConfigUtil.java
rename to src/main/java/profplan/commons/util/ConfigUtil.java
index 7b829c3c4cc..222d0ed6495 100644
--- a/src/main/java/seedu/address/commons/util/ConfigUtil.java
+++ b/src/main/java/profplan/commons/util/ConfigUtil.java
@@ -1,11 +1,11 @@
-package seedu.address.commons.util;
+package profplan.commons.util;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
-import seedu.address.commons.core.Config;
-import seedu.address.commons.exceptions.DataLoadingException;
+import profplan.commons.core.Config;
+import profplan.commons.exceptions.DataLoadingException;
/**
* A class for accessing the Config File.
diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/profplan/commons/util/FileUtil.java
similarity index 98%
rename from src/main/java/seedu/address/commons/util/FileUtil.java
rename to src/main/java/profplan/commons/util/FileUtil.java
index b1e2767cdd9..bb950cc4fc1 100644
--- a/src/main/java/seedu/address/commons/util/FileUtil.java
+++ b/src/main/java/profplan/commons/util/FileUtil.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.util;
+package profplan.commons.util;
import java.io.IOException;
import java.nio.file.Files;
diff --git a/src/main/java/seedu/address/commons/util/JsonUtil.java b/src/main/java/profplan/commons/util/JsonUtil.java
similarity index 97%
rename from src/main/java/seedu/address/commons/util/JsonUtil.java
rename to src/main/java/profplan/commons/util/JsonUtil.java
index 100cb16c395..2ff195580d3 100644
--- a/src/main/java/seedu/address/commons/util/JsonUtil.java
+++ b/src/main/java/profplan/commons/util/JsonUtil.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.util;
+package profplan.commons.util;
import static java.util.Objects.requireNonNull;
@@ -20,8 +20,8 @@
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.commons.exceptions.DataLoadingException;
+import profplan.commons.core.LogsCenter;
+import profplan.commons.exceptions.DataLoadingException;
/**
* Converts a Java object instance to JSON and vice versa
diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/profplan/commons/util/StringUtil.java
similarity index 95%
rename from src/main/java/seedu/address/commons/util/StringUtil.java
rename to src/main/java/profplan/commons/util/StringUtil.java
index 61cc8c9a1cb..f3588be5675 100644
--- a/src/main/java/seedu/address/commons/util/StringUtil.java
+++ b/src/main/java/profplan/commons/util/StringUtil.java
@@ -1,7 +1,7 @@
-package seedu.address.commons.util;
+package profplan.commons.util;
import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
+import static profplan.commons.util.AppUtil.checkArgument;
import java.io.PrintWriter;
import java.io.StringWriter;
diff --git a/src/main/java/seedu/address/commons/util/ToStringBuilder.java b/src/main/java/profplan/commons/util/ToStringBuilder.java
similarity index 97%
rename from src/main/java/seedu/address/commons/util/ToStringBuilder.java
rename to src/main/java/profplan/commons/util/ToStringBuilder.java
index d979b926734..4236cde6efc 100644
--- a/src/main/java/seedu/address/commons/util/ToStringBuilder.java
+++ b/src/main/java/profplan/commons/util/ToStringBuilder.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.util;
+package profplan.commons.util;
/**
* Builds a string representation of an object that is suitable as the return value of {@link Object#toString()}.
diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/profplan/logic/Logic.java
similarity index 53%
rename from src/main/java/seedu/address/logic/Logic.java
rename to src/main/java/profplan/logic/Logic.java
index 92cd8fa605a..d102af80ca1 100644
--- a/src/main/java/seedu/address/logic/Logic.java
+++ b/src/main/java/profplan/logic/Logic.java
@@ -1,14 +1,15 @@
-package seedu.address.logic;
+package profplan.logic;
import java.nio.file.Path;
import javafx.collections.ObservableList;
-import seedu.address.commons.core.GuiSettings;
-import seedu.address.logic.commands.CommandResult;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Person;
+import profplan.commons.core.GuiSettings;
+import profplan.logic.commands.CommandResult;
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.logic.parser.exceptions.ParseException;
+import profplan.model.Model;
+import profplan.model.ReadOnlyProfPlan;
+import profplan.model.task.Task;
/**
* API of the Logic component
@@ -24,19 +25,19 @@ public interface Logic {
CommandResult execute(String commandText) throws CommandException, ParseException;
/**
- * Returns the AddressBook.
+ * Returns the ProfPlan.
*
- * @see seedu.address.model.Model#getAddressBook()
+ * @see Model#getProfPlan()
*/
- ReadOnlyAddressBook getAddressBook();
+ ReadOnlyProfPlan getProfPlan();
- /** 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.
+ * Returns the user prefs' task list file path.
*/
- Path getAddressBookFilePath();
+ Path getProfPlanFilePath();
/**
* Returns the user prefs' GUI settings.
diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/profplan/logic/LogicManager.java
similarity index 62%
rename from src/main/java/seedu/address/logic/LogicManager.java
rename to src/main/java/profplan/logic/LogicManager.java
index 5aa3b91c7d0..f42293c9d5c 100644
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ b/src/main/java/profplan/logic/LogicManager.java
@@ -1,4 +1,4 @@
-package seedu.address.logic;
+package profplan.logic;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
@@ -6,17 +6,17 @@
import java.util.logging.Logger;
import javafx.collections.ObservableList;
-import seedu.address.commons.core.GuiSettings;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.logic.commands.Command;
-import seedu.address.logic.commands.CommandResult;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.logic.parser.AddressBookParser;
-import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.Model;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Person;
-import seedu.address.storage.Storage;
+import profplan.commons.core.GuiSettings;
+import profplan.commons.core.LogsCenter;
+import profplan.logic.commands.Command;
+import profplan.logic.commands.CommandResult;
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.logic.parser.ProfPlanParser;
+import profplan.logic.parser.exceptions.ParseException;
+import profplan.model.Model;
+import profplan.model.ReadOnlyProfPlan;
+import profplan.model.task.Task;
+import profplan.storage.Storage;
/**
* The main LogicManager of the app.
@@ -31,7 +31,7 @@ public class LogicManager implements Logic {
private final Model model;
private final Storage storage;
- private final AddressBookParser addressBookParser;
+ private final ProfPlanParser profPlanParser;
/**
* Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}.
@@ -39,7 +39,7 @@ public class LogicManager implements Logic {
public LogicManager(Model model, Storage storage) {
this.model = model;
this.storage = storage;
- addressBookParser = new AddressBookParser();
+ profPlanParser = new ProfPlanParser();
}
@Override
@@ -47,11 +47,11 @@ public CommandResult execute(String commandText) throws CommandException, ParseE
logger.info("----------------[USER COMMAND][" + commandText + "]");
CommandResult commandResult;
- Command command = addressBookParser.parseCommand(commandText);
+ Command command = profPlanParser.parseCommand(commandText);
commandResult = command.execute(model);
try {
- storage.saveAddressBook(model.getAddressBook());
+ storage.saveProfPlan(model.getProfPlan());
} catch (AccessDeniedException e) {
throw new CommandException(String.format(FILE_OPS_PERMISSION_ERROR_FORMAT, e.getMessage()), e);
} catch (IOException ioe) {
@@ -62,18 +62,18 @@ public CommandResult execute(String commandText) throws CommandException, ParseE
}
@Override
- public ReadOnlyAddressBook getAddressBook() {
- return model.getAddressBook();
+ public ReadOnlyProfPlan getProfPlan() {
+ return model.getProfPlan();
}
@Override
- public ObservableList getFilteredPersonList() {
- return model.getFilteredPersonList();
+ public ObservableList getFilteredTaskList() {
+ return model.getFilteredTaskList();
}
@Override
- public Path getAddressBookFilePath() {
- return model.getAddressBookFilePath();
+ public Path getProfPlanFilePath() {
+ return model.getProfPlanFilePath();
}
@Override
diff --git a/src/main/java/profplan/logic/Messages.java b/src/main/java/profplan/logic/Messages.java
new file mode 100644
index 00000000000..ddae6bde0d2
--- /dev/null
+++ b/src/main/java/profplan/logic/Messages.java
@@ -0,0 +1,61 @@
+package profplan.logic;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import profplan.logic.commands.AddCommand;
+import profplan.logic.parser.Prefix;
+import profplan.model.task.Task;
+
+/**
+ * Container for user visible messages.
+ */
+public class Messages {
+
+ public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command";
+ public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s";
+ public static final String MESSAGE_INVALID_TASK_DISPLAYED_INDEX = "The task index provided is invalid";
+ public static final String MESSAGE_TASKS_LISTED_OVERVIEW = "%1$d tasks listed!";
+
+ public static final String MESSAGE_ADD_COMMAND_INVALID = "Invalid command format! \n"
+ + "add: Adds a task to the task list.\n"
+ + "Compulsory Parameters: n/[name] p/[priority] d/[dueDate] \n"
+ + "Optional Parameters: recur/[recur] t/[tag...] l/[link] des/[description]\n"
+ + "Example: add n/Grade assignments p/1 t/assignment t/grade d/01-01-2023 l/www.example.com";
+ public static final String MESSAGE_DUPLICATE_FIELDS =
+ "Multiple values specified for the following single-valued field(s): ";
+ public static final String MESSAGE_INVALID_COMMAND_WORD = "The command word specified is not a command";
+
+ /**
+ * Returns an error message indicating the duplicate prefixes.
+ */
+ public static String getErrorMessageForDuplicatePrefixes(Prefix... duplicatePrefixes) {
+ assert duplicatePrefixes.length > 0;
+
+ Set duplicateFields =
+ Stream.of(duplicatePrefixes).map(Prefix::toString).collect(Collectors.toSet());
+
+ return String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_FULL_HELP);
+ }
+
+ /**
+ * Formats the {@code task} for display to the user.
+ */
+ public static String format(Task task) {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(task.getName())
+ .append("; Priority: ")
+ .append(task.getPriority())
+ .append("; Status: ")
+ .append(task.getStatus())
+ .append("; Tags: ");
+ task.getTags().forEach(builder::append);
+ builder.append("; DueDate: ")
+ .append(task.getDueDate())
+ .append("; Link: ")
+ .append(task.getLink());
+ return builder.toString();
+ }
+
+}
diff --git a/src/main/java/profplan/logic/commands/AddCommand.java b/src/main/java/profplan/logic/commands/AddCommand.java
new file mode 100644
index 00000000000..87ff666279c
--- /dev/null
+++ b/src/main/java/profplan/logic/commands/AddCommand.java
@@ -0,0 +1,90 @@
+package profplan.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static profplan.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+import static profplan.logic.parser.CliSyntax.PREFIX_DUEDATE;
+import static profplan.logic.parser.CliSyntax.PREFIX_LINK;
+import static profplan.logic.parser.CliSyntax.PREFIX_NAME;
+import static profplan.logic.parser.CliSyntax.PREFIX_PRIORITY;
+import static profplan.logic.parser.CliSyntax.PREFIX_RECURRING;
+import static profplan.logic.parser.CliSyntax.PREFIX_TAG;
+
+import profplan.commons.util.ToStringBuilder;
+import profplan.logic.Messages;
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.model.Model;
+import profplan.model.task.Task;
+
+/**
+ * Adds a task to the task list.
+ */
+public class AddCommand extends Command {
+
+ public static final String COMMAND_WORD = "add";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a task to the task list.";
+ public static final String MESSAGE_DETAILS = "Compulsory Parameters: "
+ + PREFIX_NAME + "[name] "
+ + PREFIX_PRIORITY + "[priority] "
+ + PREFIX_DUEDATE + "[dueDate] "
+ + "\nOptional Parameters: "
+ + PREFIX_RECURRING + "[recur] "
+ + PREFIX_TAG + "[tag...] "
+ + PREFIX_LINK + "[link] "
+ + PREFIX_DESCRIPTION + "[description]";
+ public static final String MESSAGE_EXAMPLE = "Example: " + COMMAND_WORD + " "
+ + PREFIX_NAME + "Grade assignments "
+ + PREFIX_PRIORITY + "1 "
+ + PREFIX_TAG + "assignment "
+ + PREFIX_TAG + "grade "
+ + PREFIX_DUEDATE + "01-01-2023 "
+ + PREFIX_LINK + "www.example.com";
+ public static final String MESSAGE_FULL_HELP = MESSAGE_USAGE + "\n" + MESSAGE_DETAILS + "\n" + MESSAGE_EXAMPLE;
+
+ public static final String MESSAGE_SUCCESS = "New task added: %1$s";
+ public static final String MESSAGE_DUPLICATE_TASK = "This task already exists in the task list";
+
+ private final Task toAdd;
+
+ /**
+ * Creates an AddCommand to add the specified {@code Task}
+ */
+ public AddCommand(Task task) {
+ requireNonNull(task);
+ toAdd = task;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ if (model.hasTask(toAdd)) {
+ throw new CommandException(MESSAGE_DUPLICATE_TASK);
+ }
+
+ model.addTask(toAdd);
+ return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(toAdd)));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof AddCommand)) {
+ return false;
+ }
+
+ AddCommand otherAddCommand = (AddCommand) other;
+ return toAdd.equals(otherAddCommand.toAdd);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("toAdd", toAdd)
+ .toString();
+ }
+}
diff --git a/src/main/java/profplan/logic/commands/ClearCommand.java b/src/main/java/profplan/logic/commands/ClearCommand.java
new file mode 100644
index 00000000000..d5ae90ddbde
--- /dev/null
+++ b/src/main/java/profplan/logic/commands/ClearCommand.java
@@ -0,0 +1,24 @@
+package profplan.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import profplan.model.Model;
+import profplan.model.ProfPlan;
+
+/**
+ * Clears the tasklist.
+ */
+public class ClearCommand extends Command {
+
+ public static final String COMMAND_WORD = "clear";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Clears the entire task list. This is IRREVERSIBLE.";
+ public static final String MESSAGE_SUCCESS = "All Tasks Deleted Successfully Prof!";
+
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.setProfPlan(new ProfPlan());
+ return new CommandResult(MESSAGE_SUCCESS);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/profplan/logic/commands/Command.java
similarity index 78%
rename from src/main/java/seedu/address/logic/commands/Command.java
rename to src/main/java/profplan/logic/commands/Command.java
index 64f18992160..4b1a490b86e 100644
--- a/src/main/java/seedu/address/logic/commands/Command.java
+++ b/src/main/java/profplan/logic/commands/Command.java
@@ -1,7 +1,7 @@
-package seedu.address.logic.commands;
+package profplan.logic.commands;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.model.Model;
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.model.Model;
/**
* Represents a command with hidden internal logic and the ability to be executed.
diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/profplan/logic/commands/CommandResult.java
similarity index 95%
rename from src/main/java/seedu/address/logic/commands/CommandResult.java
rename to src/main/java/profplan/logic/commands/CommandResult.java
index 249b6072d0d..c10ca4f2ec1 100644
--- a/src/main/java/seedu/address/logic/commands/CommandResult.java
+++ b/src/main/java/profplan/logic/commands/CommandResult.java
@@ -1,10 +1,10 @@
-package seedu.address.logic.commands;
+package profplan.logic.commands;
import static java.util.Objects.requireNonNull;
import java.util.Objects;
-import seedu.address.commons.util.ToStringBuilder;
+import profplan.commons.util.ToStringBuilder;
/**
* Represents the result of a command execution.
diff --git a/src/main/java/profplan/logic/commands/DeleteCommand.java b/src/main/java/profplan/logic/commands/DeleteCommand.java
new file mode 100644
index 00000000000..3968c6bb161
--- /dev/null
+++ b/src/main/java/profplan/logic/commands/DeleteCommand.java
@@ -0,0 +1,93 @@
+package profplan.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import profplan.commons.core.index.Index;
+import profplan.commons.util.ToStringBuilder;
+import profplan.logic.Messages;
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.model.Model;
+import profplan.model.task.Task;
+
+/**
+ * Deletes a task identified using its displayed index from the task list.
+ */
+public class DeleteCommand extends Command {
+
+ public static final String COMMAND_WORD = "delete";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Deletes the task identified by the index number used in the displayed task list.";
+ public static final String MESSAGE_EXAMPLE = "Example: " + COMMAND_WORD + " 1";
+ public static final String MESSAGE_DETAILS = "Parameters: INDEX (must be a positive integer)\n"
+ + "Tip: Use \"delete all\" to delete all tasks. "
+ + "Be careful, this is IRREVERSIBLE.";
+ public static final String MESSAGE_FULL_HELP = MESSAGE_USAGE + "\n" + MESSAGE_DETAILS + "\n" + MESSAGE_EXAMPLE;
+
+ public static final String MESSAGE_DELETE_TASK_SUCCESS = "Deleted Task: %1$s";
+
+ public static final String MESSAGE_DELETE_ALL_TASKS_SUCCESS = "All Tasks Deleted Successfully Prof!";
+
+ private final Index targetIndex;
+
+ public DeleteCommand(Index targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ public DeleteCommand() {
+ this.targetIndex = null;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredTaskList();
+
+ if (targetIndex == null && lastShownList.size() != 0) {
+ model.deleteTask();
+ return new CommandResult(MESSAGE_DELETE_ALL_TASKS_SUCCESS);
+ }
+
+ if (targetIndex == null && lastShownList.size() == 0) {
+ return new CommandResult("Can not delete all tasks in empty Task List");
+ }
+
+ if (targetIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ Task taskToDelete = lastShownList.get(targetIndex.getZeroBased());
+ model.deleteTask(taskToDelete);
+ return new CommandResult(String.format(MESSAGE_DELETE_TASK_SUCCESS, Messages.format(taskToDelete)));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof DeleteCommand)) {
+ return false;
+ }
+
+ DeleteCommand otherDeleteCommand = (DeleteCommand) other;
+ if (targetIndex == null && otherDeleteCommand.targetIndex == null) {
+ return true;
+ } else if (targetIndex != null && otherDeleteCommand.targetIndex != null) {
+ return targetIndex.equals(otherDeleteCommand.targetIndex);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetIndex", targetIndex)
+ .toString();
+ }
+}
diff --git a/src/main/java/profplan/logic/commands/DescriptionCommand.java b/src/main/java/profplan/logic/commands/DescriptionCommand.java
new file mode 100644
index 00000000000..6ed1b8fc63f
--- /dev/null
+++ b/src/main/java/profplan/logic/commands/DescriptionCommand.java
@@ -0,0 +1,91 @@
+package profplan.logic.commands;
+
+import static profplan.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.List;
+
+import profplan.commons.core.index.Index;
+import profplan.logic.Messages;
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.model.Model;
+import profplan.model.task.Description;
+import profplan.model.task.Task;
+
+/**
+ * Adds a description to a Task.
+ */
+public class DescriptionCommand extends Command {
+
+ public static final String COMMAND_WORD = "description";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Edits the description of the task identified "
+ + "by the index number used in the last task listing.";
+ public static final String MESSAGE_DETAILS = "Existing description will be overwritten by the input.\n"
+ + "Parameters: [index] (must be a positive integer) "
+ + "des/ [description]";
+ public static final String MESSAGE_EXAMPLE = "Example: " + COMMAND_WORD + " 1 "
+ + "des/ Put particular emphasis on recursion.";
+ public static final String MESSAGE_FULL_HELP = MESSAGE_USAGE + "\n" + MESSAGE_DETAILS + "\n" + MESSAGE_EXAMPLE;
+
+ public static final String MESSAGE_ADD_DESCRIPTION_SUCCESS = "Added description to Task %d";
+ public static final String MESSAGE_DELETE_DESCRIPTION_SUCCESS = "Removed description from Task: %d";
+
+ private final Index index;
+ private final Description description;
+
+ /**
+ * @param index The index of the task to attach the description to.
+ * @param description The description to be added to the task.
+ */
+ public DescriptionCommand(Index index, Description description) {
+ requireAllNonNull(index, description);
+
+ this.index = index;
+ this.description = description;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ List lastShownList = model.getFilteredTaskList();
+
+ if (index.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ Task taskToEdit = lastShownList.get(index.getZeroBased());
+ Task editedTask = new Task(taskToEdit.getName(), taskToEdit.getPriority(),
+ taskToEdit.getIsRecurring(), taskToEdit.getRecurringType(),
+ taskToEdit.getTags(), taskToEdit.getDueDate(),
+ taskToEdit.getLink(), description);
+
+ model.setTask(taskToEdit, editedTask);
+ model.updateFilteredTaskList(Model.PREDICATE_SHOW_ALL_TASKS);
+
+ return new CommandResult(generateSuccessMessage(index.getOneBased()));
+ }
+
+ /**
+ * Generates a command execution success message based on whether
+ * the description is added to or removed from {@code taskToEdit}.
+ */
+ private String generateSuccessMessage(int index) {
+ String message = !description.description.isEmpty() ? MESSAGE_ADD_DESCRIPTION_SUCCESS
+ : MESSAGE_DELETE_DESCRIPTION_SUCCESS;
+ return String.format(message, index);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof DescriptionCommand)) {
+ return false;
+ }
+
+ DescriptionCommand descriptionCommand = (DescriptionCommand) other;
+ return index.equals(descriptionCommand.index) && description.equals(descriptionCommand.description);
+ }
+}
diff --git a/src/main/java/profplan/logic/commands/DoNextCommand.java b/src/main/java/profplan/logic/commands/DoNextCommand.java
new file mode 100644
index 00000000000..21e36522af8
--- /dev/null
+++ b/src/main/java/profplan/logic/commands/DoNextCommand.java
@@ -0,0 +1,32 @@
+package profplan.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.model.Model;
+import profplan.model.task.Task;
+
+/**
+ * Recommends the prof. which task to do next, based on the priority as well as urgency.
+ */
+public class DoNextCommand extends Command {
+
+ public static final String COMMAND_WORD = "do_next";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Recommends the next task to do."
+ + "Example: " + COMMAND_WORD + "";
+ public static final String MESSAGE_FULL_HELP = MESSAGE_USAGE;
+ public static final String MESSAGE_SUCCESS = " Here is the next task you need to do Prof:";
+
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ Task t = model.getDoNextTask();
+ if (t == null) {
+ return new CommandResult("No more tasks to do Prof! Skies are clear ahead :)");
+ } else {
+ return new CommandResult(MESSAGE_SUCCESS + "\n" + t.beautifyString());
+ }
+ }
+}
diff --git a/src/main/java/profplan/logic/commands/EditCommand.java b/src/main/java/profplan/logic/commands/EditCommand.java
new file mode 100644
index 00000000000..16491166309
--- /dev/null
+++ b/src/main/java/profplan/logic/commands/EditCommand.java
@@ -0,0 +1,246 @@
+package profplan.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static profplan.logic.parser.CliSyntax.PREFIX_DUEDATE;
+import static profplan.logic.parser.CliSyntax.PREFIX_LINK;
+import static profplan.logic.parser.CliSyntax.PREFIX_NAME;
+import static profplan.logic.parser.CliSyntax.PREFIX_PRIORITY;
+import static profplan.logic.parser.CliSyntax.PREFIX_TAG;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+import profplan.commons.core.index.Index;
+import profplan.commons.util.CollectionUtil;
+import profplan.commons.util.ToStringBuilder;
+import profplan.logic.Messages;
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.model.Model;
+import profplan.model.tag.Tag;
+import profplan.model.task.DueDate;
+import profplan.model.task.Link;
+import profplan.model.task.Name;
+import profplan.model.task.Priority;
+import profplan.model.task.Task;
+
+
+
+/**
+ * Edits the details of an existing task in the task list.
+ */
+public class EditCommand extends Command {
+
+ public static final String COMMAND_WORD = "edit";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the task identified "
+ + "by the index number used in the displayed task list. ";
+
+ public static final String MESSAGE_DETAILS = "Existing values will be overwritten by the input values.\n"
+ + "Parameters: [index] "
+ + PREFIX_NAME + "[name] "
+ + PREFIX_PRIORITY + "[priority] "
+ + PREFIX_DUEDATE + "[dueDate] "
+ + PREFIX_TAG + "[tag...] "
+ + PREFIX_LINK + "[link] ";
+
+ public static final String MESSAGE_EXAMPLE = "Example: " + COMMAND_WORD + " 1 "
+ + PREFIX_PRIORITY + "4 ";
+ public static final String MESSAGE_FULL_HELP = MESSAGE_USAGE + "\n" + MESSAGE_DETAILS + "\n" + MESSAGE_EXAMPLE;
+
+ public static final String MESSAGE_EDIT_TASK_SUCCESS = "Edited Task: %1$s";
+ public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided.";
+ public static final String MESSAGE_DUPLICATE_TASK = "This task already exists in the task list.";
+
+ private final Index index;
+ private final EditTaskDescriptor editTaskDescriptor;
+
+ /**
+ * @param index of the task in the filtered task list to edit
+ * @param editTaskDescriptor details to edit the task with
+ */
+ public EditCommand(Index index, EditTaskDescriptor editTaskDescriptor) {
+ requireNonNull(index);
+ requireNonNull(editTaskDescriptor);
+
+ this.index = index;
+ this.editTaskDescriptor = new EditTaskDescriptor(editTaskDescriptor);
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredTaskList();
+
+ if (index.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ Task taskToEdit = lastShownList.get(index.getZeroBased());
+ Task editedTask = createEditedTask(taskToEdit, editTaskDescriptor);
+
+ if (!taskToEdit.isSameTask(editedTask) && model.hasTask(editedTask)) {
+ throw new CommandException(MESSAGE_DUPLICATE_TASK);
+ }
+
+ model.setTask(taskToEdit, editedTask);
+ model.updateFilteredTaskList(Model.PREDICATE_SHOW_ALL_TASKS);
+ return new CommandResult(String.format(MESSAGE_EDIT_TASK_SUCCESS, Messages.format(editedTask)));
+ }
+
+ /**
+ * Creates and returns a {@code Task} with the details of {@code taskToEdit}
+ * edited with {@code editTaskDescriptor}.
+ */
+ private static Task createEditedTask(Task taskToEdit, EditTaskDescriptor editTaskDescriptor) {
+ assert taskToEdit != null;
+
+ Name updatedName = editTaskDescriptor.getName().orElse(taskToEdit.getName());
+ Priority updatedPriority = editTaskDescriptor.getPriority().orElse(taskToEdit.getPriority());
+ Link updatedLink = editTaskDescriptor.getLink().orElse(taskToEdit.getLink());
+ Set updatedTags = editTaskDescriptor.getTags().orElse(taskToEdit.getTags());
+ DueDate updatedDueDate = editTaskDescriptor.getDueDate().orElse(taskToEdit.getDueDate());
+ return new Task(updatedName, updatedPriority, taskToEdit.getIsRecurring(),
+ taskToEdit.getRecurringType(), updatedTags,
+ updatedDueDate, updatedLink, taskToEdit.getDescription());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditCommand)) {
+ return false;
+ }
+
+ EditCommand otherEditCommand = (EditCommand) other;
+ return index.equals(otherEditCommand.index)
+ && editTaskDescriptor.equals(otherEditCommand.editTaskDescriptor);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("index", index)
+ .add("editTaskDescriptor", editTaskDescriptor)
+ .toString();
+ }
+
+ /**
+ * Stores the details to edit the task with. Each non-empty field value will replace the
+ * corresponding field value of the task.
+ */
+ public static class EditTaskDescriptor {
+ private Name name;
+ private Priority priority;
+ private Set tags;
+ private DueDate dueDate;
+ private Link link;
+
+ public EditTaskDescriptor() {}
+
+ /**
+ * Copy constructor.
+ * A defensive copy of {@code tags} is used internally.
+ */
+ public EditTaskDescriptor(EditTaskDescriptor toCopy) {
+ setName(toCopy.name);
+ setPriority(toCopy.priority);
+ setLink(toCopy.link);
+ setTags(toCopy.tags);
+ setDueDate(toCopy.dueDate);
+ }
+
+ /**
+ * Returns true if at least one field is edited.
+ */
+ public boolean isAnyFieldEdited() {
+ return CollectionUtil.isAnyNonNull(name, priority, tags, dueDate, link);
+ }
+
+ public void setName(Name name) {
+ this.name = name;
+ }
+
+ public Optional getName() {
+ return Optional.ofNullable(name);
+ }
+
+ public void setPriority(Priority priority) {
+ this.priority = priority;
+ }
+
+ public Optional getPriority() {
+ return Optional.ofNullable(priority);
+ }
+
+ /**
+ * Sets {@code tags} to this object's {@code tags}.
+ * A defensive copy of {@code tags} is used internally.
+ */
+ public void setTags(Set tags) {
+ this.tags = (tags != null) ? new HashSet<>(tags) : null;
+ }
+
+ /**
+ * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException}
+ * if modification is attempted.
+ * Returns {@code Optional#empty()} if {@code tags} is null.
+ */
+ public Optional> getTags() {
+ return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty();
+ }
+
+ public void setDueDate(DueDate date) {
+ this.dueDate = date;
+ }
+
+ public Optional getDueDate() {
+ return Optional.ofNullable(dueDate);
+ }
+
+ public void setLink(Link link) {
+ this.link = link;
+ }
+
+ public Optional getLink() {
+ return Optional.ofNullable(link);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditTaskDescriptor)) {
+ return false;
+ }
+
+ EditTaskDescriptor otherEditTaskDescriptor = (EditTaskDescriptor) other;
+ return Objects.equals(name, otherEditTaskDescriptor.name)
+ && Objects.equals(priority, otherEditTaskDescriptor.priority)
+ && Objects.equals(tags, otherEditTaskDescriptor.tags)
+ && Objects.equals(dueDate, otherEditTaskDescriptor.dueDate)
+ && Objects.equals(link, otherEditTaskDescriptor.link);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("name", name)
+ .add("priority", priority)
+ .add("tags", tags)
+ .add("dueDate", dueDate)
+ .add("link", link)
+ .toString();
+ }
+ }
+}
diff --git a/src/main/java/profplan/logic/commands/EditSettingsCommand.java b/src/main/java/profplan/logic/commands/EditSettingsCommand.java
new file mode 100644
index 00000000000..8064649af20
--- /dev/null
+++ b/src/main/java/profplan/logic/commands/EditSettingsCommand.java
@@ -0,0 +1,140 @@
+package profplan.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Objects;
+import java.util.Optional;
+
+import profplan.commons.core.Settings;
+import profplan.commons.util.ToStringBuilder;
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.model.Model;
+import profplan.model.ModelManager;
+import profplan.model.ReadOnlyUserConfigs;
+
+/**
+ * Edits the details of an existing setting in the user configs.
+ */
+public class EditSettingsCommand extends Command {
+
+ public static final String COMMAND_WORD = "set";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Sets the value of a specified configuration "
+ + "parameter in the user config files to the input value.";
+
+ public static final String MESSAGE_DETAILS = "Existing values will be overwritten by the input values.\n"
+ + "Parameters: settingField, the setting to be altered; "
+ + "and value, the new value to be set";
+
+ public static final String MESSAGE_EXAMPLE = "Example: " + COMMAND_WORD + " semesterDays "
+ + "100 ";
+ public static final String MESSAGE_FULL_HELP = MESSAGE_USAGE + "\n" + MESSAGE_DETAILS + "\n" + MESSAGE_EXAMPLE;
+
+ public static final String MESSAGE_SUCCESS = "Settings successfully updated!";
+
+ private final EditSettingsDescriptor editSettingsDescriptor;
+
+ /**
+ * @param editSettingsDescriptor details to edit the settings with
+ */
+ public EditSettingsCommand(EditSettingsDescriptor editSettingsDescriptor) {
+ requireNonNull(editSettingsDescriptor);
+
+ this.editSettingsDescriptor = new EditSettingsDescriptor(editSettingsDescriptor);
+ }
+
+ /**
+ * Executes the command and returns the result message.
+ *
+ * @param model {@code Model} which the command should operate on.
+ * @return feedback message of the operation result for display
+ * @throws CommandException If an error occurs during command execution.
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ ReadOnlyUserConfigs userConfigs = ModelManager.getUserConfigs();
+
+ Settings settingsToEdit = userConfigs.getSettings();
+ Settings editedSettings = createEditedSettings(settingsToEdit, editSettingsDescriptor);
+
+ ModelManager.setSettings(editedSettings);
+ return new CommandResult(MESSAGE_SUCCESS);
+ }
+
+ private Settings createEditedSettings(Settings settingsToEdit,
+ EditSettingsDescriptor editSettingsDescriptor) {
+ assert settingsToEdit != null;
+
+ Integer semesterDays = editSettingsDescriptor.getSemesterDays()
+ .orElse(settingsToEdit.getSemesterDays());
+
+ return new Settings(semesterDays);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditSettingsCommand)) {
+ return false;
+ }
+
+ EditSettingsCommand otherEditSettingsCommand = (EditSettingsCommand) other;
+ return editSettingsDescriptor.equals(otherEditSettingsCommand.editSettingsDescriptor);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("editSettingsDescriptor", editSettingsDescriptor)
+ .toString();
+ }
+
+ /**
+ * Stores the details to edit the settings with. The non-empty field value will replace
+ * the corresponding setting value.
+ */
+ public static class EditSettingsDescriptor {
+ private Integer semesterDays;
+
+ public EditSettingsDescriptor() {}
+
+ public EditSettingsDescriptor(EditSettingsDescriptor toCopy) {
+ setSemesterDays(toCopy.semesterDays);
+ }
+
+ public void setSemesterDays(int semesterDays) {
+ this.semesterDays = semesterDays;
+ }
+
+ public Optional getSemesterDays() {
+ return Optional.ofNullable(semesterDays);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditSettingsDescriptor)) {
+ return false;
+ }
+
+ EditSettingsDescriptor otherEditSettingsDescriptor = (EditSettingsDescriptor) other;
+ return Objects.equals(semesterDays, otherEditSettingsDescriptor.semesterDays);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("semesterDays", semesterDays)
+ .toString();
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/profplan/logic/commands/ExitCommand.java
similarity index 59%
rename from src/main/java/seedu/address/logic/commands/ExitCommand.java
rename to src/main/java/profplan/logic/commands/ExitCommand.java
index 3dd85a8ba90..43699b73917 100644
--- a/src/main/java/seedu/address/logic/commands/ExitCommand.java
+++ b/src/main/java/profplan/logic/commands/ExitCommand.java
@@ -1,6 +1,6 @@
-package seedu.address.logic.commands;
+package profplan.logic.commands;
-import seedu.address.model.Model;
+import profplan.model.Model;
/**
* Terminates the program.
@@ -9,7 +9,9 @@ public class ExitCommand extends Command {
public static final String COMMAND_WORD = "exit";
- public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ...";
+ public static final String MESSAGE_USAGE = "Exits the application";
+ public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Task List as requested ...";
+ public static final String MESSAGE_FULL_HELP = MESSAGE_USAGE;
@Override
public CommandResult execute(Model model) {
diff --git a/src/main/java/profplan/logic/commands/FilterCommand.java b/src/main/java/profplan/logic/commands/FilterCommand.java
new file mode 100644
index 00000000000..8a6ecdcc59d
--- /dev/null
+++ b/src/main/java/profplan/logic/commands/FilterCommand.java
@@ -0,0 +1,84 @@
+package profplan.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static profplan.logic.parser.CliSyntax.PREFIX_DUEDATE;
+import static profplan.logic.parser.CliSyntax.PREFIX_PRIORITY;
+import static profplan.logic.parser.CliSyntax.PREFIX_RECURRING;
+import static profplan.logic.parser.CliSyntax.PREFIX_STATUS;
+
+import java.util.function.Predicate;
+
+import profplan.commons.util.ToStringBuilder;
+import profplan.model.Model;
+import profplan.model.task.Task;
+
+
+/**
+ * Filters for all tasks in task list whose due date falls before the argument due date.
+ */
+public class FilterCommand extends Command {
+
+ public static final String COMMAND_WORD = "filter";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Filters for tasks with one or more criteria and displays them as a list with index numbers.";
+
+ public static final String MESSAGE_DETAILS = "Parameters: "
+ + PREFIX_DUEDATE + "[dueDate] "
+ + PREFIX_PRIORITY + "[priority] "
+ + PREFIX_RECURRING + "[recur] "
+ + PREFIX_STATUS + "[status]";
+
+ public static final String MESSAGE_EXAMPLE = "Example: " + COMMAND_WORD + " d/01-01-2024 s/done";
+ public static final String MESSAGE_FULL_HELP = MESSAGE_USAGE + "\n" + MESSAGE_DETAILS + "\n" + MESSAGE_EXAMPLE;
+
+ private final Predicate predicate;
+
+ private String messageSuccess = "Here are your tasks that are:\n";
+
+ /**
+ * Initialise FilterCommand object
+ */
+ public FilterCommand(Predicate predicate) {
+ this.predicate = predicate;
+ }
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.updateFilteredTaskList(predicate);
+
+ return new CommandResult(
+ String.format(messageSuccess, model.getFilteredTaskList().size()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof FilterCommand)) {
+ return false;
+ }
+
+ FilterCommand otherCommand = (FilterCommand) other;
+ return predicate.equals(otherCommand.predicate);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("predicate", predicate)
+ .toString();
+ }
+
+ public String getSuccessMessage() {
+ return messageSuccess;
+ }
+
+ public void setSuccessMessage(String newMsg) {
+ messageSuccess = newMsg;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/profplan/logic/commands/FindCommand.java
similarity index 58%
rename from src/main/java/seedu/address/logic/commands/FindCommand.java
rename to src/main/java/profplan/logic/commands/FindCommand.java
index 72b9eddd3a7..b7410a35320 100644
--- a/src/main/java/seedu/address/logic/commands/FindCommand.java
+++ b/src/main/java/profplan/logic/commands/FindCommand.java
@@ -1,24 +1,25 @@
-package seedu.address.logic.commands;
+package profplan.logic.commands;
import static java.util.Objects.requireNonNull;
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-import seedu.address.model.Model;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import profplan.commons.util.ToStringBuilder;
+import profplan.logic.Messages;
+import profplan.model.Model;
+import profplan.model.task.predicates.NameContainsKeywordsPredicate;
/**
- * Finds and lists all persons in address book whose name contains any of the argument keywords.
+ * Finds and lists all tasks in task list whose name contains any of the argument keywords.
* Keyword matching is case insensitive.
*/
public class FindCommand extends Command {
public static final String COMMAND_WORD = "find";
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of "
- + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n"
- + "Parameters: KEYWORD [MORE_KEYWORDS]...\n"
- + "Example: " + COMMAND_WORD + " alice bob charlie";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all tasks whose names contain any of "
+ + "the specified keywords (case-insensitive) and displays them as a list with index numbers.";
+ public static final String MESSAGE_DETAILS = "Parameters: [keywords...]";
+ public static final String MESSAGE_EXAMPLE = "Example: " + COMMAND_WORD + " canvas quiz";
+ public static final String MESSAGE_FULL_HELP = MESSAGE_USAGE + "\n" + MESSAGE_DETAILS + "\n" + MESSAGE_EXAMPLE;
private final NameContainsKeywordsPredicate predicate;
@@ -29,9 +30,9 @@ public FindCommand(NameContainsKeywordsPredicate predicate) {
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
- model.updateFilteredPersonList(predicate);
+ model.updateFilteredTaskList(predicate);
return new CommandResult(
- String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size()));
+ String.format(Messages.MESSAGE_TASKS_LISTED_OVERVIEW, model.getFilteredTaskList().size()));
}
@Override
diff --git a/src/main/java/profplan/logic/commands/HelpCommand.java b/src/main/java/profplan/logic/commands/HelpCommand.java
new file mode 100644
index 00000000000..cbd76a5c87a
--- /dev/null
+++ b/src/main/java/profplan/logic/commands/HelpCommand.java
@@ -0,0 +1,175 @@
+package profplan.logic.commands;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.model.Model;
+
+/**
+ * Format full help instructions for every command for display.
+ */
+public class HelpCommand extends Command {
+ public static final String COMMAND_WORD = "help";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Shows program usage instructions.";
+ public static final String MESSAGE_DETAILS = "Alternatively, "
+ + "you can use help with a command to see an example of it.\n"
+ + "Parameters: [command] (must be a valid command) "
+ + "help [command]";
+ public static final String MESSAGE_EXAMPLE = "Example: " + COMMAND_WORD + "[command]";
+ public static final String MESSAGE_FULL_HELP = MESSAGE_USAGE + "\n" + MESSAGE_DETAILS + "\n" + MESSAGE_EXAMPLE;
+
+ public static final String MESSAGE_INVALID_COMMAND_WORD = "Invalid command word!";
+ public static final String SHOWING_HELP_MESSAGE = "Opened help window.";
+ public static final String DELIMITTER_BETWEEN_COMMANDS = "\n-----------------\n";
+
+ private final String command;
+
+ /**
+ * Creates an empty HelpCommand. This is will list all commands.
+ */
+ public HelpCommand() {
+ this.command = null;
+ }
+
+ /**
+ * Creates a HelpCommand to get help for a particular command.
+ * @param command The command to get help for.
+ */
+ public HelpCommand(String command) {
+ this.command = command;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ if (this.command == null || this.command.equals("")) {
+ return new CommandResult(getAllCommandDescriptions(), false, false);
+ } else {
+ return new CommandResult(getOneCommandDescription(command));
+ }
+ }
+
+ private String getAllCommandDescriptions() {
+ ArrayList> commands = listCommands();
+ String rtn = "";
+ for (Class extends Command> command : commands) {
+ try {
+ Field usage = command.getDeclaredField("MESSAGE_USAGE");
+ System.out.println(usage.get(null).toString());
+ rtn += usage.get(null).toString() + DELIMITTER_BETWEEN_COMMANDS;
+ } catch (NoSuchFieldException e) {
+ //when command has no usage field
+ System.out.println(e);
+ continue;
+ } catch (IllegalAccessException e) {
+ //impossible as command usage MUST be public
+ System.out.println(e);
+ continue;
+ }
+ }
+ return rtn;
+ }
+
+ private String getOneCommandDescription(String command) throws CommandException {
+ switch (command) {
+
+ case AddCommand.COMMAND_WORD:
+ return AddCommand.MESSAGE_USAGE + "\n" + AddCommand.MESSAGE_EXAMPLE;
+
+ case EditCommand.COMMAND_WORD:
+ return EditCommand.MESSAGE_USAGE + "\n" + EditCommand.MESSAGE_EXAMPLE;
+
+ case EditSettingsCommand.COMMAND_WORD:
+ return EditSettingsCommand.MESSAGE_USAGE + "\n" + EditSettingsCommand.MESSAGE_EXAMPLE;
+
+ case MarkCommand.COMMAND_WORD:
+ return MarkCommand.MESSAGE_USAGE + "\n" + MarkCommand.MESSAGE_EXAMPLE;
+
+
+ case UnmarkCommand.COMMAND_WORD:
+ return UnmarkCommand.MESSAGE_USAGE + "\n" + UnmarkCommand.MESSAGE_EXAMPLE;
+
+ case DeleteCommand.COMMAND_WORD:
+ return DeleteCommand.MESSAGE_USAGE + "\n" + DeleteCommand.MESSAGE_EXAMPLE;
+
+ case DoNextCommand.COMMAND_WORD:
+ return DoNextCommand.MESSAGE_USAGE;
+
+ case ClearCommand.COMMAND_WORD:
+ return ClearCommand.MESSAGE_USAGE;
+
+ case FindCommand.COMMAND_WORD:
+ return FindCommand.MESSAGE_USAGE + "\n" + FindCommand.MESSAGE_EXAMPLE;
+
+ case FilterCommand.COMMAND_WORD:
+ return FilterCommand.MESSAGE_USAGE + "\n" + FilterCommand.MESSAGE_EXAMPLE;
+
+ case ListCommand.COMMAND_WORD:
+ return ListCommand.MESSAGE_USAGE;
+
+
+ case ExitCommand.COMMAND_WORD:
+ return ExitCommand.MESSAGE_USAGE;
+
+ case HelpCommand.COMMAND_WORD:
+ return HelpCommand.MESSAGE_USAGE + "\n" + HelpCommand.MESSAGE_EXAMPLE;
+
+ case DescriptionCommand.COMMAND_WORD:
+ return DescriptionCommand.MESSAGE_USAGE + "\n" + DescriptionCommand.MESSAGE_EXAMPLE;
+
+ case SortDueDateCommand.COMMAND_WORD:
+ return SortDueDateCommand.MESSAGE_USAGE;
+
+ case SortPriorityCommand.COMMAND_WORD:
+ return SortPriorityCommand.MESSAGE_USAGE;
+
+ case ListWeekCommand.COMMAND_WORD:
+ return ListWeekCommand.MESSAGE_USAGE;
+
+ case ListMonthCommand.COMMAND_WORD:
+ return ListMonthCommand.MESSAGE_USAGE;
+
+ case StatsCommand.COMMAND_WORD:
+ return StatsCommand.MESSAGE_USAGE;
+
+ default:
+ throw new CommandException(MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ private ArrayList> listCommands() {
+ ArrayList> rtn = new ArrayList>();
+ rtn.add(AddCommand.class);
+ rtn.add(EditCommand.class);
+ rtn.add(EditSettingsCommand.class);
+ rtn.add(MarkCommand.class);
+ rtn.add(UnmarkCommand.class);
+ rtn.add(DeleteCommand.class);
+ rtn.add(DoNextCommand.class);
+ rtn.add(ClearCommand.class);
+ rtn.add(FindCommand.class);
+ rtn.add(FilterCommand.class);
+ rtn.add(HelpCommand.class);
+ rtn.add(DescriptionCommand.class);
+ rtn.add(SortDueDateCommand.class);
+ rtn.add(SortPriorityCommand.class);
+ rtn.add(ListWeekCommand.class);
+ rtn.add(ListMonthCommand.class);
+ System.out.println(rtn.size());
+ return rtn;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof HelpCommand)) {
+ return false;
+ }
+ HelpCommand h = (HelpCommand) o;
+ if (h.command != this.command) {
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/src/main/java/profplan/logic/commands/ListCommand.java b/src/main/java/profplan/logic/commands/ListCommand.java
new file mode 100644
index 00000000000..84b2d654294
--- /dev/null
+++ b/src/main/java/profplan/logic/commands/ListCommand.java
@@ -0,0 +1,24 @@
+package profplan.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import profplan.model.Model;
+
+/**
+ * Lists all tasks in the task list to the user.
+ */
+public class ListCommand extends Command {
+
+ public static final String COMMAND_WORD = "list";
+
+ public static final String MESSAGE_USAGE = "Lists all tasks.";
+ public static final String MESSAGE_SUCCESS = "Listed all tasks";
+ public static final String MESSAGE_FULL_HELP = MESSAGE_USAGE;
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.updateFilteredTaskList(Model.PREDICATE_SHOW_ALL_TASKS);
+ return new CommandResult(MESSAGE_SUCCESS);
+ }
+}
diff --git a/src/main/java/profplan/logic/commands/ListMonthCommand.java b/src/main/java/profplan/logic/commands/ListMonthCommand.java
new file mode 100644
index 00000000000..01d659ae889
--- /dev/null
+++ b/src/main/java/profplan/logic/commands/ListMonthCommand.java
@@ -0,0 +1,29 @@
+package profplan.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.model.Model;
+import profplan.model.task.predicates.TaskInMonthPredicate;
+
+/**
+ * Lists tasks within a month of today.
+ */
+public class ListMonthCommand extends Command {
+
+ public static final String COMMAND_WORD = "list_month";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lists all tasks within a month of today.";
+ public static final String MESSAGE_FULL_HELP = MESSAGE_USAGE;
+
+ public static final String MESSAGE_SUCCESS = " Here are your tasks within a month Prof!";
+
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredTaskList(new TaskInMonthPredicate());
+ return new CommandResult(MESSAGE_SUCCESS);
+ }
+
+}
diff --git a/src/main/java/profplan/logic/commands/ListWeekCommand.java b/src/main/java/profplan/logic/commands/ListWeekCommand.java
new file mode 100644
index 00000000000..c3ff0715a88
--- /dev/null
+++ b/src/main/java/profplan/logic/commands/ListWeekCommand.java
@@ -0,0 +1,29 @@
+package profplan.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.model.Model;
+import profplan.model.task.predicates.TaskInWeekPredicate;
+
+/**
+ * Lists tasks within a week of today.
+ */
+public class ListWeekCommand extends Command {
+
+ public static final String COMMAND_WORD = "list_week";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lists all tasks within this week.";
+ public static final String MESSAGE_FULL_HELP = MESSAGE_USAGE;
+
+ public static final String MESSAGE_SUCCESS = " Here are your tasks within a week Prof!";
+
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredTaskList(new TaskInWeekPredicate());
+ return new CommandResult(MESSAGE_SUCCESS);
+ }
+
+}
diff --git a/src/main/java/profplan/logic/commands/MarkCommand.java b/src/main/java/profplan/logic/commands/MarkCommand.java
new file mode 100644
index 00000000000..14df8ceb903
--- /dev/null
+++ b/src/main/java/profplan/logic/commands/MarkCommand.java
@@ -0,0 +1,73 @@
+package profplan.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import profplan.commons.util.ToStringBuilder;
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.model.Model;
+import profplan.model.task.DueDate;
+
+/**
+ * Marks Status of task as done.
+ */
+public class MarkCommand extends Command {
+
+ public static final String COMMAND_WORD = "mark";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Marks a task as done.";
+ public static final String MESSAGE_DETAILS = "Parameters: [index]";
+ public static final String MESSAGE_EXAMPLE = "Example: " + COMMAND_WORD + " 1";
+ public static final String MESSAGE_FULL_HELP = MESSAGE_USAGE + "\n" + MESSAGE_DETAILS + "\n" + MESSAGE_EXAMPLE;
+
+ public static final String MESSAGE_SUCCESS = "Task successfully marked as done, Prof! "
+ + "Here is your updated task list";
+
+ public static final String MESSAGE_INVALID_NUMBER = "[index] should be greater than or equal to 1";
+ public static final String MESSAGE_ALREADY_DONE = "This task is already marked as done";
+
+ private final int taskNumber;
+
+ /**
+ * Creates an AddCommand to add the specified {@code Task}
+ */
+ public MarkCommand(int number) {
+ assert number > 0;
+ taskNumber = number;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ if (taskNumber > model.getFilteredTaskList().size()) {
+ throw new CommandException("Task not found please enter a valid Task Number.");
+ }
+ try {
+ model.markTask(taskNumber - 1);
+ } catch (IllegalArgumentException e) { // if adding days to recurring task increases due date past 2030
+ throw new CommandException(DueDate.MESSAGE_CONSTRAINTS);
+ }
+
+ return new CommandResult(MESSAGE_SUCCESS);
+ }
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof MarkCommand)) {
+ return false;
+ }
+
+ MarkCommand otherMarkCommand = (MarkCommand) other;
+ return taskNumber == otherMarkCommand.taskNumber;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("taskNumber", taskNumber)
+ .toString();
+ }
+}
diff --git a/src/main/java/profplan/logic/commands/SortDueDateCommand.java b/src/main/java/profplan/logic/commands/SortDueDateCommand.java
new file mode 100644
index 00000000000..9ad0792bb56
--- /dev/null
+++ b/src/main/java/profplan/logic/commands/SortDueDateCommand.java
@@ -0,0 +1,33 @@
+package profplan.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.model.Model;
+
+/**
+ * Sorts tasklist by DueDate.
+ */
+public class SortDueDateCommand extends Command {
+
+ public static final String COMMAND_WORD = "sort_duedate";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Sorts the task by due date.";
+ public static final String MESSAGE_FULL_HELP = MESSAGE_USAGE;
+
+ public static final String MESSAGE_SUCCESS = " Here is your task list Prof, sorted by nearest due date.";
+
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ try {
+ model.sortTaskByDueDate();
+ } catch (UnsupportedOperationException e) {
+ return new CommandResult(e.getMessage());
+ }
+
+ return new CommandResult(MESSAGE_SUCCESS);
+ }
+
+}
diff --git a/src/main/java/profplan/logic/commands/SortPriorityCommand.java b/src/main/java/profplan/logic/commands/SortPriorityCommand.java
new file mode 100644
index 00000000000..12dd4403511
--- /dev/null
+++ b/src/main/java/profplan/logic/commands/SortPriorityCommand.java
@@ -0,0 +1,34 @@
+package profplan.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.model.Model;
+
+/**
+ * Sorts tasklist by Priority.
+ */
+public class SortPriorityCommand extends Command {
+
+ public static final String COMMAND_WORD = "sort_priority";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Sorts the tasks based on priority.";
+ public static final String MESSAGE_EXAMPLE = "Example: " + COMMAND_WORD;
+ public static final String MESSAGE_FULL_HELP = MESSAGE_USAGE + "\n" + "\n" + MESSAGE_EXAMPLE;
+
+ public static final String MESSAGE_SUCCESS = " Here is your task list Prof, sorted based on priority";
+
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ try {
+ model.sortTaskByPriority();
+ } catch (UnsupportedOperationException e) {
+ return new CommandResult(e.getMessage());
+ }
+
+ return new CommandResult(MESSAGE_SUCCESS);
+ }
+
+}
diff --git a/src/main/java/profplan/logic/commands/StatsCommand.java b/src/main/java/profplan/logic/commands/StatsCommand.java
new file mode 100644
index 00000000000..a196873f636
--- /dev/null
+++ b/src/main/java/profplan/logic/commands/StatsCommand.java
@@ -0,0 +1,30 @@
+package profplan.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.model.Model;
+
+/**
+ * Lists tasks within a month of today.
+ */
+public class StatsCommand extends Command {
+
+ public static final String COMMAND_WORD = "stats";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + "Shows a summary of your progress";
+ public static final String MESSAGE_FULL_HELP = MESSAGE_USAGE;
+
+ public static final String MESSAGE_SUCCESS = "Here are your statistics Prof!\n";
+
+ public static final String COMPLETION_RATE_MESSAGE_FORMAT = "Completion Rate: %.1f%%";
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ String rtn = MESSAGE_SUCCESS
+ + String.format(COMPLETION_RATE_MESSAGE_FORMAT, Math.ceil(model.getCompletionRate() * 1000) / 10);
+ return new CommandResult(rtn);
+ }
+
+}
diff --git a/src/main/java/profplan/logic/commands/UnmarkCommand.java b/src/main/java/profplan/logic/commands/UnmarkCommand.java
new file mode 100644
index 00000000000..7ae6f151674
--- /dev/null
+++ b/src/main/java/profplan/logic/commands/UnmarkCommand.java
@@ -0,0 +1,72 @@
+package profplan.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import profplan.commons.util.ToStringBuilder;
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.model.Model;
+
+/**
+ * Marks Status of task as undone.
+ */
+public class UnmarkCommand extends Command {
+
+ public static final String COMMAND_WORD = "unmark";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Marks a task as undone.";
+ public static final String MESSAGE_DETAILS = "Parameters: "
+ + "[index] ";
+
+ public static final String MESSAGE_EXAMPLE = "Example: " + COMMAND_WORD + " 1";
+ public static final String MESSAGE_FULL_HELP = MESSAGE_USAGE + "\n" + MESSAGE_DETAILS + "\n" + MESSAGE_EXAMPLE;
+
+ public static final String MESSAGE_SUCCESS = "Task successfully marked as undone, Prof! "
+ + "Here is your updated task list";
+
+ public static final String MESSAGE_INVALID_NUMBER = "INDEX should be greater than or equal to 1";
+ public static final String MESSAGE_ALREADY_UNDONE = "This task is already marked as undone";
+
+ private final int taskNumber;
+
+ /**
+ * It is a constructor which assigns the number of the task which is to be unmarked
+ * to the tasknumber variable.
+ * @param number
+ */
+ public UnmarkCommand(int number) {
+ assert number > 0;
+ taskNumber = number;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ if (taskNumber > model.getFilteredTaskList().size()) {
+ throw new CommandException("Task not found please enter a valid Task Number.");
+ }
+ model.unmarkTask(taskNumber - 1);
+ return new CommandResult(MESSAGE_SUCCESS);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof UnmarkCommand)) {
+ return false;
+ }
+
+ UnmarkCommand otherMarkCommand = (UnmarkCommand) other;
+ return taskNumber == otherMarkCommand.taskNumber;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("taskNumber", taskNumber)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/profplan/logic/commands/exceptions/CommandException.java
similarity index 89%
rename from src/main/java/seedu/address/logic/commands/exceptions/CommandException.java
rename to src/main/java/profplan/logic/commands/exceptions/CommandException.java
index a16bd14f2cd..14e506d1088 100644
--- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java
+++ b/src/main/java/profplan/logic/commands/exceptions/CommandException.java
@@ -1,4 +1,4 @@
-package seedu.address.logic.commands.exceptions;
+package profplan.logic.commands.exceptions;
/**
* Represents an error which occurs during execution of a {@link Command}.
diff --git a/src/main/java/profplan/logic/parser/AddCommandParser.java b/src/main/java/profplan/logic/parser/AddCommandParser.java
new file mode 100644
index 00000000000..f4f3ad74e9e
--- /dev/null
+++ b/src/main/java/profplan/logic/parser/AddCommandParser.java
@@ -0,0 +1,69 @@
+package profplan.logic.parser;
+
+import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static profplan.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+import static profplan.logic.parser.CliSyntax.PREFIX_DUEDATE;
+import static profplan.logic.parser.CliSyntax.PREFIX_LINK;
+import static profplan.logic.parser.CliSyntax.PREFIX_NAME;
+import static profplan.logic.parser.CliSyntax.PREFIX_PRIORITY;
+import static profplan.logic.parser.CliSyntax.PREFIX_RECURRING;
+import static profplan.logic.parser.CliSyntax.PREFIX_TAG;
+
+import java.util.Set;
+import java.util.stream.Stream;
+
+import profplan.logic.commands.AddCommand;
+import profplan.logic.parser.exceptions.ParseException;
+import profplan.model.tag.Tag;
+import profplan.model.task.Description;
+import profplan.model.task.DueDate;
+import profplan.model.task.Link;
+import profplan.model.task.Name;
+import profplan.model.task.Priority;
+import profplan.model.task.Task;
+
+/**
+ * Parses input arguments and creates a new AddCommand object
+ */
+public class AddCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the AddCommand
+ * and returns an AddCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public AddCommand parse(String args) throws ParseException {
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PRIORITY, PREFIX_RECURRING,
+ PREFIX_TAG, PREFIX_DUEDATE, PREFIX_LINK, PREFIX_DESCRIPTION);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_PRIORITY, PREFIX_DUEDATE)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_FULL_HELP));
+ }
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PRIORITY, PREFIX_RECURRING);
+ Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get());
+ Priority priority = ParserUtil.parsePriority(argMultimap.getValue(PREFIX_PRIORITY).get());
+ Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));
+ DueDate dueDate = ParserUtil.parseDueDate(argMultimap.getValue(PREFIX_DUEDATE).get());
+ Link link = ParserUtil.parseLink(argMultimap.getValue(PREFIX_LINK).orElse("-"));
+ boolean isRecurring = argMultimap.getValue(PREFIX_RECURRING).isPresent();
+ Task.RecurringType recurringType = isRecurring
+ ? ParserUtil.parseRecurringType(argMultimap.getValue(PREFIX_RECURRING).get()) : null;
+ Description description = new Description(argMultimap.getValue(PREFIX_DESCRIPTION).orElse(""));
+
+ Task task = new Task(name, priority, isRecurring, recurringType, tagList, dueDate,
+ link, description);
+ return new AddCommand(task);
+ }
+
+ /**
+ * Returns true if none of the prefixes contains empty {@code Optional} values in the given
+ * {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java b/src/main/java/profplan/logic/parser/ArgumentMultimap.java
similarity index 95%
rename from src/main/java/seedu/address/logic/parser/ArgumentMultimap.java
rename to src/main/java/profplan/logic/parser/ArgumentMultimap.java
index 21e26887a83..a37a14290bd 100644
--- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java
+++ b/src/main/java/profplan/logic/parser/ArgumentMultimap.java
@@ -1,4 +1,4 @@
-package seedu.address.logic.parser;
+package profplan.logic.parser;
import java.util.ArrayList;
import java.util.HashMap;
@@ -7,8 +7,8 @@
import java.util.Optional;
import java.util.stream.Stream;
-import seedu.address.logic.Messages;
-import seedu.address.logic.parser.exceptions.ParseException;
+import profplan.logic.Messages;
+import profplan.logic.parser.exceptions.ParseException;
/**
* Stores mapping of prefixes to their respective arguments.
diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/profplan/logic/parser/ArgumentTokenizer.java
similarity index 99%
rename from src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
rename to src/main/java/profplan/logic/parser/ArgumentTokenizer.java
index 5c9aebfa488..a24560bc6be 100644
--- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
+++ b/src/main/java/profplan/logic/parser/ArgumentTokenizer.java
@@ -1,4 +1,4 @@
-package seedu.address.logic.parser;
+package profplan.logic.parser;
import java.util.ArrayList;
import java.util.Arrays;
diff --git a/src/main/java/profplan/logic/parser/CliSyntax.java b/src/main/java/profplan/logic/parser/CliSyntax.java
new file mode 100644
index 00000000000..25de3aa95ed
--- /dev/null
+++ b/src/main/java/profplan/logic/parser/CliSyntax.java
@@ -0,0 +1,18 @@
+package profplan.logic.parser;
+
+/**
+ * Contains Command Line Interface (CLI) syntax definitions common to multiple commands
+ */
+public class CliSyntax {
+
+ /* Prefix definitions */
+ public static final Prefix PREFIX_NAME = new Prefix("n/");
+ public static final Prefix PREFIX_PRIORITY = new Prefix("p/");
+ public static final Prefix PREFIX_RECURRING = new Prefix("recur/");
+ public static final Prefix PREFIX_TAG = new Prefix("t/");
+ public static final Prefix PREFIX_STATUS = new Prefix("s/");
+ public static final Prefix PREFIX_LINK = new Prefix("l/");
+ public static final Prefix PREFIX_DUEDATE = new Prefix("d/");
+ public static final Prefix PREFIX_DESCRIPTION = new Prefix("des/");
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/profplan/logic/parser/DeleteCommandParser.java
similarity index 65%
rename from src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
rename to src/main/java/profplan/logic/parser/DeleteCommandParser.java
index 3527fe76a3e..a483a18aa5d 100644
--- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
+++ b/src/main/java/profplan/logic/parser/DeleteCommandParser.java
@@ -1,10 +1,10 @@
-package seedu.address.logic.parser;
+package profplan.logic.parser;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import seedu.address.commons.core.index.Index;
-import seedu.address.logic.commands.DeleteCommand;
-import seedu.address.logic.parser.exceptions.ParseException;
+import profplan.commons.core.index.Index;
+import profplan.logic.commands.DeleteCommand;
+import profplan.logic.parser.exceptions.ParseException;
/**
* Parses input arguments and creates a new DeleteCommand object
@@ -21,8 +21,11 @@ public DeleteCommand parse(String args) throws ParseException {
Index index = ParserUtil.parseIndex(args);
return new DeleteCommand(index);
} catch (ParseException pe) {
+ if (args.trim().equals("all")) {
+ return new DeleteCommand();
+ }
throw new ParseException(
- String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe);
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_FULL_HELP), pe);
}
}
diff --git a/src/main/java/profplan/logic/parser/DescriptionCommandParser.java b/src/main/java/profplan/logic/parser/DescriptionCommandParser.java
new file mode 100644
index 00000000000..d14a19fa005
--- /dev/null
+++ b/src/main/java/profplan/logic/parser/DescriptionCommandParser.java
@@ -0,0 +1,35 @@
+package profplan.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static profplan.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+
+import profplan.commons.core.index.Index;
+import profplan.commons.exceptions.IllegalValueException;
+import profplan.logic.commands.DescriptionCommand;
+import profplan.logic.parser.exceptions.ParseException;
+import profplan.model.task.Description;
+
+/**
+ * Parses user input and creates a DescriptionCommand object.
+ */
+public class DescriptionCommandParser implements Parser {
+ @Override
+ public DescriptionCommand parse(String userInput) throws ParseException {
+ requireNonNull(userInput);
+ ArgumentMultimap argumentMultimap = ArgumentTokenizer.tokenize(userInput, PREFIX_DESCRIPTION);
+
+ Index index;
+ try {
+ index = ParserUtil.parseIndex(argumentMultimap.getPreamble());
+ } catch (IllegalValueException illegalValueException) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ DescriptionCommand.MESSAGE_FULL_HELP), illegalValueException);
+ }
+
+ Description description = new Description(argumentMultimap
+ .getValue(PREFIX_DESCRIPTION).orElse(""));
+
+ return new DescriptionCommand(index, description);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/profplan/logic/parser/EditCommandParser.java
similarity index 52%
rename from src/main/java/seedu/address/logic/parser/EditCommandParser.java
rename to src/main/java/profplan/logic/parser/EditCommandParser.java
index 46b3309a78b..0ea8dac7340 100644
--- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java
+++ b/src/main/java/profplan/logic/parser/EditCommandParser.java
@@ -1,23 +1,22 @@
-package seedu.address.logic.parser;
+package profplan.logic.parser;
import static java.util.Objects.requireNonNull;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static profplan.logic.parser.CliSyntax.PREFIX_DUEDATE;
+import static profplan.logic.parser.CliSyntax.PREFIX_LINK;
+import static profplan.logic.parser.CliSyntax.PREFIX_NAME;
+import static profplan.logic.parser.CliSyntax.PREFIX_PRIORITY;
+import static profplan.logic.parser.CliSyntax.PREFIX_TAG;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
-import seedu.address.commons.core.index.Index;
-import seedu.address.logic.commands.EditCommand;
-import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
-import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.tag.Tag;
+import profplan.commons.core.index.Index;
+import profplan.logic.commands.EditCommand;
+import profplan.logic.parser.exceptions.ParseException;
+import profplan.model.tag.Tag;
/**
* Parses input arguments and creates a new EditCommand object
@@ -32,39 +31,42 @@ public class EditCommandParser implements Parser {
public EditCommand parse(String args) throws ParseException {
requireNonNull(args);
ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
-
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PRIORITY, PREFIX_TAG, PREFIX_DUEDATE,
+ PREFIX_LINK);
Index index;
try {
index = ParserUtil.parseIndex(argMultimap.getPreamble());
} catch (ParseException pe) {
- throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe);
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_FULL_HELP), pe);
}
- argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PRIORITY, PREFIX_LINK);
- EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
+ EditCommand.EditTaskDescriptor editTaskDescriptor = new EditCommand.EditTaskDescriptor();
if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
- editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()));
+ editTaskDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()));
}
- if (argMultimap.getValue(PREFIX_PHONE).isPresent()) {
- editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()));
+ if (argMultimap.getValue(PREFIX_PRIORITY).isPresent()) {
+ editTaskDescriptor.setPriority(ParserUtil.parsePriority(argMultimap.getValue(PREFIX_PRIORITY).get()));
}
- if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
- editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()));
+
+ parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editTaskDescriptor::setTags);
+
+ if (argMultimap.getValue(PREFIX_LINK).isPresent()) {
+ editTaskDescriptor.setLink(ParserUtil.parseLink(argMultimap.getValue(PREFIX_LINK).get()));
}
- if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
- editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()));
+
+ if (argMultimap.getValue(PREFIX_DUEDATE).isPresent()) {
+ editTaskDescriptor.setDueDate(ParserUtil.parseDueDate(argMultimap.getValue(PREFIX_DUEDATE).get()));
}
- parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags);
- if (!editPersonDescriptor.isAnyFieldEdited()) {
+ if (!editTaskDescriptor.isAnyFieldEdited()) {
throw new ParseException(EditCommand.MESSAGE_NOT_EDITED);
}
- return new EditCommand(index, editPersonDescriptor);
+ return new EditCommand(index, editTaskDescriptor);
}
/**
diff --git a/src/main/java/profplan/logic/parser/EditSettingsCommandParser.java b/src/main/java/profplan/logic/parser/EditSettingsCommandParser.java
new file mode 100644
index 00000000000..71760fcc72c
--- /dev/null
+++ b/src/main/java/profplan/logic/parser/EditSettingsCommandParser.java
@@ -0,0 +1,53 @@
+package profplan.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.stream.Stream;
+
+import profplan.commons.core.Settings;
+import profplan.logic.commands.EditSettingsCommand;
+import profplan.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new EditSettingsCommand object
+ */
+public class EditSettingsCommandParser implements Parser {
+ /**
+ * Parses {@code userInput} into a command and returns it.
+ *
+ * @param userInput
+ * @throws ParseException if {@code userInput} does not conform the expected format
+ */
+ @Override
+ public EditSettingsCommand parse(String userInput) throws ParseException {
+ requireNonNull(userInput);
+
+ Prefix[] keywords = Settings.KEYWORDS;
+ ArgumentMultimap argumentMultimap = ArgumentTokenizer.tokenize(userInput, keywords);
+
+ if (!isPrefixPresent(argumentMultimap, keywords) || !argumentMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ EditSettingsCommand.MESSAGE_FULL_HELP));
+ }
+
+ EditSettingsCommand.EditSettingsDescriptor editSettingsDescriptor =
+ new EditSettingsCommand.EditSettingsDescriptor();
+
+ // semesterDays
+ if (argumentMultimap.getValue(keywords[0]).isPresent()) {
+ editSettingsDescriptor.setSemesterDays(ParserUtil
+ .parseSemesterDays(argumentMultimap.getValue(keywords[0]).get()));
+ }
+
+ return new EditSettingsCommand(editSettingsDescriptor);
+ }
+
+ /**
+ * Returns true if at least one of the prefixes does not contain empty {@code Optional} values in the given
+ * {@code ArgumentMultimap}.
+ */
+ private static boolean isPrefixPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).anyMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+}
diff --git a/src/main/java/profplan/logic/parser/FilterCommandParser.java b/src/main/java/profplan/logic/parser/FilterCommandParser.java
new file mode 100644
index 00000000000..2bf88746bf0
--- /dev/null
+++ b/src/main/java/profplan/logic/parser/FilterCommandParser.java
@@ -0,0 +1,91 @@
+package profplan.logic.parser;
+
+import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static profplan.logic.parser.CliSyntax.PREFIX_DUEDATE;
+import static profplan.logic.parser.CliSyntax.PREFIX_PRIORITY;
+import static profplan.logic.parser.CliSyntax.PREFIX_RECURRING;
+import static profplan.logic.parser.CliSyntax.PREFIX_STATUS;
+
+import java.util.ArrayList;
+import java.util.function.Predicate;
+
+import profplan.logic.commands.FilterCommand;
+import profplan.logic.parser.exceptions.ParseException;
+import profplan.model.task.Task;
+import profplan.model.task.predicates.CombinedPredicate;
+import profplan.model.task.predicates.TaskDueDatePredicate;
+import profplan.model.task.predicates.TaskPriorityPredicate;
+import profplan.model.task.predicates.TaskRecurringTypePredicate;
+import profplan.model.task.predicates.TaskStatusPredicate;
+
+
+/**
+ * Parses input arguments and creates a new FilterCommand object
+ */
+public class FilterCommandParser implements Parser {
+
+ /**
+ * Parses input, which should be a valid Status, DueDate, Priority or RecurringType
+ */
+ public FilterCommand parse(String args) throws ParseException {
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_PRIORITY, PREFIX_STATUS, PREFIX_DUEDATE, PREFIX_RECURRING);
+
+ if (!argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterCommand.MESSAGE_FULL_HELP));
+ }
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_PRIORITY, PREFIX_STATUS, PREFIX_DUEDATE, PREFIX_RECURRING);
+
+ ArrayList> predList = new ArrayList<>();
+ ArrayList filters = new ArrayList<>();
+
+ try {
+ if (argMultimap.getValue(PREFIX_PRIORITY).isPresent()) {
+ TaskPriorityPredicate newPred = new TaskPriorityPredicate(
+ ParserUtil.parsePriority(argMultimap.getValue(PREFIX_PRIORITY).get()));
+ predList.add(newPred);
+ filters.add("Priority: " + newPred.getPriority() + "\n");
+ }
+
+ if (argMultimap.getValue(PREFIX_STATUS).isPresent()) {
+ TaskStatusPredicate newPred = new TaskStatusPredicate(
+ ParserUtil.parseStatus(argMultimap.getValue(PREFIX_STATUS).get()));
+ predList.add(newPred);
+ filters.add("Status: " + newPred.getStatus() + "\n");
+ }
+
+ if (argMultimap.getValue(PREFIX_DUEDATE).isPresent()) {
+ TaskDueDatePredicate newPred = new TaskDueDatePredicate(
+ ParserUtil.parseDueDate(argMultimap.getValue(PREFIX_DUEDATE).get()));
+ predList.add(newPred);
+ filters.add("Due before: " + newPred.getDate() + "\n");
+ }
+
+ if (argMultimap.getValue(PREFIX_RECURRING).isPresent()) {
+ TaskRecurringTypePredicate newPred = new TaskRecurringTypePredicate(
+ ParserUtil.parseRecurringType(argMultimap.getValue(PREFIX_RECURRING).get()));
+ predList.add(newPred);
+ String recurringType = newPred.getRecurringType() == null
+ ? "NONE" : newPred.getRecurringType()
+ .toString();
+ filters.add("Recurring: " + recurringType + "\n");
+ }
+
+ if (predList.isEmpty()) {
+ throw new ParseException("", null);
+ }
+
+ } catch (ParseException e) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterCommand.MESSAGE_FULL_HELP));
+ }
+
+ FilterCommand cmd = new FilterCommand(new CombinedPredicate(predList));
+ StringBuilder success = new StringBuilder(cmd.getSuccessMessage());
+ for (String string : filters) {
+ success.append(string);
+ }
+ cmd.setSuccessMessage(success.toString());
+ return cmd;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/profplan/logic/parser/FindCommandParser.java
similarity index 72%
rename from src/main/java/seedu/address/logic/parser/FindCommandParser.java
rename to src/main/java/profplan/logic/parser/FindCommandParser.java
index 2867bde857b..b458c9dda90 100644
--- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java
+++ b/src/main/java/profplan/logic/parser/FindCommandParser.java
@@ -1,12 +1,12 @@
-package seedu.address.logic.parser;
+package profplan.logic.parser;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import java.util.Arrays;
-import seedu.address.logic.commands.FindCommand;
-import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import profplan.logic.commands.FindCommand;
+import profplan.logic.parser.exceptions.ParseException;
+import profplan.model.task.predicates.NameContainsKeywordsPredicate;
/**
* Parses input arguments and creates a new FindCommand object
@@ -22,7 +22,7 @@ public FindCommand parse(String args) throws ParseException {
String trimmedArgs = args.trim();
if (trimmedArgs.isEmpty()) {
throw new ParseException(
- String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_FULL_HELP));
}
String[] nameKeywords = trimmedArgs.split("\\s+");
diff --git a/src/main/java/profplan/logic/parser/HelpCommandParser.java b/src/main/java/profplan/logic/parser/HelpCommandParser.java
new file mode 100644
index 00000000000..b2143dbdd94
--- /dev/null
+++ b/src/main/java/profplan/logic/parser/HelpCommandParser.java
@@ -0,0 +1,25 @@
+package profplan.logic.parser;
+
+import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import profplan.logic.commands.HelpCommand;
+import profplan.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new AddCommand object
+ */
+public class HelpCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the AddCommand
+ * and returns an AddCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public HelpCommand parse(String args) throws ParseException {
+ String command = args.strip();
+ if (command.contains(" ")) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_FULL_HELP));
+ }
+ return new HelpCommand(command);
+ }
+}
diff --git a/src/main/java/profplan/logic/parser/MarkCommandParser.java b/src/main/java/profplan/logic/parser/MarkCommandParser.java
new file mode 100644
index 00000000000..0341d7022b4
--- /dev/null
+++ b/src/main/java/profplan/logic/parser/MarkCommandParser.java
@@ -0,0 +1,27 @@
+package profplan.logic.parser;
+
+import profplan.logic.commands.MarkCommand;
+import profplan.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new AddCommand object
+ */
+public class MarkCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the AddCommand
+ * and returns an AddCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public MarkCommand parse(String args) throws ParseException {
+ try {
+ int number = Integer.parseInt(args.strip());
+ if (number <= 0) {
+ throw new ParseException(MarkCommand.MESSAGE_INVALID_NUMBER);
+ }
+ return new MarkCommand(number);
+ } catch (NumberFormatException e) {
+ throw new ParseException(MarkCommand.MESSAGE_FULL_HELP);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/profplan/logic/parser/Parser.java
similarity index 72%
rename from src/main/java/seedu/address/logic/parser/Parser.java
rename to src/main/java/profplan/logic/parser/Parser.java
index d6551ad8e3f..2f5d00dcd2a 100644
--- a/src/main/java/seedu/address/logic/parser/Parser.java
+++ b/src/main/java/profplan/logic/parser/Parser.java
@@ -1,7 +1,7 @@
-package seedu.address.logic.parser;
+package profplan.logic.parser;
-import seedu.address.logic.commands.Command;
-import seedu.address.logic.parser.exceptions.ParseException;
+import profplan.logic.commands.Command;
+import profplan.logic.parser.exceptions.ParseException;
/**
* Represents a Parser that is able to parse user input into a {@code Command} of type {@code T}.
diff --git a/src/main/java/profplan/logic/parser/ParserUtil.java b/src/main/java/profplan/logic/parser/ParserUtil.java
new file mode 100644
index 00000000000..b6b16d315d2
--- /dev/null
+++ b/src/main/java/profplan/logic/parser/ParserUtil.java
@@ -0,0 +1,207 @@
+package profplan.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import profplan.commons.core.Settings;
+import profplan.commons.core.index.Index;
+import profplan.commons.util.StringUtil;
+import profplan.logic.parser.exceptions.ParseException;
+import profplan.model.tag.Tag;
+import profplan.model.task.DueDate;
+import profplan.model.task.Link;
+import profplan.model.task.Name;
+import profplan.model.task.Priority;
+import profplan.model.task.Status;
+import profplan.model.task.Task;
+
+/**
+ * Contains utility methods used for parsing strings in the various *Parser classes.
+ */
+public class ParserUtil {
+
+ public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer.";
+
+ /**
+ * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be
+ * trimmed.
+ * @throws ParseException if the specified index is invalid (not non-zero unsigned integer).
+ */
+ public static Index parseIndex(String oneBasedIndex) throws ParseException {
+ String trimmedIndex = oneBasedIndex.trim();
+ if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) {
+ throw new ParseException(MESSAGE_INVALID_INDEX);
+ }
+ return Index.fromOneBased(Integer.parseInt(trimmedIndex));
+ }
+
+ /**
+ * Parses a {@code String name} into a {@code Name}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code name} is invalid.
+ */
+ public static Name parseName(String name) throws ParseException {
+ requireNonNull(name);
+ String trimmedName = name.trim();
+ if (!Name.isValidName(trimmedName)) {
+ throw new ParseException(Name.MESSAGE_CONSTRAINTS);
+ }
+ return new Name(trimmedName);
+ }
+
+ /**
+ * Parses a {@code String priority} into a {@code Priority}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code priority} is invalid.
+ */
+ public static Priority parsePriority(String priority) throws ParseException {
+ requireNonNull(priority);
+ String trimmedPriority = priority.trim();
+ if (!Priority.isValidPriority(trimmedPriority)) {
+ throw new ParseException(Priority.MESSAGE_CONSTRAINTS);
+ }
+ return new Priority(trimmedPriority);
+ }
+
+ /**
+ * Parses a {@code String status} into a {@code Status}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code status} is invalid.
+ */
+ public static Status parseStatus(String status) throws ParseException {
+ requireNonNull(status);
+ String trimmedStatus = status.trim();
+ if (!Status.isValidStatus(trimmedStatus)) {
+ throw new ParseException(Status.MESSAGE_CONSTRAINTS);
+ }
+ return new Status(trimmedStatus);
+ }
+
+ /**
+ * Parses a {@code String tag} into a {@code Tag}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code tag} is invalid.
+ */
+ public static Tag parseTag(String tag) throws ParseException {
+ requireNonNull(tag);
+ String trimmedTag = tag.trim();
+ if (!Tag.isValidTagName(trimmedTag)) {
+ throw new ParseException(Tag.MESSAGE_CONSTRAINTS);
+ }
+ return new Tag(trimmedTag);
+ }
+
+ /**
+ * Parses {@code Collection tags} into a {@code Set}.
+ */
+ public static Set parseTags(Collection tags) throws ParseException {
+ requireNonNull(tags);
+ final Set tagSet = new HashSet<>();
+ for (String tagName : tags) {
+ tagSet.add(parseTag(tagName));
+ }
+ return tagSet;
+ }
+
+ /**
+ * Parses a {@code String link} into an {@code Link}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code link} is invalid.
+ */
+ public static Link parseLink(String link) throws ParseException {
+ requireNonNull(link);
+ String trimmedLink = link.trim();
+ if (!Link.isValidLink(trimmedLink)) {
+ throw new ParseException(Link.MESSAGE_CONSTRAINTS);
+ }
+ return new Link(trimmedLink);
+ }
+
+ /**
+ * Parses a {@code String date} into a {@code DueDate}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code date} is invalid.
+ */
+ public static DueDate parseDueDate(String date) throws ParseException {
+ requireNonNull(date);
+ String trimmedDate = date.trim();
+ if (!DueDate.isValidDate(trimmedDate)) {
+ throw new ParseException(DueDate.MESSAGE_CONSTRAINTS);
+ }
+ return new DueDate(trimmedDate);
+ }
+
+ /**
+ * Parses a {@code RecurringTask}.
+ *
+ * @throws ParseException if the given {@code String} is invalid.
+ */
+ public static Task.RecurringType parseRecurringType(String input) throws ParseException {
+ requireNonNull(input);
+ String processedInput = input.trim().toLowerCase();
+
+ switch (processedInput) {
+
+ case "daily":
+ case "d":
+ return Task.RecurringType.DAILY;
+
+ case "weekly":
+ case "w":
+ return Task.RecurringType.WEEKLY;
+
+ case "monthly":
+ case "m":
+ return Task.RecurringType.MONTHLY;
+
+ case "semesterly":
+ case "s":
+ return Task.RecurringType.SEMESTERLY;
+
+ case "none":
+ return null;
+
+ default:
+ throw new ParseException("The input should be one of the following:\n"
+ + "'daily', 'weekly', 'monthly', 'semesterly', or the shortforms 'd', 'w', 'm', 's'\n"
+ + "case insensitive.");
+
+ }
+ }
+
+ /**
+ * Checks if a String is a valid integer and returns the int if so.
+ */
+ public static int parseInteger(String input) throws ParseException {
+ try {
+ return Integer.parseInt(input.trim());
+ } catch (NumberFormatException e) {
+ throw new ParseException(e.getMessage());
+ }
+ }
+
+ /**
+ * Checks if a String is a valid input value for the semesterDays setting and returns it if so.
+ */
+ public static int parseSemesterDays(String input) throws ParseException {
+ int parsedInteger = -1;
+ try {
+ parsedInteger = Integer.parseInt(input.trim());
+ } catch (NumberFormatException e) {
+ throw new ParseException("Could not parse an integer. " + e.getMessage());
+ }
+ if (parsedInteger < 0) {
+ throw new ParseException(Settings.SEMESTER_DAYS_CONSTRAINTS);
+ }
+ return parsedInteger;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/Prefix.java b/src/main/java/profplan/logic/parser/Prefix.java
similarity index 95%
rename from src/main/java/seedu/address/logic/parser/Prefix.java
rename to src/main/java/profplan/logic/parser/Prefix.java
index 348b7686c8a..ee1e35dc01d 100644
--- a/src/main/java/seedu/address/logic/parser/Prefix.java
+++ b/src/main/java/profplan/logic/parser/Prefix.java
@@ -1,4 +1,4 @@
-package seedu.address.logic.parser;
+package profplan.logic.parser;
/**
* A prefix that marks the beginning of an argument in an arguments string.
diff --git a/src/main/java/profplan/logic/parser/ProfPlanParser.java b/src/main/java/profplan/logic/parser/ProfPlanParser.java
new file mode 100644
index 00000000000..daac2dd1f76
--- /dev/null
+++ b/src/main/java/profplan/logic/parser/ProfPlanParser.java
@@ -0,0 +1,150 @@
+package profplan.logic.parser;
+
+import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static profplan.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
+
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import profplan.commons.core.LogsCenter;
+import profplan.logic.commands.AddCommand;
+import profplan.logic.commands.ClearCommand;
+import profplan.logic.commands.Command;
+import profplan.logic.commands.DeleteCommand;
+import profplan.logic.commands.DescriptionCommand;
+import profplan.logic.commands.DoNextCommand;
+import profplan.logic.commands.EditCommand;
+import profplan.logic.commands.EditSettingsCommand;
+import profplan.logic.commands.ExitCommand;
+import profplan.logic.commands.FilterCommand;
+import profplan.logic.commands.FindCommand;
+import profplan.logic.commands.HelpCommand;
+import profplan.logic.commands.ListCommand;
+import profplan.logic.commands.ListMonthCommand;
+import profplan.logic.commands.ListWeekCommand;
+import profplan.logic.commands.MarkCommand;
+import profplan.logic.commands.SortDueDateCommand;
+import profplan.logic.commands.SortPriorityCommand;
+import profplan.logic.commands.StatsCommand;
+import profplan.logic.commands.UnmarkCommand;
+import profplan.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses user input.
+ */
+public class ProfPlanParser {
+
+ /**
+ * Used for initial separation of command word and args.
+ */
+ private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)");
+ private static final Logger logger = LogsCenter.getLogger(ProfPlanParser.class);
+
+ /**
+ * Parses user input into command for execution.
+ *
+ * @param userInput full user input string
+ * @return the command based on the user input
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public Command parseCommand(String userInput) throws ParseException {
+ final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim());
+ if (!matcher.matches()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE));
+ }
+
+ final String commandWord = matcher.group("commandWord");
+ final String arguments = matcher.group("arguments");
+
+ // Note to developers: Change the log level in config.json to enable lower level (i.e., FINE, FINER and lower)
+ // log messages such as the one below.
+ // Lower level log messages are used sparingly to minimize noise in the code.
+ logger.fine("Command word: " + commandWord + "; Arguments: " + arguments);
+
+ switch (commandWord) {
+
+ case AddCommand.COMMAND_WORD:
+ return new AddCommandParser().parse(arguments);
+
+ case EditCommand.COMMAND_WORD:
+ return new EditCommandParser().parse(arguments);
+
+ case EditSettingsCommand.COMMAND_WORD:
+ return new EditSettingsCommandParser().parse(arguments);
+
+ case MarkCommand.COMMAND_WORD:
+ return new MarkCommandParser().parse(arguments);
+
+ case UnmarkCommand.COMMAND_WORD:
+ return new UnmarkCommandParser().parse(arguments);
+
+ case DeleteCommand.COMMAND_WORD:
+ return new DeleteCommandParser().parse(arguments);
+
+ case DoNextCommand.COMMAND_WORD:
+ if (arguments.isBlank()) {
+ return new DoNextCommand();
+ } else {
+ logger.finer("This user input caused a ParseException: " + userInput);
+ throw new ParseException("Did you mean the 'do_next' command? Please enter 'do_next' only.");
+ }
+
+ case ClearCommand.COMMAND_WORD:
+ return new ClearCommand();
+
+ case FindCommand.COMMAND_WORD:
+ return new FindCommandParser().parse(arguments);
+
+ case FilterCommand.COMMAND_WORD:
+ return new FilterCommandParser().parse(arguments);
+
+ case ListCommand.COMMAND_WORD:
+ if (arguments.isBlank()) {
+ return new ListCommand();
+ } else {
+ logger.finer("This user input caused a ParseException: " + userInput);
+ throw new ParseException("Did you mean the 'list' command? Please enter 'list' only.");
+ }
+
+ case ExitCommand.COMMAND_WORD:
+ return new ExitCommand();
+
+ case HelpCommand.COMMAND_WORD:
+ return new HelpCommandParser().parse(arguments);
+
+ case DescriptionCommand.COMMAND_WORD:
+ return new DescriptionCommandParser().parse(arguments);
+
+ case SortDueDateCommand.COMMAND_WORD:
+ if (arguments.isBlank()) {
+ return new SortDueDateCommand();
+ } else {
+ logger.finer("This user input caused a ParseException: " + userInput);
+ throw new ParseException("Did you mean the 'sort_duedate' command? Please enter 'sort_duedate' only.");
+ }
+
+ case SortPriorityCommand.COMMAND_WORD:
+ if (arguments.isBlank()) {
+ return new SortPriorityCommand();
+ } else {
+ logger.finer("This user input caused a ParseException: " + userInput);
+ throw new ParseException("Did you mean the 'sort_priority' command? "
+ + "Please enter 'sort_priority' only.");
+ }
+
+ case ListWeekCommand.COMMAND_WORD:
+ return new ListWeekCommand();
+
+ case ListMonthCommand.COMMAND_WORD:
+ return new ListMonthCommand();
+ case StatsCommand.COMMAND_WORD:
+ return new StatsCommand();
+
+ default:
+ logger.finer("This user input caused a ParseException: " + userInput);
+ throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
+ }
+ }
+
+}
diff --git a/src/main/java/profplan/logic/parser/UnmarkCommandParser.java b/src/main/java/profplan/logic/parser/UnmarkCommandParser.java
new file mode 100644
index 00000000000..052395bdab7
--- /dev/null
+++ b/src/main/java/profplan/logic/parser/UnmarkCommandParser.java
@@ -0,0 +1,27 @@
+package profplan.logic.parser;
+
+import profplan.logic.commands.UnmarkCommand;
+import profplan.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new AddCommand object
+ */
+public class UnmarkCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the AddCommand
+ * and returns an AddCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public UnmarkCommand parse(String args) throws ParseException {
+ try {
+ int number = Integer.parseInt(args.strip());
+ if (number <= 0) {
+ throw new ParseException(UnmarkCommand.MESSAGE_INVALID_NUMBER);
+ }
+ return new UnmarkCommand(number);
+ } catch (NumberFormatException e) {
+ throw new ParseException(UnmarkCommand.MESSAGE_FULL_HELP);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java b/src/main/java/profplan/logic/parser/exceptions/ParseException.java
similarity index 73%
rename from src/main/java/seedu/address/logic/parser/exceptions/ParseException.java
rename to src/main/java/profplan/logic/parser/exceptions/ParseException.java
index 158a1a54c1c..456575d2c5d 100644
--- a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java
+++ b/src/main/java/profplan/logic/parser/exceptions/ParseException.java
@@ -1,6 +1,6 @@
-package seedu.address.logic.parser.exceptions;
+package profplan.logic.parser.exceptions;
-import seedu.address.commons.exceptions.IllegalValueException;
+import profplan.commons.exceptions.IllegalValueException;
/**
* Represents a parse error encountered by a parser.
diff --git a/src/main/java/profplan/model/Model.java b/src/main/java/profplan/model/Model.java
new file mode 100644
index 00000000000..bd0658180c1
--- /dev/null
+++ b/src/main/java/profplan/model/Model.java
@@ -0,0 +1,127 @@
+package profplan.model;
+
+import java.nio.file.Path;
+import java.util.function.Predicate;
+
+import javafx.collections.ObservableList;
+import profplan.commons.core.GuiSettings;
+import profplan.model.task.Task;
+
+/**
+ * The API of the Model component.
+ */
+public interface Model {
+ /** {@code Predicate} that always evaluate to true */
+ Predicate PREDICATE_SHOW_ALL_TASKS = unused -> true;
+
+ /**
+ * Replaces user prefs data with the data in {@code userPrefs}.
+ */
+ void setUserPrefs(ReadOnlyUserPrefs userPrefs);
+
+ /**
+ * Returns the user prefs.
+ */
+ ReadOnlyUserPrefs getUserPrefs();
+
+ /**
+ * Returns the user prefs' GUI settings.
+ */
+ GuiSettings getGuiSettings();
+
+ /**
+ * Sets the user prefs' GUI settings.
+ */
+ void setGuiSettings(GuiSettings guiSettings);
+
+ /**
+ * Returns the user prefs' task list file path.
+ */
+ Path getProfPlanFilePath();
+
+ /**
+ * Sets the user prefs' task list file path.
+ */
+ void setProfPlanFilePath(Path profPlanFilePath);
+
+ /**
+ * Replaces task list data with the data in {@code profPlan}.
+ */
+ void setProfPlan(ReadOnlyProfPlan profPlan);
+
+ /** Returns the ProfPlan */
+ ReadOnlyProfPlan getProfPlan();
+
+ /**
+ * Returns true if a task with the same identity as {@code task} exists in the task list.
+ */
+ boolean hasTask(Task task);
+
+ /**
+ * Deletes the given task.
+ * The task must exist in the task list.
+ */
+ void deleteTask(Task target);
+
+ /**
+ * Deletes all the tasks present in the list.
+ */
+ void deleteTask();
+
+
+ /**
+ * Adds the given task.
+ * {@code task} must not already exist in the task list.
+ */
+ void addTask(Task task);
+
+ /**
+ * Marks the task at given index as done.
+ * {@code index} must be in range.
+ */
+ void markTask(int index);
+
+
+ /**
+ * Marks the task at given index as undone.
+ * {@code index} must be in range.
+ */
+ void unmarkTask(int index);
+
+
+ /**
+ * Replaces the given task {@code target} with {@code editedTask}.
+ * {@code target} must exist in the task list.
+ * The task identity of {@code editedTask} must not be the same as another existing task in the task list.
+ */
+ void setTask(Task target, Task editedTask);
+
+ /** Returns an unmodifiable view of the filtered task list */
+ ObservableList getFilteredTaskList();
+
+ /**
+ * Updates the filter of the filtered task list to filter by the given {@code predicate}.
+ * @throws NullPointerException if {@code predicate} is null.
+ */
+ void updateFilteredTaskList(Predicate predicate);
+
+ /**
+ * Returns the task to do next based on the formula: priority/#daysToDueDate.
+ */
+ Task getDoNextTask();
+
+ /**
+ * Sorts the task by nearest duedate.
+ */
+ void sortTaskByDueDate();
+
+ /**
+ * Sorts the task based on priority.
+ */
+ void sortTaskByPriority();
+
+ /**
+ * Gets completion rate of existing tasks.
+ */
+ double getCompletionRate();
+}
diff --git a/src/main/java/profplan/model/ModelManager.java b/src/main/java/profplan/model/ModelManager.java
new file mode 100644
index 00000000000..e616b012a15
--- /dev/null
+++ b/src/main/java/profplan/model/ModelManager.java
@@ -0,0 +1,294 @@
+package profplan.model;
+
+import static java.util.Objects.requireNonNull;
+
+import java.nio.file.Path;
+import java.text.SimpleDateFormat;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
+import java.util.logging.Logger;
+
+import javafx.collections.ObservableList;
+import javafx.collections.transformation.FilteredList;
+import profplan.commons.core.GuiSettings;
+import profplan.commons.core.LogsCenter;
+import profplan.commons.core.Settings;
+import profplan.commons.util.CollectionUtil;
+import profplan.model.task.DueDate;
+import profplan.model.task.Priority;
+import profplan.model.task.Status;
+import profplan.model.task.Task;
+
+/**
+ * Represents the in-memory model of the task list data.
+ */
+public class ModelManager implements Model {
+
+ private static final Logger logger = LogsCenter.getLogger(ModelManager.class);
+
+ private static UserConfigs userConfigs;
+ private final ProfPlan profPlan;
+ private final UserPrefs userPrefs;
+ private final FilteredList filteredTasks;
+
+ /**
+ * Initializes a ModelManager with the given profPlan and userPrefs.
+ */
+ public ModelManager(ReadOnlyProfPlan profPlan, ReadOnlyUserPrefs userPrefs,
+ ReadOnlyUserConfigs userConfigs) {
+ CollectionUtil.requireAllNonNull(profPlan, userPrefs);
+
+ logger.fine("Initializing with task list: " + profPlan + " and user prefs " + userPrefs);
+
+ this.profPlan = new ProfPlan(profPlan);
+ this.userPrefs = new UserPrefs(userPrefs);
+ ModelManager.userConfigs = new UserConfigs(userConfigs);
+ filteredTasks = new FilteredList<>(this.profPlan.getTaskList());
+ }
+
+ public ModelManager() {
+ this(new ProfPlan(), new UserPrefs(), new UserConfigs());
+ }
+
+ //=========== UserPrefs ==================================================================================
+
+ @Override
+ public void setUserPrefs(ReadOnlyUserPrefs userPrefs) {
+ requireNonNull(userPrefs);
+ this.userPrefs.resetData(userPrefs);
+ }
+
+ @Override
+ public ReadOnlyUserPrefs getUserPrefs() {
+ return userPrefs;
+ }
+
+ @Override
+ public GuiSettings getGuiSettings() {
+ return userPrefs.getGuiSettings();
+ }
+
+ @Override
+ public void setGuiSettings(GuiSettings guiSettings) {
+ requireNonNull(guiSettings);
+ userPrefs.setGuiSettings(guiSettings);
+ }
+
+ @Override
+ public Path getProfPlanFilePath() {
+ return userPrefs.getProfPlanFilePath();
+ }
+
+ @Override
+ public void setProfPlanFilePath(Path profPlanFilePath) {
+ requireNonNull(profPlanFilePath);
+ userPrefs.setProfPlanFilePath(profPlanFilePath);
+ }
+
+ //=========== UserConfigs ==================================================================================
+
+ public static void setUserConfigs(ReadOnlyUserConfigs userConfigs) {
+ requireNonNull(userConfigs);
+ ModelManager.userConfigs.resetData(userConfigs);
+ }
+
+ public static ReadOnlyUserConfigs getUserConfigs() {
+ return userConfigs;
+ }
+
+ public static Settings getSettings() {
+ return userConfigs.getSettings();
+ }
+
+ public static void setSettings(Settings settings) {
+ requireNonNull(settings);
+ userConfigs.setSettings(settings);
+ }
+
+ //=========== ProfPlan ================================================================================
+
+ @Override
+ public void setProfPlan(ReadOnlyProfPlan profPlan) {
+ this.profPlan.resetData(profPlan);
+ }
+
+ @Override
+ public ReadOnlyProfPlan getProfPlan() {
+ return profPlan;
+ }
+
+ @Override
+ public boolean hasTask(Task task) {
+ requireNonNull(task);
+ return profPlan.hasTask(task);
+ }
+
+ @Override
+ public void deleteTask(Task target) {
+ profPlan.removeTask(target);
+ }
+
+ @Override
+ public void deleteTask() {
+ profPlan.removeTask();
+ }
+
+ @Override
+ public void addTask(Task task) {
+ profPlan.addTask(task);
+ updateFilteredTaskList(PREDICATE_SHOW_ALL_TASKS);
+ }
+
+ @Override
+ public void markTask(int index) throws IllegalArgumentException {
+ Task temp = profPlan.getTaskList().get(index);
+ temp.setStatus(Status.DONE_STATUS);
+ profPlan.setTask(profPlan.getTaskList().get(index), temp);
+ }
+
+ @Override
+ public void unmarkTask(int index) {
+ Task temp = profPlan.getTaskList().get(index);
+ temp.setStatus(Status.UNDONE_STATUS);
+ profPlan.setTask(profPlan.getTaskList().get(index), temp);
+ }
+
+ @Override
+ public void setTask(Task target, Task editedTask) {
+ CollectionUtil.requireAllNonNull(target, editedTask);
+ profPlan.setTask(target, editedTask);
+ }
+
+ //=========== Filtered Task List Accessors =============================================================
+
+ /**
+ * Returns an unmodifiable view of the list of {@code Task} backed by the internal list of
+ * {@code versionedProfPlan}
+ */
+ @Override
+ public ObservableList getFilteredTaskList() {
+ return filteredTasks;
+ }
+
+ @Override
+ public void updateFilteredTaskList(Predicate predicate) {
+ requireNonNull(predicate);
+ filteredTasks.setPredicate(predicate);
+ }
+
+ @Override
+ public void sortTaskByDueDate() {
+ if (this.filteredTasks.isEmpty()) {
+ throw new UnsupportedOperationException("Can not sort since there are no tasks "
+ + "displayed currently in the UI");
+ }
+ profPlan.getTaskList().sort(Comparator.comparing(Task::getDueDate));
+ }
+
+ @Override
+ public void sortTaskByPriority() {
+ if (this.filteredTasks.isEmpty()) {
+ throw new UnsupportedOperationException("Can not sort since there are no tasks "
+ + "displayed currently in the UI");
+ }
+ profPlan.getTaskList().sort(Comparator.comparing(Task::getPriority));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof ModelManager)) {
+ return false;
+ }
+
+ ModelManager otherModelManager = (ModelManager) other;
+ return profPlan.equals(otherModelManager.profPlan)
+ && userPrefs.equals(otherModelManager.userPrefs)
+ && filteredTasks.equals(otherModelManager.filteredTasks);
+ }
+
+ /**
+ * Returns the task to do next based on the formula: priority/#daysToDueDate.
+ * */
+ @Override
+ public Task getDoNextTask() {
+ ObservableList internalUnmodifiableList = profPlan.getTaskList();
+ Task recommendedTask = null;
+ double highestComputedValue = Double.NEGATIVE_INFINITY; // Initialize with a very low value
+
+ // Get the current date
+ SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
+ Date currentDate = new Date();
+ String curDate = dateFormat.format(currentDate);
+
+ // Iterate through the list to find the task with the highest computed value
+ for (Task task : internalUnmodifiableList) {
+ Priority priority = task.getPriority();
+ DueDate dueDate = task.getDueDate();
+
+ // Calculate the number of days left to the due date
+ double daysLeft = getDaysUntilDueDate(dueDate, curDate);
+
+ // Calculate the computed value (priority divided by days left)
+ double computedValue = priorityValue(priority) / (double) daysLeft;
+ System.out.println(task.getName().toString());
+ System.out.println(computedValue);
+
+ // Update the recommended task if we find a higher computed value
+ if ((computedValue > highestComputedValue) && (task.getStatus().equals(Status.UNDONE_STATUS))) {
+ highestComputedValue = computedValue;
+ recommendedTask = task;
+ System.out.println(task.getName().toString());
+ }
+ }
+
+ return recommendedTask;
+ }
+
+ // Helper method to calculate the number of days left to the due date
+ public double getDaysUntilDueDate(DueDate dueDate, String curDate) {
+ SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
+ try {
+ Date dueDateDate = dateFormat.parse(dueDate.toString());
+ Date currentDate = dateFormat.parse(curDate);
+
+ long difference = dueDateDate.getTime() - currentDate.getTime();
+ if (difference < 0) {
+ difference = 1;
+ }
+ double days = TimeUnit.DAYS.convert(difference, TimeUnit.MILLISECONDS);
+ if (days == 0) {
+ days = 0.1;
+ }
+ System.out.println(days);
+ return days;
+ } catch (java.text.ParseException e) {
+ // Handle parsing errors
+ return -1;
+ }
+ }
+
+ // Helper method to calculate the priority value
+ private double priorityValue(Priority priority) {
+ // convert the priority string to a numeric value.
+ return Double.parseDouble(priority.toString());
+ }
+
+ public double getCompletionRate() {
+ Predicate super Task> currentPredicate = filteredTasks.getPredicate();
+ filteredTasks.setPredicate(PREDICATE_SHOW_ALL_TASKS);
+ int totalTasks = filteredTasks.size();
+ filteredTasks.setPredicate(x -> x.getStatus().equals(Status.DONE_STATUS));
+ int doneTasks = filteredTasks.size();
+ filteredTasks.setPredicate(currentPredicate);
+ return (double) doneTasks / totalTasks;
+ }
+
+}
+
diff --git a/src/main/java/profplan/model/ProfPlan.java b/src/main/java/profplan/model/ProfPlan.java
new file mode 100644
index 00000000000..990118f388b
--- /dev/null
+++ b/src/main/java/profplan/model/ProfPlan.java
@@ -0,0 +1,138 @@
+package profplan.model;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import javafx.collections.ObservableList;
+import profplan.commons.util.ToStringBuilder;
+import profplan.model.task.Task;
+import profplan.model.task.UniqueTaskList;
+
+/**
+ * Wraps all data at the profPlan level
+ * Duplicates are not allowed (by .isSameTask comparison)
+ */
+public class ProfPlan implements ReadOnlyProfPlan {
+
+ private final UniqueTaskList tasks;
+
+ /*
+ * 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.
+ */
+ {
+ tasks = new UniqueTaskList();
+ }
+
+ public ProfPlan() {}
+
+ /**
+ * Creates an ProfPlan using the Tasks in the {@code toBeCopied}
+ */
+ public ProfPlan(ReadOnlyProfPlan toBeCopied) {
+ this();
+ resetData(toBeCopied);
+ }
+
+ //// list overwrite operations
+
+ /**
+ * Replaces the contents of the task list with {@code tasks}.
+ * {@code tasks} must not contain duplicate tasks.
+ */
+ public void setTasks(List tasks) {
+ this.tasks.setTasks(tasks);
+ }
+
+ /**
+ * Resets the existing data of this {@code ProfPlan} with {@code newData}.
+ */
+ public void resetData(ReadOnlyProfPlan newData) {
+ requireNonNull(newData);
+
+ setTasks(newData.getTaskList());
+ }
+
+ //// task-level operations
+
+ /**
+ * Returns true if a task with the same identity as {@code task} exists in the task list.
+ */
+ public boolean hasTask(Task task) {
+ requireNonNull(task);
+ return tasks.contains(task);
+ }
+
+ /**
+ * Adds a task to the task list.
+ * The task must not already exist in the task list.
+ */
+ public void addTask(Task p) {
+ tasks.add(p);
+ }
+
+ /**
+ * Replaces the given task {@code target} in the list with {@code editedTask}.
+ * {@code target} must exist in the task list.
+ * The task identity of {@code editedTask} must not be the same as another existing task in the task list.
+ */
+ public void setTask(Task target, Task editedTask) {
+ requireNonNull(editedTask);
+
+ tasks.setTask(target, editedTask);
+ }
+
+ /**
+ * Removes {@code key} from this {@code ProfPlan}.
+ * {@code key} must exist in the task list.
+ */
+ public void removeTask(Task key) {
+ tasks.remove(key);
+ }
+
+ /**
+ * Removes all the tasks from the Task List
+ */
+
+ public void removeTask() {
+ tasks.remove();
+ }
+
+ //// util methods
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("tasks", tasks)
+ .toString();
+ }
+
+ @Override
+ public ObservableList getTaskList() {
+ return tasks.getInternalList();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof ProfPlan)) {
+ return false;
+ }
+
+ ProfPlan otherProfPlan = (ProfPlan) other;
+ return tasks.equals(otherProfPlan.tasks);
+ }
+
+ @Override
+ public int hashCode() {
+ return tasks.hashCode();
+ }
+}
diff --git a/src/main/java/profplan/model/ReadOnlyProfPlan.java b/src/main/java/profplan/model/ReadOnlyProfPlan.java
new file mode 100644
index 00000000000..e1ff57aa0f7
--- /dev/null
+++ b/src/main/java/profplan/model/ReadOnlyProfPlan.java
@@ -0,0 +1,17 @@
+package profplan.model;
+
+import javafx.collections.ObservableList;
+import profplan.model.task.Task;
+
+/**
+ * Unmodifiable view of an task list
+ */
+public interface ReadOnlyProfPlan {
+
+ /**
+ * Returns an unmodifiable view of the tasks list.
+ * This list will not contain any duplicate tasks.
+ */
+ ObservableList getTaskList();
+
+}
diff --git a/src/main/java/profplan/model/ReadOnlyUserConfigs.java b/src/main/java/profplan/model/ReadOnlyUserConfigs.java
new file mode 100644
index 00000000000..ed312df1329
--- /dev/null
+++ b/src/main/java/profplan/model/ReadOnlyUserConfigs.java
@@ -0,0 +1,16 @@
+package profplan.model;
+
+import java.nio.file.Path;
+
+import profplan.commons.core.Settings;
+
+/**
+ * Unmodifiable view of user prefs.
+ */
+public interface ReadOnlyUserConfigs {
+
+ Settings getSettings();
+
+ Path getProfPlanFilePath();
+
+}
diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/profplan/model/ReadOnlyUserPrefs.java
similarity index 57%
rename from src/main/java/seedu/address/model/ReadOnlyUserPrefs.java
rename to src/main/java/profplan/model/ReadOnlyUserPrefs.java
index befd58a4c73..2bb85335d16 100644
--- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java
+++ b/src/main/java/profplan/model/ReadOnlyUserPrefs.java
@@ -1,8 +1,8 @@
-package seedu.address.model;
+package profplan.model;
import java.nio.file.Path;
-import seedu.address.commons.core.GuiSettings;
+import profplan.commons.core.GuiSettings;
/**
* Unmodifiable view of user prefs.
@@ -11,6 +11,6 @@ public interface ReadOnlyUserPrefs {
GuiSettings getGuiSettings();
- Path getAddressBookFilePath();
+ Path getProfPlanFilePath();
}
diff --git a/src/main/java/profplan/model/UserConfigs.java b/src/main/java/profplan/model/UserConfigs.java
new file mode 100644
index 00000000000..920ea3c1b80
--- /dev/null
+++ b/src/main/java/profplan/model/UserConfigs.java
@@ -0,0 +1,88 @@
+package profplan.model;
+
+import static java.util.Objects.requireNonNull;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Objects;
+
+import profplan.commons.core.Settings;
+
+/**
+ * Stores global settings used in various commands. These can be updated by the user.
+ */
+public class UserConfigs implements ReadOnlyUserConfigs {
+
+ private Settings settings = new Settings();
+ private Path profPlanFilePath = Paths.get("data" , "profplan.json");
+
+ /**
+ * Creates a {@code UserConfigs} with default values.
+ */
+ public UserConfigs() {}
+
+ /**
+ * Creates a {@code UserConfigs} with the prefs in {@code userConfigs}.
+ */
+ public UserConfigs(ReadOnlyUserConfigs userConfigs) {
+ this();
+ resetData(userConfigs);
+ }
+
+ /**
+ * Resets the existing data of this {@code UserConfigs} with {@code newUserConfigs}.
+ */
+ public void resetData(ReadOnlyUserConfigs newUserConfigs) {
+ requireNonNull(newUserConfigs);
+ setSettings(newUserConfigs.getSettings());
+ setProfPlanFilePath(newUserConfigs.getProfPlanFilePath());
+ }
+
+ public Settings getSettings() {
+ return settings;
+ }
+
+ public void setSettings(Settings settings) {
+ requireNonNull(settings);
+ this.settings = settings;
+ }
+
+ public Path getProfPlanFilePath() {
+ return profPlanFilePath;
+ }
+
+ public void setProfPlanFilePath(Path profPlanFilePath) {
+ requireNonNull(profPlanFilePath);
+ this.profPlanFilePath = profPlanFilePath;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof UserConfigs)) {
+ return false;
+ }
+
+ UserConfigs otherUserConfigs = (UserConfigs) other;
+ return settings.equals(otherUserConfigs.settings)
+ && profPlanFilePath.equals(otherUserConfigs.profPlanFilePath);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(settings, profPlanFilePath);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Settings : " + settings);
+ sb.append("\nLocal data file location : " + profPlanFilePath);
+ return sb.toString();
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/profplan/model/UserPrefs.java
similarity index 70%
rename from src/main/java/seedu/address/model/UserPrefs.java
rename to src/main/java/profplan/model/UserPrefs.java
index 6be655fb4c7..d8957400b37 100644
--- a/src/main/java/seedu/address/model/UserPrefs.java
+++ b/src/main/java/profplan/model/UserPrefs.java
@@ -1,4 +1,4 @@
-package seedu.address.model;
+package profplan.model;
import static java.util.Objects.requireNonNull;
@@ -6,7 +6,7 @@
import java.nio.file.Paths;
import java.util.Objects;
-import seedu.address.commons.core.GuiSettings;
+import profplan.commons.core.GuiSettings;
/**
* Represents User's preferences.
@@ -14,7 +14,7 @@
public class UserPrefs implements ReadOnlyUserPrefs {
private GuiSettings guiSettings = new GuiSettings();
- private Path addressBookFilePath = Paths.get("data" , "addressbook.json");
+ private Path profPlanFilePath = Paths.get("data" , "profplan.json");
/**
* Creates a {@code UserPrefs} with default values.
@@ -35,7 +35,7 @@ public UserPrefs(ReadOnlyUserPrefs userPrefs) {
public void resetData(ReadOnlyUserPrefs newUserPrefs) {
requireNonNull(newUserPrefs);
setGuiSettings(newUserPrefs.getGuiSettings());
- setAddressBookFilePath(newUserPrefs.getAddressBookFilePath());
+ setProfPlanFilePath(newUserPrefs.getProfPlanFilePath());
}
public GuiSettings getGuiSettings() {
@@ -47,13 +47,13 @@ public void setGuiSettings(GuiSettings guiSettings) {
this.guiSettings = guiSettings;
}
- public Path getAddressBookFilePath() {
- return addressBookFilePath;
+ public Path getProfPlanFilePath() {
+ return profPlanFilePath;
}
- public void setAddressBookFilePath(Path addressBookFilePath) {
- requireNonNull(addressBookFilePath);
- this.addressBookFilePath = addressBookFilePath;
+ public void setProfPlanFilePath(Path profPlanFilePath) {
+ requireNonNull(profPlanFilePath);
+ this.profPlanFilePath = profPlanFilePath;
}
@Override
@@ -69,19 +69,19 @@ public boolean equals(Object other) {
UserPrefs otherUserPrefs = (UserPrefs) other;
return guiSettings.equals(otherUserPrefs.guiSettings)
- && addressBookFilePath.equals(otherUserPrefs.addressBookFilePath);
+ && profPlanFilePath.equals(otherUserPrefs.profPlanFilePath);
}
@Override
public int hashCode() {
- return Objects.hash(guiSettings, addressBookFilePath);
+ return Objects.hash(guiSettings, profPlanFilePath);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Gui Settings : " + guiSettings);
- sb.append("\nLocal data file location : " + addressBookFilePath);
+ sb.append("\nLocal data file location : " + profPlanFilePath);
return sb.toString();
}
diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/profplan/model/tag/Tag.java
similarity index 86%
rename from src/main/java/seedu/address/model/tag/Tag.java
rename to src/main/java/profplan/model/tag/Tag.java
index f1a0d4e233b..9076ba84861 100644
--- a/src/main/java/seedu/address/model/tag/Tag.java
+++ b/src/main/java/profplan/model/tag/Tag.java
@@ -1,10 +1,11 @@
-package seedu.address.model.tag;
+package profplan.model.tag;
import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
+
+import profplan.commons.util.AppUtil;
/**
- * Represents a Tag in the address book.
+ * Represents a Tag in the task list.
* Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)}
*/
public class Tag {
@@ -21,7 +22,7 @@ public class Tag {
*/
public Tag(String tagName) {
requireNonNull(tagName);
- checkArgument(isValidTagName(tagName), MESSAGE_CONSTRAINTS);
+ AppUtil.checkArgument(isValidTagName(tagName), MESSAGE_CONSTRAINTS);
this.tagName = tagName;
}
diff --git a/src/main/java/profplan/model/task/Description.java b/src/main/java/profplan/model/task/Description.java
new file mode 100644
index 00000000000..2e9bcea120a
--- /dev/null
+++ b/src/main/java/profplan/model/task/Description.java
@@ -0,0 +1,48 @@
+package profplan.model.task;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Represents a Task's description in the task list.
+ * Guarantees: immutable
+ */
+public class Description {
+
+ public final String description;
+
+ /**
+ * Constructs a {@code Description}.
+ *
+ * @param description A description.
+ */
+ public Description(String description) {
+ requireNonNull(description);
+ this.description = description;
+ }
+
+ @Override
+ public String toString() {
+ return description;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Description)) {
+ return false;
+ }
+
+ Description otherDescription = (Description) other;
+ return description.equals(otherDescription.description);
+ }
+
+ @Override
+ public int hashCode() {
+ return description.hashCode();
+ }
+
+}
diff --git a/src/main/java/profplan/model/task/DueDate.java b/src/main/java/profplan/model/task/DueDate.java
new file mode 100644
index 00000000000..ce68e127933
--- /dev/null
+++ b/src/main/java/profplan/model/task/DueDate.java
@@ -0,0 +1,159 @@
+package profplan.model.task;
+
+import static java.util.Objects.requireNonNull;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+
+import profplan.commons.util.AppUtil;
+
+/**
+ * Represents a Task's due date in the ProfPlan.
+ */
+public class DueDate implements Comparable {
+ public static final String MESSAGE_CONSTRAINTS =
+ "Due date should be of dd-MM-yyyy format, and should be between 2000 and 2030.";
+
+ private static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
+ private static LocalDate min = LocalDate.of(1999, 12, 31);
+ private static LocalDate max = LocalDate.of(2031, 1, 1);
+
+ private String value;
+
+ private LocalDate parsedValue = null;
+
+ /**
+ * Constructs a {@code DueDate}.
+ *
+ * @param date A valid date.
+ */
+ public DueDate(String date) {
+ requireNonNull(date);
+ value = date;
+ AppUtil.checkArgument(isValidDate(this), MESSAGE_CONSTRAINTS);
+ value = parsedValue == null ? date : dateTimeFormatter.format(parsedValue);
+ }
+
+ /**
+ * Returns true if a given Duedate is of correct format.
+ */
+ public static boolean isValidDate(DueDate test) {
+ try {
+ LocalDate parsed = LocalDate.parse(test.value, dateTimeFormatter);
+ if (parsed.isBefore(max) && parsed.isAfter(min)) {
+ test.parsedValue = parsed;
+ return true;
+ } else {
+ return false;
+ }
+
+ } catch (DateTimeParseException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Overloaded method for isValidDate
+ */
+ public static boolean isValidDate(String test) {
+ try {
+ LocalDate parsed = LocalDate.parse(test, dateTimeFormatter);
+ return parsed.isBefore(max) && parsed.isAfter(min);
+
+ } catch (DateTimeParseException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Checks whether the current date is before, on equals the given date
+ */
+ public boolean isIncludedorBefore(DueDate otherDate) {
+ return this.parsedValue.isBefore(otherDate.parsedValue)
+ || this.parsedValue.equals(otherDate.parsedValue);
+ }
+
+ /**
+ * Returns true if due date is within a week of today.
+ */
+ public boolean isWithinWeek() {
+ LocalDate endOfWeek = LocalDate.now().plusWeeks(1);
+ System.out.println(this.parsedValue);
+
+ return !this.parsedValue.isBefore(LocalDate.now())
+ && !this.parsedValue.isAfter(endOfWeek);
+ }
+
+ /**
+ * Returns true if due date is within a month of today.
+ */
+ public boolean isWithinMonth() {
+ LocalDate endOfMonth = LocalDate.now().plusMonths(1);
+
+ return !this.parsedValue.isBefore(LocalDate.now())
+ && !this.parsedValue.isAfter(endOfMonth);
+ }
+
+ /**
+ * Increments a DueDate by the number of days specified.
+ * @param days The number of days to increment by. Must be between 0-31.
+ * @return A new DueDate object, with the incremented value.
+ */
+ public DueDate addDays(long days) throws IllegalArgumentException {
+ return new DueDate(parsedValue.plusDays(days).format(dateTimeFormatter));
+ }
+
+ /**
+ * Increments a DueDate by 1 month.
+ * @return A new DueDate object, with the incremented value.
+ */
+ public DueDate addMonth() {
+ return new DueDate(parsedValue.plusMonths(1).format(dateTimeFormatter));
+ }
+
+ /**
+ * Returns the format for DueDate
+ */
+ public static DateTimeFormatter getDateFormat() {
+ return dateTimeFormatter;
+ }
+
+
+ @Override
+ public String toString() {
+ return parsedValue == null ? value : dateTimeFormatter.format(parsedValue);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof DueDate)) {
+ return false;
+ }
+
+ DueDate otherDate = (DueDate) other;
+ return value.equals(otherDate.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override
+ public int compareTo(DueDate o) {
+ try {
+ LocalDate thisDate = LocalDate.parse(this.value, dateTimeFormatter);
+ LocalDate otherDate = LocalDate.parse(o.value, dateTimeFormatter);
+
+ return thisDate.compareTo(otherDate);
+ } catch (DateTimeParseException e) {
+ return 0;
+ }
+ }
+}
diff --git a/src/main/java/profplan/model/task/Link.java b/src/main/java/profplan/model/task/Link.java
new file mode 100644
index 00000000000..d50f090274e
--- /dev/null
+++ b/src/main/java/profplan/model/task/Link.java
@@ -0,0 +1,67 @@
+package profplan.model.task;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Represents a Task's link in the ProfPlan.
+ * Guarantees: immutable; is valid as declared in {@link #isValidLink(String)}
+ */
+public class Link {
+
+ public static final String MESSAGE_CONSTRAINTS = "Links should be a valid URL "
+ + "and adhere to the following constraint:\n"
+ + "The domain name is made up of domain labels "
+ + "separated by periods.\n"
+ + "The domain name must:\n"
+ + " - end with a domain label at least 2 characters long\n"
+ + " - have each domain label start and end with alphanumeric characters\n";
+
+ public static final String VALIDATION_REGEX = "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)"
+ + "?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$";
+
+ public final String value;
+
+ /**
+ * Constructs an {@code Link}.
+ *
+ * @param url A valid url.
+ */
+ public Link(String url) {
+ requireNonNull(url);
+ value = url;
+ }
+
+ /**
+ * Returns if a given string is a valid url.
+ */
+ public static boolean isValidLink(String test) {
+ return true;
+ //return (test.equals("-") || test.matches(VALIDATION_REGEX));
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Link)) {
+ return false;
+ }
+
+ Link otherLink = (Link) other;
+ return value.equals(otherLink.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/profplan/model/task/Name.java
similarity index 82%
rename from src/main/java/seedu/address/model/person/Name.java
rename to src/main/java/profplan/model/task/Name.java
index 173f15b9b00..c30b1a47307 100644
--- a/src/main/java/seedu/address/model/person/Name.java
+++ b/src/main/java/profplan/model/task/Name.java
@@ -1,10 +1,11 @@
-package seedu.address.model.person;
+package profplan.model.task;
import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
+
+import profplan.commons.util.AppUtil;
/**
- * Represents a Person's name in the address book.
+ * Represents a Task's name in the task list.
* Guarantees: immutable; is valid as declared in {@link #isValidName(String)}
*/
public class Name {
@@ -13,7 +14,7 @@ public class Name {
"Names should only contain alphanumeric characters and spaces, and it should not be blank";
/*
- * The first character of the address must not be a whitespace,
+ * The first character of the name must not be a whitespace,
* otherwise " " (a blank string) becomes a valid input.
*/
public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*";
@@ -27,7 +28,7 @@ public class Name {
*/
public Name(String name) {
requireNonNull(name);
- checkArgument(isValidName(name), MESSAGE_CONSTRAINTS);
+ AppUtil.checkArgument(isValidName(name), MESSAGE_CONSTRAINTS);
fullName = name;
}
diff --git a/src/main/java/profplan/model/task/Priority.java b/src/main/java/profplan/model/task/Priority.java
new file mode 100644
index 00000000000..f98e17ca41e
--- /dev/null
+++ b/src/main/java/profplan/model/task/Priority.java
@@ -0,0 +1,66 @@
+package profplan.model.task;
+
+import static java.util.Objects.requireNonNull;
+
+import profplan.commons.util.AppUtil;
+
+/**
+ * Represents a Task's priority in the task list.
+ * Guarantees: immutable; is valid as declared in {@link #isValidPriority(String)}
+ */
+public class Priority implements Comparable {
+
+
+ public static final String MESSAGE_CONSTRAINTS =
+ "Priority should only contain numbers between 1-10";
+ public static final String VALIDATION_REGEX = "^(10|[1-9])$"; //edit later in v1.3 to change tests and data
+ public final String value;
+
+ /**
+ * Constructs a {@code Priority}.
+ *
+ * @param priority A valid priority number.
+ */
+ public Priority(String priority) {
+ requireNonNull(priority);
+ AppUtil.checkArgument(isValidPriority(priority), MESSAGE_CONSTRAINTS);
+ value = priority;
+ }
+
+ /**
+ * Returns true if a given string is a valid priority number.
+ */
+ public static boolean isValidPriority(String test) {
+ return test.matches(VALIDATION_REGEX);
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Priority)) {
+ return false;
+ }
+
+ Priority otherPriority = (Priority) other;
+ return value.equals(otherPriority.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override
+ public int compareTo(Priority o) {
+ return (Integer.parseInt(o.value) - Integer.parseInt(this.value));
+ }
+}
diff --git a/src/main/java/profplan/model/task/Status.java b/src/main/java/profplan/model/task/Status.java
new file mode 100644
index 00000000000..91f20cd32a0
--- /dev/null
+++ b/src/main/java/profplan/model/task/Status.java
@@ -0,0 +1,68 @@
+package profplan.model.task;
+
+import static java.util.Objects.requireNonNull;
+
+import profplan.commons.util.AppUtil;
+
+/**
+ * Represents a Task's Status in ProfPlan.
+ * Guarantees: immutable; is valid as declared in {@link #isValidStatus(String)}
+ */
+public class Status {
+
+ public static final String MESSAGE_CONSTRAINTS =
+ "Status should only should only be done or undone";
+
+ public static final String VALIDATION_REGEX = "\\bdone\\b|\\bundone\\b";
+ public static final Status DONE_STATUS = new Status("done");
+ public static final Status UNDONE_STATUS = new Status("undone");
+
+ public final String status;
+
+
+
+ /**
+ * Constructs a {@code Name}.
+ *
+ * @param status A valid status.
+ */
+ public Status(String status) {
+ requireNonNull(status);
+ AppUtil.checkArgument(isValidStatus(status), MESSAGE_CONSTRAINTS);
+ this.status = status;
+ }
+
+ /**
+ * Returns true if a given string is a valid name.
+ */
+ public static boolean isValidStatus(String test) {
+ return test.matches(VALIDATION_REGEX);
+ }
+
+
+ @Override
+ public String toString() {
+ return status;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Status)) {
+ return false;
+ }
+
+ Status otherStatus = (Status) other;
+ return status.equals(otherStatus.status);
+ }
+
+ @Override
+ public int hashCode() {
+ return status.hashCode();
+ }
+
+}
diff --git a/src/main/java/profplan/model/task/Task.java b/src/main/java/profplan/model/task/Task.java
new file mode 100644
index 00000000000..5a2e2082fb5
--- /dev/null
+++ b/src/main/java/profplan/model/task/Task.java
@@ -0,0 +1,252 @@
+package profplan.model.task;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import profplan.commons.util.CollectionUtil;
+import profplan.commons.util.ToStringBuilder;
+import profplan.model.ModelManager;
+import profplan.model.tag.Tag;
+
+/**
+ * Represents a Task in the task list.
+ * Guarantees: details are present and not null, field values are validated, immutable.
+ */
+public class Task implements Comparable {
+
+ // Identity fields
+ private final Name name;
+ private final Priority priority;
+ private final boolean isRecurringTask;
+ private final RecurringType recurringType;
+
+ // Data fields
+ private final Link link;
+ private Status status;
+ private final Set tags = new HashSet<>();
+ private DueDate dueDate;
+ private final Description description;
+
+ /**
+ * Encapsulates the different types of recurring Tasks.
+ */
+ public enum RecurringType {
+ DAILY("Daily"), WEEKLY("Weekly"), MONTHLY("Monthly"), SEMESTERLY("Semesterly");
+
+ private final String name;
+ RecurringType(String name) {
+ this.name = name;
+ }
+ private String getName() {
+ return name;
+ }
+ }
+
+ /**
+ * Every field except status must be present and not null.
+ */
+ public Task(Name name, Priority priority, boolean isRecurringTask, RecurringType recurringType,
+ Set tags, DueDate dueDate,
+ Link link, Description description) {
+ CollectionUtil.requireAllNonNull(name, priority, tags, dueDate);
+ this.name = name;
+ this.priority = priority;
+ this.isRecurringTask = isRecurringTask;
+ this.recurringType = recurringType;
+ this.status = Status.UNDONE_STATUS;
+ this.tags.addAll(tags);
+ this.dueDate = dueDate;
+ this.link = link;
+ this.description = description;
+
+ if (isRecurringTask) {
+ this.tags.add(new Tag(recurringType.getName()));
+ }
+ }
+
+ /**
+ * Every field must be present
+ */
+ public Task(Name name, Priority priority, boolean isRecurringTask, RecurringType recurringType,
+ Status status,
+ Set tags,
+ DueDate dueDate, Link link, Description description) {
+ CollectionUtil.requireAllNonNull(name, priority, tags, dueDate);
+ this.name = name;
+ this.priority = priority;
+ this.isRecurringTask = isRecurringTask;
+ this.recurringType = recurringType;
+ this.status = status;
+ this.tags.addAll(tags);
+ this.dueDate = dueDate;
+ this.link = link;
+ this.description = description;
+
+ if (isRecurringTask) {
+ this.tags.add(new Tag(recurringType.getName()));
+ }
+ }
+
+ /**
+ * Overloaded constructor to create a Task given another Task
+ * @param task The task to copy from.
+ */
+ public Task(Task task) {
+ this.name = task.name;
+ this.priority = task.priority;
+ this.isRecurringTask = task.isRecurringTask;
+ this.recurringType = task.recurringType;
+ this.tags.addAll(task.getTags());
+ this.status = task.status;
+ this.dueDate = task.dueDate;
+ this.link = task.link;
+ this.description = task.description;
+ }
+
+ public Name getName() {
+ return name;
+ }
+
+ public Priority getPriority() {
+ return priority;
+ }
+
+ public boolean getIsRecurring() {
+ return isRecurringTask;
+ }
+
+ public RecurringType getRecurringType() {
+ return recurringType;
+ }
+
+ public Link getLink() {
+ return link;
+ }
+
+ public DueDate getDueDate() {
+ return dueDate;
+ }
+
+ public Status getStatus() {
+ return status;
+ }
+
+ public void setStatus(Status status) throws IllegalArgumentException {
+ if (status == Status.DONE_STATUS && this.isRecurringTask) {
+
+ switch (recurringType) {
+
+ case DAILY:
+ dueDate = dueDate.addDays(1);
+ break;
+
+ case WEEKLY:
+ dueDate = dueDate.addDays(7);
+ break;
+
+ case MONTHLY:
+ dueDate = dueDate.addMonth();
+ break;
+
+ case SEMESTERLY:
+ dueDate = dueDate.addDays(ModelManager.getSettings().getSemesterDays());
+ break;
+
+ default:
+ throw new RuntimeException("Execution should not reach this point.");
+
+ }
+
+ } else {
+ this.status = status;
+ }
+ }
+
+ /**
+ * Returns an immutable tag set, which throws {@code UnsupportedOperationException}
+ * if modification is attempted.
+ */
+ public Set getTags() {
+ return Collections.unmodifiableSet(tags);
+ }
+
+ public Description getDescription() {
+ return description;
+ }
+
+ /**
+ * Returns true if both tasks have the same name.
+ * This defines a weaker notion of equality between two tasks.
+ */
+ public boolean isSameTask(Task otherTask) {
+ if (otherTask == this) {
+ return true;
+ }
+
+ return otherTask != null
+ && otherTask.getName().equals(getName());
+ }
+
+ /**
+ * Returns true if both tasks have the same identity and data fields.
+ * This defines a stronger notion of equality between two tasks.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Task)) {
+ return false;
+ }
+
+ Task otherTask = (Task) other;
+ return name.equals(otherTask.name)
+ && priority.equals(otherTask.priority)
+ && tags.equals(otherTask.tags)
+ && dueDate.equals(otherTask.dueDate)
+ && status.equals(otherTask.status)
+ && link.equals(otherTask.link)
+ && description.equals(otherTask.description);
+ }
+
+ @Override
+ public int hashCode() {
+ // use this method for custom fields hashing instead of implementing your own
+ return Objects.hash(name, priority, status, tags, dueDate, link, description);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("name", name)
+ .add("priority", priority)
+ .add("status", status)
+ .add("tags", tags)
+ .add("dueDate", dueDate)
+ .add("link", link)
+ .add("description", description)
+ .toString();
+ }
+
+
+ /**
+ * Returns a beautified string representation of the task.
+ * @return a string representation of the task with name, priority and dueDate.
+ */
+ public String beautifyString() {
+ return this.getName().toString() + ", Priority: " + this.getPriority().toString()
+ + ", DueDate: " + this.getDueDate().toString();
+ }
+
+
+ @Override
+ public int compareTo(Task o) {
+ return this.dueDate.compareTo(o.dueDate);
+ }
+
+}
diff --git a/src/main/java/profplan/model/task/UniqueTaskList.java b/src/main/java/profplan/model/task/UniqueTaskList.java
new file mode 100644
index 00000000000..22020ec1db2
--- /dev/null
+++ b/src/main/java/profplan/model/task/UniqueTaskList.java
@@ -0,0 +1,187 @@
+package profplan.model.task;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Iterator;
+import java.util.List;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import profplan.commons.util.CollectionUtil;
+import profplan.model.task.exceptions.DuplicateTaskException;
+import profplan.model.task.exceptions.TaskNotFoundException;
+
+/**
+ * A list of tasks that enforces uniqueness between its elements and does not allow nulls.
+ * A task is considered unique by comparing using {@code Task#isSameTask(Task)}. As such, adding and updating of
+ * tasks 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 UniqueTaskList. However, the removal of a task 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.
+ *
+ * @see Task#isSameTask(Task)
+ */
+public class UniqueTaskList implements Iterable {
+
+ 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);
+ }
+
+ /**
+ * Adds a task to the list.
+ * The task must not already exist in the list.
+ */
+ public void add(Task toAdd) {
+ requireNonNull(toAdd);
+ if (contains(toAdd)) {
+ throw new DuplicateTaskException();
+ }
+ internalList.add(toAdd);
+ }
+
+ /**
+ * Marks the task at given index as done
+ * The index must be in range.
+ */
+ public void mark(int index) {
+ if (index > internalList.size()) {
+ throw new TaskNotFoundException();
+ }
+ internalList.get(index).setStatus(Status.DONE_STATUS);
+ }
+
+ /**
+ * Marks the task at given index as undone
+ * The index must be in range.
+ */
+ public void unmark(int index) {
+ if (index > internalList.size()) {
+ throw new TaskNotFoundException();
+ }
+ internalList.get(index).setStatus(Status.UNDONE_STATUS);
+ }
+
+ /**
+ * Replaces the task {@code target} in the list with {@code editedTask}.
+ * {@code target} must exist in the list.
+ * The task identity of {@code editedTask} must not be the same as another existing task in the list.
+ */
+ public void setTask(Task target, Task editedTask) {
+ CollectionUtil.requireAllNonNull(target, editedTask);
+
+ int index = internalList.indexOf(target);
+ if (index == -1) {
+ throw new TaskNotFoundException();
+ }
+
+ if (!target.isSameTask(editedTask) && contains(editedTask)) {
+ throw new DuplicateTaskException();
+ }
+
+ internalList.set(index, editedTask);
+ }
+
+ /**
+ * Removes the equivalent task from the list.
+ * The task must exist in the list.
+ */
+ public void remove(Task toRemove) {
+ requireNonNull(toRemove);
+ if (!internalList.remove(toRemove)) {
+ throw new TaskNotFoundException();
+ }
+ }
+
+ /**
+ * removes all the tasks present in the list.
+ */
+ public void remove() {
+ if (internalList.size() != 0) {
+ internalList.clear();
+ } else {
+ throw new TaskNotFoundException();
+ }
+ }
+
+ public void setTasks(UniqueTaskList replacement) {
+ requireNonNull(replacement);
+ internalList.setAll(replacement.internalList);
+ }
+
+ /**
+ * Replaces the contents of this list with {@code tasks}.
+ * {@code tasks} must not contain duplicate tasks.
+ */
+ public void setTasks(List tasks) {
+ CollectionUtil.requireAllNonNull(tasks);
+ if (!tasksAreUnique(tasks)) {
+ throw new DuplicateTaskException();
+ }
+
+ internalList.setAll(tasks);
+ }
+
+ /**
+ * Returns the backing list as an unmodifiable {@code ObservableList}.
+ */
+ public ObservableList asUnmodifiableObservableList() {
+ return internalUnmodifiableList;
+ }
+
+ public ObservableList getInternalList() {
+ return internalList;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return internalList.iterator();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof UniqueTaskList)) {
+ return false;
+ }
+
+ UniqueTaskList otherUniqueTaskList = (UniqueTaskList) other;
+ return internalList.equals(otherUniqueTaskList.internalList);
+ }
+
+ @Override
+ public int hashCode() {
+ return internalList.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return internalList.toString();
+ }
+
+ /**
+ * Returns true if {@code tasks} contains only unique tasks.
+ */
+ private boolean tasksAreUnique(List tasks) {
+ for (int i = 0; i < tasks.size() - 1; i++) {
+ for (int j = i + 1; j < tasks.size(); j++) {
+ if (tasks.get(i).isSameTask(tasks.get(j))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/profplan/model/task/exceptions/DuplicateTaskException.java b/src/main/java/profplan/model/task/exceptions/DuplicateTaskException.java
new file mode 100644
index 00000000000..b9ca06d3dda
--- /dev/null
+++ b/src/main/java/profplan/model/task/exceptions/DuplicateTaskException.java
@@ -0,0 +1,11 @@
+package profplan.model.task.exceptions;
+
+/**
+ * Signals that the operation will result in duplicate Tasks (Tasks are considered duplicates if they have the same
+ * identity).
+ */
+public class DuplicateTaskException extends RuntimeException {
+ public DuplicateTaskException() {
+ super("Operation would result in duplicate tasks");
+ }
+}
diff --git a/src/main/java/profplan/model/task/exceptions/TaskNotFoundException.java b/src/main/java/profplan/model/task/exceptions/TaskNotFoundException.java
new file mode 100644
index 00000000000..6e6cb97554d
--- /dev/null
+++ b/src/main/java/profplan/model/task/exceptions/TaskNotFoundException.java
@@ -0,0 +1,6 @@
+package profplan.model.task.exceptions;
+
+/**
+ * Signals that the operation is unable to find the specified task.
+ */
+public class TaskNotFoundException extends RuntimeException {}
diff --git a/src/main/java/profplan/model/task/predicates/CombinedPredicate.java b/src/main/java/profplan/model/task/predicates/CombinedPredicate.java
new file mode 100644
index 00000000000..74ee75c7d72
--- /dev/null
+++ b/src/main/java/profplan/model/task/predicates/CombinedPredicate.java
@@ -0,0 +1,49 @@
+package profplan.model.task.predicates;
+
+import java.util.ArrayList;
+import java.util.function.Predicate;
+
+import profplan.commons.util.ToStringBuilder;
+import profplan.model.task.Task;
+
+/**
+ * A predicate that is the AND of several predicates
+ */
+public class CombinedPredicate implements Predicate {
+ private ArrayList> predicateList;
+
+ public CombinedPredicate(ArrayList> predicates) {
+ this.predicateList = predicates;
+ }
+
+ @Override
+ public boolean test(Task task) {
+ for (Predicate predicate : predicateList) {
+ if (!predicate.test(task)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof CombinedPredicate)) {
+ return false;
+ }
+
+ CombinedPredicate otherPredicate = (CombinedPredicate) other;
+ return predicateList.equals(otherPredicate.predicateList);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add(
+ "predicates", predicateList).toString();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/profplan/model/task/predicates/NameContainsKeywordsPredicate.java
similarity index 75%
rename from src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java
rename to src/main/java/profplan/model/task/predicates/NameContainsKeywordsPredicate.java
index 62d19be2977..d90c87fb4fe 100644
--- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java
+++ b/src/main/java/profplan/model/task/predicates/NameContainsKeywordsPredicate.java
@@ -1,15 +1,16 @@
-package seedu.address.model.person;
+package profplan.model.task.predicates;
import java.util.List;
import java.util.function.Predicate;
-import seedu.address.commons.util.StringUtil;
-import seedu.address.commons.util.ToStringBuilder;
+import profplan.commons.util.StringUtil;
+import profplan.commons.util.ToStringBuilder;
+import profplan.model.task.Task;
/**
- * Tests that a {@code Person}'s {@code Name} matches any of the keywords given.
+ * Tests that a {@code Task}'s {@code Name} matches any of the keywords given.
*/
-public class NameContainsKeywordsPredicate implements Predicate {
+public class NameContainsKeywordsPredicate implements Predicate {
private final List keywords;
public NameContainsKeywordsPredicate(List keywords) {
@@ -17,9 +18,9 @@ public NameContainsKeywordsPredicate(List keywords) {
}
@Override
- public boolean test(Person person) {
+ public boolean test(Task task) {
return keywords.stream()
- .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword));
+ .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(task.getName().fullName, keyword));
}
@Override
diff --git a/src/main/java/profplan/model/task/predicates/TaskDueDatePredicate.java b/src/main/java/profplan/model/task/predicates/TaskDueDatePredicate.java
new file mode 100644
index 00000000000..9c106b97d5e
--- /dev/null
+++ b/src/main/java/profplan/model/task/predicates/TaskDueDatePredicate.java
@@ -0,0 +1,47 @@
+package profplan.model.task.predicates;
+
+import java.util.function.Predicate;
+
+import profplan.commons.util.ToStringBuilder;
+import profplan.model.task.DueDate;
+import profplan.model.task.Task;
+
+/**
+ * Tests that a {@code Task}'s {@code DueDate} falls before the given duedate.
+ */
+public class TaskDueDatePredicate implements Predicate {
+ private final DueDate date;
+
+ public TaskDueDatePredicate(DueDate date) {
+ this.date = date;
+ }
+
+ public DueDate getDate() {
+ return this.date;
+ }
+
+ @Override
+ public boolean test(Task task) {
+ return task.getDueDate().isIncludedorBefore(date);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof TaskDueDatePredicate)) {
+ return false;
+ }
+
+ TaskDueDatePredicate otherPredicate = (TaskDueDatePredicate) other;
+ return date.equals(otherPredicate.date);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add("date", date).toString();
+ }
+}
diff --git a/src/main/java/profplan/model/task/predicates/TaskInMonthPredicate.java b/src/main/java/profplan/model/task/predicates/TaskInMonthPredicate.java
new file mode 100644
index 00000000000..c729ae2e624
--- /dev/null
+++ b/src/main/java/profplan/model/task/predicates/TaskInMonthPredicate.java
@@ -0,0 +1,25 @@
+package profplan.model.task.predicates;
+
+import java.util.function.Predicate;
+
+import profplan.model.task.Task;
+
+/**
+ * Tests that a {@code Task}'s {@code DueDate} falls within a month of the current date.
+ */
+public class TaskInMonthPredicate implements Predicate {
+
+ @Override
+ public boolean test(Task task) {
+ return task.getDueDate().isWithinMonth();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ return (other instanceof TaskInMonthPredicate);
+ }
+}
+
diff --git a/src/main/java/profplan/model/task/predicates/TaskInWeekPredicate.java b/src/main/java/profplan/model/task/predicates/TaskInWeekPredicate.java
new file mode 100644
index 00000000000..cfdb1900ef5
--- /dev/null
+++ b/src/main/java/profplan/model/task/predicates/TaskInWeekPredicate.java
@@ -0,0 +1,24 @@
+package profplan.model.task.predicates;
+
+import java.util.function.Predicate;
+
+import profplan.model.task.Task;
+
+/**
+ * Tests that a {@code Task}'s {@code DueDate} falls within a week of the current date.
+ */
+public class TaskInWeekPredicate implements Predicate {
+
+ @Override
+ public boolean test(Task task) {
+ return task.getDueDate().isWithinWeek();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ return (other instanceof TaskInWeekPredicate);
+ }
+}
diff --git a/src/main/java/profplan/model/task/predicates/TaskPriorityPredicate.java b/src/main/java/profplan/model/task/predicates/TaskPriorityPredicate.java
new file mode 100644
index 00000000000..1049b37d483
--- /dev/null
+++ b/src/main/java/profplan/model/task/predicates/TaskPriorityPredicate.java
@@ -0,0 +1,47 @@
+package profplan.model.task.predicates;
+
+import java.util.function.Predicate;
+
+import profplan.commons.util.ToStringBuilder;
+import profplan.model.task.Priority;
+import profplan.model.task.Task;
+
+/**
+ * Tests that a {@code Task}'s {@code Priority} matches the given priority.
+ */
+public class TaskPriorityPredicate implements Predicate {
+ private final Priority priority;
+
+ public TaskPriorityPredicate(Priority priority) {
+ this.priority = priority;
+ }
+
+ public Priority getPriority() {
+ return this.priority;
+ }
+
+ @Override
+ public boolean test(Task task) {
+ return task.getPriority().equals(priority);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof TaskPriorityPredicate)) {
+ return false;
+ }
+
+ TaskPriorityPredicate otherPredicate = (TaskPriorityPredicate) other;
+ return priority.equals(otherPredicate.priority);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add("priority", priority).toString();
+ }
+}
diff --git a/src/main/java/profplan/model/task/predicates/TaskRecurringTypePredicate.java b/src/main/java/profplan/model/task/predicates/TaskRecurringTypePredicate.java
new file mode 100644
index 00000000000..81936c0020c
--- /dev/null
+++ b/src/main/java/profplan/model/task/predicates/TaskRecurringTypePredicate.java
@@ -0,0 +1,56 @@
+package profplan.model.task.predicates;
+
+import java.util.function.Predicate;
+
+import profplan.commons.util.ToStringBuilder;
+import profplan.model.task.Task;
+import profplan.model.task.Task.RecurringType;
+
+/**
+ * Tests that a {@code Task}'s {@code Recurring} matches the given recurring.
+ */
+public class TaskRecurringTypePredicate implements Predicate {
+ private final RecurringType recur;
+
+ public TaskRecurringTypePredicate(RecurringType recur) {
+ this.recur = recur;
+ }
+
+ public RecurringType getRecurringType() {
+ return this.recur;
+ }
+
+ @Override
+ public boolean test(Task task) {
+ return this.recur == null
+ ? !task.getIsRecurring()
+ : this.recur.equals(task.getRecurringType());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof TaskRecurringTypePredicate)) {
+ return false;
+ }
+
+ TaskRecurringTypePredicate otherPredicate = (TaskRecurringTypePredicate) other;
+ if (recur == null) {
+ return otherPredicate.recur == null;
+ }
+ return otherPredicate == null
+ ? false
+ : recur.equals(otherPredicate.recur);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("recurringType", recur == null ? "None" : recur)
+ .toString();
+ }
+}
diff --git a/src/main/java/profplan/model/task/predicates/TaskStatusPredicate.java b/src/main/java/profplan/model/task/predicates/TaskStatusPredicate.java
new file mode 100644
index 00000000000..bff4f324d25
--- /dev/null
+++ b/src/main/java/profplan/model/task/predicates/TaskStatusPredicate.java
@@ -0,0 +1,47 @@
+package profplan.model.task.predicates;
+
+import java.util.function.Predicate;
+
+import profplan.commons.util.ToStringBuilder;
+import profplan.model.task.Status;
+import profplan.model.task.Task;
+
+/**
+ * Tests that a {@code Task}'s {@code Priority} matches the given priority.
+ */
+public class TaskStatusPredicate implements Predicate {
+ private final Status status;
+
+ public TaskStatusPredicate(Status status) {
+ this.status = status;
+ }
+
+ public Status getStatus() {
+ return this.status;
+ }
+
+ @Override
+ public boolean test(Task task) {
+ return task.getStatus().equals(status);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof TaskStatusPredicate)) {
+ return false;
+ }
+
+ TaskStatusPredicate otherPredicate = (TaskStatusPredicate) other;
+ return status.equals(otherPredicate.status);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add("status", status).toString();
+ }
+}
diff --git a/src/main/java/profplan/model/util/SampleDataUtil.java b/src/main/java/profplan/model/util/SampleDataUtil.java
new file mode 100644
index 00000000000..022c46cc5e5
--- /dev/null
+++ b/src/main/java/profplan/model/util/SampleDataUtil.java
@@ -0,0 +1,73 @@
+package profplan.model.util;
+
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import profplan.model.ProfPlan;
+import profplan.model.ReadOnlyProfPlan;
+import profplan.model.tag.Tag;
+import profplan.model.task.Description;
+import profplan.model.task.DueDate;
+import profplan.model.task.Link;
+import profplan.model.task.Name;
+import profplan.model.task.Priority;
+import profplan.model.task.Task;
+
+/**
+ * Contains utility methods for populating {@code ProfPlan} with sample data.
+ */
+public class SampleDataUtil {
+ public static Task[] getSampleTasks() {
+ return new Task[] {
+ new Task(new Name("3D Chip Printing Research"), new Priority("8"), false, null,
+ getTagSet("research", "application"), new DueDate("01-01-2024"),
+ new Link("https://nus.edu.sg/nuslibraries/spaces/tel-imaginarium/services/3d-printing"),
+ new Description("Test blueprints and apply for 3D Printing machine access.")),
+ new Task(new Name("Review Student Project Proposal"), new Priority("5"), false, null,
+ getTagSet("student", "research"), new DueDate("30-11-2023"),
+ new Link("urop.com"), new Description("Review Raman's UROP Proposal.")),
+ new Task(new Name("CS2100 Lecture"), new Priority("7"), true, Task.RecurringType.WEEKLY,
+ getTagSet("course", "lecture"), new DueDate("21-11-2023"),
+ new Link("https://nus-sg.zoom.us/j/84615636187?pwd=RWRHc1hiTDlUaU54dUIrRVFiT2l2Zz09"),
+ new Description("9AM Lecture in COM1-Seminar Room")),
+ new Task(new Name("Read Neural Network Paper"), new Priority("6"), false, null,
+ getTagSet("reading", "research"), new DueDate("25-11-2023"),
+ new Link("arivx.com/12366"), new Description("Read and review newly released paper on CNNs")),
+ new Task(new Name("CS2106 Course Admin"), new Priority("5"), true, Task.RecurringType.WEEKLY,
+ getTagSet("course", "admin"), new DueDate("10-12-2023"),
+ new Link("https://blog.nus.edu.sg/cs2106/about/"),
+ new Description("Release Quiz 9 and Midterm Grades")),
+ new Task(new Name("Grant Application"), new Priority("8"), false, null,
+ getTagSet("grant", "research"), new DueDate("20-07-2024"),
+ new Link("-"), new Description("NUS SOC Grant Application for DLD Project"))
+ };
+ }
+
+ public static ReadOnlyProfPlan getSampleProfPlan() {
+ ProfPlan sampleAb = new ProfPlan();
+ for (Task sampleTask : getSampleTasks()) {
+ sampleAb.addTask(sampleTask);
+ }
+ return sampleAb;
+ }
+
+ /**
+ * Returns a tag set containing the list of strings given.
+ */
+ public static Set getTagSet(String... strings) {
+ return Arrays.stream(strings)
+ .map(Tag::new)
+ .collect(Collectors.toSet());
+ }
+
+ /**
+ * Returns a Task set containing the list of Tasks given.
+ */
+ public static Set getTaskSet(Task... tasks) {
+ return Arrays.stream(tasks)
+ .map(Task::new)
+ .collect(Collectors.toSet());
+ }
+
+}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/profplan/storage/JsonAdaptedTag.java
similarity index 89%
rename from src/main/java/seedu/address/storage/JsonAdaptedTag.java
rename to src/main/java/profplan/storage/JsonAdaptedTag.java
index 0df22bdb754..de6930d5506 100644
--- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java
+++ b/src/main/java/profplan/storage/JsonAdaptedTag.java
@@ -1,10 +1,10 @@
-package seedu.address.storage;
+package profplan.storage;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
-import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.tag.Tag;
+import profplan.commons.exceptions.IllegalValueException;
+import profplan.model.tag.Tag;
/**
* Jackson-friendly version of {@link Tag}.
diff --git a/src/main/java/profplan/storage/JsonAdaptedTask.java b/src/main/java/profplan/storage/JsonAdaptedTask.java
new file mode 100644
index 00000000000..11bb1764997
--- /dev/null
+++ b/src/main/java/profplan/storage/JsonAdaptedTask.java
@@ -0,0 +1,145 @@
+package profplan.storage;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import profplan.commons.exceptions.IllegalValueException;
+import profplan.model.tag.Tag;
+import profplan.model.task.Description;
+import profplan.model.task.DueDate;
+import profplan.model.task.Link;
+import profplan.model.task.Name;
+import profplan.model.task.Priority;
+import profplan.model.task.Status;
+import profplan.model.task.Task;
+
+/**
+ * Jackson-friendly version of {@link Task}.
+ */
+class JsonAdaptedTask {
+
+ public static final String MISSING_FIELD_MESSAGE_FORMAT = "Task's %s field is missing!";
+
+ private final String name;
+ private final String priority;
+ private final boolean isRecurring;
+ private final Task.RecurringType recurringType;
+ private final String dueDate;
+ private final String status;
+ private final String link;
+ private final List tags = new ArrayList<>();
+ private final String description;
+
+ /**
+ * Constructs a {@code JsonAdaptedTask} with the given task details.
+ */
+ @JsonCreator
+ public JsonAdaptedTask(@JsonProperty("name") String name, @JsonProperty("priority") String priority,
+ @JsonProperty("isRecurring") boolean isRecurring,
+ @JsonProperty("recurringType") Task.RecurringType recurringType,
+ @JsonProperty("tags") List tags,
+ @JsonProperty("status") String status, @JsonProperty("dueDate") String dueDate,
+ @JsonProperty("link") String link,
+ @JsonProperty("description") String description) {
+ this.name = name;
+ this.priority = priority;
+ this.isRecurring = isRecurring;
+ this.recurringType = recurringType;
+ this.dueDate = dueDate;
+ this.status = status;
+ this.link = link;
+ if (tags != null) {
+ this.tags.addAll(tags);
+ }
+ this.description = description;
+ }
+
+ /**
+ * Converts a given {@code Task} into this class for Jackson use.
+ */
+ public JsonAdaptedTask(Task source) {
+ name = source.getName().fullName;
+ priority = source.getPriority().value;
+ isRecurring = source.getIsRecurring();
+ recurringType = source.getRecurringType();
+ tags.addAll(source.getTags().stream()
+ .map(JsonAdaptedTag::new)
+ .collect(Collectors.toList()));
+ link = source.getLink().value;
+ dueDate = source.getDueDate().toString();
+ status = source.getStatus().status;
+ description = source.getDescription().description;
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted task object into the model's {@code Task} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted task.
+ */
+ public Task toModelType() throws IllegalValueException {
+ final List taskTags = new ArrayList<>();
+ for (JsonAdaptedTag tag : tags) {
+ taskTags.add(tag.toModelType());
+ }
+
+ if (name == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()));
+ }
+ if (!Name.isValidName(name)) {
+ throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS);
+ }
+ final Name modelName = new Name(name);
+
+ if (priority == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ Priority.class.getSimpleName()));
+ }
+ if (!Priority.isValidPriority(priority)) {
+ throw new IllegalValueException(Priority.MESSAGE_CONSTRAINTS);
+ }
+ final Priority modelPriority = new Priority(priority);
+
+ final boolean modelIsRecurring = isRecurring;
+
+ final Task.RecurringType modelRecurringType = recurringType;
+
+ final Set modelTags = new HashSet<>(taskTags);
+
+ String linkToLoad = link;
+ if (link == null) {
+ linkToLoad = "-";
+ }
+
+ final Link modelLink = new Link(linkToLoad);
+
+ if (dueDate == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, DueDate.class.getSimpleName()));
+ }
+ if (!DueDate.isValidDate(dueDate)) {
+ throw new IllegalValueException(DueDate.MESSAGE_CONSTRAINTS);
+ }
+ final DueDate modelDueDate = new DueDate(dueDate);
+
+ Status modelStatus = Status.UNDONE_STATUS;
+ if (status != null) {
+ modelStatus = new Status(status);
+ }
+
+ if (description == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ Description.class.getSimpleName()));
+ }
+ final Description modelDescription = new Description(description);
+
+ return new Task(modelName, modelPriority, modelIsRecurring, modelRecurringType,
+ modelStatus, modelTags, modelDueDate,
+ modelLink, modelDescription);
+ }
+
+}
diff --git a/src/main/java/profplan/storage/JsonProfPlanStorage.java b/src/main/java/profplan/storage/JsonProfPlanStorage.java
new file mode 100644
index 00000000000..69fd2196bd4
--- /dev/null
+++ b/src/main/java/profplan/storage/JsonProfPlanStorage.java
@@ -0,0 +1,80 @@
+package profplan.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 profplan.commons.core.LogsCenter;
+import profplan.commons.exceptions.DataLoadingException;
+import profplan.commons.exceptions.IllegalValueException;
+import profplan.commons.util.FileUtil;
+import profplan.commons.util.JsonUtil;
+import profplan.model.ReadOnlyProfPlan;
+
+/**
+ * A class to access ProfPlan data stored as a json file on the hard disk.
+ */
+public class JsonProfPlanStorage implements ProfPlanStorage {
+
+ private static final Logger logger = LogsCenter.getLogger(JsonProfPlanStorage.class);
+
+ private Path filePath;
+
+ public JsonProfPlanStorage(Path filePath) {
+ this.filePath = filePath;
+ }
+
+ public Path getProfPlanFilePath() {
+ return filePath;
+ }
+
+ @Override
+ public Optional readProfPlan() throws DataLoadingException {
+ return readProfPlan(filePath);
+ }
+
+ /**
+ * Similar to {@link #readProfPlan()}.
+ *
+ * @param filePath location of the data. Cannot be null.
+ * @throws DataLoadingException if loading the data from storage failed.
+ */
+ public Optional readProfPlan(Path filePath) throws DataLoadingException {
+ requireNonNull(filePath);
+
+ Optional jsonProfPlan = JsonUtil.readJsonFile(
+ filePath, JsonSerializableProfPlan.class);
+ if (!jsonProfPlan.isPresent()) {
+ return Optional.empty();
+ }
+
+ try {
+ return Optional.of(jsonProfPlan.get().toModelType());
+ } catch (IllegalValueException ive) {
+ logger.info("Illegal values found in " + filePath + ": " + ive.getMessage());
+ throw new DataLoadingException(ive);
+ }
+ }
+
+ @Override
+ public void saveProfPlan(ReadOnlyProfPlan profPlan) throws IOException {
+ saveProfPlan(profPlan, filePath);
+ }
+
+ /**
+ * Similar to {@link #saveProfPlan(ReadOnlyProfPlan)}.
+ *
+ * @param filePath location of the data. Cannot be null.
+ */
+ public void saveProfPlan(ReadOnlyProfPlan profPlan, Path filePath) throws IOException {
+ requireNonNull(profPlan);
+ requireNonNull(filePath);
+
+ FileUtil.createIfMissing(filePath);
+ JsonUtil.saveJsonFile(new JsonSerializableProfPlan(profPlan), filePath);
+ }
+
+}
diff --git a/src/main/java/profplan/storage/JsonSerializableProfPlan.java b/src/main/java/profplan/storage/JsonSerializableProfPlan.java
new file mode 100644
index 00000000000..7d46e7e1547
--- /dev/null
+++ b/src/main/java/profplan/storage/JsonSerializableProfPlan.java
@@ -0,0 +1,60 @@
+package profplan.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 profplan.commons.exceptions.IllegalValueException;
+import profplan.model.ProfPlan;
+import profplan.model.ReadOnlyProfPlan;
+import profplan.model.task.Task;
+
+/**
+ * An Immutable ProfPlan that is serializable to JSON format.
+ */
+@JsonRootName(value = "profplan")
+class JsonSerializableProfPlan {
+
+ public static final String MESSAGE_DUPLICATE_TASK = "Tasks list contains duplicate task(s).";
+
+ private final List tasks = new ArrayList<>();
+
+ /**
+ * Constructs a {@code JsonSerializableProfPlan} with the given tasks.
+ */
+ @JsonCreator
+ public JsonSerializableProfPlan(@JsonProperty("tasks") List tasks) {
+ this.tasks.addAll(tasks);
+ }
+
+ /**
+ * Converts a given {@code ReadOnlyProfPlan} into this class for Jackson use.
+ *
+ * @param source future changes to this will not affect the created {@code JsonSerializableProfPlan}.
+ */
+ public JsonSerializableProfPlan(ReadOnlyProfPlan source) {
+ tasks.addAll(source.getTaskList().stream().map(JsonAdaptedTask::new).collect(Collectors.toList()));
+ }
+
+ /**
+ * Converts this task list into the model's {@code ProfPlan} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated.
+ */
+ public ProfPlan toModelType() throws IllegalValueException {
+ ProfPlan profPlan = new ProfPlan();
+ for (JsonAdaptedTask jsonAdaptedTask : tasks) {
+ Task task = jsonAdaptedTask.toModelType();
+ if (profPlan.hasTask(task)) {
+ throw new IllegalValueException(MESSAGE_DUPLICATE_TASK);
+ }
+ profPlan.addTask(task);
+ }
+ return profPlan;
+ }
+
+}
diff --git a/src/main/java/profplan/storage/JsonUserConfigsStorage.java b/src/main/java/profplan/storage/JsonUserConfigsStorage.java
new file mode 100644
index 00000000000..363adb136f7
--- /dev/null
+++ b/src/main/java/profplan/storage/JsonUserConfigsStorage.java
@@ -0,0 +1,47 @@
+package profplan.storage;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Optional;
+
+import profplan.commons.exceptions.DataLoadingException;
+import profplan.commons.util.JsonUtil;
+import profplan.model.ReadOnlyUserConfigs;
+import profplan.model.UserConfigs;
+
+/**
+ * A class to access UserConfigs stored in the hard disk as a json file
+ */
+public class JsonUserConfigsStorage implements UserConfigsStorage {
+
+ private Path filePath;
+
+ public JsonUserConfigsStorage(Path filePath) {
+ this.filePath = filePath;
+ }
+
+ @Override
+ public Path getUserConfigsFilePath() {
+ return filePath;
+ }
+
+ @Override
+ public Optional readUserConfigs() throws DataLoadingException {
+ return readUserConfigs(filePath);
+ }
+
+ /**
+ * Similar to {@link #readUserConfigs()}
+ * @param configsFilePath location of the data. Cannot be null.
+ * @throws DataLoadingException if the file format is not as expected.
+ */
+ public Optional readUserConfigs(Path configsFilePath) throws DataLoadingException {
+ return JsonUtil.readJsonFile(configsFilePath, UserConfigs.class);
+ }
+
+ @Override
+ public void saveUserConfigs(ReadOnlyUserConfigs userConfigs) throws IOException {
+ JsonUtil.saveJsonFile(userConfigs, filePath);
+ }
+
+}
diff --git a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java b/src/main/java/profplan/storage/JsonUserPrefsStorage.java
similarity index 83%
rename from src/main/java/seedu/address/storage/JsonUserPrefsStorage.java
rename to src/main/java/profplan/storage/JsonUserPrefsStorage.java
index 48a9754807d..6b95d0ca61f 100644
--- a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java
+++ b/src/main/java/profplan/storage/JsonUserPrefsStorage.java
@@ -1,13 +1,13 @@
-package seedu.address.storage;
+package profplan.storage;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
-import seedu.address.commons.exceptions.DataLoadingException;
-import seedu.address.commons.util.JsonUtil;
-import seedu.address.model.ReadOnlyUserPrefs;
-import seedu.address.model.UserPrefs;
+import profplan.commons.exceptions.DataLoadingException;
+import profplan.commons.util.JsonUtil;
+import profplan.model.ReadOnlyUserPrefs;
+import profplan.model.UserPrefs;
/**
* A class to access UserPrefs stored in the hard disk as a json file
diff --git a/src/main/java/profplan/storage/ProfPlanStorage.java b/src/main/java/profplan/storage/ProfPlanStorage.java
new file mode 100644
index 00000000000..7a286fb083d
--- /dev/null
+++ b/src/main/java/profplan/storage/ProfPlanStorage.java
@@ -0,0 +1,46 @@
+package profplan.storage;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Optional;
+
+import profplan.commons.exceptions.DataLoadingException;
+import profplan.model.ProfPlan;
+import profplan.model.ReadOnlyProfPlan;
+
+/**
+ * Represents a storage for {@link ProfPlan}.
+ */
+public interface ProfPlanStorage {
+
+ /**
+ * Returns the file path of the data file.
+ */
+ Path getProfPlanFilePath();
+
+ /**
+ * Returns ProfPlan data as a {@link ReadOnlyProfPlan}.
+ * Returns {@code Optional.empty()} if storage file is not found.
+ *
+ * @throws DataLoadingException if loading the data from storage failed.
+ */
+ Optional readProfPlan() throws DataLoadingException;
+
+ /**
+ * @see #getProfPlanFilePath()
+ */
+ Optional readProfPlan(Path filePath) throws DataLoadingException;
+
+ /**
+ * Saves the given {@link ReadOnlyProfPlan} to the storage.
+ * @param profPlan cannot be null.
+ * @throws IOException if there was any problem writing to the file.
+ */
+ void saveProfPlan(ReadOnlyProfPlan profPlan) throws IOException;
+
+ /**
+ * @see #saveProfPlan(ReadOnlyProfPlan)
+ */
+ void saveProfPlan(ReadOnlyProfPlan profPlan, Path filePath) throws IOException;
+
+}
diff --git a/src/main/java/profplan/storage/Storage.java b/src/main/java/profplan/storage/Storage.java
new file mode 100644
index 00000000000..0a620b4ef7d
--- /dev/null
+++ b/src/main/java/profplan/storage/Storage.java
@@ -0,0 +1,43 @@
+package profplan.storage;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Optional;
+
+import profplan.commons.exceptions.DataLoadingException;
+import profplan.model.ReadOnlyProfPlan;
+import profplan.model.ReadOnlyUserConfigs;
+import profplan.model.ReadOnlyUserPrefs;
+import profplan.model.UserConfigs;
+import profplan.model.UserPrefs;
+
+/**
+ * API of the Storage component
+ */
+public interface Storage extends ProfPlanStorage, UserPrefsStorage, UserConfigsStorage {
+
+ @Override
+ Optional readUserPrefs() throws DataLoadingException;
+
+ @Override
+ void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException;
+
+ @Override
+ Path getUserConfigsFilePath();
+
+ @Override
+ Optional readUserConfigs() throws DataLoadingException;
+
+ @Override
+ void saveUserConfigs(ReadOnlyUserConfigs userConfigs) throws IOException;
+
+ @Override
+ Path getProfPlanFilePath();
+
+ @Override
+ Optional readProfPlan() throws DataLoadingException;
+
+ @Override
+ void saveProfPlan(ReadOnlyProfPlan profPlan) throws IOException;
+
+}
diff --git a/src/main/java/profplan/storage/StorageManager.java b/src/main/java/profplan/storage/StorageManager.java
new file mode 100644
index 00000000000..998ef99a366
--- /dev/null
+++ b/src/main/java/profplan/storage/StorageManager.java
@@ -0,0 +1,99 @@
+package profplan.storage;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Optional;
+import java.util.logging.Logger;
+
+import profplan.commons.core.LogsCenter;
+import profplan.commons.exceptions.DataLoadingException;
+import profplan.model.ReadOnlyProfPlan;
+import profplan.model.ReadOnlyUserConfigs;
+import profplan.model.ReadOnlyUserPrefs;
+import profplan.model.UserConfigs;
+import profplan.model.UserPrefs;
+
+/**
+ * Manages storage of ProfPlan data in local storage.
+ */
+public class StorageManager implements Storage {
+
+ private static final Logger logger = LogsCenter.getLogger(StorageManager.class);
+ private ProfPlanStorage profPlanStorage;
+ private UserPrefsStorage userPrefsStorage;
+ private UserConfigsStorage userConfigsStorage;
+
+ /**
+ * Creates a {@code StorageManager} with the given {@code ProfPlanStorage} and {@code UserPrefStorage}.
+ */
+ public StorageManager(ProfPlanStorage profPlanStorage, UserPrefsStorage userPrefsStorage,
+ UserConfigsStorage userConfigsStorage) {
+ this.profPlanStorage = profPlanStorage;
+ this.userPrefsStorage = userPrefsStorage;
+ this.userConfigsStorage = userConfigsStorage;
+ }
+
+ // ================ UserPrefs methods ==============================
+
+ @Override
+ public Path getUserPrefsFilePath() {
+ return userPrefsStorage.getUserPrefsFilePath();
+ }
+
+ @Override
+ public Optional readUserPrefs() throws DataLoadingException {
+ return userPrefsStorage.readUserPrefs();
+ }
+
+ @Override
+ public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException {
+ userPrefsStorage.saveUserPrefs(userPrefs);
+ }
+
+ // ================ UserConfigs methods ==============================
+
+ @Override
+ public Path getUserConfigsFilePath() {
+ return userConfigsStorage.getUserConfigsFilePath();
+ }
+
+ @Override
+ public Optional readUserConfigs() throws DataLoadingException {
+ return userConfigsStorage.readUserConfigs();
+ }
+
+ @Override
+ public void saveUserConfigs(ReadOnlyUserConfigs userConfigs) throws IOException {
+ userConfigsStorage.saveUserConfigs(userConfigs);
+ }
+
+ // ================ ProfPlan methods ==============================
+
+ @Override
+ public Path getProfPlanFilePath() {
+ return profPlanStorage.getProfPlanFilePath();
+ }
+
+ @Override
+ public Optional readProfPlan() throws DataLoadingException {
+ return readProfPlan(profPlanStorage.getProfPlanFilePath());
+ }
+
+ @Override
+ public Optional readProfPlan(Path filePath) throws DataLoadingException {
+ logger.fine("Attempting to read data from file: " + filePath);
+ return profPlanStorage.readProfPlan(filePath);
+ }
+
+ @Override
+ public void saveProfPlan(ReadOnlyProfPlan profPlan) throws IOException {
+ saveProfPlan(profPlan, profPlanStorage.getProfPlanFilePath());
+ }
+
+ @Override
+ public void saveProfPlan(ReadOnlyProfPlan profPlan, Path filePath) throws IOException {
+ logger.fine("Attempting to write to data file: " + filePath);
+ profPlanStorage.saveProfPlan(profPlan, filePath);
+ }
+
+}
diff --git a/src/main/java/profplan/storage/UserConfigsStorage.java b/src/main/java/profplan/storage/UserConfigsStorage.java
new file mode 100644
index 00000000000..81f23b3cdde
--- /dev/null
+++ b/src/main/java/profplan/storage/UserConfigsStorage.java
@@ -0,0 +1,36 @@
+package profplan.storage;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Optional;
+
+import profplan.commons.exceptions.DataLoadingException;
+import profplan.model.ReadOnlyUserConfigs;
+import profplan.model.UserConfigs;
+
+/**
+ * Represents a storage for {@link profplan.model.UserConfigs}.
+ */
+public interface UserConfigsStorage {
+
+ /**
+ * Returns the file path of the UserConfigs data file.
+ */
+ Path getUserConfigsFilePath();
+
+ /**
+ * Returns UserConfigs data from storage.
+ * Returns {@code Optional.empty()} if storage file is not found.
+ *
+ * @throws DataLoadingException if the loading of data from preference file failed.
+ */
+ Optional readUserConfigs() throws DataLoadingException;
+
+ /**
+ * Saves the given {@link ReadOnlyUserConfigs} to the storage.
+ * @param userConfigs cannot be null.
+ * @throws IOException if there was any problem writing to the file.
+ */
+ void saveUserConfigs(ReadOnlyUserConfigs userConfigs) throws IOException;
+
+}
diff --git a/src/main/java/seedu/address/storage/UserPrefsStorage.java b/src/main/java/profplan/storage/UserPrefsStorage.java
similarity index 69%
rename from src/main/java/seedu/address/storage/UserPrefsStorage.java
rename to src/main/java/profplan/storage/UserPrefsStorage.java
index e94ca422ea8..a52666e2a3c 100644
--- a/src/main/java/seedu/address/storage/UserPrefsStorage.java
+++ b/src/main/java/profplan/storage/UserPrefsStorage.java
@@ -1,15 +1,15 @@
-package seedu.address.storage;
+package profplan.storage;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
-import seedu.address.commons.exceptions.DataLoadingException;
-import seedu.address.model.ReadOnlyUserPrefs;
-import seedu.address.model.UserPrefs;
+import profplan.commons.exceptions.DataLoadingException;
+import profplan.model.ReadOnlyUserPrefs;
+import profplan.model.UserPrefs;
/**
- * Represents a storage for {@link seedu.address.model.UserPrefs}.
+ * Represents a storage for {@link UserPrefs}.
*/
public interface UserPrefsStorage {
@@ -27,7 +27,7 @@ public interface UserPrefsStorage {
Optional readUserPrefs() throws DataLoadingException;
/**
- * Saves the given {@link seedu.address.model.ReadOnlyUserPrefs} to the storage.
+ * Saves the given {@link ReadOnlyUserPrefs} to the storage.
* @param userPrefs cannot be null.
* @throws IOException if there was any problem writing to the file.
*/
diff --git a/src/main/java/seedu/address/ui/CommandBox.java b/src/main/java/profplan/ui/CommandBox.java
similarity index 89%
rename from src/main/java/seedu/address/ui/CommandBox.java
rename to src/main/java/profplan/ui/CommandBox.java
index 9e75478664b..087becf2e04 100644
--- a/src/main/java/seedu/address/ui/CommandBox.java
+++ b/src/main/java/profplan/ui/CommandBox.java
@@ -1,12 +1,13 @@
-package seedu.address.ui;
+package profplan.ui;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
import javafx.scene.layout.Region;
-import seedu.address.logic.commands.CommandResult;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.logic.parser.exceptions.ParseException;
+import profplan.logic.Logic;
+import profplan.logic.commands.CommandResult;
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.logic.parser.exceptions.ParseException;
/**
* The UI component that is responsible for receiving user command inputs.
@@ -77,7 +78,7 @@ public interface CommandExecutor {
/**
* Executes the command and returns the result.
*
- * @see seedu.address.logic.Logic#execute(String)
+ * @see Logic#execute(String)
*/
CommandResult execute(String commandText) throws CommandException, ParseException;
}
diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/profplan/ui/HelpWindow.java
similarity index 93%
rename from src/main/java/seedu/address/ui/HelpWindow.java
rename to src/main/java/profplan/ui/HelpWindow.java
index 3f16b2fcf26..835587fc3a9 100644
--- a/src/main/java/seedu/address/ui/HelpWindow.java
+++ b/src/main/java/profplan/ui/HelpWindow.java
@@ -1,4 +1,4 @@
-package seedu.address.ui;
+package profplan.ui;
import java.util.logging.Logger;
@@ -8,14 +8,14 @@
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.stage.Stage;
-import seedu.address.commons.core.LogsCenter;
+import profplan.commons.core.LogsCenter;
/**
* Controller for a help page
*/
public class HelpWindow extends UiPart {
- public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html";
+ public static final String USERGUIDE_URL = "https://ay2324s1-cs2103t-w15-1.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/profplan/ui/MainWindow.java b/src/main/java/profplan/ui/MainWindow.java
new file mode 100644
index 00000000000..6377dd92f7d
--- /dev/null
+++ b/src/main/java/profplan/ui/MainWindow.java
@@ -0,0 +1,356 @@
+package profplan.ui;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import javafx.beans.property.ReadOnlyObjectWrapper;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.collections.ObservableMap;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.scene.control.ListView;
+import javafx.scene.control.MenuItem;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.TextInputControl;
+import javafx.scene.input.KeyCombination;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.layout.GridPane;
+import javafx.scene.layout.StackPane;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+import profplan.commons.core.GuiSettings;
+import profplan.commons.core.LogsCenter;
+import profplan.logic.Logic;
+import profplan.logic.commands.CommandResult;
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.logic.parser.exceptions.ParseException;
+import profplan.model.task.DueDate;
+import profplan.model.task.Task;
+
+/**
+ * The Main Window. Provides the basic application layout containing
+ * a menu bar and space where other JavaFX elements can be placed.
+ */
+public class MainWindow extends UiPart {
+
+ private static final String FXML = "MainWindow.fxml";
+
+ private final Logger logger = LogsCenter.getLogger(getClass());
+
+ private Stage primaryStage;
+ private Logic logic;
+
+ // Independent Ui parts residing in this Ui container
+ private TaskListPanel taskListPanel;
+ private ResultDisplay resultDisplay;
+ private HelpWindow helpWindow;
+
+ @FXML
+ private StackPane commandBoxPlaceholder;
+
+ @FXML
+ private MenuItem helpMenuItem;
+
+ @FXML
+ private StackPane taskListPanelPlaceholder;
+
+ @FXML
+ private StackPane resultDisplayPlaceholder;
+
+ @FXML
+ private VBox taskList;
+
+ @FXML
+ private GridPane resultGrid;
+
+ @FXML
+ private TableView matrixDisplay;
+
+ @FXML
+ private StackPane statusbarPlaceholder;
+
+ private class Order {
+
+ private final ObservableMap> priorityTask = FXCollections.observableHashMap();
+ private final String priority;
+
+ public Order(ObservableMap urgencies, String priority) {
+ this.priority = priority;
+ for (long i = 1; i <= 10; i++) {
+ priorityTask.put(i, new ArrayList<>());
+ }
+
+ for (Task task : urgencies.keySet()) {
+ if (task.getPriority().toString().equals(priority)) {
+ priorityTask.getOrDefault(urgencies.get(task), new ArrayList<>()).add(task);
+ }
+ }
+ }
+
+ public List getUrgency(long i) {
+ return priorityTask.getOrDefault(i, new ArrayList<>()).stream()
+ .map(t -> t.getName().toString()).collect(Collectors.toList());
+ }
+
+ public String getPriority() {
+ return priority;
+ }
+ }
+
+ /**
+ * Creates a {@code MainWindow} with the given {@code Stage} and {@code Logic}.
+ */
+ public MainWindow(Stage primaryStage, Logic logic) {
+ super(FXML, primaryStage);
+
+ // Set dependencies
+ this.primaryStage = primaryStage;
+ this.logic = logic;
+
+ // Configure the UI
+ setWindowDefaultSize(logic.getGuiSettings());
+
+ setAccelerators();
+
+ helpWindow = new HelpWindow();
+ }
+
+ public Stage getPrimaryStage() {
+ return primaryStage;
+ }
+
+ private void setAccelerators() {
+ setAccelerator(helpMenuItem, KeyCombination.valueOf("F1"));
+ }
+
+ /**
+ * Sets the accelerator of a MenuItem.
+ * @param keyCombination the KeyCombination value of the accelerator
+ */
+ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) {
+ menuItem.setAccelerator(keyCombination);
+
+ /*
+ * TODO: the code below can be removed once the bug reported here
+ * https://bugs.openjdk.java.net/browse/JDK-8131666
+ * is fixed in later version of SDK.
+ *
+ * According to the bug report, TextInputControl (TextField, TextArea) will
+ * consume function-key events. Because CommandBox contains a TextField, and
+ * ResultDisplay contains a TextArea, thus some accelerators (e.g F1) will
+ * not work when the focus is in them because the key event is consumed by
+ * the TextInputControl(s).
+ *
+ * For now, we add following event filter to capture such key events and open
+ * help window purposely so to support accelerators even when focus is
+ * in CommandBox or ResultDisplay.
+ */
+ getRoot().addEventFilter(KeyEvent.KEY_PRESSED, event -> {
+ if (event.getTarget() instanceof TextInputControl && keyCombination.match(event)) {
+ menuItem.getOnAction().handle(new ActionEvent());
+ event.consume();
+ }
+ });
+ }
+
+ /**
+ * Calculate number of days till due date
+ */
+ private long getDaysUntilDueDate(DueDate dueDate, String curDate) {
+ SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
+ try {
+ Date dueDateDate = dateFormat.parse(dueDate.toString());
+ Date currentDate = dateFormat.parse(curDate);
+
+ long difference = dueDateDate.getTime() - currentDate.getTime();
+ if (difference < 0) {
+ difference = 1;
+ }
+ long days = TimeUnit.DAYS.convert(difference, TimeUnit.MILLISECONDS);
+ return days;
+ } catch (java.text.ParseException e) {
+ // Handle parsing errors
+ return -1;
+ }
+ }
+
+ /**
+ * Fills up all the placeholders of this window.
+ */
+ void fillInnerParts() {
+ taskListPanel = new TaskListPanel(logic.getFilteredTaskList());
+ taskListPanelPlaceholder.getChildren().add(taskListPanel.getRoot());
+
+ resultDisplay = new ResultDisplay();
+ resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot());
+
+ StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getProfPlanFilePath());
+ statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot());
+
+ CommandBox commandBox = new CommandBox(this::executeCommand);
+ commandBoxPlaceholder.getChildren().add(commandBox.getRoot());
+
+ matrixDisplay.setEditable(false);
+
+ taskListPanelPlaceholder.prefWidthProperty().bind(resultGrid.widthProperty().multiply(0.3));
+
+ ArrayList>> columns = new ArrayList<>();
+ TableColumn> priority = new TableColumn<>("");
+ priority.setEditable(false);
+ priority.setSortable(false);
+ priority.setResizable(false);
+ priority.prefWidthProperty().bind(matrixDisplay.widthProperty().multiply(0.1));
+ priority.setCellValueFactory(o -> {
+ ListView temp = new ListView<>(FXCollections.observableArrayList(o.getValue().priority));
+ temp.setSelectionModel(null);
+ return new ReadOnlyObjectWrapper<>(temp);
+ });
+ columns.add(priority);
+
+ loadMatrix();
+
+ for (int i = 1; i <= 10; i++) {
+ TableColumn> urgency = new TableColumn<>(String.valueOf(i));
+ int finalI = i;
+ urgency.setCellValueFactory(o -> {
+ ListView temp =
+ new ListView<>(FXCollections.observableArrayList(o.getValue().getUrgency(finalI)));
+ temp.setSelectionModel(null);
+ return new ReadOnlyObjectWrapper<>(temp);
+ });
+ urgency.setEditable(false);
+ urgency.setSortable(false);
+ urgency.setResizable(false);
+ urgency.prefWidthProperty().bind(matrixDisplay.widthProperty().multiply(0.2));
+ columns.add(urgency);
+ }
+ matrixDisplay.getColumns().addAll(columns);
+
+ matrixDisplay.setSelectionModel(null);
+ matrixDisplay.fixedCellSizeProperty().bind(matrixDisplay.heightProperty().multiply(0.2));
+ }
+
+ /**
+ * Sets the default size based on {@code guiSettings}.
+ */
+ private void setWindowDefaultSize(GuiSettings guiSettings) {
+ primaryStage.setHeight(guiSettings.getWindowHeight());
+ primaryStage.setWidth(guiSettings.getWindowWidth());
+ if (guiSettings.getWindowCoordinates() != null) {
+ primaryStage.setX(guiSettings.getWindowCoordinates().getX());
+ primaryStage.setY(guiSettings.getWindowCoordinates().getY());
+ }
+ }
+
+ /**
+ * Opens the help window or focuses on it if it's already opened.
+ */
+ @FXML
+ public void handleHelp() {
+ if (!helpWindow.isShowing()) {
+ helpWindow.show();
+ } else {
+ helpWindow.focus();
+ }
+ }
+
+ void show() {
+ primaryStage.show();
+ }
+
+ /**
+ * Closes the application.
+ */
+ @FXML
+ private void handleExit() {
+ GuiSettings guiSettings = new GuiSettings(primaryStage.getWidth(), primaryStage.getHeight(),
+ (int) primaryStage.getX(), (int) primaryStage.getY());
+ logic.setGuiSettings(guiSettings);
+ helpWindow.hide();
+ primaryStage.hide();
+ }
+
+ public TaskListPanel getTaskListPanel() {
+ return taskListPanel;
+ }
+
+ private void loadMatrix() {
+ ObservableList rows = FXCollections.observableArrayList();
+
+ ObservableList filteredTasks = logic.getFilteredTaskList();
+ ObservableMap taskUrgency = FXCollections.observableHashMap();
+ SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
+ Date currentDate = new Date();
+ String curDate = dateFormat.format(currentDate);
+ if (filteredTasks.isEmpty()) {
+ for (int i = 10; i >= 1; i--) {
+ Order temp = new Order(taskUrgency, String.valueOf(i));
+ rows.add(temp);
+ }
+ matrixDisplay.setItems(rows);
+ matrixDisplay.refresh();
+ } else {
+ for (Task t : filteredTasks) {
+ long daysLeft = getDaysUntilDueDate(t.getDueDate(), curDate);
+ taskUrgency.put(t, daysLeft);
+ }
+ long minDaysLeft = Collections.min(taskUrgency.values());
+ long maxDaysLeft = Collections.max(taskUrgency.values());
+ long intermediate = (maxDaysLeft - minDaysLeft) / 9;
+ long split;
+ if (intermediate == 0) {
+ split = 1;
+ } else {
+ split = intermediate;
+ }
+ taskUrgency.replaceAll((t, v) -> (v - minDaysLeft) / split + 1);
+ taskUrgency.replaceAll((t, v) -> 10 - v + 1);
+
+
+ for (int i = 10; i >= 1; i--) {
+ Order temp = new Order(taskUrgency, String.valueOf(i));
+ rows.add(temp);
+ }
+ matrixDisplay.setItems(rows);
+ matrixDisplay.refresh();
+
+ }
+ }
+
+ /**
+ * Executes the command and returns the result.
+ *
+ * @see Logic#execute(String)
+ */
+ private CommandResult executeCommand(String commandText) throws CommandException, ParseException {
+ try {
+ CommandResult commandResult = logic.execute(commandText);
+ logger.info("Result: " + commandResult.getFeedbackToUser());
+ resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser());
+
+ loadMatrix();
+
+ if (commandResult.isShowHelp()) {
+ handleHelp();
+ }
+
+ if (commandResult.isExit()) {
+ handleExit();
+ }
+
+ return commandResult;
+ } catch (CommandException | ParseException e) {
+ logger.info("An error occurred while executing command: " + commandText);
+ resultDisplay.setFeedbackToUser(e.getMessage());
+ throw e;
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/profplan/ui/ResultDisplay.java
similarity index 95%
rename from src/main/java/seedu/address/ui/ResultDisplay.java
rename to src/main/java/profplan/ui/ResultDisplay.java
index 7d98e84eedf..0afb439327e 100644
--- a/src/main/java/seedu/address/ui/ResultDisplay.java
+++ b/src/main/java/profplan/ui/ResultDisplay.java
@@ -1,4 +1,4 @@
-package seedu.address.ui;
+package profplan.ui;
import static java.util.Objects.requireNonNull;
diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/profplan/ui/StatusBarFooter.java
similarity index 96%
rename from src/main/java/seedu/address/ui/StatusBarFooter.java
rename to src/main/java/profplan/ui/StatusBarFooter.java
index b577f829423..758e938efb1 100644
--- a/src/main/java/seedu/address/ui/StatusBarFooter.java
+++ b/src/main/java/profplan/ui/StatusBarFooter.java
@@ -1,4 +1,4 @@
-package seedu.address.ui;
+package profplan.ui;
import java.nio.file.Path;
import java.nio.file.Paths;
diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/profplan/ui/TaskCard.java
similarity index 51%
rename from src/main/java/seedu/address/ui/PersonCard.java
rename to src/main/java/profplan/ui/TaskCard.java
index 094c42cda82..c42f0c3b0a8 100644
--- a/src/main/java/seedu/address/ui/PersonCard.java
+++ b/src/main/java/profplan/ui/TaskCard.java
@@ -1,4 +1,4 @@
-package seedu.address.ui;
+package profplan.ui;
import java.util.Comparator;
@@ -7,14 +7,14 @@
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
-import seedu.address.model.person.Person;
+import profplan.model.task.Task;
/**
- * An UI component that displays information of a {@code Person}.
+ * An UI component that displays information of a {@code Task}.
*/
-public class PersonCard extends UiPart {
+public class TaskCard extends UiPart {
- private static final String FXML = "PersonListCard.fxml";
+ private static final String FXML = "TaskListCard.fxml";
/**
* Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX.
@@ -24,7 +24,7 @@ public class PersonCard extends UiPart {
* @see The issue on AddressBook level 4
*/
- public final Person person;
+ public final Task task;
@FXML
private HBox cardPane;
@@ -33,27 +33,34 @@ public class PersonCard extends UiPart {
@FXML
private Label id;
@FXML
- private Label phone;
+ private Label priority;
@FXML
- private Label address;
+ private Label dueDate;
@FXML
- private Label email;
+ private Label status;
+
@FXML
private FlowPane tags;
+ @FXML
+ private Label link;
+ @FXML
+ private Label description;
/**
- * Creates a {@code PersonCode} with the given {@code Person} and index to display.
+ * Creates a {@code TaskCode} with the given {@code Task} and index to display.
*/
- public PersonCard(Person person, int displayedIndex) {
+ public TaskCard(Task task, int displayedIndex) {
super(FXML);
- this.person = person;
+ this.task = task;
id.setText(displayedIndex + ". ");
- name.setText(person.getName().fullName);
- phone.setText(person.getPhone().value);
- address.setText(person.getAddress().value);
- email.setText(person.getEmail().value);
- person.getTags().stream()
+ name.setText(task.getName().fullName);
+ priority.setText("Priority: " + task.getPriority().value);
+ dueDate.setText("DueDate: " + task.getDueDate().toString());
+ status.setText(" " + task.getStatus().status + " ");
+ description.setText(task.getDescription().description);
+ task.getTags().stream()
.sorted(Comparator.comparing(tag -> tag.tagName))
.forEach(tag -> tags.getChildren().add(new Label(tag.tagName)));
+ link.setText(task.getLink().value);
}
}
diff --git a/src/main/java/profplan/ui/TaskListPanel.java b/src/main/java/profplan/ui/TaskListPanel.java
new file mode 100644
index 00000000000..831a1e3187d
--- /dev/null
+++ b/src/main/java/profplan/ui/TaskListPanel.java
@@ -0,0 +1,49 @@
+package profplan.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 profplan.commons.core.LogsCenter;
+import profplan.model.task.Task;
+
+/**
+ * Panel containing the list of tasks.
+ */
+public class TaskListPanel extends UiPart {
+ private static final String FXML = "TaskListPanel.fxml";
+ private final Logger logger = LogsCenter.getLogger(TaskListPanel.class);
+
+ @FXML
+ private ListView taskListView;
+
+ /**
+ * Creates a {@code TaskListPanel} with the given {@code ObservableList}.
+ */
+ public TaskListPanel(ObservableList taskList) {
+ super(FXML);
+ taskListView.setItems(taskList);
+ taskListView.setCellFactory(listView -> new TaskListViewCell());
+ }
+
+ /**
+ * Custom {@code ListCell} that displays the graphics of a {@code Task} using a {@code TaskCard}.
+ */
+ class TaskListViewCell extends ListCell {
+ @Override
+ protected void updateItem(Task task, boolean empty) {
+ super.updateItem(task, empty);
+
+ if (empty || task == null) {
+ setGraphic(null);
+ setText(null);
+ } else {
+ setGraphic(new TaskCard(task, getIndex() + 1).getRoot());
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/ui/Ui.java b/src/main/java/profplan/ui/Ui.java
similarity index 86%
rename from src/main/java/seedu/address/ui/Ui.java
rename to src/main/java/profplan/ui/Ui.java
index 17aa0b494fe..dcf9a30d809 100644
--- a/src/main/java/seedu/address/ui/Ui.java
+++ b/src/main/java/profplan/ui/Ui.java
@@ -1,4 +1,4 @@
-package seedu.address.ui;
+package profplan.ui;
import javafx.stage.Stage;
diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/profplan/ui/UiManager.java
similarity index 91%
rename from src/main/java/seedu/address/ui/UiManager.java
rename to src/main/java/profplan/ui/UiManager.java
index fdf024138bc..d19beacb535 100644
--- a/src/main/java/seedu/address/ui/UiManager.java
+++ b/src/main/java/profplan/ui/UiManager.java
@@ -1,4 +1,4 @@
-package seedu.address.ui;
+package profplan.ui;
import java.util.logging.Logger;
@@ -7,10 +7,10 @@
import javafx.scene.control.Alert.AlertType;
import javafx.scene.image.Image;
import javafx.stage.Stage;
-import seedu.address.MainApp;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.commons.util.StringUtil;
-import seedu.address.logic.Logic;
+import profplan.MainApp;
+import profplan.commons.core.LogsCenter;
+import profplan.commons.util.StringUtil;
+import profplan.logic.Logic;
/**
* The manager of the UI component.
@@ -20,7 +20,7 @@ public class UiManager implements Ui {
public static final String ALERT_DIALOG_PANE_FIELD_ID = "alertDialogPane";
private static final Logger logger = LogsCenter.getLogger(UiManager.class);
- private static final String ICON_APPLICATION = "/images/address_book_32.png";
+ private static final String ICON_APPLICATION = "/images/profplan_32.png";
private Logic logic;
private MainWindow mainWindow;
diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/profplan/ui/UiPart.java
similarity index 97%
rename from src/main/java/seedu/address/ui/UiPart.java
rename to src/main/java/profplan/ui/UiPart.java
index fc820e01a9c..81c9cca849e 100644
--- a/src/main/java/seedu/address/ui/UiPart.java
+++ b/src/main/java/profplan/ui/UiPart.java
@@ -1,4 +1,4 @@
-package seedu.address.ui;
+package profplan.ui;
import static java.util.Objects.requireNonNull;
@@ -6,7 +6,7 @@
import java.net.URL;
import javafx.fxml.FXMLLoader;
-import seedu.address.MainApp;
+import profplan.MainApp;
/**
* Represents a distinct part of the UI. e.g. Windows, dialogs, panels, status bars, etc.
@@ -65,9 +65,9 @@ public T getRoot() {
*/
private void loadFxmlFile(URL location, T root) {
requireNonNull(location);
+ fxmlLoader.setRoot(root);
fxmlLoader.setLocation(location);
fxmlLoader.setController(this);
- fxmlLoader.setRoot(root);
try {
fxmlLoader.load();
} catch (IOException e) {
diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java
deleted file mode 100644
index ecd32c31b53..00000000000
--- a/src/main/java/seedu/address/logic/Messages.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package seedu.address.logic;
-
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import seedu.address.logic.parser.Prefix;
-import seedu.address.model.person.Person;
-
-/**
- * Container for user visible messages.
- */
-public class Messages {
-
- public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command";
- public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s";
- public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid";
- public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!";
- public static final String MESSAGE_DUPLICATE_FIELDS =
- "Multiple values specified for the following single-valued field(s): ";
-
- /**
- * Returns an error message indicating the duplicate prefixes.
- */
- public static String getErrorMessageForDuplicatePrefixes(Prefix... duplicatePrefixes) {
- assert duplicatePrefixes.length > 0;
-
- Set duplicateFields =
- Stream.of(duplicatePrefixes).map(Prefix::toString).collect(Collectors.toSet());
-
- return MESSAGE_DUPLICATE_FIELDS + String.join(" ", duplicateFields);
- }
-
- /**
- * Formats the {@code person} for display to the user.
- */
- public static String format(Person person) {
- final StringBuilder builder = new StringBuilder();
- builder.append(person.getName())
- .append("; Phone: ")
- .append(person.getPhone())
- .append("; Email: ")
- .append(person.getEmail())
- .append("; Address: ")
- .append(person.getAddress())
- .append("; Tags: ");
- person.getTags().forEach(builder::append);
- return builder.toString();
- }
-
-}
diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java
deleted file mode 100644
index 5d7185a9680..00000000000
--- a/src/main/java/seedu/address/logic/commands/AddCommand.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.model.Model;
-import seedu.address.model.person.Person;
-
-/**
- * Adds a person to the address book.
- */
-public class AddCommand extends Command {
-
- public static final String COMMAND_WORD = "add";
-
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. "
- + "Parameters: "
- + PREFIX_NAME + "NAME "
- + PREFIX_PHONE + "PHONE "
- + PREFIX_EMAIL + "EMAIL "
- + PREFIX_ADDRESS + "ADDRESS "
- + "[" + PREFIX_TAG + "TAG]...\n"
- + "Example: " + COMMAND_WORD + " "
- + PREFIX_NAME + "John Doe "
- + PREFIX_PHONE + "98765432 "
- + PREFIX_EMAIL + "johnd@example.com "
- + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 "
- + PREFIX_TAG + "friends "
- + PREFIX_TAG + "owesMoney";
-
- public static final String MESSAGE_SUCCESS = "New person added: %1$s";
- public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book";
-
- private final Person toAdd;
-
- /**
- * Creates an AddCommand to add the specified {@code Person}
- */
- public AddCommand(Person person) {
- requireNonNull(person);
- toAdd = person;
- }
-
- @Override
- public CommandResult execute(Model model) throws CommandException {
- requireNonNull(model);
-
- if (model.hasPerson(toAdd)) {
- throw new CommandException(MESSAGE_DUPLICATE_PERSON);
- }
-
- model.addPerson(toAdd);
- return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(toAdd)));
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof AddCommand)) {
- return false;
- }
-
- AddCommand otherAddCommand = (AddCommand) other;
- return toAdd.equals(otherAddCommand.toAdd);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("toAdd", toAdd)
- .toString();
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java
deleted file mode 100644
index 9c86b1fa6e4..00000000000
--- a/src/main/java/seedu/address/logic/commands/ClearCommand.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-
-import seedu.address.model.AddressBook;
-import seedu.address.model.Model;
-
-/**
- * Clears the address book.
- */
-public class ClearCommand extends Command {
-
- public static final String COMMAND_WORD = "clear";
- public static final String MESSAGE_SUCCESS = "Address book has been cleared!";
-
-
- @Override
- public CommandResult execute(Model model) {
- requireNonNull(model);
- model.setAddressBook(new AddressBook());
- return new CommandResult(MESSAGE_SUCCESS);
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
deleted file mode 100644
index 1135ac19b74..00000000000
--- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-
-import java.util.List;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.model.Model;
-import seedu.address.model.person.Person;
-
-/**
- * Deletes a person identified using it's displayed index from the address book.
- */
-public class DeleteCommand extends Command {
-
- public static final String COMMAND_WORD = "delete";
-
- public static final String MESSAGE_USAGE = COMMAND_WORD
- + ": Deletes the person identified by the index number used in the displayed person list.\n"
- + "Parameters: INDEX (must be a positive integer)\n"
- + "Example: " + COMMAND_WORD + " 1";
-
- public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s";
-
- private final Index targetIndex;
-
- public DeleteCommand(Index targetIndex) {
- this.targetIndex = targetIndex;
- }
-
- @Override
- public CommandResult execute(Model model) throws CommandException {
- requireNonNull(model);
- List lastShownList = model.getFilteredPersonList();
-
- if (targetIndex.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
- }
-
- Person personToDelete = lastShownList.get(targetIndex.getZeroBased());
- model.deletePerson(personToDelete);
- return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, Messages.format(personToDelete)));
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof DeleteCommand)) {
- return false;
- }
-
- DeleteCommand otherDeleteCommand = (DeleteCommand) other;
- return targetIndex.equals(otherDeleteCommand.targetIndex);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("targetIndex", targetIndex)
- .toString();
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java
deleted file mode 100644
index 4b581c7331e..00000000000
--- a/src/main/java/seedu/address/logic/commands/EditCommand.java
+++ /dev/null
@@ -1,242 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.commons.util.CollectionUtil;
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.model.Model;
-import seedu.address.model.person.Address;
-import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
-
-/**
- * Edits the details of an existing person in the address book.
- */
-public class EditCommand extends Command {
-
- public static final String COMMAND_WORD = "edit";
-
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified "
- + "by the index number used in the displayed person list. "
- + "Existing values will be overwritten by the input values.\n"
- + "Parameters: INDEX (must be a positive integer) "
- + "[" + PREFIX_NAME + "NAME] "
- + "[" + PREFIX_PHONE + "PHONE] "
- + "[" + PREFIX_EMAIL + "EMAIL] "
- + "[" + PREFIX_ADDRESS + "ADDRESS] "
- + "[" + PREFIX_TAG + "TAG]...\n"
- + "Example: " + COMMAND_WORD + " 1 "
- + PREFIX_PHONE + "91234567 "
- + PREFIX_EMAIL + "johndoe@example.com";
-
- public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s";
- public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided.";
- public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book.";
-
- private final Index index;
- private final EditPersonDescriptor editPersonDescriptor;
-
- /**
- * @param index of the person in the filtered person list to edit
- * @param editPersonDescriptor details to edit the person with
- */
- public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) {
- requireNonNull(index);
- requireNonNull(editPersonDescriptor);
-
- this.index = index;
- this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor);
- }
-
- @Override
- public CommandResult execute(Model model) throws CommandException {
- requireNonNull(model);
- List lastShownList = model.getFilteredPersonList();
-
- if (index.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
- }
-
- Person personToEdit = lastShownList.get(index.getZeroBased());
- Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor);
-
- if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) {
- throw new CommandException(MESSAGE_DUPLICATE_PERSON);
- }
-
- model.setPerson(personToEdit, editedPerson);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson)));
- }
-
- /**
- * Creates and returns a {@code Person} with the details of {@code personToEdit}
- * edited with {@code editPersonDescriptor}.
- */
- private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) {
- assert personToEdit != null;
-
- Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName());
- Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone());
- Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail());
- Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress());
- Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags());
-
- return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags);
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof EditCommand)) {
- return false;
- }
-
- EditCommand otherEditCommand = (EditCommand) other;
- return index.equals(otherEditCommand.index)
- && editPersonDescriptor.equals(otherEditCommand.editPersonDescriptor);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("index", index)
- .add("editPersonDescriptor", editPersonDescriptor)
- .toString();
- }
-
- /**
- * Stores the details to edit the person with. Each non-empty field value will replace the
- * corresponding field value of the person.
- */
- public static class EditPersonDescriptor {
- private Name name;
- private Phone phone;
- private Email email;
- private Address address;
- private Set tags;
-
- public EditPersonDescriptor() {}
-
- /**
- * Copy constructor.
- * A defensive copy of {@code tags} is used internally.
- */
- public EditPersonDescriptor(EditPersonDescriptor toCopy) {
- setName(toCopy.name);
- setPhone(toCopy.phone);
- setEmail(toCopy.email);
- setAddress(toCopy.address);
- setTags(toCopy.tags);
- }
-
- /**
- * Returns true if at least one field is edited.
- */
- public boolean isAnyFieldEdited() {
- return CollectionUtil.isAnyNonNull(name, phone, email, address, tags);
- }
-
- public void setName(Name name) {
- this.name = name;
- }
-
- public Optional getName() {
- return Optional.ofNullable(name);
- }
-
- public void setPhone(Phone phone) {
- this.phone = phone;
- }
-
- public Optional getPhone() {
- return Optional.ofNullable(phone);
- }
-
- public void setEmail(Email email) {
- this.email = email;
- }
-
- public Optional getEmail() {
- return Optional.ofNullable(email);
- }
-
- public void setAddress(Address address) {
- this.address = address;
- }
-
- public Optional getAddress() {
- return Optional.ofNullable(address);
- }
-
- /**
- * Sets {@code tags} to this object's {@code tags}.
- * A defensive copy of {@code tags} is used internally.
- */
- public void setTags(Set tags) {
- this.tags = (tags != null) ? new HashSet<>(tags) : null;
- }
-
- /**
- * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException}
- * if modification is attempted.
- * Returns {@code Optional#empty()} if {@code tags} is null.
- */
- public Optional> getTags() {
- return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty();
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof EditPersonDescriptor)) {
- return false;
- }
-
- EditPersonDescriptor otherEditPersonDescriptor = (EditPersonDescriptor) other;
- return Objects.equals(name, otherEditPersonDescriptor.name)
- && Objects.equals(phone, otherEditPersonDescriptor.phone)
- && Objects.equals(email, otherEditPersonDescriptor.email)
- && Objects.equals(address, otherEditPersonDescriptor.address)
- && Objects.equals(tags, otherEditPersonDescriptor.tags);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("name", name)
- .add("phone", phone)
- .add("email", email)
- .add("address", address)
- .add("tags", tags)
- .toString();
- }
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java
deleted file mode 100644
index bf824f91bd0..00000000000
--- a/src/main/java/seedu/address/logic/commands/HelpCommand.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package seedu.address.logic.commands;
-
-import seedu.address.model.Model;
-
-/**
- * Format full help instructions for every command for display.
- */
-public class HelpCommand extends Command {
-
- public static final String COMMAND_WORD = "help";
-
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Shows program usage instructions.\n"
- + "Example: " + COMMAND_WORD;
-
- public static final String SHOWING_HELP_MESSAGE = "Opened help window.";
-
- @Override
- public CommandResult execute(Model model) {
- return new CommandResult(SHOWING_HELP_MESSAGE, true, false);
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java
deleted file mode 100644
index 84be6ad2596..00000000000
--- a/src/main/java/seedu/address/logic/commands/ListCommand.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
-
-import seedu.address.model.Model;
-
-/**
- * Lists all persons in the address book to the user.
- */
-public class ListCommand extends Command {
-
- public static final String COMMAND_WORD = "list";
-
- public static final String MESSAGE_SUCCESS = "Listed all persons";
-
-
- @Override
- public CommandResult execute(Model model) {
- requireNonNull(model);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(MESSAGE_SUCCESS);
- }
-}
diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
deleted file mode 100644
index 4ff1a97ed77..00000000000
--- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-
-import java.util.Set;
-import java.util.stream.Stream;
-
-import seedu.address.logic.commands.AddCommand;
-import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.Address;
-import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
-
-/**
- * Parses input arguments and creates a new AddCommand object
- */
-public class AddCommandParser implements Parser {
-
- /**
- * Parses the given {@code String} of arguments in the context of the AddCommand
- * and returns an AddCommand object for execution.
- * @throws ParseException if the user input does not conform the expected format
- */
- public AddCommand parse(String args) throws ParseException {
- ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
-
- if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL)
- || !argMultimap.getPreamble().isEmpty()) {
- throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
- }
-
- argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
- Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get());
- Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get());
- Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get());
- Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get());
- Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));
-
- Person person = new Person(name, phone, email, address, tagList);
-
- return new AddCommand(person);
- }
-
- /**
- * Returns true if none of the prefixes contains empty {@code Optional} values in the given
- * {@code ArgumentMultimap}.
- */
- private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
- return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
- }
-
-}
diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
deleted file mode 100644
index 3149ee07e0b..00000000000
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
-
-import java.util.logging.Logger;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.logic.commands.AddCommand;
-import seedu.address.logic.commands.ClearCommand;
-import seedu.address.logic.commands.Command;
-import seedu.address.logic.commands.DeleteCommand;
-import seedu.address.logic.commands.EditCommand;
-import seedu.address.logic.commands.ExitCommand;
-import seedu.address.logic.commands.FindCommand;
-import seedu.address.logic.commands.HelpCommand;
-import seedu.address.logic.commands.ListCommand;
-import seedu.address.logic.parser.exceptions.ParseException;
-
-/**
- * Parses user input.
- */
-public class AddressBookParser {
-
- /**
- * Used for initial separation of command word and args.
- */
- private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)");
- private static final Logger logger = LogsCenter.getLogger(AddressBookParser.class);
-
- /**
- * Parses user input into command for execution.
- *
- * @param userInput full user input string
- * @return the command based on the user input
- * @throws ParseException if the user input does not conform the expected format
- */
- public Command parseCommand(String userInput) throws ParseException {
- final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim());
- if (!matcher.matches()) {
- throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE));
- }
-
- final String commandWord = matcher.group("commandWord");
- final String arguments = matcher.group("arguments");
-
- // Note to developers: Change the log level in config.json to enable lower level (i.e., FINE, FINER and lower)
- // log messages such as the one below.
- // Lower level log messages are used sparingly to minimize noise in the code.
- logger.fine("Command word: " + commandWord + "; Arguments: " + arguments);
-
- switch (commandWord) {
-
- case AddCommand.COMMAND_WORD:
- return new AddCommandParser().parse(arguments);
-
- case EditCommand.COMMAND_WORD:
- return new EditCommandParser().parse(arguments);
-
- case DeleteCommand.COMMAND_WORD:
- return new DeleteCommandParser().parse(arguments);
-
- case ClearCommand.COMMAND_WORD:
- return new ClearCommand();
-
- case FindCommand.COMMAND_WORD:
- return new FindCommandParser().parse(arguments);
-
- case ListCommand.COMMAND_WORD:
- return new ListCommand();
-
- case ExitCommand.COMMAND_WORD:
- return new ExitCommand();
-
- case HelpCommand.COMMAND_WORD:
- return new HelpCommand();
-
- default:
- logger.finer("This user input caused a ParseException: " + userInput);
- 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
deleted file mode 100644
index 75b1a9bf119..00000000000
--- a/src/main/java/seedu/address/logic/parser/CliSyntax.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package seedu.address.logic.parser;
-
-/**
- * Contains Command Line Interface (CLI) syntax definitions common to multiple commands
- */
-public class CliSyntax {
-
- /* Prefix definitions */
- public static final Prefix PREFIX_NAME = new Prefix("n/");
- public static final Prefix PREFIX_PHONE = new Prefix("p/");
- public static final Prefix PREFIX_EMAIL = new Prefix("e/");
- public static final Prefix PREFIX_ADDRESS = new Prefix("a/");
- public static final Prefix PREFIX_TAG = new Prefix("t/");
-
-}
diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java
deleted file mode 100644
index b117acb9c55..00000000000
--- a/src/main/java/seedu/address/logic/parser/ParserUtil.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package seedu.address.logic.parser;
-
-import static java.util.Objects.requireNonNull;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.commons.util.StringUtil;
-import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.Address;
-import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
-
-/**
- * Contains utility methods used for parsing strings in the various *Parser classes.
- */
-public class ParserUtil {
-
- public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer.";
-
- /**
- * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be
- * trimmed.
- * @throws ParseException if the specified index is invalid (not non-zero unsigned integer).
- */
- public static Index parseIndex(String oneBasedIndex) throws ParseException {
- String trimmedIndex = oneBasedIndex.trim();
- if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) {
- throw new ParseException(MESSAGE_INVALID_INDEX);
- }
- return Index.fromOneBased(Integer.parseInt(trimmedIndex));
- }
-
- /**
- * Parses a {@code String name} into a {@code Name}.
- * Leading and trailing whitespaces will be trimmed.
- *
- * @throws ParseException if the given {@code name} is invalid.
- */
- public static Name parseName(String name) throws ParseException {
- requireNonNull(name);
- String trimmedName = name.trim();
- if (!Name.isValidName(trimmedName)) {
- throw new ParseException(Name.MESSAGE_CONSTRAINTS);
- }
- return new Name(trimmedName);
- }
-
- /**
- * Parses a {@code String phone} into a {@code Phone}.
- * Leading and trailing whitespaces will be trimmed.
- *
- * @throws ParseException if the given {@code phone} is invalid.
- */
- public static Phone parsePhone(String phone) throws ParseException {
- requireNonNull(phone);
- String trimmedPhone = phone.trim();
- if (!Phone.isValidPhone(trimmedPhone)) {
- throw new ParseException(Phone.MESSAGE_CONSTRAINTS);
- }
- return new Phone(trimmedPhone);
- }
-
- /**
- * Parses a {@code String address} into an {@code Address}.
- * Leading and trailing whitespaces will be trimmed.
- *
- * @throws ParseException if the given {@code address} is invalid.
- */
- public static Address parseAddress(String address) throws ParseException {
- requireNonNull(address);
- String trimmedAddress = address.trim();
- if (!Address.isValidAddress(trimmedAddress)) {
- throw new ParseException(Address.MESSAGE_CONSTRAINTS);
- }
- return new Address(trimmedAddress);
- }
-
- /**
- * Parses a {@code String email} into an {@code Email}.
- * Leading and trailing whitespaces will be trimmed.
- *
- * @throws ParseException if the given {@code email} is invalid.
- */
- public static Email parseEmail(String email) throws ParseException {
- requireNonNull(email);
- String trimmedEmail = email.trim();
- if (!Email.isValidEmail(trimmedEmail)) {
- throw new ParseException(Email.MESSAGE_CONSTRAINTS);
- }
- return new Email(trimmedEmail);
- }
-
- /**
- * Parses a {@code String tag} into a {@code Tag}.
- * Leading and trailing whitespaces will be trimmed.
- *
- * @throws ParseException if the given {@code tag} is invalid.
- */
- public static Tag parseTag(String tag) throws ParseException {
- requireNonNull(tag);
- String trimmedTag = tag.trim();
- if (!Tag.isValidTagName(trimmedTag)) {
- throw new ParseException(Tag.MESSAGE_CONSTRAINTS);
- }
- return new Tag(trimmedTag);
- }
-
- /**
- * Parses {@code Collection tags} into a {@code Set}.
- */
- public static Set parseTags(Collection tags) throws ParseException {
- requireNonNull(tags);
- final Set tagSet = new HashSet<>();
- for (String tagName : tags) {
- tagSet.add(parseTag(tagName));
- }
- return tagSet;
- }
-}
diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java
deleted file mode 100644
index 73397161e84..00000000000
--- a/src/main/java/seedu/address/model/AddressBook.java
+++ /dev/null
@@ -1,130 +0,0 @@
-package seedu.address.model;
-
-import static java.util.Objects.requireNonNull;
-
-import java.util.List;
-
-import javafx.collections.ObservableList;
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.UniquePersonList;
-
-/**
- * Wraps all data at the address-book level
- * Duplicates are not allowed (by .isSamePerson comparison)
- */
-public class AddressBook implements ReadOnlyAddressBook {
-
- private final UniquePersonList persons;
-
- /*
- * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication
- * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html
- *
- * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication
- * among constructors.
- */
- {
- persons = new UniquePersonList();
- }
-
- public AddressBook() {}
-
- /**
- * Creates an AddressBook using the Persons in the {@code toBeCopied}
- */
- public AddressBook(ReadOnlyAddressBook toBeCopied) {
- this();
- resetData(toBeCopied);
- }
-
- //// list overwrite operations
-
- /**
- * Replaces the contents of the person list with {@code persons}.
- * {@code persons} must not contain duplicate persons.
- */
- public void setPersons(List persons) {
- this.persons.setPersons(persons);
- }
-
- /**
- * Resets the existing data of this {@code AddressBook} with {@code newData}.
- */
- public void resetData(ReadOnlyAddressBook newData) {
- requireNonNull(newData);
-
- setPersons(newData.getPersonList());
- }
-
- //// person-level operations
-
- /**
- * Returns true if a person with the same identity as {@code person} exists in the address book.
- */
- public boolean hasPerson(Person person) {
- requireNonNull(person);
- return persons.contains(person);
- }
-
- /**
- * Adds a person to the address book.
- * The person must not already exist in the address book.
- */
- public void addPerson(Person p) {
- persons.add(p);
- }
-
- /**
- * Replaces the given person {@code target} in the list with {@code editedPerson}.
- * {@code target} must exist in the address book.
- * The person identity of {@code editedPerson} must not be the same as another existing person in the address book.
- */
- public void setPerson(Person target, Person editedPerson) {
- requireNonNull(editedPerson);
-
- persons.setPerson(target, editedPerson);
- }
-
- /**
- * Removes {@code key} from this {@code AddressBook}.
- * {@code key} must exist in the address book.
- */
- public void removePerson(Person key) {
- persons.remove(key);
- }
-
- //// util methods
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("persons", persons)
- .toString();
- }
-
- @Override
- public ObservableList getPersonList() {
- return persons.asUnmodifiableObservableList();
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof AddressBook)) {
- return false;
- }
-
- AddressBook otherAddressBook = (AddressBook) other;
- return persons.equals(otherAddressBook.persons);
- }
-
- @Override
- public int hashCode() {
- return persons.hashCode();
- }
-}
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
deleted file mode 100644
index d54df471c1f..00000000000
--- a/src/main/java/seedu/address/model/Model.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package seedu.address.model;
-
-import java.nio.file.Path;
-import java.util.function.Predicate;
-
-import javafx.collections.ObservableList;
-import seedu.address.commons.core.GuiSettings;
-import seedu.address.model.person.Person;
-
-/**
- * The API of the Model component.
- */
-public interface Model {
- /** {@code Predicate} that always evaluate to true */
- Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true;
-
- /**
- * Replaces user prefs data with the data in {@code userPrefs}.
- */
- void setUserPrefs(ReadOnlyUserPrefs userPrefs);
-
- /**
- * Returns the user prefs.
- */
- ReadOnlyUserPrefs getUserPrefs();
-
- /**
- * Returns the user prefs' GUI settings.
- */
- GuiSettings getGuiSettings();
-
- /**
- * Sets the user prefs' GUI settings.
- */
- void setGuiSettings(GuiSettings guiSettings);
-
- /**
- * Returns the user prefs' address book file path.
- */
- Path getAddressBookFilePath();
-
- /**
- * Sets the user prefs' address book file path.
- */
- void setAddressBookFilePath(Path addressBookFilePath);
-
- /**
- * Replaces address book data with the data in {@code addressBook}.
- */
- void setAddressBook(ReadOnlyAddressBook addressBook);
-
- /** Returns the AddressBook */
- ReadOnlyAddressBook getAddressBook();
-
- /**
- * Returns true if a person with the same identity as {@code person} exists in the address book.
- */
- boolean hasPerson(Person person);
-
- /**
- * Deletes the given person.
- * The person must exist in the address book.
- */
- void deletePerson(Person target);
-
- /**
- * Adds the given person.
- * {@code person} must not already exist in the address book.
- */
- void addPerson(Person person);
-
- /**
- * Replaces the given person {@code target} with {@code editedPerson}.
- * {@code target} must exist in the address book.
- * The person identity of {@code editedPerson} must not be the same as another existing person in the address book.
- */
- void setPerson(Person target, Person editedPerson);
-
- /** Returns an unmodifiable view of the filtered person list */
- ObservableList getFilteredPersonList();
-
- /**
- * Updates the filter of the filtered person list to filter by the given {@code predicate}.
- * @throws NullPointerException if {@code predicate} is null.
- */
- void updateFilteredPersonList(Predicate predicate);
-}
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
deleted file mode 100644
index 57bc563fde6..00000000000
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ /dev/null
@@ -1,148 +0,0 @@
-package seedu.address.model;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
-
-import java.nio.file.Path;
-import java.util.function.Predicate;
-import java.util.logging.Logger;
-
-import javafx.collections.ObservableList;
-import javafx.collections.transformation.FilteredList;
-import seedu.address.commons.core.GuiSettings;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.model.person.Person;
-
-/**
- * Represents the in-memory model of the address book data.
- */
-public class ModelManager implements Model {
- private static final Logger logger = LogsCenter.getLogger(ModelManager.class);
-
- private final AddressBook addressBook;
- private final UserPrefs userPrefs;
- private final FilteredList filteredPersons;
-
- /**
- * Initializes a ModelManager with the given addressBook and userPrefs.
- */
- public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) {
- requireAllNonNull(addressBook, userPrefs);
-
- logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs);
-
- this.addressBook = new AddressBook(addressBook);
- this.userPrefs = new UserPrefs(userPrefs);
- filteredPersons = new FilteredList<>(this.addressBook.getPersonList());
- }
-
- public ModelManager() {
- this(new AddressBook(), new UserPrefs());
- }
-
- //=========== UserPrefs ==================================================================================
-
- @Override
- public void setUserPrefs(ReadOnlyUserPrefs userPrefs) {
- requireNonNull(userPrefs);
- this.userPrefs.resetData(userPrefs);
- }
-
- @Override
- public ReadOnlyUserPrefs getUserPrefs() {
- return userPrefs;
- }
-
- @Override
- public GuiSettings getGuiSettings() {
- return userPrefs.getGuiSettings();
- }
-
- @Override
- public void setGuiSettings(GuiSettings guiSettings) {
- requireNonNull(guiSettings);
- userPrefs.setGuiSettings(guiSettings);
- }
-
- @Override
- public Path getAddressBookFilePath() {
- return userPrefs.getAddressBookFilePath();
- }
-
- @Override
- public void setAddressBookFilePath(Path addressBookFilePath) {
- requireNonNull(addressBookFilePath);
- userPrefs.setAddressBookFilePath(addressBookFilePath);
- }
-
- //=========== AddressBook ================================================================================
-
- @Override
- public void setAddressBook(ReadOnlyAddressBook addressBook) {
- this.addressBook.resetData(addressBook);
- }
-
- @Override
- public ReadOnlyAddressBook getAddressBook() {
- return addressBook;
- }
-
- @Override
- public boolean hasPerson(Person person) {
- requireNonNull(person);
- return addressBook.hasPerson(person);
- }
-
- @Override
- public void deletePerson(Person target) {
- addressBook.removePerson(target);
- }
-
- @Override
- public void addPerson(Person person) {
- addressBook.addPerson(person);
- updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- }
-
- @Override
- public void setPerson(Person target, Person editedPerson) {
- requireAllNonNull(target, editedPerson);
-
- addressBook.setPerson(target, editedPerson);
- }
-
- //=========== Filtered Person List Accessors =============================================================
-
- /**
- * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of
- * {@code versionedAddressBook}
- */
- @Override
- public ObservableList getFilteredPersonList() {
- return filteredPersons;
- }
-
- @Override
- public void updateFilteredPersonList(Predicate predicate) {
- requireNonNull(predicate);
- filteredPersons.setPredicate(predicate);
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof ModelManager)) {
- return false;
- }
-
- ModelManager otherModelManager = (ModelManager) other;
- return addressBook.equals(otherModelManager.addressBook)
- && userPrefs.equals(otherModelManager.userPrefs)
- && filteredPersons.equals(otherModelManager.filteredPersons);
- }
-
-}
diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
deleted file mode 100644
index 6ddc2cd9a29..00000000000
--- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package seedu.address.model;
-
-import javafx.collections.ObservableList;
-import seedu.address.model.person.Person;
-
-/**
- * Unmodifiable view of an address book
- */
-public interface ReadOnlyAddressBook {
-
- /**
- * Returns an unmodifiable view of the persons list.
- * This list will not contain any duplicate persons.
- */
- ObservableList getPersonList();
-
-}
diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/person/Address.java
deleted file mode 100644
index 469a2cc9a1e..00000000000
--- a/src/main/java/seedu/address/model/person/Address.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package seedu.address.model.person;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
-
-/**
- * Represents a Person's address in the address book.
- * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)}
- */
-public class Address {
-
- public static final String MESSAGE_CONSTRAINTS = "Addresses can take any values, and it should not be blank";
-
- /*
- * The first character of the address must not be a whitespace,
- * otherwise " " (a blank string) becomes a valid input.
- */
- public static final String VALIDATION_REGEX = "[^\\s].*";
-
- public final String value;
-
- /**
- * Constructs an {@code Address}.
- *
- * @param address A valid address.
- */
- public Address(String address) {
- requireNonNull(address);
- checkArgument(isValidAddress(address), MESSAGE_CONSTRAINTS);
- value = address;
- }
-
- /**
- * Returns true if a given string is a valid email.
- */
- public static boolean isValidAddress(String test) {
- return test.matches(VALIDATION_REGEX);
- }
-
- @Override
- public String toString() {
- return value;
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Address)) {
- return false;
- }
-
- Address otherAddress = (Address) other;
- return value.equals(otherAddress.value);
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/person/Email.java
deleted file mode 100644
index c62e512bc29..00000000000
--- a/src/main/java/seedu/address/model/person/Email.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package seedu.address.model.person;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
-
-/**
- * Represents a Person's email in the address book.
- * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)}
- */
-public class Email {
-
- private static final String SPECIAL_CHARACTERS = "+_.-";
- public static final String MESSAGE_CONSTRAINTS = "Emails should be of the format local-part@domain "
- + "and adhere to the following constraints:\n"
- + "1. The local-part should only contain alphanumeric characters and these special characters, excluding "
- + "the parentheses, (" + SPECIAL_CHARACTERS + "). The local-part may not start or end with any special "
- + "characters.\n"
- + "2. This is followed by a '@' and then a domain name. The domain name is made up of domain labels "
- + "separated by periods.\n"
- + "The domain name must:\n"
- + " - end with a domain label at least 2 characters long\n"
- + " - have each domain label start and end with alphanumeric characters\n"
- + " - have each domain label consist of alphanumeric characters, separated only by hyphens, if any.";
- // alphanumeric and special characters
- private static final String ALPHANUMERIC_NO_UNDERSCORE = "[^\\W_]+"; // alphanumeric characters except underscore
- private static final String LOCAL_PART_REGEX = "^" + ALPHANUMERIC_NO_UNDERSCORE + "([" + SPECIAL_CHARACTERS + "]"
- + ALPHANUMERIC_NO_UNDERSCORE + ")*";
- private static final String DOMAIN_PART_REGEX = ALPHANUMERIC_NO_UNDERSCORE
- + "(-" + ALPHANUMERIC_NO_UNDERSCORE + ")*";
- private static final String DOMAIN_LAST_PART_REGEX = "(" + DOMAIN_PART_REGEX + "){2,}$"; // At least two chars
- private static final String DOMAIN_REGEX = "(" + DOMAIN_PART_REGEX + "\\.)*" + DOMAIN_LAST_PART_REGEX;
- public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@" + DOMAIN_REGEX;
-
- public final String value;
-
- /**
- * Constructs an {@code Email}.
- *
- * @param email A valid email address.
- */
- public Email(String email) {
- requireNonNull(email);
- checkArgument(isValidEmail(email), MESSAGE_CONSTRAINTS);
- value = email;
- }
-
- /**
- * Returns if a given string is a valid email.
- */
- public static boolean isValidEmail(String test) {
- return test.matches(VALIDATION_REGEX);
- }
-
- @Override
- public String toString() {
- return value;
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Email)) {
- return false;
- }
-
- Email otherEmail = (Email) other;
- return value.equals(otherEmail.value);
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java
deleted file mode 100644
index abe8c46b535..00000000000
--- a/src/main/java/seedu/address/model/person/Person.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package seedu.address.model.person;
-
-import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.model.tag.Tag;
-
-/**
- * Represents a Person in the address book.
- * Guarantees: details are present and not null, field values are validated, immutable.
- */
-public class Person {
-
- // Identity fields
- private final Name name;
- private final Phone phone;
- private final Email email;
-
- // Data fields
- private final Address address;
- private final Set tags = new HashSet<>();
-
- /**
- * Every field must be present and not null.
- */
- public Person(Name name, Phone phone, Email email, Address address, Set tags) {
- requireAllNonNull(name, phone, email, address, tags);
- this.name = name;
- this.phone = phone;
- this.email = email;
- this.address = address;
- this.tags.addAll(tags);
- }
-
- public Name getName() {
- return name;
- }
-
- public Phone getPhone() {
- return phone;
- }
-
- public Email getEmail() {
- return email;
- }
-
- public Address getAddress() {
- return address;
- }
-
- /**
- * Returns an immutable tag set, which throws {@code UnsupportedOperationException}
- * if modification is attempted.
- */
- public Set getTags() {
- return Collections.unmodifiableSet(tags);
- }
-
- /**
- * Returns true if both persons have the same name.
- * This defines a weaker notion of equality between two persons.
- */
- public boolean isSamePerson(Person otherPerson) {
- if (otherPerson == this) {
- return true;
- }
-
- return otherPerson != null
- && otherPerson.getName().equals(getName());
- }
-
- /**
- * Returns true if both persons have the same identity and data fields.
- * This defines a stronger notion of equality between two persons.
- */
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Person)) {
- return false;
- }
-
- Person otherPerson = (Person) other;
- return name.equals(otherPerson.name)
- && phone.equals(otherPerson.phone)
- && email.equals(otherPerson.email)
- && address.equals(otherPerson.address)
- && tags.equals(otherPerson.tags);
- }
-
- @Override
- public int hashCode() {
- // use this method for custom fields hashing instead of implementing your own
- return Objects.hash(name, phone, email, address, tags);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("name", name)
- .add("phone", phone)
- .add("email", email)
- .add("address", address)
- .add("tags", tags)
- .toString();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/person/Phone.java
deleted file mode 100644
index d733f63d739..00000000000
--- a/src/main/java/seedu/address/model/person/Phone.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package seedu.address.model.person;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
-
-/**
- * Represents a Person's phone number in the address book.
- * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)}
- */
-public class Phone {
-
-
- public static final String MESSAGE_CONSTRAINTS =
- "Phone numbers should only contain numbers, and it should be at least 3 digits long";
- public static final String VALIDATION_REGEX = "\\d{3,}";
- public final String value;
-
- /**
- * Constructs a {@code Phone}.
- *
- * @param phone A valid phone number.
- */
- public Phone(String phone) {
- requireNonNull(phone);
- checkArgument(isValidPhone(phone), MESSAGE_CONSTRAINTS);
- value = phone;
- }
-
- /**
- * Returns true if a given string is a valid phone number.
- */
- public static boolean isValidPhone(String test) {
- return test.matches(VALIDATION_REGEX);
- }
-
- @Override
- public String toString() {
- return value;
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Phone)) {
- return false;
- }
-
- Phone otherPhone = (Phone) other;
- return value.equals(otherPhone.value);
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java
deleted file mode 100644
index cc0a68d79f9..00000000000
--- a/src/main/java/seedu/address/model/person/UniquePersonList.java
+++ /dev/null
@@ -1,150 +0,0 @@
-package seedu.address.model.person;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
-
-import java.util.Iterator;
-import java.util.List;
-
-import javafx.collections.FXCollections;
-import javafx.collections.ObservableList;
-import seedu.address.model.person.exceptions.DuplicatePersonException;
-import seedu.address.model.person.exceptions.PersonNotFoundException;
-
-/**
- * A list of persons that enforces uniqueness between its elements and does not allow nulls.
- * A person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, adding and updating of
- * persons uses Person#isSamePerson(Person) for equality so as to ensure that the person being added or updated is
- * unique in terms of identity in the UniquePersonList. However, the removal of a person uses Person#equals(Object) so
- * as to ensure that the person with exactly the same fields will be removed.
- *
- * Supports a minimal set of list operations.
- *
- * @see Person#isSamePerson(Person)
- */
-public class UniquePersonList implements Iterable {
-
- private final ObservableList internalList = FXCollections.observableArrayList();
- private final ObservableList internalUnmodifiableList =
- FXCollections.unmodifiableObservableList(internalList);
-
- /**
- * Returns true if the list contains an equivalent person as the given argument.
- */
- public boolean contains(Person toCheck) {
- requireNonNull(toCheck);
- return internalList.stream().anyMatch(toCheck::isSamePerson);
- }
-
- /**
- * Adds a person to the list.
- * The person must not already exist in the list.
- */
- public void add(Person toAdd) {
- requireNonNull(toAdd);
- if (contains(toAdd)) {
- throw new DuplicatePersonException();
- }
- internalList.add(toAdd);
- }
-
- /**
- * Replaces the person {@code target} in the list with {@code editedPerson}.
- * {@code target} must exist in the list.
- * The person identity of {@code editedPerson} must not be the same as another existing person in the list.
- */
- public void setPerson(Person target, Person editedPerson) {
- requireAllNonNull(target, editedPerson);
-
- int index = internalList.indexOf(target);
- if (index == -1) {
- throw new PersonNotFoundException();
- }
-
- if (!target.isSamePerson(editedPerson) && contains(editedPerson)) {
- throw new DuplicatePersonException();
- }
-
- internalList.set(index, editedPerson);
- }
-
- /**
- * Removes the equivalent person from the list.
- * The person must exist in the list.
- */
- public void remove(Person toRemove) {
- requireNonNull(toRemove);
- if (!internalList.remove(toRemove)) {
- throw new PersonNotFoundException();
- }
- }
-
- public void setPersons(UniquePersonList replacement) {
- requireNonNull(replacement);
- internalList.setAll(replacement.internalList);
- }
-
- /**
- * Replaces the contents of this list with {@code persons}.
- * {@code persons} must not contain duplicate persons.
- */
- public void setPersons(List persons) {
- requireAllNonNull(persons);
- if (!personsAreUnique(persons)) {
- throw new DuplicatePersonException();
- }
-
- internalList.setAll(persons);
- }
-
- /**
- * Returns the backing list as an unmodifiable {@code ObservableList}.
- */
- public ObservableList asUnmodifiableObservableList() {
- return internalUnmodifiableList;
- }
-
- @Override
- public Iterator iterator() {
- return internalList.iterator();
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof UniquePersonList)) {
- return false;
- }
-
- UniquePersonList otherUniquePersonList = (UniquePersonList) other;
- return internalList.equals(otherUniquePersonList.internalList);
- }
-
- @Override
- public int hashCode() {
- return internalList.hashCode();
- }
-
- @Override
- public String toString() {
- return internalList.toString();
- }
-
- /**
- * Returns true if {@code persons} contains only unique persons.
- */
- private boolean personsAreUnique(List persons) {
- for (int i = 0; i < persons.size() - 1; i++) {
- for (int j = i + 1; j < persons.size(); j++) {
- if (persons.get(i).isSamePerson(persons.get(j))) {
- return false;
- }
- }
- }
- return true;
- }
-}
diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java
deleted file mode 100644
index d7290f59442..00000000000
--- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package seedu.address.model.person.exceptions;
-
-/**
- * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same
- * identity).
- */
-public class DuplicatePersonException extends RuntimeException {
- public DuplicatePersonException() {
- super("Operation would result in duplicate persons");
- }
-}
diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java
deleted file mode 100644
index fa764426ca7..00000000000
--- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package seedu.address.model.person.exceptions;
-
-/**
- * Signals that the operation is unable to find the specified person.
- */
-public class PersonNotFoundException extends RuntimeException {}
diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java
deleted file mode 100644
index 1806da4facf..00000000000
--- a/src/main/java/seedu/address/model/util/SampleDataUtil.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package seedu.address.model.util;
-
-import java.util.Arrays;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import seedu.address.model.AddressBook;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Address;
-import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
-
-/**
- * Contains utility methods for populating {@code AddressBook} with sample data.
- */
-public class SampleDataUtil {
- public static Person[] getSamplePersons() {
- return new Person[] {
- new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"),
- new Address("Blk 30 Geylang Street 29, #06-40"),
- getTagSet("friends")),
- new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"),
- new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"),
- getTagSet("colleagues", "friends")),
- new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"),
- new Address("Blk 11 Ang Mo Kio Street 74, #11-04"),
- getTagSet("neighbours")),
- new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"),
- new Address("Blk 436 Serangoon Gardens Street 26, #16-43"),
- getTagSet("family")),
- new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"),
- new Address("Blk 47 Tampines Street 20, #17-35"),
- getTagSet("classmates")),
- new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"),
- new Address("Blk 45 Aljunied Street 85, #11-31"),
- getTagSet("colleagues"))
- };
- }
-
- public static ReadOnlyAddressBook getSampleAddressBook() {
- AddressBook sampleAb = new AddressBook();
- for (Person samplePerson : getSamplePersons()) {
- sampleAb.addPerson(samplePerson);
- }
- return sampleAb;
- }
-
- /**
- * Returns a tag set containing the list of strings given.
- */
- public static Set getTagSet(String... strings) {
- return Arrays.stream(strings)
- .map(Tag::new)
- .collect(Collectors.toSet());
- }
-
-}
diff --git a/src/main/java/seedu/address/storage/AddressBookStorage.java b/src/main/java/seedu/address/storage/AddressBookStorage.java
deleted file mode 100644
index f2e015105ae..00000000000
--- a/src/main/java/seedu/address/storage/AddressBookStorage.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package seedu.address.storage;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Optional;
-
-import seedu.address.commons.exceptions.DataLoadingException;
-import seedu.address.model.ReadOnlyAddressBook;
-
-/**
- * Represents a storage for {@link seedu.address.model.AddressBook}.
- */
-public interface AddressBookStorage {
-
- /**
- * Returns the file path of the data file.
- */
- Path getAddressBookFilePath();
-
- /**
- * Returns AddressBook data as a {@link ReadOnlyAddressBook}.
- * Returns {@code Optional.empty()} if storage file is not found.
- *
- * @throws DataLoadingException if loading the data from storage failed.
- */
- Optional readAddressBook() throws DataLoadingException;
-
- /**
- * @see #getAddressBookFilePath()
- */
- Optional readAddressBook(Path filePath) throws DataLoadingException;
-
- /**
- * Saves the given {@link ReadOnlyAddressBook} to the storage.
- * @param addressBook cannot be null.
- * @throws IOException if there was any problem writing to the file.
- */
- void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException;
-
- /**
- * @see #saveAddressBook(ReadOnlyAddressBook)
- */
- void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException;
-
-}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
deleted file mode 100644
index bd1ca0f56c8..00000000000
--- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package seedu.address.storage;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.person.Address;
-import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
-
-/**
- * Jackson-friendly version of {@link Person}.
- */
-class JsonAdaptedPerson {
-
- public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!";
-
- private final String name;
- private final String phone;
- private final String email;
- private final String address;
- private final List tags = new ArrayList<>();
-
- /**
- * Constructs a {@code JsonAdaptedPerson} with the given person details.
- */
- @JsonCreator
- public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone,
- @JsonProperty("email") String email, @JsonProperty("address") String address,
- @JsonProperty("tags") List tags) {
- this.name = name;
- this.phone = phone;
- this.email = email;
- this.address = address;
- if (tags != null) {
- this.tags.addAll(tags);
- }
- }
-
- /**
- * Converts a given {@code Person} into this class for Jackson use.
- */
- public JsonAdaptedPerson(Person source) {
- name = source.getName().fullName;
- phone = source.getPhone().value;
- email = source.getEmail().value;
- address = source.getAddress().value;
- tags.addAll(source.getTags().stream()
- .map(JsonAdaptedTag::new)
- .collect(Collectors.toList()));
- }
-
- /**
- * Converts this Jackson-friendly adapted person object into the model's {@code Person} object.
- *
- * @throws IllegalValueException if there were any data constraints violated in the adapted person.
- */
- public Person toModelType() throws IllegalValueException {
- final List personTags = new ArrayList<>();
- for (JsonAdaptedTag tag : tags) {
- personTags.add(tag.toModelType());
- }
-
- if (name == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()));
- }
- if (!Name.isValidName(name)) {
- throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS);
- }
- final Name modelName = new Name(name);
-
- if (phone == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()));
- }
- if (!Phone.isValidPhone(phone)) {
- throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS);
- }
- final Phone modelPhone = new Phone(phone);
-
- if (email == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName()));
- }
- if (!Email.isValidEmail(email)) {
- throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS);
- }
- final Email modelEmail = new Email(email);
-
- if (address == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()));
- }
- if (!Address.isValidAddress(address)) {
- throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS);
- }
- final Address modelAddress = new Address(address);
-
- final Set modelTags = new HashSet<>(personTags);
- return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags);
- }
-
-}
diff --git a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java b/src/main/java/seedu/address/storage/JsonAddressBookStorage.java
deleted file mode 100644
index 41e06f264e1..00000000000
--- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package seedu.address.storage;
-
-import static java.util.Objects.requireNonNull;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Optional;
-import java.util.logging.Logger;
-
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.commons.exceptions.DataLoadingException;
-import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.commons.util.FileUtil;
-import seedu.address.commons.util.JsonUtil;
-import seedu.address.model.ReadOnlyAddressBook;
-
-/**
- * A class to access AddressBook data stored as a json file on the hard disk.
- */
-public class JsonAddressBookStorage implements AddressBookStorage {
-
- private static final Logger logger = LogsCenter.getLogger(JsonAddressBookStorage.class);
-
- private Path filePath;
-
- public JsonAddressBookStorage(Path filePath) {
- this.filePath = filePath;
- }
-
- public Path getAddressBookFilePath() {
- return filePath;
- }
-
- @Override
- public Optional readAddressBook() throws DataLoadingException {
- return readAddressBook(filePath);
- }
-
- /**
- * Similar to {@link #readAddressBook()}.
- *
- * @param filePath location of the data. Cannot be null.
- * @throws DataLoadingException if loading the data from storage failed.
- */
- public Optional readAddressBook(Path filePath) throws DataLoadingException {
- requireNonNull(filePath);
-
- Optional jsonAddressBook = JsonUtil.readJsonFile(
- filePath, JsonSerializableAddressBook.class);
- if (!jsonAddressBook.isPresent()) {
- return Optional.empty();
- }
-
- try {
- return Optional.of(jsonAddressBook.get().toModelType());
- } catch (IllegalValueException ive) {
- logger.info("Illegal values found in " + filePath + ": " + ive.getMessage());
- throw new DataLoadingException(ive);
- }
- }
-
- @Override
- public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException {
- saveAddressBook(addressBook, filePath);
- }
-
- /**
- * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)}.
- *
- * @param filePath location of the data. Cannot be null.
- */
- public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException {
- requireNonNull(addressBook);
- requireNonNull(filePath);
-
- FileUtil.createIfMissing(filePath);
- JsonUtil.saveJsonFile(new JsonSerializableAddressBook(addressBook), filePath);
- }
-
-}
diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
deleted file mode 100644
index 5efd834091d..00000000000
--- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package seedu.address.storage;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonRootName;
-
-import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.AddressBook;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Person;
-
-/**
- * An Immutable AddressBook that is serializable to JSON format.
- */
-@JsonRootName(value = "addressbook")
-class JsonSerializableAddressBook {
-
- public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s).";
-
- private final List persons = new ArrayList<>();
-
- /**
- * Constructs a {@code JsonSerializableAddressBook} with the given persons.
- */
- @JsonCreator
- public JsonSerializableAddressBook(@JsonProperty("persons") List persons) {
- this.persons.addAll(persons);
- }
-
- /**
- * Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use.
- *
- * @param source future changes to this will not affect the created {@code JsonSerializableAddressBook}.
- */
- public JsonSerializableAddressBook(ReadOnlyAddressBook source) {
- persons.addAll(source.getPersonList().stream().map(JsonAdaptedPerson::new).collect(Collectors.toList()));
- }
-
- /**
- * Converts this address book into the model's {@code AddressBook} object.
- *
- * @throws IllegalValueException if there were any data constraints violated.
- */
- public AddressBook toModelType() throws IllegalValueException {
- AddressBook addressBook = new AddressBook();
- for (JsonAdaptedPerson jsonAdaptedPerson : persons) {
- Person person = jsonAdaptedPerson.toModelType();
- if (addressBook.hasPerson(person)) {
- throw new IllegalValueException(MESSAGE_DUPLICATE_PERSON);
- }
- addressBook.addPerson(person);
- }
- return addressBook;
- }
-
-}
diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/seedu/address/storage/Storage.java
deleted file mode 100644
index 9fba0c7a1d6..00000000000
--- a/src/main/java/seedu/address/storage/Storage.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package seedu.address.storage;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Optional;
-
-import seedu.address.commons.exceptions.DataLoadingException;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.ReadOnlyUserPrefs;
-import seedu.address.model.UserPrefs;
-
-/**
- * API of the Storage component
- */
-public interface Storage extends AddressBookStorage, UserPrefsStorage {
-
- @Override
- Optional readUserPrefs() throws DataLoadingException;
-
- @Override
- void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException;
-
- @Override
- Path getAddressBookFilePath();
-
- @Override
- Optional readAddressBook() throws DataLoadingException;
-
- @Override
- void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException;
-
-}
diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java
deleted file mode 100644
index 8b84a9024d5..00000000000
--- a/src/main/java/seedu/address/storage/StorageManager.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package seedu.address.storage;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Optional;
-import java.util.logging.Logger;
-
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.commons.exceptions.DataLoadingException;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.ReadOnlyUserPrefs;
-import seedu.address.model.UserPrefs;
-
-/**
- * Manages storage of AddressBook data in local storage.
- */
-public class StorageManager implements Storage {
-
- private static final Logger logger = LogsCenter.getLogger(StorageManager.class);
- private AddressBookStorage addressBookStorage;
- private UserPrefsStorage userPrefsStorage;
-
- /**
- * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}.
- */
- public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) {
- this.addressBookStorage = addressBookStorage;
- this.userPrefsStorage = userPrefsStorage;
- }
-
- // ================ UserPrefs methods ==============================
-
- @Override
- public Path getUserPrefsFilePath() {
- return userPrefsStorage.getUserPrefsFilePath();
- }
-
- @Override
- public Optional readUserPrefs() throws DataLoadingException {
- return userPrefsStorage.readUserPrefs();
- }
-
- @Override
- public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException {
- userPrefsStorage.saveUserPrefs(userPrefs);
- }
-
-
- // ================ AddressBook methods ==============================
-
- @Override
- public Path getAddressBookFilePath() {
- return addressBookStorage.getAddressBookFilePath();
- }
-
- @Override
- public Optional readAddressBook() throws DataLoadingException {
- return readAddressBook(addressBookStorage.getAddressBookFilePath());
- }
-
- @Override
- public Optional readAddressBook(Path filePath) throws DataLoadingException {
- logger.fine("Attempting to read data from file: " + filePath);
- return addressBookStorage.readAddressBook(filePath);
- }
-
- @Override
- public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException {
- saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath());
- }
-
- @Override
- public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException {
- logger.fine("Attempting to write to data file: " + filePath);
- addressBookStorage.saveAddressBook(addressBook, filePath);
- }
-
-}
diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java
deleted file mode 100644
index 79e74ef37c0..00000000000
--- a/src/main/java/seedu/address/ui/MainWindow.java
+++ /dev/null
@@ -1,196 +0,0 @@
-package seedu.address.ui;
-
-import java.util.logging.Logger;
-
-import javafx.event.ActionEvent;
-import javafx.fxml.FXML;
-import javafx.scene.control.MenuItem;
-import javafx.scene.control.TextInputControl;
-import javafx.scene.input.KeyCombination;
-import javafx.scene.input.KeyEvent;
-import javafx.scene.layout.StackPane;
-import javafx.stage.Stage;
-import seedu.address.commons.core.GuiSettings;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.logic.Logic;
-import seedu.address.logic.commands.CommandResult;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.logic.parser.exceptions.ParseException;
-
-/**
- * The Main Window. Provides the basic application layout containing
- * a menu bar and space where other JavaFX elements can be placed.
- */
-public class MainWindow extends UiPart {
-
- private static final String FXML = "MainWindow.fxml";
-
- private final Logger logger = LogsCenter.getLogger(getClass());
-
- private Stage primaryStage;
- private Logic logic;
-
- // Independent Ui parts residing in this Ui container
- private PersonListPanel personListPanel;
- private ResultDisplay resultDisplay;
- private HelpWindow helpWindow;
-
- @FXML
- private StackPane commandBoxPlaceholder;
-
- @FXML
- private MenuItem helpMenuItem;
-
- @FXML
- private StackPane personListPanelPlaceholder;
-
- @FXML
- private StackPane resultDisplayPlaceholder;
-
- @FXML
- private StackPane statusbarPlaceholder;
-
- /**
- * Creates a {@code MainWindow} with the given {@code Stage} and {@code Logic}.
- */
- public MainWindow(Stage primaryStage, Logic logic) {
- super(FXML, primaryStage);
-
- // Set dependencies
- this.primaryStage = primaryStage;
- this.logic = logic;
-
- // Configure the UI
- setWindowDefaultSize(logic.getGuiSettings());
-
- setAccelerators();
-
- helpWindow = new HelpWindow();
- }
-
- public Stage getPrimaryStage() {
- return primaryStage;
- }
-
- private void setAccelerators() {
- setAccelerator(helpMenuItem, KeyCombination.valueOf("F1"));
- }
-
- /**
- * Sets the accelerator of a MenuItem.
- * @param keyCombination the KeyCombination value of the accelerator
- */
- private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) {
- menuItem.setAccelerator(keyCombination);
-
- /*
- * TODO: the code below can be removed once the bug reported here
- * https://bugs.openjdk.java.net/browse/JDK-8131666
- * is fixed in later version of SDK.
- *
- * According to the bug report, TextInputControl (TextField, TextArea) will
- * consume function-key events. Because CommandBox contains a TextField, and
- * ResultDisplay contains a TextArea, thus some accelerators (e.g F1) will
- * not work when the focus is in them because the key event is consumed by
- * the TextInputControl(s).
- *
- * For now, we add following event filter to capture such key events and open
- * help window purposely so to support accelerators even when focus is
- * in CommandBox or ResultDisplay.
- */
- getRoot().addEventFilter(KeyEvent.KEY_PRESSED, event -> {
- if (event.getTarget() instanceof TextInputControl && keyCombination.match(event)) {
- menuItem.getOnAction().handle(new ActionEvent());
- event.consume();
- }
- });
- }
-
- /**
- * Fills up all the placeholders of this window.
- */
- void fillInnerParts() {
- personListPanel = new PersonListPanel(logic.getFilteredPersonList());
- personListPanelPlaceholder.getChildren().add(personListPanel.getRoot());
-
- resultDisplay = new ResultDisplay();
- resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot());
-
- StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath());
- statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot());
-
- CommandBox commandBox = new CommandBox(this::executeCommand);
- commandBoxPlaceholder.getChildren().add(commandBox.getRoot());
- }
-
- /**
- * Sets the default size based on {@code guiSettings}.
- */
- private void setWindowDefaultSize(GuiSettings guiSettings) {
- primaryStage.setHeight(guiSettings.getWindowHeight());
- primaryStage.setWidth(guiSettings.getWindowWidth());
- if (guiSettings.getWindowCoordinates() != null) {
- primaryStage.setX(guiSettings.getWindowCoordinates().getX());
- primaryStage.setY(guiSettings.getWindowCoordinates().getY());
- }
- }
-
- /**
- * Opens the help window or focuses on it if it's already opened.
- */
- @FXML
- public void handleHelp() {
- if (!helpWindow.isShowing()) {
- helpWindow.show();
- } else {
- helpWindow.focus();
- }
- }
-
- void show() {
- primaryStage.show();
- }
-
- /**
- * Closes the application.
- */
- @FXML
- private void handleExit() {
- GuiSettings guiSettings = new GuiSettings(primaryStage.getWidth(), primaryStage.getHeight(),
- (int) primaryStage.getX(), (int) primaryStage.getY());
- logic.setGuiSettings(guiSettings);
- helpWindow.hide();
- primaryStage.hide();
- }
-
- public PersonListPanel getPersonListPanel() {
- return personListPanel;
- }
-
- /**
- * Executes the command and returns the result.
- *
- * @see seedu.address.logic.Logic#execute(String)
- */
- private CommandResult executeCommand(String commandText) throws CommandException, ParseException {
- try {
- CommandResult commandResult = logic.execute(commandText);
- logger.info("Result: " + commandResult.getFeedbackToUser());
- resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser());
-
- if (commandResult.isShowHelp()) {
- handleHelp();
- }
-
- if (commandResult.isExit()) {
- handleExit();
- }
-
- return commandResult;
- } catch (CommandException | ParseException e) {
- logger.info("An error occurred while executing command: " + commandText);
- resultDisplay.setFeedbackToUser(e.getMessage());
- throw e;
- }
- }
-}
diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java
deleted file mode 100644
index f4c501a897b..00000000000
--- a/src/main/java/seedu/address/ui/PersonListPanel.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package seedu.address.ui;
-
-import java.util.logging.Logger;
-
-import javafx.collections.ObservableList;
-import javafx.fxml.FXML;
-import javafx.scene.control.ListCell;
-import javafx.scene.control.ListView;
-import javafx.scene.layout.Region;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.model.person.Person;
-
-/**
- * Panel containing the list of persons.
- */
-public class PersonListPanel extends UiPart {
- private static final String FXML = "PersonListPanel.fxml";
- private final Logger logger = LogsCenter.getLogger(PersonListPanel.class);
-
- @FXML
- private ListView personListView;
-
- /**
- * Creates a {@code PersonListPanel} with the given {@code ObservableList}.
- */
- public PersonListPanel(ObservableList personList) {
- super(FXML);
- personListView.setItems(personList);
- personListView.setCellFactory(listView -> new PersonListViewCell());
- }
-
- /**
- * Custom {@code ListCell} that displays the graphics of a {@code Person} using a {@code PersonCard}.
- */
- class PersonListViewCell extends ListCell {
- @Override
- protected void updateItem(Person person, boolean empty) {
- super.updateItem(person, empty);
-
- if (empty || person == null) {
- setGraphic(null);
- setText(null);
- } else {
- setGraphic(new PersonCard(person, getIndex() + 1).getRoot());
- }
- }
- }
-
-}
diff --git a/src/main/resources/images/address_book_32.png b/src/main/resources/images/address_book_32.png
deleted file mode 100644
index 29810cf1fd9..00000000000
Binary files a/src/main/resources/images/address_book_32.png and /dev/null differ
diff --git a/src/main/resources/images/profplan_32.png b/src/main/resources/images/profplan_32.png
new file mode 100644
index 00000000000..9ef091702ea
Binary files /dev/null and b/src/main/resources/images/profplan_32.png differ
diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css
index 36e6b001cd8..273bf6edfbf 100644
--- a/src/main/resources/view/DarkTheme.css
+++ b/src/main/resources/view/DarkTheme.css
@@ -39,17 +39,19 @@
-fx-max-height: 0;
}
+
.table-view {
-fx-base: #1d1d1d;
-fx-control-inner-background: #1d1d1d;
-fx-background-color: #1d1d1d;
-fx-table-cell-border-color: transparent;
-fx-table-header-border-color: transparent;
- -fx-padding: 5;
+ -fx-padding: 10;
}
+
.table-view .column-header-background {
- -fx-background-color: transparent;
+ -fx-background-color: #44106B;
}
.table-view .column-header, .table-view .filler {
@@ -66,16 +68,25 @@
.table-view .column-header .label {
-fx-font-size: 20pt;
- -fx-font-family: "Segoe UI Light";
+ -fx-font-family: "Segoe UI Semibold";
-fx-text-fill: white;
-fx-alignment: center-left;
-fx-opacity: 1;
}
+.table-view:focused .table-row-cell.list-cell {
+ -fx-background-color: #DC4247;
+}
+
.table-view:focused .table-row-cell:filled:focused:selected {
- -fx-background-color: -fx-focus-color;
+ -fx-background-color: transparent;
+}
+
+.table-view .table-row-cell:focused {
+ -fx-background-color: transparent;
}
+
.split-pane:horizontal .split-pane-divider {
-fx-background-color: derive(#1d1d1d, 20%);
-fx-border-color: transparent transparent transparent #4d4d4d;
@@ -100,11 +111,11 @@
}
.list-cell:filled:even {
- -fx-background-color: #3c3e3f;
+ -fx-background-color: #32337D;
}
.list-cell:filled:odd {
- -fx-background-color: #515658;
+ -fx-background-color: #4C3C78;
}
.list-cell:filled:selected {
@@ -146,15 +157,38 @@
-fx-background-color: derive(#1d1d1d, 30%);
}
-.result-display {
+.table-view {
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
+
+.table-view .column-header-background .label {
+ -fx-font-family: "Segoe UI Light";
+ -fx-font-size: 10pt;
+ -fx-text-fill: white;
+ -fx-background-color: transparent;
+ -fx-alignment: CENTER;
+}
+
+.matrix-display {
-fx-background-color: transparent;
-fx-font-family: "Segoe UI Light";
-fx-font-size: 13pt;
-fx-text-fill: white;
}
+.matrix-display .table-row-cell {
+ -fx-background-color: #794ECC;
+}
+
+.matrix-display .list-cell {
+ -fx-background-color: #3c3e3f;
+ -fx-font-size: 11pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
+}
.result-display .label {
- -fx-text-fill: black !important;
+ -fx-text-fill: white !important;
}
.status-bar .label {
@@ -318,23 +352,29 @@
}
#commandTextField {
- -fx-background-color: transparent #383838 transparent #383838;
+ -fx-background-color: #ADDBB6;
-fx-background-insets: 0;
- -fx-border-color: #383838 #383838 #ffffff #383838;
+ -fx-border-color: #383838 #383838 #50AB64 #383838;
-fx-border-insets: 0;
- -fx-border-width: 1;
- -fx-font-family: "Segoe UI Light";
+ -fx-border-width: 3;
+ -fx-font-family: "Segoe UI Bold";
-fx-font-size: 13pt;
- -fx-text-fill: white;
+ -fx-text-fill: black;
+ -fx-prompt-text-fill: #545454;
}
-#filterField, #personListPanel, #personWebpage {
+#filterField, #taskListPanel, #taskWebpage {
-fx-effect: innershadow(gaussian, black, 10, 0, 0, 0);
}
+#resultDisplay {
+
+}
#resultDisplay .content {
- -fx-background-color: transparent, #383838, transparent, #383838;
+ -fx-background-color: black;
-fx-background-radius: 0;
+ -fx-border-width: 3;
+ -fx-border-color: black;
}
#tags {
@@ -344,9 +384,25 @@
#tags .label {
-fx-text-fill: white;
- -fx-background-color: #3e7b91;
+ -fx-background-color: #D97930;
-fx-padding: 1 3 1 3;
-fx-border-radius: 2;
-fx-background-radius: 2;
-fx-font-size: 11;
}
+
+.list-view {
+ -fx-padding: 5px ;
+}
+
+.list-cell {
+ -fx-padding: 5px ;
+ -fx-background-color: transparent;
+ -fx-background-insets: 0px, 5px ;
+}
+
+.list-cell:empty {
+ -fx-padding: 5px ;
+ -fx-background-color: transparent ;
+ -fx-background-insets: 0 ;
+}
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
index 7778f666a0a..a357c5414bb 100644
--- a/src/main/resources/view/MainWindow.fxml
+++ b/src/main/resources/view/MainWindow.fxml
@@ -1,20 +1,24 @@
+
-
+
+
+
+
+
-
+
-
+
@@ -33,24 +37,43 @@
-
+
-
+
-
+
-
+
+
+
+
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/ResultDisplay.fxml b/src/main/resources/view/ResultDisplay.fxml
index 01b691792a9..e8f3d0dfecc 100644
--- a/src/main/resources/view/ResultDisplay.fxml
+++ b/src/main/resources/view/ResultDisplay.fxml
@@ -3,7 +3,6 @@
-
-
+
+
diff --git a/src/main/resources/view/TaskListCard.fxml b/src/main/resources/view/TaskListCard.fxml
new file mode 100644
index 00000000000..9564c3e0f0f
--- /dev/null
+++ b/src/main/resources/view/TaskListCard.fxml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/TaskListPanel.fxml b/src/main/resources/view/TaskListPanel.fxml
new file mode 100644
index 00000000000..096ccf83953
--- /dev/null
+++ b/src/main/resources/view/TaskListPanel.fxml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json
deleted file mode 100644
index 6a4d2b7181c..00000000000
--- a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "persons": [ {
- "name": "Valid Person",
- "phone": "9482424",
- "email": "hans@example.com",
- "address": "4th street"
- }, {
- "name": "Person With Invalid Phone Field",
- "phone": "948asdf2424",
- "email": "hans@example.com",
- "address": "4th street"
- } ]
-}
diff --git a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json
deleted file mode 100644
index ccd21f7d1a9..00000000000
--- a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "persons": [ {
- "name": "Person with invalid name field: Ha!ns Mu@ster",
- "phone": "9482424",
- "email": "hans@example.com",
- "address": "4th street"
- } ]
-}
diff --git a/src/test/data/JsonProfPlanStorageTest/invalidAndValidTaskProfPlan.json b/src/test/data/JsonProfPlanStorageTest/invalidAndValidTaskProfPlan.json
new file mode 100644
index 00000000000..bd8ff8afac4
--- /dev/null
+++ b/src/test/data/JsonProfPlanStorageTest/invalidAndValidTaskProfPlan.json
@@ -0,0 +1,11 @@
+{
+ "tasks": [ {
+ "name": "Valid Task",
+ "priority": "4",
+ "dueDate" : "01-01-2000"
+ }, {
+ "name": "Task With Invalid Priority Field",
+ "priority": "948asdf2424",
+ "dueDate" : "01-01-2000"
+ } ]
+}
diff --git a/src/test/data/JsonProfPlanStorageTest/invalidTaskProfPlan.json b/src/test/data/JsonProfPlanStorageTest/invalidTaskProfPlan.json
new file mode 100644
index 00000000000..70c9ee8976a
--- /dev/null
+++ b/src/test/data/JsonProfPlanStorageTest/invalidTaskProfPlan.json
@@ -0,0 +1,7 @@
+{
+ "tasks": [ {
+ "name": "Task with invalid name field: Ha!ns Mu@ster",
+ "priority": "4",
+ "dueDate" : "01-01-2000"
+ } ]
+}
diff --git a/src/test/data/JsonAddressBookStorageTest/notJsonFormatAddressBook.json b/src/test/data/JsonProfPlanStorageTest/notJsonFormatProfPlan.json
similarity index 100%
rename from src/test/data/JsonAddressBookStorageTest/notJsonFormatAddressBook.json
rename to src/test/data/JsonProfPlanStorageTest/notJsonFormatProfPlan.json
diff --git a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
deleted file mode 100644
index a7427fe7aa2..00000000000
--- a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "persons": [ {
- "name": "Alice Pauline",
- "phone": "94351253",
- "email": "alice@example.com",
- "address": "123, Jurong West Ave 6, #08-111",
- "tags": [ "friends" ]
- }, {
- "name": "Alice Pauline",
- "phone": "94351253",
- "email": "pauline@example.com",
- "address": "4th street"
- } ]
-}
diff --git a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
deleted file mode 100644
index ad3f135ae42..00000000000
--- a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "persons": [ {
- "name": "Hans Muster",
- "phone": "9482424",
- "email": "invalid@email!3e",
- "address": "4th street"
- } ]
-}
diff --git a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
deleted file mode 100644
index 72262099d35..00000000000
--- a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
+++ /dev/null
@@ -1,46 +0,0 @@
-{
- "_comment": "AddressBook save file which contains the same Person values as in TypicalPersons#getTypicalAddressBook()",
- "persons" : [ {
- "name" : "Alice Pauline",
- "phone" : "94351253",
- "email" : "alice@example.com",
- "address" : "123, Jurong West Ave 6, #08-111",
- "tags" : [ "friends" ]
- }, {
- "name" : "Benson Meier",
- "phone" : "98765432",
- "email" : "johnd@example.com",
- "address" : "311, Clementi Ave 2, #02-25",
- "tags" : [ "owesMoney", "friends" ]
- }, {
- "name" : "Carl Kurz",
- "phone" : "95352563",
- "email" : "heinz@example.com",
- "address" : "wall street",
- "tags" : [ ]
- }, {
- "name" : "Daniel Meier",
- "phone" : "87652533",
- "email" : "cornelia@example.com",
- "address" : "10th street",
- "tags" : [ "friends" ]
- }, {
- "name" : "Elle Meyer",
- "phone" : "9482224",
- "email" : "werner@example.com",
- "address" : "michegan ave",
- "tags" : [ ]
- }, {
- "name" : "Fiona Kunz",
- "phone" : "9482427",
- "email" : "lydia@example.com",
- "address" : "little tokyo",
- "tags" : [ ]
- }, {
- "name" : "George Best",
- "phone" : "9482442",
- "email" : "anna@example.com",
- "address" : "4th street",
- "tags" : [ ]
- } ]
-}
diff --git a/src/test/data/JsonSerializableProfPlanTest/duplicateTaskProfPlan.json b/src/test/data/JsonSerializableProfPlanTest/duplicateTaskProfPlan.json
new file mode 100644
index 00000000000..8862c5f39d2
--- /dev/null
+++ b/src/test/data/JsonSerializableProfPlanTest/duplicateTaskProfPlan.json
@@ -0,0 +1,14 @@
+{
+ "tasks": [ {
+ "name": "Alice Pauline",
+ "priority": "3",
+ "tags": [ "friends" ],
+ "dueDate" : "01-01-2000",
+ "description" : ""
+ }, {
+ "name": "Alice Pauline",
+ "priority": "5",
+ "dueDate" : "01-01-2000",
+ "description" : ""
+ } ]
+}
diff --git a/src/test/data/JsonSerializableProfPlanTest/invalidTaskProfPlan.json b/src/test/data/JsonSerializableProfPlanTest/invalidTaskProfPlan.json
new file mode 100644
index 00000000000..71b41a916f1
--- /dev/null
+++ b/src/test/data/JsonSerializableProfPlanTest/invalidTaskProfPlan.json
@@ -0,0 +1,8 @@
+{
+ "tasks": [ {
+ "name": "",
+ "priority": "9482424",
+ "dueDate" : "01-01-2000",
+ "description" : ""
+ } ]
+}
diff --git a/src/test/data/JsonSerializableProfPlanTest/typicalTasksProfPlan.json b/src/test/data/JsonSerializableProfPlanTest/typicalTasksProfPlan.json
new file mode 100644
index 00000000000..6b6671bfdd4
--- /dev/null
+++ b/src/test/data/JsonSerializableProfPlanTest/typicalTasksProfPlan.json
@@ -0,0 +1,46 @@
+{
+ "_comment": "AddressBook save file which contains the same Task values as in TypicalTasks#getTypicalAddressBook()",
+ "tasks" : [ {
+ "name" : "Alice Pauline",
+ "priority" : "1",
+ "tags" : [ "friends" ],
+ "dueDate" : "01-01-2000",
+ "description" : ""
+ }, {
+ "name" : "Benson Meier",
+ "priority" : "2",
+ "tags" : [ "owesMoney", "friends" ],
+ "dueDate" : "01-01-2000",
+ "description" : ""
+ }, {
+ "name" : "Carl Kurz",
+ "priority" : "3",
+ "tags" : [ ],
+ "dueDate" : "01-01-2000",
+ "description" : ""
+ }, {
+ "name" : "Daniel Meier",
+ "priority" : "4",
+ "tags" : [ "friends" ],
+ "dueDate" : "01-01-2000",
+ "description" : ""
+ }, {
+ "name" : "Elle Meyer",
+ "priority" : "5",
+ "tags" : [ ],
+ "dueDate" : "01-01-2000",
+ "description" : ""
+ }, {
+ "name" : "Fiona Kunz",
+ "priority" : "6",
+ "tags" : [ ],
+ "dueDate" : "01-01-2000",
+ "description" : ""
+ }, {
+ "name" : "George Best",
+ "priority" : "10",
+ "tags" : [ ],
+ "dueDate" : "01-01-2000",
+ "description" : ""
+ } ]
+}
diff --git a/src/test/data/JsonUserConfigsStorageTest/EmptyUserConfigs.json b/src/test/data/JsonUserConfigsStorageTest/EmptyUserConfigs.json
new file mode 100644
index 00000000000..0db3279e44b
--- /dev/null
+++ b/src/test/data/JsonUserConfigsStorageTest/EmptyUserConfigs.json
@@ -0,0 +1,3 @@
+{
+
+}
diff --git a/src/test/data/JsonUserConfigsStorageTest/ExtraValuesUserConfig.json b/src/test/data/JsonUserConfigsStorageTest/ExtraValuesUserConfig.json
new file mode 100644
index 00000000000..3fe713d2d72
--- /dev/null
+++ b/src/test/data/JsonUserConfigsStorageTest/ExtraValuesUserConfig.json
@@ -0,0 +1,7 @@
+{
+ "settings" : {
+ "semesterDays" : 180,
+ "extra" : "some value"
+ },
+ "profPlanFilePath" : "data\\profplan.json"
+}
diff --git a/src/test/data/JsonUserConfigsStorageTest/NotJsonFormatUserConfigs.json b/src/test/data/JsonUserConfigsStorageTest/NotJsonFormatUserConfigs.json
new file mode 100644
index 00000000000..b738f344942
--- /dev/null
+++ b/src/test/data/JsonUserConfigsStorageTest/NotJsonFormatUserConfigs.json
@@ -0,0 +1 @@
+Not a json file!
diff --git a/src/test/data/JsonUserConfigsStorageTest/TypicalUserConfig.json b/src/test/data/JsonUserConfigsStorageTest/TypicalUserConfig.json
new file mode 100644
index 00000000000..4eb7b9b20a3
--- /dev/null
+++ b/src/test/data/JsonUserConfigsStorageTest/TypicalUserConfig.json
@@ -0,0 +1,6 @@
+{
+ "settings" : {
+ "semesterDays" : 180
+ },
+ "profPlanFilePath" : "data\\profplan.json"
+}
diff --git a/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json b/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json
index 1037548a9cd..3bb3775fe00 100644
--- a/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json
+++ b/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json
@@ -9,5 +9,5 @@
"z" : 99
}
},
- "addressBookFilePath" : "addressbook.json"
+ "profPlanFilePath" : "addressbook.json"
}
diff --git a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json
index b819bed900a..2b476e68867 100644
--- a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json
+++ b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json
@@ -7,5 +7,5 @@
"y" : 100
}
},
- "addressBookFilePath" : "addressbook.json"
+ "profPlanFilePath" : "addressbook.json"
}
diff --git a/src/test/java/seedu/address/AppParametersTest.java b/src/test/java/profplan/AppParametersTest.java
similarity index 99%
rename from src/test/java/seedu/address/AppParametersTest.java
rename to src/test/java/profplan/AppParametersTest.java
index 133cc008bce..f81a927895f 100644
--- a/src/test/java/seedu/address/AppParametersTest.java
+++ b/src/test/java/profplan/AppParametersTest.java
@@ -1,4 +1,4 @@
-package seedu.address;
+package profplan;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
diff --git a/src/test/java/seedu/address/commons/core/ConfigTest.java b/src/test/java/profplan/commons/core/ConfigTest.java
similarity index 95%
rename from src/test/java/seedu/address/commons/core/ConfigTest.java
rename to src/test/java/profplan/commons/core/ConfigTest.java
index d3ba2a52a89..0736c79faa5 100644
--- a/src/test/java/seedu/address/commons/core/ConfigTest.java
+++ b/src/test/java/profplan/commons/core/ConfigTest.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.core;
+package profplan.commons.core;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
diff --git a/src/test/java/seedu/address/commons/core/GuiSettingsTest.java b/src/test/java/profplan/commons/core/GuiSettingsTest.java
similarity index 93%
rename from src/test/java/seedu/address/commons/core/GuiSettingsTest.java
rename to src/test/java/profplan/commons/core/GuiSettingsTest.java
index b7876c4349d..7eed1e533ac 100644
--- a/src/test/java/seedu/address/commons/core/GuiSettingsTest.java
+++ b/src/test/java/profplan/commons/core/GuiSettingsTest.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.core;
+package profplan.commons.core;
import static org.junit.jupiter.api.Assertions.assertEquals;
diff --git a/src/test/java/seedu/address/commons/core/VersionTest.java b/src/test/java/profplan/commons/core/VersionTest.java
similarity index 95%
rename from src/test/java/seedu/address/commons/core/VersionTest.java
rename to src/test/java/profplan/commons/core/VersionTest.java
index 495cd231554..0aceef9f6ff 100644
--- a/src/test/java/seedu/address/commons/core/VersionTest.java
+++ b/src/test/java/profplan/commons/core/VersionTest.java
@@ -1,11 +1,12 @@
-package seedu.address.commons.core;
+package profplan.commons.core;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.testutil.Assert.assertThrows;
import org.junit.jupiter.api.Test;
+import profplan.testutil.Assert;
+
public class VersionTest {
@Test
@@ -17,7 +18,7 @@ public void versionParsing_acceptableVersionString_parsedVersionCorrectly() {
@Test
public void versionParsing_wrongVersionString_throwIllegalArgumentException() {
- assertThrows(IllegalArgumentException.class, () -> Version.fromString("This is not a version string"));
+ Assert.assertThrows(IllegalArgumentException.class, () -> Version.fromString("This is not a version string"));
}
@Test
diff --git a/src/test/java/seedu/address/commons/core/index/IndexTest.java b/src/test/java/profplan/commons/core/index/IndexTest.java
similarity index 69%
rename from src/test/java/seedu/address/commons/core/index/IndexTest.java
rename to src/test/java/profplan/commons/core/index/IndexTest.java
index fc395ab964b..de8cfa97dd7 100644
--- a/src/test/java/seedu/address/commons/core/index/IndexTest.java
+++ b/src/test/java/profplan/commons/core/index/IndexTest.java
@@ -1,18 +1,19 @@
-package seedu.address.commons.core.index;
+package profplan.commons.core.index;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.testutil.Assert.assertThrows;
import org.junit.jupiter.api.Test;
+import profplan.testutil.Assert;
+
public class IndexTest {
@Test
public void createOneBasedIndex() {
// invalid index
- assertThrows(IndexOutOfBoundsException.class, () -> Index.fromOneBased(0));
+ Assert.assertThrows(IndexOutOfBoundsException.class, () -> Index.fromOneBased(0));
// check equality using the same base
assertEquals(1, Index.fromOneBased(1).getOneBased());
@@ -26,7 +27,7 @@ public void createOneBasedIndex() {
@Test
public void createZeroBasedIndex() {
// invalid index
- assertThrows(IndexOutOfBoundsException.class, () -> Index.fromZeroBased(-1));
+ Assert.assertThrows(IndexOutOfBoundsException.class, () -> Index.fromZeroBased(-1));
// check equality using the same base
assertEquals(0, Index.fromZeroBased(0).getZeroBased());
@@ -39,23 +40,23 @@ public void createZeroBasedIndex() {
@Test
public void equals() {
- final Index fifthPersonIndex = Index.fromOneBased(5);
+ final Index fifthTaskIndex = Index.fromOneBased(5);
// same values -> returns true
- assertTrue(fifthPersonIndex.equals(Index.fromOneBased(5)));
- assertTrue(fifthPersonIndex.equals(Index.fromZeroBased(4)));
+ assertTrue(fifthTaskIndex.equals(Index.fromOneBased(5)));
+ assertTrue(fifthTaskIndex.equals(Index.fromZeroBased(4)));
// same object -> returns true
- assertTrue(fifthPersonIndex.equals(fifthPersonIndex));
+ assertTrue(fifthTaskIndex.equals(fifthTaskIndex));
// null -> returns false
- assertFalse(fifthPersonIndex.equals(null));
+ assertFalse(fifthTaskIndex.equals(null));
// different types -> returns false
- assertFalse(fifthPersonIndex.equals(5.0f));
+ assertFalse(fifthTaskIndex.equals(5.0f));
// different index -> returns false
- assertFalse(fifthPersonIndex.equals(Index.fromOneBased(1)));
+ assertFalse(fifthTaskIndex.equals(Index.fromOneBased(1)));
}
@Test
diff --git a/src/test/java/seedu/address/commons/util/AppUtilTest.java b/src/test/java/profplan/commons/util/AppUtilTest.java
similarity index 59%
rename from src/test/java/seedu/address/commons/util/AppUtilTest.java
rename to src/test/java/profplan/commons/util/AppUtilTest.java
index 594de1e6365..c11a739478c 100644
--- a/src/test/java/seedu/address/commons/util/AppUtilTest.java
+++ b/src/test/java/profplan/commons/util/AppUtilTest.java
@@ -1,20 +1,21 @@
-package seedu.address.commons.util;
+package profplan.commons.util;
import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static seedu.address.testutil.Assert.assertThrows;
import org.junit.jupiter.api.Test;
+import profplan.testutil.Assert;
+
public class AppUtilTest {
@Test
public void getImage_exitingImage() {
- assertNotNull(AppUtil.getImage("/images/address_book_32.png"));
+ assertNotNull(AppUtil.getImage("/images/profplan_32.png"));
}
@Test
public void getImage_nullGiven_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> AppUtil.getImage(null));
+ Assert.assertThrows(NullPointerException.class, () -> AppUtil.getImage(null));
}
@Test
@@ -25,12 +26,13 @@ public void checkArgument_true_nothingHappens() {
@Test
public void checkArgument_falseWithoutErrorMessage_throwsIllegalArgumentException() {
- assertThrows(IllegalArgumentException.class, () -> AppUtil.checkArgument(false));
+ Assert.assertThrows(IllegalArgumentException.class, () -> AppUtil.checkArgument(false));
}
@Test
public void checkArgument_falseWithErrorMessage_throwsIllegalArgumentException() {
String errorMessage = "error message";
- assertThrows(IllegalArgumentException.class, errorMessage, () -> AppUtil.checkArgument(false, errorMessage));
+ Assert.assertThrows(IllegalArgumentException.class, errorMessage, () -> AppUtil.checkArgument(false,
+ errorMessage));
}
}
diff --git a/src/test/java/seedu/address/commons/util/CollectionUtilTest.java b/src/test/java/profplan/commons/util/CollectionUtilTest.java
similarity index 92%
rename from src/test/java/seedu/address/commons/util/CollectionUtilTest.java
rename to src/test/java/profplan/commons/util/CollectionUtilTest.java
index b467a3dc025..d54dfcec662 100644
--- a/src/test/java/seedu/address/commons/util/CollectionUtilTest.java
+++ b/src/test/java/profplan/commons/util/CollectionUtilTest.java
@@ -1,9 +1,8 @@
-package seedu.address.commons.util;
+package profplan.commons.util;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
-import static seedu.address.testutil.Assert.assertThrows;
+import static profplan.commons.util.CollectionUtil.requireAllNonNull;
import java.util.Arrays;
import java.util.Collection;
@@ -12,6 +11,8 @@
import org.junit.jupiter.api.Test;
+import profplan.testutil.Assert;
+
public class CollectionUtilTest {
@Test
public void requireAllNonNullVarargs() {
@@ -87,7 +88,7 @@ public void isAnyNonNull() {
* if {@code objects} or any element of {@code objects} is null.
*/
private void assertNullPointerExceptionThrown(Object... objects) {
- assertThrows(NullPointerException.class, () -> requireAllNonNull(objects));
+ Assert.assertThrows(NullPointerException.class, () -> requireAllNonNull(objects));
}
/**
@@ -95,7 +96,7 @@ private void assertNullPointerExceptionThrown(Object... objects) {
* if {@code collection} or any element of {@code collection} is null.
*/
private void assertNullPointerExceptionThrown(Collection> collection) {
- assertThrows(NullPointerException.class, () -> requireAllNonNull(collection));
+ Assert.assertThrows(NullPointerException.class, () -> requireAllNonNull(collection));
}
private void assertNullPointerExceptionNotThrown(Object... objects) {
diff --git a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java b/src/test/java/profplan/commons/util/ConfigUtilTest.java
similarity index 86%
rename from src/test/java/seedu/address/commons/util/ConfigUtilTest.java
rename to src/test/java/profplan/commons/util/ConfigUtilTest.java
index 69d7b89cfd8..cca5f534560 100644
--- a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java
+++ b/src/test/java/profplan/commons/util/ConfigUtilTest.java
@@ -1,8 +1,7 @@
-package seedu.address.commons.util;
+package profplan.commons.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
-import static seedu.address.testutil.Assert.assertThrows;
import java.io.IOException;
import java.nio.file.Path;
@@ -13,8 +12,9 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
-import seedu.address.commons.core.Config;
-import seedu.address.commons.exceptions.DataLoadingException;
+import profplan.commons.core.Config;
+import profplan.commons.exceptions.DataLoadingException;
+import profplan.testutil.Assert;
public class ConfigUtilTest {
@@ -25,7 +25,7 @@ public class ConfigUtilTest {
@Test
public void read_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> read(null));
+ Assert.assertThrows(NullPointerException.class, () -> read(null));
}
@Test
@@ -35,7 +35,7 @@ public void read_missingFile_emptyResult() throws DataLoadingException {
@Test
public void read_notJsonFormat_exceptionThrown() {
- assertThrows(DataLoadingException.class, () -> read("NotJsonFormatConfig.json"));
+ Assert.assertThrows(DataLoadingException.class, () -> read("NotJsonFormatConfig.json"));
}
@Test
@@ -75,12 +75,12 @@ private Optional read(String configFileInTestDataFolder) throws DataLoad
@Test
public void save_nullConfig_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> save(null, "SomeFile.json"));
+ Assert.assertThrows(NullPointerException.class, () -> save(null, "SomeFile.json"));
}
@Test
public void save_nullFile_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> save(new Config(), null));
+ Assert.assertThrows(NullPointerException.class, () -> save(new Config(), null));
}
@Test
diff --git a/src/test/java/seedu/address/commons/util/FileUtilTest.java b/src/test/java/profplan/commons/util/FileUtilTest.java
similarity index 71%
rename from src/test/java/seedu/address/commons/util/FileUtilTest.java
rename to src/test/java/profplan/commons/util/FileUtilTest.java
index 1fe5478c756..6d854a3d512 100644
--- a/src/test/java/seedu/address/commons/util/FileUtilTest.java
+++ b/src/test/java/profplan/commons/util/FileUtilTest.java
@@ -1,11 +1,12 @@
-package seedu.address.commons.util;
+package profplan.commons.util;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.testutil.Assert.assertThrows;
import org.junit.jupiter.api.Test;
+import profplan.testutil.Assert;
+
public class FileUtilTest {
@Test
@@ -17,7 +18,7 @@ public void isValidPath() {
assertFalse(FileUtil.isValidPath("a\0"));
// null path -> throws NullPointerException
- assertThrows(NullPointerException.class, () -> FileUtil.isValidPath(null));
+ Assert.assertThrows(NullPointerException.class, () -> FileUtil.isValidPath(null));
}
}
diff --git a/src/test/java/seedu/address/commons/util/JsonUtilTest.java b/src/test/java/profplan/commons/util/JsonUtilTest.java
similarity index 92%
rename from src/test/java/seedu/address/commons/util/JsonUtilTest.java
rename to src/test/java/profplan/commons/util/JsonUtilTest.java
index d4907539dee..8603807e9e3 100644
--- a/src/test/java/seedu/address/commons/util/JsonUtilTest.java
+++ b/src/test/java/profplan/commons/util/JsonUtilTest.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.util;
+package profplan.commons.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -7,8 +7,8 @@
import org.junit.jupiter.api.Test;
-import seedu.address.testutil.SerializableTestClass;
-import seedu.address.testutil.TestUtil;
+import profplan.testutil.SerializableTestClass;
+import profplan.testutil.TestUtil;
/**
* Tests JSON Read and Write
diff --git a/src/test/java/profplan/commons/util/SampleDataUtilTest.java b/src/test/java/profplan/commons/util/SampleDataUtilTest.java
new file mode 100644
index 00000000000..7c8e7a0488d
--- /dev/null
+++ b/src/test/java/profplan/commons/util/SampleDataUtilTest.java
@@ -0,0 +1,19 @@
+package profplan.commons.util;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.model.task.DueDate;
+import profplan.model.task.Task;
+import profplan.model.util.SampleDataUtil;
+
+public class SampleDataUtilTest {
+ @Test
+ public void sampleDataUtil_test() {
+ Task[] tasks = SampleDataUtil.getSampleTasks();
+ for (Task task : tasks) {
+ assertTrue(DueDate.isValidDate(task.getDueDate().toString()));
+ }
+ }
+}
diff --git a/src/test/java/seedu/address/commons/util/StringUtilTest.java b/src/test/java/profplan/commons/util/StringUtilTest.java
similarity index 89%
rename from src/test/java/seedu/address/commons/util/StringUtilTest.java
rename to src/test/java/profplan/commons/util/StringUtilTest.java
index c56d407bf3f..5e7da1bcf94 100644
--- a/src/test/java/seedu/address/commons/util/StringUtilTest.java
+++ b/src/test/java/profplan/commons/util/StringUtilTest.java
@@ -1,13 +1,14 @@
-package seedu.address.commons.util;
+package profplan.commons.util;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.testutil.Assert.assertThrows;
import java.io.FileNotFoundException;
import org.junit.jupiter.api.Test;
+import profplan.testutil.Assert;
+
public class StringUtilTest {
//---------------- Tests for isNonZeroUnsignedInteger --------------------------------------
@@ -56,24 +57,25 @@ public void isNonZeroUnsignedInteger() {
@Test
public void containsWordIgnoreCase_nullWord_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> StringUtil.containsWordIgnoreCase("typical sentence", null));
+ Assert.assertThrows(NullPointerException.class, () -> StringUtil.containsWordIgnoreCase(
+ "typical sentence", null));
}
@Test
public void containsWordIgnoreCase_emptyWord_throwsIllegalArgumentException() {
- assertThrows(IllegalArgumentException.class, "Word parameter cannot be empty", ()
+ Assert.assertThrows(IllegalArgumentException.class, "Word parameter cannot be empty", ()
-> StringUtil.containsWordIgnoreCase("typical sentence", " "));
}
@Test
public void containsWordIgnoreCase_multipleWords_throwsIllegalArgumentException() {
- assertThrows(IllegalArgumentException.class, "Word parameter should be a single word", ()
+ Assert.assertThrows(IllegalArgumentException.class, "Word parameter should be a single word", ()
-> StringUtil.containsWordIgnoreCase("typical sentence", "aaa BBB"));
}
@Test
public void containsWordIgnoreCase_nullSentence_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> StringUtil.containsWordIgnoreCase(null, "abc"));
+ Assert.assertThrows(NullPointerException.class, () -> StringUtil.containsWordIgnoreCase(null, "abc"));
}
/*
@@ -137,7 +139,7 @@ public void getDetails_exceptionGiven() {
@Test
public void getDetails_nullGiven_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> StringUtil.getDetails(null));
+ Assert.assertThrows(NullPointerException.class, () -> StringUtil.getDetails(null));
}
}
diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/profplan/logic/LogicManagerTest.java
similarity index 60%
rename from src/test/java/seedu/address/logic/LogicManagerTest.java
rename to src/test/java/profplan/logic/LogicManagerTest.java
index baf8ce336a2..33bd2148d5d 100644
--- a/src/test/java/seedu/address/logic/LogicManagerTest.java
+++ b/src/test/java/profplan/logic/LogicManagerTest.java
@@ -1,14 +1,8 @@
-package seedu.address.logic;
+package profplan.logic;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX;
-import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
-import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY;
-import static seedu.address.testutil.Assert.assertThrows;
-import static seedu.address.testutil.TypicalPersons.AMY;
+import static profplan.logic.Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX;
+import static profplan.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
@@ -18,20 +12,25 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
-import seedu.address.logic.commands.AddCommand;
-import seedu.address.logic.commands.CommandResult;
-import seedu.address.logic.commands.ListCommand;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.Model;
-import seedu.address.model.ModelManager;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.UserPrefs;
-import seedu.address.model.person.Person;
-import seedu.address.storage.JsonAddressBookStorage;
-import seedu.address.storage.JsonUserPrefsStorage;
-import seedu.address.storage.StorageManager;
-import seedu.address.testutil.PersonBuilder;
+import profplan.logic.commands.AddCommand;
+import profplan.logic.commands.CommandResult;
+import profplan.logic.commands.CommandTestUtil;
+import profplan.logic.commands.ListCommand;
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.logic.parser.exceptions.ParseException;
+import profplan.model.Model;
+import profplan.model.ModelManager;
+import profplan.model.ReadOnlyProfPlan;
+import profplan.model.UserConfigs;
+import profplan.model.UserPrefs;
+import profplan.model.task.Task;
+import profplan.storage.JsonProfPlanStorage;
+import profplan.storage.JsonUserConfigsStorage;
+import profplan.storage.JsonUserPrefsStorage;
+import profplan.storage.StorageManager;
+import profplan.testutil.Assert;
+import profplan.testutil.TaskBuilder;
+import profplan.testutil.TypicalTasks;
public class LogicManagerTest {
private static final IOException DUMMY_IO_EXCEPTION = new IOException("dummy IO exception");
@@ -45,10 +44,12 @@ public class LogicManagerTest {
@BeforeEach
public void setUp() {
- JsonAddressBookStorage addressBookStorage =
- new JsonAddressBookStorage(temporaryFolder.resolve("addressBook.json"));
+ JsonProfPlanStorage profPlanStorage =
+ new JsonProfPlanStorage(temporaryFolder.resolve("addressBook.json"));
JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("userPrefs.json"));
- StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage);
+ JsonUserConfigsStorage userConfigsStorage = new JsonUserConfigsStorage(
+ temporaryFolder.resolve("userConfigs.json"));
+ StorageManager storage = new StorageManager(profPlanStorage, userPrefsStorage, userConfigsStorage);
logic = new LogicManager(model, storage);
}
@@ -61,7 +62,7 @@ public void execute_invalidCommandFormat_throwsParseException() {
@Test
public void execute_commandExecutionError_throwsCommandException() {
String deleteCommand = "delete 9";
- assertCommandException(deleteCommand, MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ assertCommandException(deleteCommand, MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
}
@Test
@@ -71,20 +72,8 @@ public void execute_validCommand_success() throws Exception {
}
@Test
- public void execute_storageThrowsIoException_throwsCommandException() {
- assertCommandFailureForExceptionFromStorage(DUMMY_IO_EXCEPTION, String.format(
- LogicManager.FILE_OPS_ERROR_FORMAT, DUMMY_IO_EXCEPTION.getMessage()));
- }
-
- @Test
- public void execute_storageThrowsAdException_throwsCommandException() {
- assertCommandFailureForExceptionFromStorage(DUMMY_AD_EXCEPTION, String.format(
- LogicManager.FILE_OPS_PERMISSION_ERROR_FORMAT, DUMMY_AD_EXCEPTION.getMessage()));
- }
-
- @Test
- public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() {
- assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredPersonList().remove(0));
+ public void getFilteredTaskList_modifyList_throwsUnsupportedOperationException() {
+ Assert.assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredTaskList().remove(0));
}
/**
@@ -123,7 +112,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.getProfPlan(), new UserPrefs(), new UserConfigs());
assertCommandFailure(inputCommand, expectedException, expectedMessage, expectedModel);
}
@@ -136,7 +125,7 @@ private void assertCommandFailure(String inputCommand, Class extends Throwable
*/
private void assertCommandFailure(String inputCommand, Class extends Throwable> expectedException,
String expectedMessage, Model expectedModel) {
- assertThrows(expectedException, expectedMessage, () -> logic.execute(inputCommand));
+ Assert.assertThrows(expectedException, expectedMessage, () -> logic.execute(inputCommand));
assertEquals(expectedModel, model);
}
@@ -149,10 +138,10 @@ private void assertCommandFailure(String inputCommand, Class extends Throwable
private void assertCommandFailureForExceptionFromStorage(IOException e, String expectedMessage) {
Path prefPath = temporaryFolder.resolve("ExceptionUserPrefs.json");
- // Inject LogicManager with an AddressBookStorage that throws the IOException e when saving
- JsonAddressBookStorage addressBookStorage = new JsonAddressBookStorage(prefPath) {
+ // Inject LogicManager with an ProfPlanStorage that throws the IOException e when saving
+ JsonProfPlanStorage profPlanStorage = new JsonProfPlanStorage(prefPath) {
@Override
- public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath)
+ public void saveProfPlan(ReadOnlyProfPlan addressBook, Path filePath)
throws IOException {
throw e;
}
@@ -160,16 +149,17 @@ public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath)
JsonUserPrefsStorage userPrefsStorage =
new JsonUserPrefsStorage(temporaryFolder.resolve("ExceptionUserPrefs.json"));
- StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage);
+ JsonUserConfigsStorage userConfigsStorage =
+ new JsonUserConfigsStorage(temporaryFolder.resolve("ExceptionUserConfigs.json"));
+ StorageManager storage = new StorageManager(profPlanStorage, userPrefsStorage, userConfigsStorage);
logic = new LogicManager(model, storage);
// Triggers the saveAddressBook method by executing an add command
- String addCommand = AddCommand.COMMAND_WORD + NAME_DESC_AMY + PHONE_DESC_AMY
- + EMAIL_DESC_AMY + ADDRESS_DESC_AMY;
- Person expectedPerson = new PersonBuilder(AMY).withTags().build();
+ String addCommand = AddCommand.COMMAND_WORD + CommandTestUtil.NAME_DESC_AMY + CommandTestUtil.PRIORITY_DESC_AMY;
+ Task expectedTask = new TaskBuilder(TypicalTasks.AMY).withTags().build();
ModelManager expectedModel = new ModelManager();
- expectedModel.addPerson(expectedPerson);
+ expectedModel.addTask(expectedTask);
assertCommandFailure(addCommand, CommandException.class, expectedMessage, expectedModel);
}
}
diff --git a/src/test/java/profplan/logic/commands/AddCommandIntegrationTest.java b/src/test/java/profplan/logic/commands/AddCommandIntegrationTest.java
new file mode 100644
index 00000000000..9bcf3046943
--- /dev/null
+++ b/src/test/java/profplan/logic/commands/AddCommandIntegrationTest.java
@@ -0,0 +1,48 @@
+package profplan.logic.commands;
+
+import static profplan.logic.commands.CommandTestUtil.assertCommandSuccess;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import profplan.logic.Messages;
+import profplan.model.Model;
+import profplan.model.ModelManager;
+import profplan.model.UserConfigs;
+import profplan.model.UserPrefs;
+import profplan.model.task.Task;
+import profplan.testutil.TaskBuilder;
+import profplan.testutil.TypicalTasks;
+
+/**
+ * Contains integration tests (interaction with the Model) for {@code AddCommand}.
+ */
+public class AddCommandIntegrationTest {
+
+ private Model model;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager(TypicalTasks.getTypicalProfPlan(), new UserPrefs(), new UserConfigs());
+ }
+
+ @Test
+ public void execute_newTask_success() {
+ Task validTask = new TaskBuilder().build();
+
+ Model expectedModel = new ModelManager(model.getProfPlan(), new UserPrefs(), new UserConfigs());
+ expectedModel.addTask(validTask);
+
+ assertCommandSuccess(new AddCommand(validTask), model,
+ String.format(AddCommand.MESSAGE_SUCCESS, Messages.format(validTask)),
+ expectedModel);
+ }
+
+ @Test
+ public void execute_duplicateTask_throwsCommandException() {
+ Task taskInList = model.getProfPlan().getTaskList().get(0);
+ CommandTestUtil.assertCommandFailure(new AddCommand(taskInList), model,
+ AddCommand.MESSAGE_DUPLICATE_TASK);
+ }
+
+}
diff --git a/src/test/java/profplan/logic/commands/AddCommandTest.java b/src/test/java/profplan/logic/commands/AddCommandTest.java
new file mode 100644
index 00000000000..357fbb1d63f
--- /dev/null
+++ b/src/test/java/profplan/logic/commands/AddCommandTest.java
@@ -0,0 +1,240 @@
+package profplan.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.function.Predicate;
+
+import org.junit.jupiter.api.Test;
+
+import javafx.collections.ObservableList;
+import profplan.commons.core.GuiSettings;
+import profplan.logic.Messages;
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.model.Model;
+import profplan.model.ProfPlan;
+import profplan.model.ReadOnlyProfPlan;
+import profplan.model.ReadOnlyUserPrefs;
+import profplan.model.task.Task;
+import profplan.testutil.Assert;
+import profplan.testutil.TaskBuilder;
+import profplan.testutil.TypicalTasks;
+
+public class AddCommandTest {
+
+ @Test
+ public void constructor_nullTask_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> new AddCommand(null));
+ }
+
+ @Test
+ public void execute_taskAcceptedByModel_addSuccessful() throws Exception {
+ ModelStubAcceptingTaskAdded modelStub = new ModelStubAcceptingTaskAdded();
+ Task validTask = new TaskBuilder().build();
+
+ CommandResult commandResult = new AddCommand(validTask).execute(modelStub);
+
+ assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, Messages.format(validTask)),
+ commandResult.getFeedbackToUser());
+ assertEquals(Arrays.asList(validTask), modelStub.tasksAdded);
+ }
+
+ @Test
+ public void execute_duplicateTask_throwsCommandException() {
+ Task validTask = new TaskBuilder().build();
+ AddCommand addCommand = new AddCommand(validTask);
+ ModelStub modelStub = new ModelStubWithTask(validTask);
+
+ Assert.assertThrows(CommandException.class, AddCommand.MESSAGE_DUPLICATE_TASK, () -> addCommand.execute(
+ modelStub));
+ }
+
+ @Test
+ public void equals() {
+ Task alice = new TaskBuilder().withName("Alice").build();
+ Task bob = new TaskBuilder().withName("Bob").build();
+ AddCommand addAliceCommand = new AddCommand(alice);
+ AddCommand addBobCommand = new AddCommand(bob);
+
+ // same object -> returns true
+ assertTrue(addAliceCommand.equals(addAliceCommand));
+
+ // same values -> returns true
+ AddCommand addAliceCommandCopy = new AddCommand(alice);
+ assertTrue(addAliceCommand.equals(addAliceCommandCopy));
+
+ // different types -> returns false
+ assertFalse(addAliceCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(addAliceCommand.equals(null));
+
+ // different task -> returns false
+ assertFalse(addAliceCommand.equals(addBobCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ AddCommand addCommand = new AddCommand(TypicalTasks.ALICE);
+ String expected = AddCommand.class.getCanonicalName() + "{toAdd=" + TypicalTasks.ALICE + "}";
+ assertEquals(expected, addCommand.toString());
+ }
+
+ /**
+ * 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 getProfPlanFilePath() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setProfPlanFilePath(Path profPlanFilePath) {
+ 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 markTask(int index) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void unmarkTask(int index) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setProfPlan(ReadOnlyProfPlan newData) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ReadOnlyProfPlan getProfPlan() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public boolean hasTask(Task task) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void deleteTask(Task target) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void deleteTask() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setTask(Task target, Task editedTask) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ObservableList getFilteredTaskList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void updateFilteredTaskList(Predicate predicate) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+
+ public Task getDoNextTask() {
+ throw new AssertionError("THis method should not be called.");
+ }
+
+ public void sortTaskByDueDate() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void sortTaskByPriority() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public double getCompletionRate() {
+ throw new AssertionError("This method should not be called.");
+ }
+ }
+
+ /**
+ * A Model stub that contains a single task.
+ */
+ private class ModelStubWithTask extends ModelStub {
+ private final Task task;
+
+ ModelStubWithTask(Task task) {
+ requireNonNull(task);
+ this.task = task;
+ }
+
+ @Override
+ public boolean hasTask(Task task) {
+ requireNonNull(task);
+ return this.task.isSameTask(task);
+ }
+ }
+
+ /**
+ * A Model stub that always accept the task being added.
+ */
+ private class ModelStubAcceptingTaskAdded extends ModelStub {
+ final ArrayList tasksAdded = new ArrayList<>();
+
+ @Override
+ public boolean hasTask(Task task) {
+ requireNonNull(task);
+ return tasksAdded.stream().anyMatch(task::isSameTask);
+ }
+
+ @Override
+ public void addTask(Task task) {
+ requireNonNull(task);
+ tasksAdded.add(task);
+ }
+
+ @Override
+ public ReadOnlyProfPlan getProfPlan() {
+ return new ProfPlan();
+ }
+ }
+
+}
diff --git a/src/test/java/profplan/logic/commands/ClearCommandTest.java b/src/test/java/profplan/logic/commands/ClearCommandTest.java
new file mode 100644
index 00000000000..aea9a5bad7c
--- /dev/null
+++ b/src/test/java/profplan/logic/commands/ClearCommandTest.java
@@ -0,0 +1,33 @@
+package profplan.logic.commands;
+
+import static profplan.logic.commands.CommandTestUtil.assertCommandSuccess;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.model.Model;
+import profplan.model.ModelManager;
+import profplan.model.ProfPlan;
+import profplan.model.UserConfigs;
+import profplan.model.UserPrefs;
+import profplan.testutil.TypicalTasks;
+
+public class ClearCommandTest {
+
+ @Test
+ public void execute_emptyProfPlan_success() {
+ Model model = new ModelManager();
+ Model expectedModel = new ModelManager();
+
+ assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel);
+ }
+
+ @Test
+ public void execute_nonEmptyProfPlan_success() {
+ Model model = new ModelManager(TypicalTasks.getTypicalProfPlan(), new UserPrefs(), new UserConfigs());
+ Model expectedModel = new ModelManager(TypicalTasks.getTypicalProfPlan(), new UserPrefs(), new UserConfigs());
+ expectedModel.setProfPlan(new ProfPlan());
+
+ assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel);
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/commands/CommandResultTest.java b/src/test/java/profplan/logic/commands/CommandResultTest.java
similarity index 98%
rename from src/test/java/seedu/address/logic/commands/CommandResultTest.java
rename to src/test/java/profplan/logic/commands/CommandResultTest.java
index 7b8c7cd4546..34e855d9db4 100644
--- a/src/test/java/seedu/address/logic/commands/CommandResultTest.java
+++ b/src/test/java/profplan/logic/commands/CommandResultTest.java
@@ -1,4 +1,4 @@
-package seedu.address.logic.commands;
+package profplan.logic.commands;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
diff --git a/src/test/java/profplan/logic/commands/CommandTestUtil.java b/src/test/java/profplan/logic/commands/CommandTestUtil.java
new file mode 100644
index 00000000000..dc012c5d6e9
--- /dev/null
+++ b/src/test/java/profplan/logic/commands/CommandTestUtil.java
@@ -0,0 +1,125 @@
+package profplan.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static profplan.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+import static profplan.logic.parser.CliSyntax.PREFIX_DUEDATE;
+import static profplan.logic.parser.CliSyntax.PREFIX_NAME;
+import static profplan.logic.parser.CliSyntax.PREFIX_PRIORITY;
+import static profplan.logic.parser.CliSyntax.PREFIX_TAG;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import profplan.commons.core.index.Index;
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.model.Model;
+import profplan.model.ProfPlan;
+import profplan.model.task.Task;
+import profplan.model.task.predicates.NameContainsKeywordsPredicate;
+import profplan.testutil.Assert;
+import profplan.testutil.EditTaskDescriptorBuilder;
+
+/**
+ * Contains helper methods for testing commands.
+ */
+public class CommandTestUtil {
+
+ public static final String VALID_NAME_AMY = "Amy Bee";
+ public static final String VALID_NAME_BOB = "Bob Choo";
+ public static final String VALID_PRIORITY_AMY = "1";
+ public static final String VALID_PRIORITY_BOB = "2";
+ public static final String VALID_TAG_HUSBAND = "husband";
+ public static final String VALID_TAG_FRIEND = "friend";
+ public static final String VALID_LINK_EXAMPLE = "www.exmaple.com";
+ public static final String VALID_LINK_GOOGLE = "www.google.com";
+ public static final String VALID_DESCRIPTION = "this is a valid description";
+ public static final String EMPTY_DESCRIPTION = "";
+ public static final String UNUSUAL_VALID_DESCRIPTION = "!@#$%^&*()_ ;'d;'~~~";
+
+ public static final String DUEDATE_DESC = " " + PREFIX_DUEDATE + "10-12-2023";
+
+ 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 PRIORITY_DESC_AMY = " " + PREFIX_PRIORITY + VALID_PRIORITY_AMY;
+ public static final String PRIORITY_DESC_BOB = " " + PREFIX_PRIORITY + VALID_PRIORITY_BOB;
+ public static final String DESCRIPTION_DESC_AMY = " " + PREFIX_DESCRIPTION + VALID_DESCRIPTION;
+ public static final String DESCRIPTION_DESC_BOB = " " + PREFIX_DESCRIPTION + VALID_DESCRIPTION;
+ public static final String TAG_DESC_FRIEND = " " + PREFIX_TAG + VALID_TAG_FRIEND;
+ public static final String TAG_DESC_HUSBAND = " " + PREFIX_TAG + VALID_TAG_HUSBAND;
+
+ public static final String INVALID_NAME_DESC = " " + PREFIX_NAME + "James&"; // '&' not allowed in names
+ public static final String INVALID_PRIORITY_DESC = " " + PREFIX_PRIORITY + "911a"; // 'a' not allowed in priorities
+ public static final String INVALID_TAG_DESC = " " + PREFIX_TAG + "hubby*"; // '*' not allowed in tags
+
+ public static final String PREAMBLE_WHITESPACE = "\t \r \n";
+ public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble";
+
+ public static final EditCommand.EditTaskDescriptor DESC_AMY;
+ public static final EditCommand.EditTaskDescriptor DESC_BOB;
+
+ static {
+ DESC_AMY = new EditTaskDescriptorBuilder().withName(VALID_NAME_AMY)
+ .withPriority(VALID_PRIORITY_AMY).withTags(VALID_TAG_FRIEND).build();
+ DESC_BOB = new EditTaskDescriptorBuilder().withName(VALID_NAME_BOB)
+ .withPriority(VALID_PRIORITY_BOB).withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build();
+ }
+
+ /**
+ * Executes the given {@code command}, confirms that
+ * - the returned {@link CommandResult} matches {@code expectedCommandResult}
+ * - the {@code actualModel} matches {@code expectedModel}
+ */
+ public static void assertCommandSuccess(Command command, Model actualModel, CommandResult expectedCommandResult,
+ Model expectedModel) {
+ try {
+ CommandResult result = command.execute(actualModel);
+ assertEquals(expectedCommandResult, result);
+ assertEquals(expectedModel, actualModel);
+ } catch (CommandException ce) {
+ throw new AssertionError("Execution of command should not fail.", ce);
+ }
+ }
+
+ /**
+ * Convenience wrapper to {@link #assertCommandSuccess(Command, Model, CommandResult, Model)}
+ * that takes a string {@code expectedMessage}.
+ */
+ public static void assertCommandSuccess(Command command, Model actualModel, String expectedMessage,
+ Model expectedModel) {
+ CommandResult expectedCommandResult = new CommandResult(expectedMessage);
+ assertCommandSuccess(command, actualModel, expectedCommandResult, expectedModel);
+ }
+
+ /**
+ * Executes the given {@code command}, confirms that
+ * - a {@code CommandException} is thrown
+ * - the CommandException message matches {@code expectedMessage}
+ * - the task list, filtered task list and selected task in {@code actualModel} remain unchanged
+ */
+ public static void assertCommandFailure(Command command, Model actualModel, String expectedMessage) {
+ // we are unable to defensively copy the model for comparison later, so we can
+ // only do so by copying its components.
+ ProfPlan expectedProfPlan = new ProfPlan(actualModel.getProfPlan());
+ List expectedFilteredList = new ArrayList<>(actualModel.getFilteredTaskList());
+
+ Assert.assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel));
+ assertEquals(expectedProfPlan, actualModel.getProfPlan());
+ assertEquals(expectedFilteredList, actualModel.getFilteredTaskList());
+ }
+ /**
+ * Updates {@code model}'s filtered list to show only the task at the given {@code targetIndex} in the
+ * {@code model}'s task list.
+ */
+ public static void showTaskAtIndex(Model model, Index targetIndex) {
+ assertTrue(targetIndex.getZeroBased() < model.getFilteredTaskList().size());
+
+ Task task = model.getFilteredTaskList().get(targetIndex.getZeroBased());
+ final String[] splitName = task.getName().fullName.split("\\s+");
+ model.updateFilteredTaskList(new NameContainsKeywordsPredicate(Arrays.asList(splitName[0])));
+
+ assertEquals(1, model.getFilteredTaskList().size());
+ }
+
+}
diff --git a/src/test/java/profplan/logic/commands/DeleteCommandTest.java b/src/test/java/profplan/logic/commands/DeleteCommandTest.java
new file mode 100644
index 00000000000..275aef846a2
--- /dev/null
+++ b/src/test/java/profplan/logic/commands/DeleteCommandTest.java
@@ -0,0 +1,120 @@
+package profplan.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 profplan.logic.commands.CommandTestUtil.assertCommandFailure;
+import static profplan.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static profplan.logic.commands.CommandTestUtil.showTaskAtIndex;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.commons.core.index.Index;
+import profplan.logic.Messages;
+import profplan.model.Model;
+import profplan.model.ModelManager;
+import profplan.model.UserConfigs;
+import profplan.model.UserPrefs;
+import profplan.model.task.Task;
+import profplan.testutil.TypicalIndexes;
+import profplan.testutil.TypicalTasks;
+
+/**
+ * Contains integration tests (interaction with the Model) and unit tests for
+ * {@code DeleteCommand}.
+ */
+public class DeleteCommandTest {
+
+ private Model model = new ModelManager(TypicalTasks.getTypicalProfPlan(), new UserPrefs(), new UserConfigs());
+
+ @Test
+ public void execute_validIndexUnfilteredList_success() {
+ Task taskToDelete = model.getFilteredTaskList().get(TypicalIndexes.INDEX_FIRST_TASK.getZeroBased());
+ DeleteCommand deleteCommand = new DeleteCommand(TypicalIndexes.INDEX_FIRST_TASK);
+
+ String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_TASK_SUCCESS,
+ Messages.format(taskToDelete));
+
+ ModelManager expectedModel = new ModelManager(model.getProfPlan(), new UserPrefs(), new UserConfigs());
+ expectedModel.deleteTask(taskToDelete);
+
+ assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidIndexUnfilteredList_throwsCommandException() {
+ Index outOfBoundIndex = Index.fromOneBased(model.getFilteredTaskList().size() + 1);
+ DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex);
+
+ assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void execute_validIndexFilteredList_success() {
+ showTaskAtIndex(model, TypicalIndexes.INDEX_FIRST_TASK);
+
+ Task taskToDelete = model.getFilteredTaskList().get(TypicalIndexes.INDEX_FIRST_TASK.getZeroBased());
+ DeleteCommand deleteCommand = new DeleteCommand(TypicalIndexes.INDEX_FIRST_TASK);
+
+ String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_TASK_SUCCESS,
+ Messages.format(taskToDelete));
+
+ Model expectedModel = new ModelManager(model.getProfPlan(), new UserPrefs(), new UserConfigs());
+ expectedModel.deleteTask(taskToDelete);
+ showNoTask(expectedModel);
+
+ assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidIndexFilteredList_throwsCommandException() {
+ showTaskAtIndex(model, TypicalIndexes.INDEX_FIRST_TASK);
+
+ Index outOfBoundIndex = TypicalIndexes.INDEX_SECOND_TASK;
+ // ensures that outOfBoundIndex is still in bounds of task list
+ assertTrue(outOfBoundIndex.getZeroBased() < model.getProfPlan().getTaskList().size());
+
+ DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex);
+
+ assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void equals() {
+ DeleteCommand deleteFirstCommand = new DeleteCommand(TypicalIndexes.INDEX_FIRST_TASK);
+ DeleteCommand deleteSecondCommand = new DeleteCommand(TypicalIndexes.INDEX_SECOND_TASK);
+
+ // same object -> returns true
+ assertTrue(deleteFirstCommand.equals(deleteFirstCommand));
+
+ // same values -> returns true
+ DeleteCommand deleteFirstCommandCopy = new DeleteCommand(TypicalIndexes.INDEX_FIRST_TASK);
+ assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy));
+
+ // different types -> returns false
+ assertFalse(deleteFirstCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(deleteFirstCommand.equals(null));
+
+ // different task -> returns false
+ assertFalse(deleteFirstCommand.equals(deleteSecondCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index targetIndex = Index.fromOneBased(1);
+ DeleteCommand deleteCommand = new DeleteCommand(targetIndex);
+ String expected = DeleteCommand.class.getCanonicalName() + "{targetIndex=" + targetIndex + "}";
+ assertEquals(expected, deleteCommand.toString());
+ }
+
+ /**
+ * Updates {@code model}'s filtered list to show no one.
+ */
+ private void showNoTask(Model model) {
+ model.updateFilteredTaskList(p -> false);
+
+ assertTrue(model.getFilteredTaskList().isEmpty());
+ }
+}
diff --git a/src/test/java/profplan/logic/commands/EditCommandTest.java b/src/test/java/profplan/logic/commands/EditCommandTest.java
new file mode 100644
index 00000000000..3e1676a3691
--- /dev/null
+++ b/src/test/java/profplan/logic/commands/EditCommandTest.java
@@ -0,0 +1,184 @@
+package profplan.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 profplan.logic.commands.CommandTestUtil.assertCommandSuccess;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.commons.core.index.Index;
+import profplan.logic.Messages;
+import profplan.logic.commands.EditCommand.EditTaskDescriptor;
+import profplan.model.Model;
+import profplan.model.ModelManager;
+import profplan.model.ProfPlan;
+import profplan.model.UserConfigs;
+import profplan.model.UserPrefs;
+import profplan.model.task.Task;
+import profplan.testutil.EditTaskDescriptorBuilder;
+import profplan.testutil.TaskBuilder;
+import profplan.testutil.TypicalIndexes;
+import profplan.testutil.TypicalTasks;
+
+/**
+ * Contains integration tests (interaction with the Model) and unit tests for EditCommand.
+ */
+public class EditCommandTest {
+
+ private Model model = new ModelManager(TypicalTasks.getTypicalProfPlan(), new UserPrefs(), new UserConfigs());
+
+ @Test
+ public void execute_allFieldsSpecifiedUnfilteredList_success() {
+ Task editedTask = new TaskBuilder().build();
+ EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder(editedTask).build();
+ EditCommand editCommand = new EditCommand(TypicalIndexes.INDEX_FIRST_TASK, descriptor);
+
+ String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, Messages.format(editedTask));
+
+ Model expectedModel = new ModelManager(new ProfPlan(model.getProfPlan()), new UserPrefs(), new UserConfigs());
+ expectedModel.setTask(model.getFilteredTaskList().get(0), editedTask);
+
+ assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_someFieldsSpecifiedUnfilteredList_success() {
+ Index indexLastTask = Index.fromOneBased(model.getFilteredTaskList().size());
+ Task lastTask = model.getFilteredTaskList().get(indexLastTask.getZeroBased());
+
+ TaskBuilder taskInList = new TaskBuilder(lastTask);
+ Task editedTask = taskInList.withName(CommandTestUtil.VALID_NAME_BOB)
+ .withPriority(CommandTestUtil.VALID_PRIORITY_BOB)
+ .withTags(CommandTestUtil.VALID_TAG_HUSBAND).build();
+
+ EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder().withName(CommandTestUtil.VALID_NAME_BOB)
+ .withPriority(CommandTestUtil.VALID_PRIORITY_BOB).withTags(CommandTestUtil.VALID_TAG_HUSBAND).build();
+ EditCommand editCommand = new EditCommand(indexLastTask, descriptor);
+
+ String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, Messages.format(editedTask));
+
+ Model expectedModel = new ModelManager(new ProfPlan(model.getProfPlan()), new UserPrefs(), new UserConfigs());
+ expectedModel.setTask(lastTask, editedTask);
+
+ assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_noFieldSpecifiedUnfilteredList_success() {
+ EditCommand editCommand = new EditCommand(TypicalIndexes.INDEX_FIRST_TASK, new EditTaskDescriptor());
+ Task editedTask = model.getFilteredTaskList().get(TypicalIndexes.INDEX_FIRST_TASK.getZeroBased());
+
+ String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, Messages.format(editedTask));
+
+ Model expectedModel = new ModelManager(new ProfPlan(model.getProfPlan()), new UserPrefs(), new UserConfigs());
+
+ assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_filteredList_success() {
+ CommandTestUtil.showTaskAtIndex(model, TypicalIndexes.INDEX_FIRST_TASK);
+
+ Task taskInFilteredList = model.getFilteredTaskList().get(
+ TypicalIndexes.INDEX_FIRST_TASK.getZeroBased());
+ Task editedTask = new TaskBuilder(taskInFilteredList).withName(CommandTestUtil.VALID_NAME_BOB).build();
+ EditCommand editCommand = new EditCommand(TypicalIndexes.INDEX_FIRST_TASK,
+ new EditTaskDescriptorBuilder().withName(CommandTestUtil.VALID_NAME_BOB).build());
+
+ String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_TASK_SUCCESS, Messages.format(editedTask));
+
+ Model expectedModel = new ModelManager(new ProfPlan(model.getProfPlan()), new UserPrefs(), new UserConfigs());
+ expectedModel.setTask(model.getFilteredTaskList().get(0), editedTask);
+
+ assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_duplicateTaskUnfilteredList_failure() {
+ Task firstTask = model.getFilteredTaskList().get(TypicalIndexes.INDEX_FIRST_TASK.getZeroBased());
+ EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder(firstTask).build();
+ EditCommand editCommand = new EditCommand(TypicalIndexes.INDEX_SECOND_TASK, descriptor);
+
+ CommandTestUtil.assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_TASK);
+ }
+
+ @Test
+ public void execute_duplicateTaskFilteredList_failure() {
+ CommandTestUtil.showTaskAtIndex(model, TypicalIndexes.INDEX_FIRST_TASK);
+
+ // edit task in filtered list into a duplicate in task list
+ Task taskInList = model.getProfPlan().getTaskList().get(TypicalIndexes.INDEX_SECOND_TASK
+ .getZeroBased());
+ EditCommand editCommand = new EditCommand(TypicalIndexes.INDEX_FIRST_TASK,
+ new EditTaskDescriptorBuilder(taskInList).build());
+
+ CommandTestUtil.assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_TASK);
+ }
+
+ @Test
+ public void execute_invalidTaskIndexUnfilteredList_failure() {
+ Index outOfBoundIndex = Index.fromOneBased(model.getFilteredTaskList().size() + 1);
+ EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder().withName(CommandTestUtil.VALID_NAME_BOB)
+ .build();
+ EditCommand editCommand = new EditCommand(outOfBoundIndex, descriptor);
+
+ CommandTestUtil.assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ /**
+ * Edit filtered list where index is larger than size of filtered list,
+ * but smaller than size of task list
+ */
+ @Test
+ public void execute_invalidTaskIndexFilteredList_failure() {
+ CommandTestUtil.showTaskAtIndex(model, TypicalIndexes.INDEX_FIRST_TASK);
+ Index outOfBoundIndex = TypicalIndexes.INDEX_SECOND_TASK;
+ // ensures that outOfBoundIndex is still in bounds of task list list
+ assertTrue(outOfBoundIndex.getZeroBased() < model.getProfPlan().getTaskList().size());
+
+ EditCommand editCommand = new EditCommand(outOfBoundIndex,
+ new EditTaskDescriptorBuilder().withName(CommandTestUtil.VALID_NAME_BOB).build());
+
+ CommandTestUtil.assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void equals() {
+ final EditCommand standardCommand = new EditCommand(TypicalIndexes.INDEX_FIRST_TASK,
+ CommandTestUtil.DESC_AMY);
+
+ // same values -> returns true
+ EditTaskDescriptor copyDescriptor = new EditTaskDescriptor(CommandTestUtil.DESC_AMY);
+ EditCommand commandWithSameValues = new EditCommand(TypicalIndexes.INDEX_FIRST_TASK, copyDescriptor);
+ assertTrue(standardCommand.equals(commandWithSameValues));
+
+ // same object -> returns true
+ assertTrue(standardCommand.equals(standardCommand));
+
+ // null -> returns false
+ assertFalse(standardCommand.equals(null));
+
+ // different types -> returns false
+ assertFalse(standardCommand.equals(new ClearCommand()));
+
+ // different index -> returns false
+ assertFalse(standardCommand.equals(new EditCommand(TypicalIndexes.INDEX_SECOND_TASK,
+ CommandTestUtil.DESC_AMY)));
+
+ // different descriptor -> returns false
+ assertFalse(standardCommand.equals(new EditCommand(TypicalIndexes.INDEX_FIRST_TASK,
+ CommandTestUtil.DESC_BOB)));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index index = Index.fromOneBased(1);
+ EditTaskDescriptor editTaskDescriptor = new EditTaskDescriptor();
+ EditCommand editCommand = new EditCommand(index, editTaskDescriptor);
+ String expected = EditCommand.class.getCanonicalName() + "{index=" + index + ", editTaskDescriptor="
+ + editTaskDescriptor + "}";
+ assertEquals(expected, editCommand.toString());
+ }
+
+}
diff --git a/src/test/java/profplan/logic/commands/EditTaskDescriptorTest.java b/src/test/java/profplan/logic/commands/EditTaskDescriptorTest.java
new file mode 100644
index 00000000000..d58d8fdb982
--- /dev/null
+++ b/src/test/java/profplan/logic/commands/EditTaskDescriptorTest.java
@@ -0,0 +1,61 @@
+package profplan.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 profplan.logic.commands.CommandTestUtil.DESC_AMY;
+import static profplan.logic.commands.CommandTestUtil.DESC_BOB;
+import static profplan.logic.commands.CommandTestUtil.VALID_NAME_BOB;
+import static profplan.logic.commands.CommandTestUtil.VALID_PRIORITY_BOB;
+import static profplan.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.logic.commands.EditCommand.EditTaskDescriptor;
+import profplan.testutil.EditTaskDescriptorBuilder;
+
+public class EditTaskDescriptorTest {
+
+ @Test
+ public void equals() {
+ // same values -> returns true
+ EditTaskDescriptor descriptorWithSameValues = new EditTaskDescriptor(DESC_AMY);
+ assertTrue(DESC_AMY.equals(descriptorWithSameValues));
+
+ // same object -> returns true
+ assertTrue(DESC_AMY.equals(DESC_AMY));
+
+ // null -> returns false
+ assertFalse(DESC_AMY.equals(null));
+
+ // different types -> returns false
+ assertFalse(DESC_AMY.equals(5));
+
+ // different values -> returns false
+ assertFalse(DESC_AMY.equals(DESC_BOB));
+
+ // different name -> returns false
+ EditTaskDescriptor editedAmy = new EditTaskDescriptorBuilder(DESC_AMY).withName(VALID_NAME_BOB).build();
+ assertFalse(DESC_AMY.equals(editedAmy));
+
+ // different priority -> returns false
+ editedAmy = new EditTaskDescriptorBuilder(DESC_AMY).withPriority(VALID_PRIORITY_BOB).build();
+ assertFalse(DESC_AMY.equals(editedAmy));
+
+ // different tags -> returns false
+ editedAmy = new EditTaskDescriptorBuilder(DESC_AMY).withTags(VALID_TAG_HUSBAND).build();
+ assertFalse(DESC_AMY.equals(editedAmy));
+ }
+
+ @Test
+ public void toStringMethod() {
+ EditTaskDescriptor editTaskDescriptor = new EditTaskDescriptor();
+ String expected = EditTaskDescriptor.class.getCanonicalName() + "{name="
+ + editTaskDescriptor.getName().orElse(null) + ", priority="
+ + editTaskDescriptor.getPriority().orElse(null) + ", tags="
+ + editTaskDescriptor.getTags().orElse(null) + ", dueDate="
+ + editTaskDescriptor.getDueDate().orElse(null) + ", link="
+ + editTaskDescriptor.getLink().orElse(null) + "}";
+ assertEquals(expected, editTaskDescriptor.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java b/src/test/java/profplan/logic/commands/ExitCommandTest.java
similarity index 60%
rename from src/test/java/seedu/address/logic/commands/ExitCommandTest.java
rename to src/test/java/profplan/logic/commands/ExitCommandTest.java
index 9533c473875..bceb17e5a88 100644
--- a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java
+++ b/src/test/java/profplan/logic/commands/ExitCommandTest.java
@@ -1,12 +1,12 @@
-package seedu.address.logic.commands;
+package profplan.logic.commands;
-import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
-import static seedu.address.logic.commands.ExitCommand.MESSAGE_EXIT_ACKNOWLEDGEMENT;
+import static profplan.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static profplan.logic.commands.ExitCommand.MESSAGE_EXIT_ACKNOWLEDGEMENT;
import org.junit.jupiter.api.Test;
-import seedu.address.model.Model;
-import seedu.address.model.ModelManager;
+import profplan.model.Model;
+import profplan.model.ModelManager;
public class ExitCommandTest {
private Model model = new ModelManager();
diff --git a/src/test/java/profplan/logic/commands/FilterCommandTest.java b/src/test/java/profplan/logic/commands/FilterCommandTest.java
new file mode 100644
index 00000000000..d6f24a69db1
--- /dev/null
+++ b/src/test/java/profplan/logic/commands/FilterCommandTest.java
@@ -0,0 +1,153 @@
+package profplan.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 org.junit.jupiter.api.Test;
+
+import profplan.model.task.DueDate;
+import profplan.model.task.Priority;
+import profplan.model.task.Status;
+import profplan.model.task.Task.RecurringType;
+import profplan.model.task.predicates.TaskDueDatePredicate;
+import profplan.model.task.predicates.TaskPriorityPredicate;
+import profplan.model.task.predicates.TaskRecurringTypePredicate;
+import profplan.model.task.predicates.TaskStatusPredicate;
+
+/**
+ * Contains integration tests (interaction with the Model) for {@code FilterCommand}.
+ */
+public class FilterCommandTest {
+
+ @Test
+ public void equalsDueDate() {
+ TaskDueDatePredicate firstPredicate =
+ new TaskDueDatePredicate(new DueDate("01-01-2000"));
+ TaskDueDatePredicate secondPredicate =
+ new TaskDueDatePredicate(new DueDate("02-02-2002"));
+
+ FilterCommand filterFirstCommand = new FilterCommand(firstPredicate);
+ FilterCommand filterSecondCommand = new FilterCommand(secondPredicate);
+
+ // same object -> returns true
+ assertTrue(filterFirstCommand.equals(filterFirstCommand));
+
+ // same values -> returns true
+ FilterCommand filterFirstCommandCopy = new FilterCommand(firstPredicate);
+ assertTrue(filterFirstCommand.equals(filterFirstCommandCopy));
+
+ // different types -> returns false
+ assertFalse(filterFirstCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(filterFirstCommand.equals(null));
+
+ // different task -> returns false
+ assertFalse(filterFirstCommand.equals(filterSecondCommand));
+ }
+
+ @Test
+ public void equalsPriority() {
+ TaskPriorityPredicate firstPredicate =
+ new TaskPriorityPredicate(new Priority("2"));
+ TaskPriorityPredicate secondPredicate =
+ new TaskPriorityPredicate(new Priority("3"));
+
+ FilterCommand filterFirstCommand = new FilterCommand(firstPredicate);
+ FilterCommand filterSecondCommand = new FilterCommand(secondPredicate);
+
+ // same object -> returns true
+ assertTrue(filterFirstCommand.equals(filterFirstCommand));
+
+ // same values -> returns true
+ FilterCommand filterFirstCommandCopy = new FilterCommand(firstPredicate);
+ assertTrue(filterFirstCommand.equals(filterFirstCommandCopy));
+
+ // different types -> returns false
+ assertFalse(filterFirstCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(filterFirstCommand.equals(null));
+
+ // different task -> returns false
+ assertFalse(filterFirstCommand.equals(filterSecondCommand));
+ }
+
+ @Test
+ public void equalsStatus() {
+ TaskStatusPredicate firstPredicate =
+ new TaskStatusPredicate(new Status("done"));
+ TaskStatusPredicate secondPredicate =
+ new TaskStatusPredicate(new Status("undone"));
+
+ FilterCommand filterFirstCommand = new FilterCommand(firstPredicate);
+ FilterCommand filterSecondCommand = new FilterCommand(secondPredicate);
+
+ // same object -> returns true
+ assertTrue(filterFirstCommand.equals(filterFirstCommand));
+
+ // same values -> returns true
+ FilterCommand filterFirstCommandCopy = new FilterCommand(firstPredicate);
+ assertTrue(filterFirstCommand.equals(filterFirstCommandCopy));
+
+ // different types -> returns false
+ assertFalse(filterFirstCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(filterFirstCommand.equals(null));
+
+ // different task -> returns false
+ assertFalse(filterFirstCommand.equals(filterSecondCommand));
+ }
+
+ @Test
+ public void equalsRecurring() {
+ TaskRecurringTypePredicate firstPredicate =
+ new TaskRecurringTypePredicate(RecurringType.DAILY);
+ TaskRecurringTypePredicate secondPredicate =
+ new TaskRecurringTypePredicate(RecurringType.MONTHLY);
+
+ FilterCommand filterFirstCommand = new FilterCommand(firstPredicate);
+ FilterCommand filterSecondCommand = new FilterCommand(secondPredicate);
+
+ // same object -> returns true
+ assertTrue(filterFirstCommand.equals(filterFirstCommand));
+
+ // same values -> returns true
+ FilterCommand filterFirstCommandCopy = new FilterCommand(firstPredicate);
+ assertTrue(filterFirstCommand.equals(filterFirstCommandCopy));
+
+ // different types -> returns false
+ assertFalse(filterFirstCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(filterFirstCommand.equals(null));
+
+ // different task -> returns false
+ assertFalse(filterFirstCommand.equals(filterSecondCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ TaskDueDatePredicate predicateDueDate = new TaskDueDatePredicate(new DueDate("01-01-2000"));
+ FilterCommand filterCommandDueDate = new FilterCommand(predicateDueDate);
+ String expectedDueDate = FilterCommand.class.getCanonicalName() + "{predicate=" + predicateDueDate + "}";
+ assertEquals(expectedDueDate, filterCommandDueDate.toString());
+
+ TaskPriorityPredicate predicatePriority = new TaskPriorityPredicate(new Priority("1"));
+ FilterCommand filterCommandPriority = new FilterCommand(predicatePriority);
+ String expectedPriority = FilterCommand.class.getCanonicalName() + "{predicate=" + predicatePriority + "}";
+ assertEquals(expectedPriority, filterCommandPriority.toString());
+
+ TaskStatusPredicate predicateStatus = new TaskStatusPredicate(new Status("done"));
+ FilterCommand filterCommandStatus = new FilterCommand(predicateStatus);
+ String expectedStatus = FilterCommand.class.getCanonicalName() + "{predicate=" + predicateStatus + "}";
+ assertEquals(expectedStatus, filterCommandStatus.toString());
+
+ TaskRecurringTypePredicate predicateRecurring = new TaskRecurringTypePredicate(RecurringType.SEMESTERLY);
+ FilterCommand filterCommandRecurring = new FilterCommand(predicateRecurring);
+ String expectedRecurring = FilterCommand.class.getCanonicalName() + "{predicate=" + predicateRecurring + "}";
+ assertEquals(expectedRecurring, filterCommandRecurring.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/FindCommandTest.java b/src/test/java/profplan/logic/commands/FindCommandTest.java
similarity index 63%
rename from src/test/java/seedu/address/logic/commands/FindCommandTest.java
rename to src/test/java/profplan/logic/commands/FindCommandTest.java
index b8b7dbba91a..35ac884bca8 100644
--- a/src/test/java/seedu/address/logic/commands/FindCommandTest.java
+++ b/src/test/java/profplan/logic/commands/FindCommandTest.java
@@ -1,31 +1,30 @@
-package seedu.address.logic.commands;
+package profplan.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.Messages.MESSAGE_PERSONS_LISTED_OVERVIEW;
-import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
-import static seedu.address.testutil.TypicalPersons.CARL;
-import static seedu.address.testutil.TypicalPersons.ELLE;
-import static seedu.address.testutil.TypicalPersons.FIONA;
-import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import static profplan.logic.Messages.MESSAGE_TASKS_LISTED_OVERVIEW;
+import static profplan.logic.commands.CommandTestUtil.assertCommandSuccess;
import java.util.Arrays;
import java.util.Collections;
import org.junit.jupiter.api.Test;
-import seedu.address.model.Model;
-import seedu.address.model.ModelManager;
-import seedu.address.model.UserPrefs;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import profplan.model.Model;
+import profplan.model.ModelManager;
+import profplan.model.UserConfigs;
+import profplan.model.UserPrefs;
+import profplan.model.task.predicates.NameContainsKeywordsPredicate;
+import profplan.testutil.TypicalTasks;
/**
* 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(TypicalTasks.getTypicalProfPlan(), new UserPrefs(), new UserConfigs());
+ private Model expectedModel = new ModelManager(TypicalTasks.getTypicalProfPlan(),
+ new UserPrefs(), new UserConfigs());
@Test
public void equals() {
@@ -50,28 +49,29 @@ public void equals() {
// null -> returns false
assertFalse(findFirstCommand.equals(null));
- // different person -> returns false
+ // different task -> returns false
assertFalse(findFirstCommand.equals(findSecondCommand));
}
@Test
- public void execute_zeroKeywords_noPersonFound() {
- String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 0);
+ public void execute_zeroKeywords_noTaskFound() {
+ String expectedMessage = String.format(MESSAGE_TASKS_LISTED_OVERVIEW, 0);
NameContainsKeywordsPredicate predicate = preparePredicate(" ");
FindCommand command = new FindCommand(predicate);
- expectedModel.updateFilteredPersonList(predicate);
+ expectedModel.updateFilteredTaskList(predicate);
assertCommandSuccess(command, model, expectedMessage, expectedModel);
- assertEquals(Collections.emptyList(), model.getFilteredPersonList());
+ assertEquals(Collections.emptyList(), model.getFilteredTaskList());
}
@Test
- public void execute_multipleKeywords_multiplePersonsFound() {
- String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 3);
+ public void execute_multipleKeywords_multipleTasksFound() {
+ String expectedMessage = String.format(MESSAGE_TASKS_LISTED_OVERVIEW, 3);
NameContainsKeywordsPredicate predicate = preparePredicate("Kurz Elle Kunz");
FindCommand command = new FindCommand(predicate);
- expectedModel.updateFilteredPersonList(predicate);
+ expectedModel.updateFilteredTaskList(predicate);
assertCommandSuccess(command, model, expectedMessage, expectedModel);
- assertEquals(Arrays.asList(CARL, ELLE, FIONA), model.getFilteredPersonList());
+ assertEquals(Arrays.asList(TypicalTasks.CARL, TypicalTasks.ELLE, TypicalTasks.FIONA),
+ model.getFilteredTaskList());
}
@Test
diff --git a/src/test/java/profplan/logic/commands/HelpCommandTest.java b/src/test/java/profplan/logic/commands/HelpCommandTest.java
new file mode 100644
index 00000000000..7d2f21eab53
--- /dev/null
+++ b/src/test/java/profplan/logic/commands/HelpCommandTest.java
@@ -0,0 +1,383 @@
+package profplan.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_WORD;
+import static profplan.logic.commands.CommandTestUtil.assertCommandFailure;
+import static profplan.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static profplan.logic.commands.HelpCommand.DELIMITTER_BETWEEN_COMMANDS;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.logic.commands.exceptions.CommandException;
+import profplan.model.Model;
+import profplan.model.ModelManager;
+
+public class HelpCommandTest {
+ private Model model = new ModelManager();
+ private Model expectedModel = new ModelManager();
+
+ @Test
+ public void equality_help_success() {
+ assertTrue(new HelpCommand("add").equals(new HelpCommand("add")));
+ assertTrue(new HelpCommand().equals(new HelpCommand()));
+ }
+
+ @Test
+ public void equality_help_failure() {
+ assertFalse(new HelpCommand("add").equals(new HelpCommand("delete")));
+ assertFalse(new HelpCommand().equals(new HelpCommand("invalidCommand")));
+ assertFalse(new HelpCommand().equals(new ListCommand()));
+ }
+
+ @Test
+ public void execute_help_success() {
+ CommandResult expectedCommandResult = new CommandResult(getAllCommandDescriptions(), false, false);
+ assertCommandSuccess(new HelpCommand(), model, expectedCommandResult, expectedModel);
+ }
+
+ @Test
+ public void execute_helpInvalidCommand_failure() {
+ assertCommandFailure(new HelpCommand("Invalid Command"),
+ model, HelpCommand.MESSAGE_INVALID_COMMAND_WORD);
+ }
+
+ @Test
+ public void execute_helpAdd_success() {
+ try {
+ CommandResult expectedCommandResult = new CommandResult(
+ getOneCommandDescription(AddCommand.COMMAND_WORD), false, false);
+ assertCommandSuccess(new HelpCommand(AddCommand.COMMAND_WORD), model,
+ expectedCommandResult, expectedModel);
+ } catch (CommandException e) {
+ assertCommandFailure(null, model, MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ @Test
+ public void execute_helpDelete_success() {
+ try {
+ CommandResult expectedCommandResult = new CommandResult(
+ getOneCommandDescription(DeleteCommand.COMMAND_WORD), false, false);
+ assertCommandSuccess(new HelpCommand(DeleteCommand.COMMAND_WORD), model,
+ expectedCommandResult, expectedModel);
+ } catch (CommandException e) {
+ assertCommandFailure(null, model, MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ @Test
+ public void execute_helpEdit_success() {
+ try {
+ CommandResult expectedCommandResult = new CommandResult(
+ getOneCommandDescription(EditCommand.COMMAND_WORD), false, false);
+ assertCommandSuccess(new HelpCommand(EditCommand.COMMAND_WORD),
+ model, expectedCommandResult, expectedModel);
+ } catch (CommandException e) {
+ assertCommandFailure(null, model, MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ @Test
+ public void execute_helpEditSettings_success() {
+ try {
+ CommandResult expectedCommandResult = new CommandResult(
+ getOneCommandDescription(EditSettingsCommand.COMMAND_WORD), false, false);
+ assertCommandSuccess(new HelpCommand(EditSettingsCommand.COMMAND_WORD),
+ model, expectedCommandResult, expectedModel);
+ } catch (CommandException e) {
+ assertCommandFailure(null, model, MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ @Test
+ public void execute_helpMark_success() {
+ try {
+ CommandResult expectedCommandResult = new CommandResult(
+ getOneCommandDescription(MarkCommand.COMMAND_WORD), false, false);
+ assertCommandSuccess(new HelpCommand(MarkCommand.COMMAND_WORD), model,
+ expectedCommandResult, expectedModel);
+ } catch (CommandException e) {
+ assertCommandFailure(null, model, MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ @Test
+ public void execute_helpUnmark_success() {
+ try {
+ CommandResult expectedCommandResult = new CommandResult(
+ getOneCommandDescription(UnmarkCommand.COMMAND_WORD), false, false);
+ assertCommandSuccess(new HelpCommand(UnmarkCommand.COMMAND_WORD), model,
+ expectedCommandResult, expectedModel);
+ } catch (CommandException e) {
+ assertCommandFailure(null, model, MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ @Test
+ public void execute_helpDoNext_success() {
+ try {
+ CommandResult expectedCommandResult = new CommandResult(
+ getOneCommandDescription(DoNextCommand.COMMAND_WORD), false, false);
+ assertCommandSuccess(new HelpCommand(DoNextCommand.COMMAND_WORD),
+ model, expectedCommandResult, expectedModel);
+ } catch (CommandException e) {
+ assertCommandFailure(null, model, MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ @Test
+ public void execute_helpClear_success() {
+ try {
+ CommandResult expectedCommandResult = new CommandResult(
+ getOneCommandDescription(ClearCommand.COMMAND_WORD), false, false);
+ assertCommandSuccess(new HelpCommand(ClearCommand.COMMAND_WORD), model,
+ expectedCommandResult, expectedModel);
+ } catch (CommandException e) {
+ assertCommandFailure(null, model, MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ @Test
+ public void execute_helpFind_success() {
+ try {
+ CommandResult expectedCommandResult = new CommandResult(
+ getOneCommandDescription(FindCommand.COMMAND_WORD), false, false);
+ assertCommandSuccess(new HelpCommand(FindCommand.COMMAND_WORD),
+ model, expectedCommandResult, expectedModel);
+ } catch (CommandException e) {
+ assertCommandFailure(null, model, MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ @Test
+ public void execute_helpFilter_success() {
+ try {
+ CommandResult expectedCommandResult = new CommandResult(
+ getOneCommandDescription(FilterCommand.COMMAND_WORD), false, false);
+ assertCommandSuccess(new HelpCommand(FilterCommand.COMMAND_WORD),
+ model, expectedCommandResult, expectedModel);
+ } catch (CommandException e) {
+ assertCommandFailure(null, model, MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ @Test
+ public void execute_helpList_success() {
+ try {
+ CommandResult expectedCommandResult = new CommandResult(
+ getOneCommandDescription(ListCommand.COMMAND_WORD), false, false);
+ assertCommandSuccess(new HelpCommand(ListCommand.COMMAND_WORD),
+ model, expectedCommandResult, expectedModel);
+ } catch (CommandException e) {
+ assertCommandFailure(null, model, MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ @Test
+ public void execute_helpExit_success() {
+ try {
+ CommandResult expectedCommandResult = new CommandResult(
+ getOneCommandDescription(ExitCommand.COMMAND_WORD), false, false);
+ assertCommandSuccess(new HelpCommand(ExitCommand.COMMAND_WORD),
+ model, expectedCommandResult, expectedModel);
+ } catch (CommandException e) {
+ assertCommandFailure(null, model, MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ @Test
+ public void execute_helpHelp_success() {
+ try {
+ CommandResult expectedCommandResult = new CommandResult(
+ getOneCommandDescription(HelpCommand.COMMAND_WORD), false, false);
+ assertCommandSuccess(new HelpCommand(HelpCommand.COMMAND_WORD),
+ model, expectedCommandResult, expectedModel);
+ } catch (CommandException e) {
+ assertCommandFailure(null, model, MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ @Test
+ public void execute_helpDescription_success() {
+ try {
+ CommandResult expectedCommandResult = new CommandResult(
+ getOneCommandDescription(DescriptionCommand.COMMAND_WORD), false, false);
+ assertCommandSuccess(new HelpCommand(DescriptionCommand.COMMAND_WORD),
+ model, expectedCommandResult, expectedModel);
+ } catch (CommandException e) {
+ assertCommandFailure(null, model, MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ @Test
+ public void execute_helpSortDueDate_success() {
+ try {
+ CommandResult expectedCommandResult = new CommandResult(
+ getOneCommandDescription(SortDueDateCommand.COMMAND_WORD), false, false);
+ assertCommandSuccess(new HelpCommand(SortDueDateCommand.COMMAND_WORD),
+ model, expectedCommandResult, expectedModel);
+ } catch (CommandException e) {
+ assertCommandFailure(null, model, MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ @Test
+ public void execute_helpSortPriority_success() {
+ try {
+ CommandResult expectedCommandResult = new CommandResult(
+ getOneCommandDescription(SortPriorityCommand.COMMAND_WORD), false, false);
+ assertCommandSuccess(new HelpCommand(SortPriorityCommand.COMMAND_WORD),
+ model, expectedCommandResult, expectedModel);
+ } catch (CommandException e) {
+ assertCommandFailure(null, model, MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ @Test
+ public void execute_helpListWeek_success() {
+ try {
+ CommandResult expectedCommandResult = new CommandResult(
+ getOneCommandDescription(ListWeekCommand.COMMAND_WORD), false, false);
+ assertCommandSuccess(new HelpCommand(ListWeekCommand.COMMAND_WORD),
+ model, expectedCommandResult, expectedModel);
+ } catch (CommandException e) {
+ assertCommandFailure(null, model, MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ @Test
+ public void execute_helpListMonth_success() {
+ try {
+ CommandResult expectedCommandResult = new CommandResult(
+ getOneCommandDescription(ListMonthCommand.COMMAND_WORD), false, false);
+ assertCommandSuccess(new HelpCommand(ListMonthCommand.COMMAND_WORD),
+ model, expectedCommandResult, expectedModel);
+ } catch (CommandException e) {
+ assertCommandFailure(null, model, MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ @Test
+ public void execute_helpStats_success() {
+ try {
+ CommandResult expectedCommandResult = new CommandResult(
+ getOneCommandDescription(StatsCommand.COMMAND_WORD), false, false);
+ assertCommandSuccess(new HelpCommand(StatsCommand.COMMAND_WORD),
+ model, expectedCommandResult, expectedModel);
+ } catch (CommandException e) {
+ assertCommandFailure(null, model, MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ private String getOneCommandDescription(String command) throws CommandException {
+ switch (command) {
+
+ case AddCommand.COMMAND_WORD:
+ return AddCommand.MESSAGE_USAGE + "\n" + AddCommand.MESSAGE_EXAMPLE;
+
+ case EditCommand.COMMAND_WORD:
+ return EditCommand.MESSAGE_USAGE + "\n" + EditCommand.MESSAGE_EXAMPLE;
+
+ case EditSettingsCommand.COMMAND_WORD:
+ return EditSettingsCommand.MESSAGE_USAGE + "\n" + EditSettingsCommand.MESSAGE_EXAMPLE;
+
+ case MarkCommand.COMMAND_WORD:
+ return MarkCommand.MESSAGE_USAGE + "\n" + MarkCommand.MESSAGE_EXAMPLE;
+
+ case UnmarkCommand.COMMAND_WORD:
+ return UnmarkCommand.MESSAGE_USAGE + "\n" + UnmarkCommand.MESSAGE_EXAMPLE;
+
+ case DeleteCommand.COMMAND_WORD:
+ return DeleteCommand.MESSAGE_USAGE + "\n" + DeleteCommand.MESSAGE_EXAMPLE;
+
+ case DoNextCommand.COMMAND_WORD:
+ return DoNextCommand.MESSAGE_USAGE;
+
+ case ClearCommand.COMMAND_WORD:
+ return ClearCommand.MESSAGE_USAGE;
+
+ case FindCommand.COMMAND_WORD:
+ return FindCommand.MESSAGE_USAGE + "\n" + FindCommand.MESSAGE_EXAMPLE;
+
+ case FilterCommand.COMMAND_WORD:
+ return FilterCommand.MESSAGE_USAGE + "\n" + FilterCommand.MESSAGE_EXAMPLE;
+
+ case ListCommand.COMMAND_WORD:
+ return ListCommand.MESSAGE_USAGE;
+
+ case ExitCommand.COMMAND_WORD:
+ return ExitCommand.MESSAGE_USAGE;
+
+ case HelpCommand.COMMAND_WORD:
+ return HelpCommand.MESSAGE_USAGE + "\n" + HelpCommand.MESSAGE_EXAMPLE;
+
+ case DescriptionCommand.COMMAND_WORD:
+ return DescriptionCommand.MESSAGE_USAGE + "\n" + DescriptionCommand.MESSAGE_EXAMPLE;
+
+ case SortDueDateCommand.COMMAND_WORD:
+ return SortDueDateCommand.MESSAGE_USAGE;
+
+ case SortPriorityCommand.COMMAND_WORD:
+ return SortPriorityCommand.MESSAGE_USAGE;
+
+ case ListWeekCommand.COMMAND_WORD:
+ return ListWeekCommand.MESSAGE_USAGE;
+
+ case ListMonthCommand.COMMAND_WORD:
+ return ListMonthCommand.MESSAGE_USAGE;
+
+ case StatsCommand.COMMAND_WORD:
+ return StatsCommand.MESSAGE_USAGE;
+
+ default:
+ throw new CommandException(MESSAGE_INVALID_COMMAND_WORD);
+ }
+ }
+
+ private String getAllCommandDescriptions() {
+ ArrayList> commands = listCommands();
+ String rtn = "";
+ for (Class extends Command> command : commands) {
+ try {
+ Field usage = command.getDeclaredField("MESSAGE_USAGE");
+ System.out.println(usage.get(null).toString());
+ rtn += usage.get(null).toString() + DELIMITTER_BETWEEN_COMMANDS;
+ } catch (NoSuchFieldException e) {
+ //impossible unless your command has no command word??
+ System.out.println(e);
+ continue;
+ } catch (IllegalAccessException e) {
+ //impossible as command word MUST be public
+ System.out.println(e);
+ continue;
+ }
+ }
+ return rtn;
+ }
+
+ private ArrayList> listCommands() {
+ ArrayList> rtn = new ArrayList>();
+ rtn.add(AddCommand.class);
+ rtn.add(EditCommand.class);
+ rtn.add(EditSettingsCommand.class);
+ rtn.add(MarkCommand.class);
+ rtn.add(UnmarkCommand.class);
+ rtn.add(DeleteCommand.class);
+ rtn.add(DoNextCommand.class);
+ rtn.add(ClearCommand.class);
+ rtn.add(FindCommand.class);
+ rtn.add(FilterCommand.class);
+ rtn.add(HelpCommand.class);
+ rtn.add(DescriptionCommand.class);
+ rtn.add(SortDueDateCommand.class);
+ rtn.add(SortPriorityCommand.class);
+ rtn.add(ListWeekCommand.class);
+ rtn.add(ListMonthCommand.class);
+ System.out.println(rtn.size());
+ return rtn;
+ }
+}
diff --git a/src/test/java/profplan/logic/commands/ListCommandTest.java b/src/test/java/profplan/logic/commands/ListCommandTest.java
new file mode 100644
index 00000000000..e7fa35ca721
--- /dev/null
+++ b/src/test/java/profplan/logic/commands/ListCommandTest.java
@@ -0,0 +1,55 @@
+package profplan.logic.commands;
+
+import static profplan.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static profplan.logic.commands.CommandTestUtil.showTaskAtIndex;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import profplan.model.Model;
+import profplan.model.ModelManager;
+import profplan.model.UserConfigs;
+import profplan.model.UserPrefs;
+import profplan.model.task.predicates.TaskInMonthPredicate;
+import profplan.model.task.predicates.TaskInWeekPredicate;
+import profplan.testutil.TypicalIndexes;
+import profplan.testutil.TypicalTasks;
+
+/**
+ * Contains integration tests (interaction with the Model)
+ * and unit tests for ListCommand, ListWeekCommand, and ListMonthCommand.
+ */
+public class ListCommandTest {
+
+ private Model model;
+ private Model expectedModel;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager(TypicalTasks.getTypicalProfPlan(), new UserPrefs(), new UserConfigs());
+ expectedModel = new ModelManager(model.getProfPlan(), new UserPrefs(), new UserConfigs());
+ }
+
+ @Test
+ public void execute_listIsNotFiltered_showsSameList() {
+ assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel);
+ }
+
+ @Test
+ public void execute_listIsFiltered_showsEverything() {
+ showTaskAtIndex(model, TypicalIndexes.INDEX_FIRST_TASK);
+ assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel);
+ }
+
+ @Test
+ public void testListWeek() {
+ expectedModel.updateFilteredTaskList(new TaskInWeekPredicate());
+ assertCommandSuccess(new ListWeekCommand(), model, ListWeekCommand.MESSAGE_SUCCESS, expectedModel);
+ }
+
+ @Test
+ public void testListMonth() {
+ expectedModel.updateFilteredTaskList(new TaskInMonthPredicate());
+ assertCommandSuccess(new ListMonthCommand(), model, ListMonthCommand.MESSAGE_SUCCESS, expectedModel);
+ }
+}
diff --git a/src/test/java/profplan/logic/commands/MarkCommandTest.java b/src/test/java/profplan/logic/commands/MarkCommandTest.java
new file mode 100644
index 00000000000..01c6b2bd1e5
--- /dev/null
+++ b/src/test/java/profplan/logic/commands/MarkCommandTest.java
@@ -0,0 +1,125 @@
+package profplan.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static profplan.testutil.TypicalTasks.getTypicalProfPlan;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.logic.Messages;
+import profplan.model.Model;
+import profplan.model.ModelManager;
+import profplan.model.UserConfigs;
+import profplan.model.UserPrefs;
+import profplan.model.task.Status;
+import profplan.model.task.Task;
+import profplan.testutil.Assert;
+import profplan.testutil.TaskBuilder;
+import profplan.testutil.TypicalTasks;
+
+public class MarkCommandTest {
+
+ private Model model = new ModelManager(getTypicalProfPlan(), new UserPrefs(), new UserConfigs());
+
+
+ @Test
+ public void execute_validIndex_success() {
+ Task model1 = TypicalTasks.AMY;
+ model1.setStatus(Status.UNDONE_STATUS);
+ Task editedTask = TypicalTasks.AMY;
+ editedTask.setStatus(Status.DONE_STATUS);
+
+ Model tempModel = new ModelManager();
+ Model expectedModel = new ModelManager();
+ expectedModel.setProfPlan(tempModel.getProfPlan());
+
+ tempModel.addTask(model1);
+ expectedModel.addTask(editedTask);
+
+ MarkCommand markCommand = new MarkCommand(1);
+
+ assertDoesNotThrow(() -> markCommand.execute(tempModel));
+
+ String expectedMessage = String.format(MarkCommand.MESSAGE_SUCCESS, Messages.format(editedTask));
+ assertEquals(expectedModel.getFilteredTaskList(), tempModel.getFilteredTaskList());
+ }
+
+ @Test
+ public void constructor_taskNumberAsZero_throwsNullPointerException() {
+ Assert.assertThrows(AssertionError.class, () -> new MarkCommand(0));
+ }
+
+
+
+ @Test
+ public void execute_invalidTaskNumber_throwsAssertionFailure() throws Exception {
+ Model modelStub = new ModelManager();
+ Task validTask = new TaskBuilder().build();
+
+ modelStub.addTask(validTask);
+
+ assertThrows(AssertionError.class, ()-> new MarkCommand(-1).execute(modelStub));
+ }
+
+ @Test
+ public void execute_negativeIndexToMark_throwsAssertionFailureException() {
+ int outOfBoundIndex = -100;
+ assertThrows(AssertionError.class, ()-> new MarkCommand(outOfBoundIndex));
+ }
+
+ @Test
+ public void execute_zeroIndexToMark_throwsAssertionFailureException() {
+ int outOfBoundIndex = 0;
+ assertThrows(AssertionError.class, ()-> new MarkCommand(outOfBoundIndex));
+ }
+
+
+ @Test
+ public void testEqualsWithVariousScenarios() {
+ MarkCommand markCommand1 = new MarkCommand(1);
+ MarkCommand markCommand2 = new MarkCommand(1);
+ MarkCommand markCommand3 = new MarkCommand(2);
+ Object notMarkCommand = new Object();
+
+
+ assertTrue(markCommand1.equals(markCommand1));
+
+ // Test for null
+ assertFalse(markCommand1.equals(null));
+
+
+ assertTrue(markCommand1.equals(markCommand2));
+ assertTrue(markCommand2.equals(markCommand1));
+
+
+ MarkCommand markCommand4 = new MarkCommand(1);
+ assertTrue(markCommand1.equals(markCommand2));
+ assertTrue(markCommand2.equals(markCommand4));
+ assertTrue(markCommand1.equals(markCommand4));
+
+
+ assertTrue(markCommand1.equals(markCommand2));
+ assertTrue(markCommand1.equals(markCommand2));
+
+
+ assertFalse(markCommand1.equals(markCommand3));
+
+
+ assertFalse(markCommand1.equals(notMarkCommand));
+ }
+
+ @Test
+ public void toString_indexToMark_success() {
+ MarkCommand markCommand = new MarkCommand(1);
+ String expectedStatus = MarkCommand.class.getCanonicalName() + "{taskNumber=" + 1 + "}";
+ assertEquals(markCommand.toString(), expectedStatus);
+ }
+}
+
+
+
+
+
diff --git a/src/test/java/profplan/logic/commands/SortDueDateCommandTest.java b/src/test/java/profplan/logic/commands/SortDueDateCommandTest.java
new file mode 100644
index 00000000000..fb448019c8d
--- /dev/null
+++ b/src/test/java/profplan/logic/commands/SortDueDateCommandTest.java
@@ -0,0 +1,31 @@
+package profplan.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static profplan.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static profplan.testutil.TypicalTasks.getTypicalProfPlan;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.model.Model;
+import profplan.model.ModelManager;
+import profplan.model.ProfPlan;
+import profplan.model.UserConfigs;
+import profplan.model.UserPrefs;
+
+public class SortDueDateCommandTest {
+
+ private Model model = new ModelManager(getTypicalProfPlan(), new UserPrefs(), new UserConfigs());
+
+ @Test
+ public void execute_model_success() {
+ Model expectedModel = new ModelManager(new ProfPlan(model.getProfPlan()), new UserPrefs(), new UserConfigs());
+ expectedModel.sortTaskByDueDate();
+ Model tempModel = new ModelManager(new ProfPlan(model.getProfPlan()), new UserPrefs(), new UserConfigs());
+ tempModel.sortTaskByDueDate();
+ SortDueDateCommand sortDueDateCommand = new SortDueDateCommand();
+ assertDoesNotThrow(() -> sortDueDateCommand.execute(tempModel));
+ String expectedMessage = String.format(SortDueDateCommand.MESSAGE_SUCCESS);
+ assertCommandSuccess(sortDueDateCommand, model, expectedMessage, expectedModel);
+ }
+
+}
diff --git a/src/test/java/profplan/logic/commands/SortPriorityCommandTest.java b/src/test/java/profplan/logic/commands/SortPriorityCommandTest.java
new file mode 100644
index 00000000000..a85b5f05f2d
--- /dev/null
+++ b/src/test/java/profplan/logic/commands/SortPriorityCommandTest.java
@@ -0,0 +1,31 @@
+package profplan.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static profplan.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static profplan.testutil.TypicalTasks.getTypicalProfPlan;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.model.Model;
+import profplan.model.ModelManager;
+import profplan.model.ProfPlan;
+import profplan.model.UserConfigs;
+import profplan.model.UserPrefs;
+
+public class SortPriorityCommandTest {
+
+ private Model model = new ModelManager(getTypicalProfPlan(), new UserPrefs(), new UserConfigs());
+
+ @Test
+ public void execute_model_success() {
+ Model expectedModel = new ModelManager(new ProfPlan(model.getProfPlan()), new UserPrefs(), new UserConfigs());
+ expectedModel.sortTaskByPriority();
+ Model tempModel = new ModelManager(new ProfPlan(model.getProfPlan()), new UserPrefs(), new UserConfigs());
+ tempModel.sortTaskByPriority();
+ SortPriorityCommand sortPriorityCommand = new SortPriorityCommand();
+ assertDoesNotThrow(() -> sortPriorityCommand.execute(tempModel));
+ String expectedMessage = String.format(SortPriorityCommand.MESSAGE_SUCCESS);
+ assertCommandSuccess(sortPriorityCommand, model, expectedMessage, expectedModel);
+ }
+
+}
diff --git a/src/test/java/profplan/logic/commands/StatsCommandTest.java b/src/test/java/profplan/logic/commands/StatsCommandTest.java
new file mode 100644
index 00000000000..6b5ef7c0e38
--- /dev/null
+++ b/src/test/java/profplan/logic/commands/StatsCommandTest.java
@@ -0,0 +1,31 @@
+package profplan.logic.commands;
+
+import static profplan.logic.commands.CommandTestUtil.assertCommandSuccess;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import profplan.model.Model;
+import profplan.model.ModelManager;
+import profplan.model.UserConfigs;
+import profplan.model.UserPrefs;
+import profplan.testutil.TypicalTasks;
+
+public class StatsCommandTest {
+ private Model model;
+ private Model expectedModel;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager(TypicalTasks.getTypicalProfPlan(), new UserPrefs(), new UserConfigs());
+ expectedModel = new ModelManager(model.getProfPlan(), new UserPrefs(), new UserConfigs());
+ }
+
+ @Test
+ public void execute_listIsNotFiltered_showsSameList() {
+ assertCommandSuccess(new StatsCommand(), model,
+ StatsCommand.MESSAGE_SUCCESS + String.format(StatsCommand.COMPLETION_RATE_MESSAGE_FORMAT,
+ Math.ceil(model.getCompletionRate() * 1000) / 10),
+ expectedModel);
+ }
+}
diff --git a/src/test/java/profplan/logic/commands/UnmarkCommandTest.java b/src/test/java/profplan/logic/commands/UnmarkCommandTest.java
new file mode 100644
index 00000000000..e966c718914
--- /dev/null
+++ b/src/test/java/profplan/logic/commands/UnmarkCommandTest.java
@@ -0,0 +1,120 @@
+package profplan.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.logic.Messages;
+import profplan.model.Model;
+import profplan.model.ModelManager;
+import profplan.model.task.Status;
+import profplan.model.task.Task;
+import profplan.testutil.Assert;
+import profplan.testutil.TaskBuilder;
+import profplan.testutil.TypicalTasks;
+
+public class UnmarkCommandTest {
+
+
+
+ @Test
+ public void execute_validIndex_success() {
+ Task model1 = TypicalTasks.AMY;
+ model1.setStatus(Status.DONE_STATUS);
+ Task editedTask = TypicalTasks.AMY;
+ editedTask.setStatus(Status.UNDONE_STATUS);
+
+ Model tempModel = new ModelManager();
+ Model expectedModel = new ModelManager();
+ expectedModel.setProfPlan(tempModel.getProfPlan());
+
+ tempModel.addTask(model1);
+ expectedModel.addTask(editedTask);
+
+ UnmarkCommand unmarkCommand = new UnmarkCommand(1);
+
+ assertDoesNotThrow(() -> unmarkCommand.execute(tempModel));
+
+ String expectedMessage = String.format(UnmarkCommand.MESSAGE_SUCCESS, Messages.format(editedTask));
+ assertEquals(expectedModel.getFilteredTaskList(), tempModel.getFilteredTaskList());
+ }
+
+
+ @Test
+ public void constructor_taskNumberAsZero_throwsNullPointerException() {
+ Assert.assertThrows(AssertionError.class, () -> new UnmarkCommand(0));
+ }
+
+ @Test
+ public void execute_invalidTaskNumber_throwsAssertionFailure() throws Exception {
+ Model modelStub = new ModelManager();
+ Task validTask = new TaskBuilder().build();
+
+ modelStub.addTask(validTask);
+
+ assertThrows(AssertionError.class, ()-> new UnmarkCommand(-1).execute(modelStub));
+ }
+
+ @Test
+ public void execute_negativeIndexToMark_throwsAssertionFailureException() {
+ int outOfBoundIndex = -100;
+ assertThrows(AssertionError.class, ()-> new UnmarkCommand(outOfBoundIndex));
+ }
+
+ @Test
+ public void execute_zeroIndexToMark_throwsAssertionFailureException() {
+ int outOfBoundIndex = 0;
+ assertThrows(AssertionError.class, ()-> new UnmarkCommand(outOfBoundIndex));
+ }
+
+
+ @Test
+ public void testEqualsWithVariousScenarios() {
+ UnmarkCommand unmarkCommand1 = new UnmarkCommand(1);
+ UnmarkCommand markCommand2 = new UnmarkCommand(1);
+ UnmarkCommand markCommand3 = new UnmarkCommand(2);
+ Object notMarkCommand = new Object();
+
+
+ assertTrue(unmarkCommand1.equals(unmarkCommand1));
+
+ // Test for null
+ assertFalse(unmarkCommand1.equals(null));
+
+
+ assertTrue(unmarkCommand1.equals(markCommand2));
+ assertTrue(markCommand2.equals(unmarkCommand1));
+
+
+ UnmarkCommand markCommand4 = new UnmarkCommand(1);
+ assertTrue(unmarkCommand1.equals(markCommand2));
+ assertTrue(markCommand2.equals(markCommand4));
+ assertTrue(unmarkCommand1.equals(markCommand4));
+
+
+ assertTrue(unmarkCommand1.equals(markCommand2));
+ assertTrue(unmarkCommand1.equals(markCommand2));
+
+
+ assertFalse(unmarkCommand1.equals(markCommand3));
+
+
+ assertFalse(unmarkCommand1.equals(notMarkCommand));
+ }
+
+ @Test
+ public void toString_indexToUnmark_success() {
+ UnmarkCommand unmarkCommand = new UnmarkCommand(1);
+ String expectedStatus = UnmarkCommand.class.getCanonicalName() + "{taskNumber=" + 1 + "}";
+ assertEquals(unmarkCommand.toString(), expectedStatus);
+ }
+}
+
+
+
+
+
diff --git a/src/test/java/profplan/logic/parser/AddCommandParserTest.java b/src/test/java/profplan/logic/parser/AddCommandParserTest.java
new file mode 100644
index 00000000000..4f84ce61344
--- /dev/null
+++ b/src/test/java/profplan/logic/parser/AddCommandParserTest.java
@@ -0,0 +1,151 @@
+package profplan.logic.parser;
+
+import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static profplan.logic.parser.CliSyntax.PREFIX_NAME;
+import static profplan.logic.parser.CliSyntax.PREFIX_PRIORITY;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.logic.Messages;
+import profplan.logic.commands.AddCommand;
+import profplan.logic.commands.CommandTestUtil;
+import profplan.model.task.Task;
+import profplan.testutil.TaskBuilder;
+import profplan.testutil.TypicalTasks;
+
+public class AddCommandParserTest {
+ private AddCommandParser parser = new AddCommandParser();
+
+ @Test
+ public void parse_allFieldsPresent_success() {
+ Task expectedTask = new TaskBuilder(TypicalTasks.BOB).withTags(CommandTestUtil.VALID_TAG_FRIEND)
+ .build();
+
+ // whitespace only preamble
+ CommandParserTestUtil.assertParseSuccess(parser, CommandTestUtil.PREAMBLE_WHITESPACE
+ + CommandTestUtil.NAME_DESC_BOB + CommandTestUtil.PRIORITY_DESC_BOB + CommandTestUtil.DUEDATE_DESC
+ + CommandTestUtil.TAG_DESC_FRIEND, new AddCommand(expectedTask));
+
+
+ // multiple tags - all accepted
+ Task expectedTaskMultipleTags = new TaskBuilder(TypicalTasks.BOB)
+ .withTags(CommandTestUtil.VALID_TAG_FRIEND, CommandTestUtil.VALID_TAG_HUSBAND)
+ .build();
+ CommandParserTestUtil.assertParseSuccess(parser,
+ CommandTestUtil.NAME_DESC_BOB + CommandTestUtil.PRIORITY_DESC_BOB
+ + CommandTestUtil.DUEDATE_DESC
+ + CommandTestUtil.TAG_DESC_HUSBAND
+ + CommandTestUtil.TAG_DESC_FRIEND,
+ new AddCommand(expectedTaskMultipleTags));
+ }
+
+ @Test
+ public void parse_repeatedNonTagValue_failure() {
+ String validExpectedTaskString = CommandTestUtil.NAME_DESC_BOB + CommandTestUtil.PRIORITY_DESC_BOB
+ + CommandTestUtil.TAG_DESC_FRIEND;
+
+ // multiple names
+ CommandParserTestUtil.assertParseFailure(parser, CommandTestUtil.NAME_DESC_AMY
+ + validExpectedTaskString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
+
+ // multiple prioritys
+ CommandParserTestUtil.assertParseFailure(parser, CommandTestUtil.PRIORITY_DESC_AMY
+ + validExpectedTaskString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PRIORITY));
+
+ // multiple fields repeated
+ CommandParserTestUtil.assertParseFailure(parser,
+ validExpectedTaskString + CommandTestUtil.PRIORITY_DESC_AMY
+ + CommandTestUtil.NAME_DESC_AMY
+ + validExpectedTaskString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME, PREFIX_PRIORITY));
+
+ // invalid value followed by valid value
+
+ // invalid name
+ CommandParserTestUtil.assertParseFailure(parser, CommandTestUtil.INVALID_NAME_DESC
+ + validExpectedTaskString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
+
+ // invalid priority
+ CommandParserTestUtil.assertParseFailure(parser, CommandTestUtil.INVALID_PRIORITY_DESC
+ + validExpectedTaskString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PRIORITY));
+
+ // valid value followed by invalid value
+
+ // invalid name
+ CommandParserTestUtil.assertParseFailure(parser, validExpectedTaskString
+ + CommandTestUtil.INVALID_NAME_DESC,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
+
+ // invalid priority
+ CommandParserTestUtil.assertParseFailure(parser, validExpectedTaskString
+ + CommandTestUtil.INVALID_PRIORITY_DESC,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PRIORITY));
+ }
+
+ @Test
+ public void parse_optionalFieldsMissing_success() {
+ // zero tags
+ Task expectedTask = new TaskBuilder(TypicalTasks.AMY).withTags().build();
+ CommandParserTestUtil.assertParseSuccess(parser,
+ CommandTestUtil.NAME_DESC_AMY + CommandTestUtil.PRIORITY_DESC_AMY
+ + CommandTestUtil.DUEDATE_DESC,
+ new AddCommand(expectedTask));
+ }
+
+ @Test
+ public void parse_compulsoryFieldMissing_failure() {
+ String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_FULL_HELP);
+
+ // missing name prefix
+ CommandParserTestUtil.assertParseFailure(parser, CommandTestUtil.PRIORITY_DESC_BOB
+ + CommandTestUtil.DUEDATE_DESC,
+ expectedMessage);
+
+ // missing priority prefix
+ CommandParserTestUtil.assertParseFailure(parser, CommandTestUtil.VALID_NAME_BOB
+ + CommandTestUtil.DUEDATE_DESC,
+ expectedMessage);
+
+ // missing due date prefix
+ CommandParserTestUtil.assertParseFailure(parser, CommandTestUtil.VALID_NAME_BOB
+ + CommandTestUtil.PRIORITY_DESC_BOB,
+ expectedMessage);
+ }
+
+
+ @Test
+ public void parse_invalidValue_failure() {
+ // invalid name
+ CommandParserTestUtil.assertParseFailure(parser, CommandTestUtil.INVALID_NAME_DESC
+ + CommandTestUtil.PRIORITY_DESC_BOB + CommandTestUtil.TAG_DESC_HUSBAND
+ + CommandTestUtil.TAG_DESC_FRIEND, String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ AddCommand.MESSAGE_FULL_HELP));
+
+ // invalid priority
+ CommandParserTestUtil.assertParseFailure(parser, CommandTestUtil.NAME_DESC_BOB
+ + CommandTestUtil.INVALID_PRIORITY_DESC + CommandTestUtil.TAG_DESC_HUSBAND
+ + CommandTestUtil.TAG_DESC_FRIEND, String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ AddCommand.MESSAGE_FULL_HELP));
+
+ // invalid tag
+ CommandParserTestUtil.assertParseFailure(parser, CommandTestUtil.NAME_DESC_BOB
+ + CommandTestUtil.PRIORITY_DESC_BOB
+ + CommandTestUtil.INVALID_TAG_DESC + CommandTestUtil.VALID_TAG_FRIEND,
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_FULL_HELP));
+
+ // two invalid values, only first invalid value reported
+ CommandParserTestUtil.assertParseFailure(parser, CommandTestUtil.INVALID_NAME_DESC
+ + CommandTestUtil.PRIORITY_DESC_BOB, String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ AddCommand.MESSAGE_FULL_HELP));
+
+ // non-empty preamble
+ CommandParserTestUtil.assertParseFailure(parser, CommandTestUtil.PREAMBLE_NON_EMPTY
+ + CommandTestUtil.NAME_DESC_BOB + CommandTestUtil.PRIORITY_DESC_BOB
+ + CommandTestUtil.TAG_DESC_HUSBAND + CommandTestUtil.TAG_DESC_FRIEND,
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_FULL_HELP));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java b/src/test/java/profplan/logic/parser/ArgumentTokenizerTest.java
similarity index 99%
rename from src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java
rename to src/test/java/profplan/logic/parser/ArgumentTokenizerTest.java
index c97308935f5..3aece12e7cd 100644
--- a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java
+++ b/src/test/java/profplan/logic/parser/ArgumentTokenizerTest.java
@@ -1,4 +1,4 @@
-package seedu.address.logic.parser;
+package profplan.logic.parser;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
diff --git a/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java b/src/test/java/profplan/logic/parser/CommandParserTestUtil.java
similarity index 89%
rename from src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java
rename to src/test/java/profplan/logic/parser/CommandParserTestUtil.java
index 9bf1ccf1cef..d0c7cc41e4f 100644
--- a/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java
+++ b/src/test/java/profplan/logic/parser/CommandParserTestUtil.java
@@ -1,9 +1,9 @@
-package seedu.address.logic.parser;
+package profplan.logic.parser;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import seedu.address.logic.commands.Command;
-import seedu.address.logic.parser.exceptions.ParseException;
+import profplan.logic.commands.Command;
+import profplan.logic.parser.exceptions.ParseException;
/**
* Contains helper methods for testing command parsers.
diff --git a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java b/src/test/java/profplan/logic/parser/DeleteCommandParserTest.java
similarity index 52%
rename from src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java
rename to src/test/java/profplan/logic/parser/DeleteCommandParserTest.java
index 6a40e14a649..55b10c72005 100644
--- a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java
+++ b/src/test/java/profplan/logic/parser/DeleteCommandParserTest.java
@@ -1,13 +1,11 @@
-package seedu.address.logic.parser;
+package profplan.logic.parser;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
-import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
-import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import org.junit.jupiter.api.Test;
-import seedu.address.logic.commands.DeleteCommand;
+import profplan.logic.commands.DeleteCommand;
+import profplan.testutil.TypicalIndexes;
/**
* As we are only doing white-box testing, our test cases do not cover path variations
@@ -22,11 +20,18 @@ public class DeleteCommandParserTest {
@Test
public void parse_validArgs_returnsDeleteCommand() {
- assertParseSuccess(parser, "1", new DeleteCommand(INDEX_FIRST_PERSON));
+ CommandParserTestUtil.assertParseSuccess(parser, "1", new DeleteCommand(TypicalIndexes.INDEX_FIRST_TASK));
+ }
+
+ @Test
+ public void parse_validArgsAll_returnsDeleteCommand() {
+ CommandParserTestUtil.assertParseSuccess(parser, "all", new DeleteCommand());
}
@Test
public void parse_invalidArgs_throwsParseException() {
- assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE));
+ CommandParserTestUtil.assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ DeleteCommand.MESSAGE_FULL_HELP));
}
+
}
diff --git a/src/test/java/profplan/logic/parser/DescriptionCommandParserTest.java b/src/test/java/profplan/logic/parser/DescriptionCommandParserTest.java
new file mode 100644
index 00000000000..edd819ef064
--- /dev/null
+++ b/src/test/java/profplan/logic/parser/DescriptionCommandParserTest.java
@@ -0,0 +1,53 @@
+package profplan.logic.parser;
+
+import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.logic.commands.DescriptionCommand;
+import profplan.model.task.Description;
+import profplan.testutil.TypicalIndexes;
+
+public class DescriptionCommandParserTest {
+
+ private DescriptionCommandParser parser = new DescriptionCommandParser();
+
+ @Test
+ public void parse_emptyArg_throwsParseException() {
+ CommandParserTestUtil.assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ DescriptionCommand.MESSAGE_FULL_HELP));
+ }
+
+ @Test
+ public void parse_validArgs_returnsDescriptionCommand() {
+ DescriptionCommand expectedDescriptionCommand =
+ new DescriptionCommand(TypicalIndexes.INDEX_FIRST_TASK, new Description("test description"));
+
+ CommandParserTestUtil.assertParseSuccess(parser, " 1 des/ test description ",
+ expectedDescriptionCommand);
+ }
+
+ @Test
+ public void parse_invalidIndex_throwsParseException() {
+ CommandParserTestUtil.assertParseFailure(parser, " abc des/def ",
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DescriptionCommand.MESSAGE_FULL_HELP));
+ }
+
+ @Test
+ public void parse_emptyDescription_returnsDescriptionCommand() {
+ DescriptionCommand expectedDescriptionCommand =
+ new DescriptionCommand(TypicalIndexes.INDEX_FIRST_TASK, new Description(""));
+
+ CommandParserTestUtil.assertParseSuccess(parser, " 1 des/ ",
+ expectedDescriptionCommand);
+ }
+
+ @Test
+ public void parse_specialCharactersDescription_returnsDescriptionCommand() {
+ DescriptionCommand expectedDescriptionCommand =
+ new DescriptionCommand(TypicalIndexes.INDEX_FIRST_TASK, new Description("!@#$%^&*()_"));
+
+ CommandParserTestUtil.assertParseSuccess(parser, " 1 des/ !@#$%^&*()_ ",
+ expectedDescriptionCommand);
+ }
+}
diff --git a/src/test/java/profplan/logic/parser/EditCommandParserTest.java b/src/test/java/profplan/logic/parser/EditCommandParserTest.java
new file mode 100644
index 00000000000..91e5a0b97d9
--- /dev/null
+++ b/src/test/java/profplan/logic/parser/EditCommandParserTest.java
@@ -0,0 +1,185 @@
+package profplan.logic.parser;
+
+import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static profplan.logic.parser.CliSyntax.PREFIX_PRIORITY;
+import static profplan.logic.parser.CliSyntax.PREFIX_TAG;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.commons.core.index.Index;
+import profplan.logic.Messages;
+import profplan.logic.commands.CommandTestUtil;
+import profplan.logic.commands.EditCommand;
+import profplan.logic.commands.EditCommand.EditTaskDescriptor;
+import profplan.model.tag.Tag;
+import profplan.model.task.Name;
+import profplan.model.task.Priority;
+import profplan.testutil.EditTaskDescriptorBuilder;
+import profplan.testutil.TypicalIndexes;
+
+public class EditCommandParserTest {
+
+ private static final String TAG_EMPTY = " " + PREFIX_TAG;
+
+ private static final String MESSAGE_INVALID_FORMAT =
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_FULL_HELP);
+
+ private EditCommandParser parser = new EditCommandParser();
+
+ @Test
+ public void parse_missingParts_failure() {
+ // no index specified
+ CommandParserTestUtil.assertParseFailure(parser, CommandTestUtil.VALID_NAME_AMY, MESSAGE_INVALID_FORMAT);
+
+ // no field specified
+ CommandParserTestUtil.assertParseFailure(parser, "1", EditCommand.MESSAGE_NOT_EDITED);
+
+ // no index and no field specified
+ CommandParserTestUtil.assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT);
+ }
+
+ @Test
+ public void parse_invalidPreamble_failure() {
+ // negative index
+ CommandParserTestUtil.assertParseFailure(parser, "-5" + CommandTestUtil.NAME_DESC_AMY,
+ MESSAGE_INVALID_FORMAT);
+
+ // zero index
+ CommandParserTestUtil.assertParseFailure(parser, "0" + CommandTestUtil.NAME_DESC_AMY,
+ MESSAGE_INVALID_FORMAT);
+
+ // invalid arguments being parsed as preamble
+ CommandParserTestUtil.assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT);
+
+ // invalid prefix being parsed as preamble
+ CommandParserTestUtil.assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT);
+ }
+
+ @Test
+ public void parse_invalidValue_failure() {
+ CommandParserTestUtil.assertParseFailure(parser, "1" + CommandTestUtil.INVALID_NAME_DESC,
+ Name.MESSAGE_CONSTRAINTS); // invalid name
+ CommandParserTestUtil.assertParseFailure(parser, "1" + CommandTestUtil.INVALID_PRIORITY_DESC,
+ Priority.MESSAGE_CONSTRAINTS); // invalid priority
+ CommandParserTestUtil.assertParseFailure(parser, "1" + CommandTestUtil.INVALID_TAG_DESC,
+ Tag.MESSAGE_CONSTRAINTS); // invalid tag
+
+ // invalid priority
+ CommandParserTestUtil.assertParseFailure(parser, "1" + CommandTestUtil.INVALID_PRIORITY_DESC,
+ Priority.MESSAGE_CONSTRAINTS);
+
+ // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Task} being edited,
+ // parsing it together with a valid tag results in error
+ CommandParserTestUtil.assertParseFailure(parser, "1" + CommandTestUtil.TAG_DESC_FRIEND
+ + CommandTestUtil.TAG_DESC_HUSBAND + TAG_EMPTY, Tag.MESSAGE_CONSTRAINTS);
+ CommandParserTestUtil.assertParseFailure(parser, "1" + CommandTestUtil.TAG_DESC_FRIEND
+ + TAG_EMPTY + CommandTestUtil.TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS);
+ CommandParserTestUtil.assertParseFailure(parser, "1" + TAG_EMPTY + CommandTestUtil.TAG_DESC_FRIEND
+ + CommandTestUtil.TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS);
+
+ // multiple invalid values, but only the first invalid value is captured
+ CommandParserTestUtil.assertParseFailure(parser, "1" + CommandTestUtil.INVALID_NAME_DESC
+ + CommandTestUtil.VALID_PRIORITY_AMY,
+ Name.MESSAGE_CONSTRAINTS);
+ }
+
+ @Test
+ public void parse_allFieldsSpecified_success() {
+ Index targetIndex = TypicalIndexes.INDEX_SECOND_TASK;
+ String userInput = targetIndex.getOneBased() + CommandTestUtil.PRIORITY_DESC_BOB
+ + CommandTestUtil.TAG_DESC_HUSBAND + CommandTestUtil.NAME_DESC_AMY
+ + CommandTestUtil.TAG_DESC_FRIEND;
+
+ EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder().withName(CommandTestUtil.VALID_NAME_AMY)
+ .withPriority(CommandTestUtil.VALID_PRIORITY_BOB).withTags(CommandTestUtil.VALID_TAG_HUSBAND,
+ CommandTestUtil.VALID_TAG_FRIEND).build();
+ EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
+
+ CommandParserTestUtil.assertParseSuccess(parser, userInput, expectedCommand);
+ }
+
+ @Test
+ public void parse_someFieldsSpecified_success() {
+ Index targetIndex = TypicalIndexes.INDEX_FIRST_TASK;
+ String userInput = targetIndex.getOneBased() + CommandTestUtil.PRIORITY_DESC_BOB;
+
+ EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder()
+ .withPriority(CommandTestUtil.VALID_PRIORITY_BOB)
+ .build();
+ EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
+
+ CommandParserTestUtil.assertParseSuccess(parser, userInput, expectedCommand);
+ }
+
+ @Test
+ public void parse_oneFieldSpecified_success() {
+ // name
+ Index targetIndex = TypicalIndexes.INDEX_THIRD_TASK;
+ String userInput = targetIndex.getOneBased() + CommandTestUtil.NAME_DESC_AMY;
+ EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder().withName(CommandTestUtil.VALID_NAME_AMY)
+ .build();
+ EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
+ CommandParserTestUtil.assertParseSuccess(parser, userInput, expectedCommand);
+
+ // priority
+ userInput = targetIndex.getOneBased() + CommandTestUtil.PRIORITY_DESC_AMY;
+ descriptor = new EditTaskDescriptorBuilder().withPriority(CommandTestUtil.VALID_PRIORITY_AMY).build();
+ expectedCommand = new EditCommand(targetIndex, descriptor);
+ CommandParserTestUtil.assertParseSuccess(parser, userInput, expectedCommand);
+
+ // tags
+ userInput = targetIndex.getOneBased() + CommandTestUtil.TAG_DESC_FRIEND;
+ descriptor = new EditTaskDescriptorBuilder().withTags(CommandTestUtil.VALID_TAG_FRIEND).build();
+ expectedCommand = new EditCommand(targetIndex, descriptor);
+ CommandParserTestUtil.assertParseSuccess(parser, userInput, expectedCommand);
+ }
+
+ @Test
+ public void parse_multipleRepeatedFields_failure() {
+ // More extensive testing of duplicate parameter detections is done in
+ // AddCommandParserTest#parse_repeatedNonTagValue_failure()
+
+ // valid followed by invalid
+ Index targetIndex = TypicalIndexes.INDEX_FIRST_TASK;
+ String userInput = targetIndex.getOneBased() + CommandTestUtil.INVALID_PRIORITY_DESC
+ + CommandTestUtil.PRIORITY_DESC_BOB;
+
+ CommandParserTestUtil.assertParseFailure(parser, userInput,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PRIORITY));
+
+ // invalid followed by valid
+ userInput = targetIndex.getOneBased() + CommandTestUtil.PRIORITY_DESC_BOB
+ + CommandTestUtil.INVALID_PRIORITY_DESC;
+
+ CommandParserTestUtil.assertParseFailure(parser, userInput,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PRIORITY));
+
+ // mulltiple valid fields repeated
+ userInput = targetIndex.getOneBased() + CommandTestUtil.PRIORITY_DESC_AMY
+ + CommandTestUtil.TAG_DESC_FRIEND + CommandTestUtil.PRIORITY_DESC_AMY
+ + CommandTestUtil.TAG_DESC_FRIEND
+ + CommandTestUtil.PRIORITY_DESC_BOB
+ + CommandTestUtil.TAG_DESC_HUSBAND;
+
+ CommandParserTestUtil.assertParseFailure(parser, userInput,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PRIORITY));
+
+ // multiple invalid values
+ userInput = targetIndex.getOneBased() + CommandTestUtil.INVALID_PRIORITY_DESC
+ + CommandTestUtil.INVALID_PRIORITY_DESC;
+
+ CommandParserTestUtil.assertParseFailure(parser, userInput,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PRIORITY));
+ }
+
+ @Test
+ public void parse_resetTags_success() {
+ Index targetIndex = TypicalIndexes.INDEX_THIRD_TASK;
+ String userInput = targetIndex.getOneBased() + TAG_EMPTY;
+
+ EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder().withTags().build();
+ EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
+
+ CommandParserTestUtil.assertParseSuccess(parser, userInput, expectedCommand);
+ }
+}
diff --git a/src/test/java/profplan/logic/parser/EditSettingsCommandParserTest.java b/src/test/java/profplan/logic/parser/EditSettingsCommandParserTest.java
new file mode 100644
index 00000000000..986d17955d4
--- /dev/null
+++ b/src/test/java/profplan/logic/parser/EditSettingsCommandParserTest.java
@@ -0,0 +1,24 @@
+package profplan.logic.parser;
+
+import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.logic.commands.EditSettingsCommand;
+
+public class EditSettingsCommandParserTest {
+
+ private EditSettingsCommandParser parser = new EditSettingsCommandParser();
+
+ @Test
+ public void parse_emptyArgs_throwsParseException() {
+ CommandParserTestUtil.assertParseFailure(parser, " ",
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditSettingsCommand.MESSAGE_FULL_HELP));
+ }
+
+ @Test
+ public void parse_invalidParameter_throwsParseException() {
+ CommandParserTestUtil.assertParseFailure(parser, " set aaaaaa 123 ",
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditSettingsCommand.MESSAGE_FULL_HELP));
+ }
+}
diff --git a/src/test/java/profplan/logic/parser/FilterCommandParserTest.java b/src/test/java/profplan/logic/parser/FilterCommandParserTest.java
new file mode 100644
index 00000000000..97b6ebab674
--- /dev/null
+++ b/src/test/java/profplan/logic/parser/FilterCommandParserTest.java
@@ -0,0 +1,137 @@
+package profplan.logic.parser;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.ArrayList;
+import java.util.function.Predicate;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.logic.commands.FilterCommand;
+import profplan.logic.parser.exceptions.ParseException;
+import profplan.model.task.DueDate;
+import profplan.model.task.Priority;
+import profplan.model.task.Status;
+import profplan.model.task.Task;
+import profplan.model.task.Task.RecurringType;
+import profplan.model.task.predicates.CombinedPredicate;
+import profplan.model.task.predicates.TaskDueDatePredicate;
+import profplan.model.task.predicates.TaskPriorityPredicate;
+import profplan.model.task.predicates.TaskRecurringTypePredicate;
+import profplan.model.task.predicates.TaskStatusPredicate;
+
+public class FilterCommandParserTest {
+
+ private FilterCommandParser parser = new FilterCommandParser();
+
+ @Test
+ public void parse_emptyArg_throwsParseException() {
+ CommandParserTestUtil.assertParseFailure(parser, "", String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ FilterCommand.MESSAGE_FULL_HELP));
+ }
+
+ @Test
+ public void parse_validDueDate_returnsFilterCommand() {
+ ArrayList> preds = new ArrayList<>();
+ preds.add(new TaskDueDatePredicate(new DueDate("01-01-2000")));
+
+ // no leading and trailing whitespaces
+ FilterCommand expectedFilterCommand =
+ new FilterCommand(new CombinedPredicate(preds));
+ CommandParserTestUtil.assertParseSuccess(parser, " d/01-01-2000", expectedFilterCommand);
+
+ // multiple whitespaces between keywords
+ CommandParserTestUtil.assertParseSuccess(parser, " \n d/01-01-2000\t", expectedFilterCommand);
+ }
+
+ @Test
+ public void parse_validPriority_returnsFilterCommand() {
+ ArrayList> preds = new ArrayList<>();
+ preds.add(new TaskPriorityPredicate(new Priority("1")));
+
+ // no leading and trailing whitespaces
+ FilterCommand expectedFilterCommand =
+ new FilterCommand(new CombinedPredicate(preds));
+ CommandParserTestUtil.assertParseSuccess(parser, " p/1", expectedFilterCommand);
+
+ // multiple whitespaces between keywords
+ CommandParserTestUtil.assertParseSuccess(parser, " \n p/1\t", expectedFilterCommand);
+ }
+
+ @Test
+ public void parse_validStatus_returnsFilterCommand() {
+ ArrayList> preds = new ArrayList<>();
+ preds.add(new TaskStatusPredicate(new Status("done")));
+
+ // no leading and trailing whitespaces
+ FilterCommand expectedFilterCommand =
+ new FilterCommand(new CombinedPredicate(preds));
+ CommandParserTestUtil.assertParseSuccess(parser, " s/done", expectedFilterCommand);
+
+ // multiple whitespaces between keywords
+ CommandParserTestUtil.assertParseSuccess(parser, " \n s/done\t", expectedFilterCommand);
+ }
+
+ @Test
+ public void parse_validRecurringType_returnsFilterCommand() {
+ ArrayList> preds = new ArrayList<>();
+ preds.add(new TaskRecurringTypePredicate(RecurringType.DAILY));
+
+ // no leading and trailing whitespaces
+ FilterCommand expectedFilterCommand =
+ new FilterCommand(new CombinedPredicate(preds));
+ CommandParserTestUtil.assertParseSuccess(parser, " recur/daily", expectedFilterCommand);
+
+ // multiple whitespaces between keywords
+ CommandParserTestUtil.assertParseSuccess(parser, " \n recur/daily\t", expectedFilterCommand);
+ }
+
+ @Test
+ public void testMessageSuccessDueDate() {
+ FilterCommand filterDueDate = null;
+ try {
+ filterDueDate = parser.parse("d/01-01-2024");
+ } catch (ParseException e) {
+ return;
+ }
+ String successMessageDueDate = "Here are your tasks that are:\nDue before: 01-01-2024";
+ assertEquals(filterDueDate.getSuccessMessage(), successMessageDueDate);
+ }
+
+ @Test
+ public void testMessageSuccessPriority() {
+ FilterCommand filterPriority = null;
+ try {
+ filterPriority = parser.parse("p/3");
+ } catch (ParseException e) {
+ return;
+ }
+ String successMessagePriority = "Here are your tasks that are:\nPriority: 3";
+ assertEquals(filterPriority.getSuccessMessage(), successMessagePriority);
+ }
+
+ @Test
+ public void testMessageSuccessStatus() {
+ FilterCommand filterStatus = null;
+ try {
+ filterStatus = parser.parse("s/done");
+ } catch (ParseException e) {
+ return;
+ }
+ String successMessageStatus = "Here are your tasks that are:\nStatus: done";
+ assertEquals(filterStatus.getSuccessMessage(), successMessageStatus);
+ }
+
+ @Test
+ public void testMessageRecurStatus() {
+ FilterCommand filterStatus = null;
+ try {
+ filterStatus = parser.parse("recur/weekly");
+ } catch (ParseException e) {
+ return;
+ }
+ String successMessageStatus = "Here are your tasks that are:\nRecurring: WEEKLY";
+ assertEquals(filterStatus.getSuccessMessage(), successMessageStatus);
+ }
+}
diff --git a/src/test/java/profplan/logic/parser/FindCommandParserTest.java b/src/test/java/profplan/logic/parser/FindCommandParserTest.java
new file mode 100644
index 00000000000..531bd195f87
--- /dev/null
+++ b/src/test/java/profplan/logic/parser/FindCommandParserTest.java
@@ -0,0 +1,33 @@
+package profplan.logic.parser;
+
+import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Arrays;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.logic.commands.FindCommand;
+import profplan.model.task.predicates.NameContainsKeywordsPredicate;
+
+public class FindCommandParserTest {
+
+ private FindCommandParser parser = new FindCommandParser();
+
+ @Test
+ public void parse_emptyArg_throwsParseException() {
+ CommandParserTestUtil.assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ FindCommand.MESSAGE_FULL_HELP));
+ }
+
+ @Test
+ public void parse_validArgs_returnsFindCommand() {
+ // no leading and trailing whitespaces
+ FindCommand expectedFindCommand =
+ new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob")));
+ CommandParserTestUtil.assertParseSuccess(parser, "Alice Bob", expectedFindCommand);
+
+ // multiple whitespaces between keywords
+ CommandParserTestUtil.assertParseSuccess(parser, " \n Alice \n \t Bob \t", expectedFindCommand);
+ }
+
+}
diff --git a/src/test/java/profplan/logic/parser/HelpCommandParserTest.java b/src/test/java/profplan/logic/parser/HelpCommandParserTest.java
new file mode 100644
index 00000000000..fe77a1aa7bf
--- /dev/null
+++ b/src/test/java/profplan/logic/parser/HelpCommandParserTest.java
@@ -0,0 +1,33 @@
+package profplan.logic.parser;
+
+import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.logic.commands.HelpCommand;
+
+public class HelpCommandParserTest {
+
+ private HelpCommandParser parser = new HelpCommandParser();
+
+ @Test
+ public void parse_emptyArg_returnsEmptyHelpCommand() {
+ CommandParserTestUtil.assertParseSuccess(parser, " ", new HelpCommand(""));
+ }
+
+ @Test
+ public void parse_validArgs_returnsHelpCommand() {
+ // no leading and trailing whitespaces
+ HelpCommand expectedHelpCommand =
+ new HelpCommand("add");
+ CommandParserTestUtil.assertParseSuccess(parser, "add", expectedHelpCommand);
+ }
+
+ @Test
+ public void parse_invalidArgs_returnsHelpCommand() {
+ // no leading and trailing whitespaces
+ CommandParserTestUtil.assertParseFailure(parser, "invalid Command",
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ HelpCommand.MESSAGE_FULL_HELP));
+ }
+}
diff --git a/src/test/java/profplan/logic/parser/MarkCommandParserTest.java b/src/test/java/profplan/logic/parser/MarkCommandParserTest.java
new file mode 100644
index 00000000000..2e7e48c5d0f
--- /dev/null
+++ b/src/test/java/profplan/logic/parser/MarkCommandParserTest.java
@@ -0,0 +1,48 @@
+package profplan.logic.parser;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.logic.commands.MarkCommand;
+
+public class MarkCommandParserTest {
+
+ private MarkCommandParser parser = new MarkCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsMarkCommand() {
+ CommandParserTestUtil.assertParseSuccess(parser, "1", new MarkCommand(1));
+ }
+
+ @Test
+ public void parse_negativeNumber_throwsParseException() {
+ CommandParserTestUtil.assertParseFailure(parser, "-1", MarkCommand.MESSAGE_INVALID_NUMBER);
+ }
+ @Test
+ public void parse_zero_throwsParseException() {
+ CommandParserTestUtil.assertParseFailure(parser, "0", MarkCommand.MESSAGE_INVALID_NUMBER);
+ }
+
+ @Test
+ public void parse_nonNumeric_throwsParseException() {
+ CommandParserTestUtil.assertParseFailure(parser, "a", String.format(
+ MarkCommand.MESSAGE_FULL_HELP));
+ }
+
+ @Test
+ public void parse_emptyString_throwsParseException() {
+ CommandParserTestUtil.assertParseFailure(parser, "", String.format(
+ MarkCommand.MESSAGE_FULL_HELP));
+ }
+
+ @Test
+ public void parse_whitespaceOnly_throwsParseException() {
+ CommandParserTestUtil.assertParseFailure(parser, " ", String.format(
+ MarkCommand.MESSAGE_FULL_HELP));
+ }
+
+ @Test
+ public void parse_validNumberWithWhitespace_returnsMarkCommand() {
+ CommandParserTestUtil.assertParseSuccess(parser, " 3 ", new MarkCommand(3));
+ }
+
+}
diff --git a/src/test/java/profplan/logic/parser/ParserUtilTest.java b/src/test/java/profplan/logic/parser/ParserUtilTest.java
new file mode 100644
index 00000000000..fe7cde194bf
--- /dev/null
+++ b/src/test/java/profplan/logic/parser/ParserUtilTest.java
@@ -0,0 +1,144 @@
+package profplan.logic.parser;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static profplan.logic.parser.ParserUtil.MESSAGE_INVALID_INDEX;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.logic.parser.exceptions.ParseException;
+import profplan.model.tag.Tag;
+import profplan.model.task.Name;
+import profplan.model.task.Priority;
+import profplan.testutil.Assert;
+import profplan.testutil.TypicalIndexes;
+
+public class ParserUtilTest {
+ private static final String INVALID_NAME = "R@chel";
+ private static final String INVALID_PRIORITY = "+651234";
+ private static final String INVALID_TAG = "#friend";
+
+ private static final String VALID_NAME = "Rachel Walker";
+ private static final String VALID_PRIORITY = "6";
+ private static final String VALID_TAG_1 = "friend";
+ private static final String VALID_TAG_2 = "neighbour";
+
+ private static final String WHITESPACE = " \t\r\n";
+
+ @Test
+ public void parseIndex_invalidInput_throwsParseException() {
+ Assert.assertThrows(ParseException.class, () -> ParserUtil.parseIndex("10 a"));
+ }
+
+ @Test
+ public void parseIndex_outOfRangeInput_throwsParseException() {
+ Assert.assertThrows(ParseException.class, MESSAGE_INVALID_INDEX, ()
+ -> ParserUtil.parseIndex(Long.toString(Integer.MAX_VALUE + 1)));
+ }
+
+ @Test
+ public void parseIndex_validInput_success() throws Exception {
+ // No whitespaces
+ assertEquals(TypicalIndexes.INDEX_FIRST_TASK, ParserUtil.parseIndex("1"));
+
+ // Leading and trailing whitespaces
+ assertEquals(TypicalIndexes.INDEX_FIRST_TASK, ParserUtil.parseIndex(" 1 "));
+ }
+
+ @Test
+ public void parseName_null_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> ParserUtil.parseName((String) null));
+ }
+
+ @Test
+ public void parseName_invalidValue_throwsParseException() {
+ Assert.assertThrows(ParseException.class, () -> ParserUtil.parseName(INVALID_NAME));
+ }
+
+ @Test
+ public void parseName_validValueWithoutWhitespace_returnsName() throws Exception {
+ Name expectedName = new Name(VALID_NAME);
+ assertEquals(expectedName, ParserUtil.parseName(VALID_NAME));
+ }
+
+ @Test
+ public void parseName_validValueWithWhitespace_returnsTrimmedName() throws Exception {
+ String nameWithWhitespace = WHITESPACE + VALID_NAME + WHITESPACE;
+ Name expectedName = new Name(VALID_NAME);
+ assertEquals(expectedName, ParserUtil.parseName(nameWithWhitespace));
+ }
+
+ @Test
+ public void parsePriority_null_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> ParserUtil.parsePriority((String) null));
+ }
+
+ @Test
+ public void parsePriority_invalidValue_throwsParseException() {
+ Assert.assertThrows(ParseException.class, () -> ParserUtil.parsePriority(INVALID_PRIORITY));
+ }
+
+ @Test
+ public void parsePriority_validValueWithoutWhitespace_returnsPriority() throws Exception {
+ Priority expectedPriority = new Priority(VALID_PRIORITY);
+ assertEquals(expectedPriority, ParserUtil.parsePriority(VALID_PRIORITY));
+ }
+
+ @Test
+ public void parsePriority_validValueWithWhitespace_returnsTrimmedPriority() throws Exception {
+ String priorityWithWhitespace = WHITESPACE + VALID_PRIORITY + WHITESPACE;
+ Priority expectedPriority = new Priority(VALID_PRIORITY);
+ assertEquals(expectedPriority, ParserUtil.parsePriority(priorityWithWhitespace));
+ }
+
+ @Test
+ public void parseTag_null_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> ParserUtil.parseTag(null));
+ }
+
+ @Test
+ public void parseTag_invalidValue_throwsParseException() {
+ Assert.assertThrows(ParseException.class, () -> ParserUtil.parseTag(INVALID_TAG));
+ }
+
+ @Test
+ public void parseTag_validValueWithoutWhitespace_returnsTag() throws Exception {
+ Tag expectedTag = new Tag(VALID_TAG_1);
+ assertEquals(expectedTag, ParserUtil.parseTag(VALID_TAG_1));
+ }
+
+ @Test
+ public void parseTag_validValueWithWhitespace_returnsTrimmedTag() throws Exception {
+ String tagWithWhitespace = WHITESPACE + VALID_TAG_1 + WHITESPACE;
+ Tag expectedTag = new Tag(VALID_TAG_1);
+ assertEquals(expectedTag, ParserUtil.parseTag(tagWithWhitespace));
+ }
+
+ @Test
+ public void parseTags_null_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> ParserUtil.parseTags(null));
+ }
+
+ @Test
+ public void parseTags_collectionWithInvalidTags_throwsParseException() {
+ Assert.assertThrows(ParseException.class, () -> ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, INVALID_TAG)));
+ }
+
+ @Test
+ public void parseTags_emptyCollection_returnsEmptySet() throws Exception {
+ assertTrue(ParserUtil.parseTags(Collections.emptyList()).isEmpty());
+ }
+
+ @Test
+ public void parseTags_collectionWithValidTags_returnsTagSet() throws Exception {
+ Set actualTagSet = ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, VALID_TAG_2));
+ Set expectedTagSet = new HashSet(Arrays.asList(new Tag(VALID_TAG_1), new Tag(VALID_TAG_2)));
+
+ assertEquals(expectedTagSet, actualTagSet);
+ }
+}
diff --git a/src/test/java/profplan/logic/parser/ProfPlanParserTest.java b/src/test/java/profplan/logic/parser/ProfPlanParserTest.java
new file mode 100644
index 00000000000..3dec2a597b8
--- /dev/null
+++ b/src/test/java/profplan/logic/parser/ProfPlanParserTest.java
@@ -0,0 +1,100 @@
+package profplan.logic.parser;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static profplan.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static profplan.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.logic.commands.AddCommand;
+import profplan.logic.commands.ClearCommand;
+import profplan.logic.commands.DeleteCommand;
+import profplan.logic.commands.EditCommand;
+import profplan.logic.commands.EditCommand.EditTaskDescriptor;
+import profplan.logic.commands.ExitCommand;
+import profplan.logic.commands.FindCommand;
+import profplan.logic.commands.HelpCommand;
+import profplan.logic.commands.ListCommand;
+import profplan.logic.parser.exceptions.ParseException;
+import profplan.model.task.Task;
+import profplan.model.task.predicates.NameContainsKeywordsPredicate;
+import profplan.testutil.Assert;
+import profplan.testutil.EditTaskDescriptorBuilder;
+import profplan.testutil.TaskBuilder;
+import profplan.testutil.TaskUtil;
+import profplan.testutil.TypicalIndexes;
+
+public class ProfPlanParserTest {
+
+ private final ProfPlanParser parser = new ProfPlanParser();
+
+ @Test
+ public void parseCommand_add() throws Exception {
+ Task task = new TaskBuilder().build();
+ AddCommand command = (AddCommand) parser.parseCommand(TaskUtil.getAddCommand(task));
+ assertEquals(new AddCommand(task), command);
+ }
+
+ @Test
+ public void parseCommand_clear() throws Exception {
+ assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD) instanceof ClearCommand);
+ assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD + " 3") instanceof ClearCommand);
+ }
+
+ @Test
+ public void parseCommand_delete() throws Exception {
+ DeleteCommand command = (DeleteCommand) parser.parseCommand(DeleteCommand.COMMAND_WORD + " "
+ + TypicalIndexes.INDEX_FIRST_TASK.getOneBased());
+ assertEquals(new DeleteCommand(TypicalIndexes.INDEX_FIRST_TASK), command);
+ }
+
+ @Test
+ public void parseCommand_edit() throws Exception {
+ Task task = new TaskBuilder().build();
+ EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder(task).build();
+ EditCommand command = (EditCommand) parser.parseCommand(EditCommand.COMMAND_WORD + " "
+ + TypicalIndexes.INDEX_FIRST_TASK.getOneBased() + " "
+ + TaskUtil.getEditTaskDescriptorDetails(descriptor));
+ assertEquals(new EditCommand(TypicalIndexes.INDEX_FIRST_TASK, descriptor), command);
+ }
+
+ @Test
+ public void parseCommand_exit() throws Exception {
+ assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD) instanceof ExitCommand);
+ assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD + " 3") instanceof ExitCommand);
+ }
+
+ @Test
+ public void parseCommand_find() throws Exception {
+ List keywords = Arrays.asList("foo", "bar", "baz");
+ FindCommand command = (FindCommand) parser.parseCommand(FindCommand.COMMAND_WORD + " " + keywords.stream()
+ .collect(Collectors.joining(" ")));
+ assertEquals(new FindCommand(new NameContainsKeywordsPredicate(keywords)), command);
+ }
+
+ @Test
+ public void parseCommand_help() throws Exception {
+ assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD) instanceof HelpCommand);
+ }
+
+ @Test
+ public void parseCommand_list() throws Exception {
+ assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD) instanceof ListCommand);
+ }
+
+ @Test
+ public void parseCommand_unrecognisedInput_throwsParseException() {
+ Assert.assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ HelpCommand.MESSAGE_USAGE), () -> parser.parseCommand(""));
+ }
+
+ @Test
+ public void parseCommand_unknownCommand_throwsParseException() {
+ Assert.assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, () -> parser.parseCommand("unknownCommand"));
+ }
+}
diff --git a/src/test/java/profplan/logic/parser/UnmarkCommandParserTest.java b/src/test/java/profplan/logic/parser/UnmarkCommandParserTest.java
new file mode 100644
index 00000000000..e86b7d39573
--- /dev/null
+++ b/src/test/java/profplan/logic/parser/UnmarkCommandParserTest.java
@@ -0,0 +1,47 @@
+package profplan.logic.parser;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.logic.commands.UnmarkCommand;
+
+public class UnmarkCommandParserTest {
+
+ private UnmarkCommandParser parser = new UnmarkCommandParser();
+ @Test
+ public void parse_validArgs_returnsMarkCommand() {
+ CommandParserTestUtil.assertParseSuccess(parser, "1", new UnmarkCommand(1));
+ }
+
+ @Test
+ public void parse_negativeNumber_throwsParseException() {
+ CommandParserTestUtil.assertParseFailure(parser, "-1", UnmarkCommand.MESSAGE_INVALID_NUMBER);
+ }
+ @Test
+ public void parse_zero_throwsParseException() {
+ CommandParserTestUtil.assertParseFailure(parser, "0", UnmarkCommand.MESSAGE_INVALID_NUMBER);
+ }
+
+ @Test
+ public void parse_nonNumeric_throwsParseException() {
+ CommandParserTestUtil.assertParseFailure(parser, "a", String.format(
+ UnmarkCommand.MESSAGE_FULL_HELP));
+ }
+
+ @Test
+ public void parse_emptyString_throwsParseException() {
+ CommandParserTestUtil.assertParseFailure(parser, "", String.format(
+ UnmarkCommand.MESSAGE_FULL_HELP));
+ }
+
+ @Test
+ public void parse_whitespaceOnly_throwsParseException() {
+ CommandParserTestUtil.assertParseFailure(parser, " ", String.format(
+ UnmarkCommand.MESSAGE_FULL_HELP));
+ }
+
+ @Test
+ public void parse_validNumberWithWhitespace_returnsMarkCommand() {
+ CommandParserTestUtil.assertParseSuccess(parser, " 3 ", new UnmarkCommand(3));
+ }
+
+}
diff --git a/src/test/java/profplan/model/ModelManagerTest.java b/src/test/java/profplan/model/ModelManagerTest.java
new file mode 100644
index 00000000000..58b5f5ac6eb
--- /dev/null
+++ b/src/test/java/profplan/model/ModelManagerTest.java
@@ -0,0 +1,156 @@
+package profplan.model;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static profplan.model.Model.PREDICATE_SHOW_ALL_TASKS;
+import static profplan.testutil.TypicalTasks.ALICE;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.commons.core.GuiSettings;
+import profplan.model.task.DueDate;
+import profplan.model.task.predicates.NameContainsKeywordsPredicate;
+import profplan.testutil.Assert;
+import profplan.testutil.ProfPlanBuilder;
+import profplan.testutil.TypicalTasks;
+
+public class ModelManagerTest {
+
+ private ModelManager modelManager = new ModelManager();
+
+ @Test
+ public void constructor() {
+ assertEquals(new UserPrefs(), modelManager.getUserPrefs());
+ assertEquals(new GuiSettings(), modelManager.getGuiSettings());
+ assertEquals(new ProfPlan(), new ProfPlan(modelManager.getProfPlan()));
+ }
+
+ @Test
+ public void setUserPrefs_nullUserPrefs_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> modelManager.setUserPrefs(null));
+ }
+
+ @Test
+ public void setUserPrefs_validUserPrefs_copiesUserPrefs() {
+ UserPrefs userPrefs = new UserPrefs();
+ userPrefs.setProfPlanFilePath(Paths.get("address/book/file/path"));
+ userPrefs.setGuiSettings(new GuiSettings(1, 2, 3, 4));
+ modelManager.setUserPrefs(userPrefs);
+ assertEquals(userPrefs, modelManager.getUserPrefs());
+
+ // Modifying userPrefs should not modify modelManager's userPrefs
+ UserPrefs oldUserPrefs = new UserPrefs(userPrefs);
+ userPrefs.setProfPlanFilePath(Paths.get("new/address/book/file/path"));
+ assertEquals(oldUserPrefs, modelManager.getUserPrefs());
+ }
+
+ @Test
+ public void setGuiSettings_nullGuiSettings_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> modelManager.setGuiSettings(null));
+ }
+
+ @Test
+ public void setGuiSettings_validGuiSettings_setsGuiSettings() {
+ GuiSettings guiSettings = new GuiSettings(1, 2, 3, 4);
+ modelManager.setGuiSettings(guiSettings);
+ assertEquals(guiSettings, modelManager.getGuiSettings());
+ }
+
+ @Test
+ public void setProfPlanFilePath_nullPath_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> modelManager.setProfPlanFilePath(null));
+ }
+
+ @Test
+ public void setProfPlanFilePath_validPath_setsProfPlanFilePath() {
+ Path path = Paths.get("address/book/file/path");
+ modelManager.setProfPlanFilePath(path);
+ assertEquals(path, modelManager.getProfPlanFilePath());
+ }
+
+ @Test
+ public void hasTask_nullTask_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> modelManager.hasTask(null));
+ }
+
+ @Test
+ public void hasTask_taskNotInProfPlan_returnsFalse() {
+ assertFalse(modelManager.hasTask(ALICE));
+ }
+
+ @Test
+ public void hasTask_taskInProfPlan_returnsTrue() {
+ modelManager.addTask(ALICE);
+ assertTrue(modelManager.hasTask(ALICE));
+ }
+
+ @Test
+ public void getFilteredTaskList_modifyList_throwsUnsupportedOperationException() {
+ Assert.assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredTaskList().remove(0));
+ }
+
+ @Test
+ public void getDaysUntilDueDateTest_validDate() {
+ double difference = modelManager.getDaysUntilDueDate(new DueDate("05-02-2024"), "03-02-2024");
+ assertEquals(difference, 2);
+ // Assert.assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredTaskList().remove(0));
+ }
+
+ @Test
+ public void getDaysUntilDueDateTest_pastDueDate() {
+ double difference = modelManager.getDaysUntilDueDate(new DueDate("01-02-2024"), "03-02-2024");
+ assertEquals(difference, 0.1);
+ // Assert.assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredTaskList().remove(0));
+ }
+
+ @Test
+ public void getDaysUntilDueDateTest_sameDate() {
+ double difference = modelManager.getDaysUntilDueDate(new DueDate("01-02-2024"), "01-02-2024");
+ assertEquals(difference, 0.1);
+ // Assert.assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredTaskList().remove(0));
+ }
+
+ @Test
+ public void equals() {
+ ProfPlan profPlan = new ProfPlanBuilder().withTask(
+ ALICE).withTask(TypicalTasks.BENSON).build();
+ ProfPlan differentProfPlan = new ProfPlan();
+ UserPrefs userPrefs = new UserPrefs();
+ UserConfigs userConfigs = new UserConfigs();
+
+ // same values -> returns true
+ modelManager = new ModelManager(profPlan, userPrefs, userConfigs);
+ ModelManager modelManagerCopy = new ModelManager(profPlan, userPrefs, userConfigs);
+ assertTrue(modelManager.equals(modelManagerCopy));
+
+ // same object -> returns true
+ assertTrue(modelManager.equals(modelManager));
+
+ // null -> returns false
+ assertFalse(modelManager.equals(null));
+
+ // different types -> returns false
+ assertFalse(modelManager.equals(5));
+
+ // different addressBook -> returns false
+ assertFalse(modelManager.equals(new ModelManager(differentProfPlan, userPrefs, userConfigs)));
+
+ // different filteredList -> returns false
+ String[] keywords = ALICE.getName().fullName.split("\\s+");
+ modelManager.updateFilteredTaskList(new NameContainsKeywordsPredicate(Arrays.asList(keywords)));
+ assertFalse(modelManager.equals(new ModelManager(profPlan, userPrefs, userConfigs)));
+
+ // resets modelManager to initial state for upcoming tests
+ modelManager.updateFilteredTaskList(PREDICATE_SHOW_ALL_TASKS);
+
+ // different userPrefs -> returns false
+ UserPrefs differentUserPrefs = new UserPrefs();
+ differentUserPrefs.setProfPlanFilePath(Paths.get("differentFilePath"));
+ assertFalse(modelManager.equals(new ModelManager(profPlan, differentUserPrefs, userConfigs)));
+ }
+}
diff --git a/src/test/java/profplan/model/ProfPlanTest.java b/src/test/java/profplan/model/ProfPlanTest.java
new file mode 100644
index 00000000000..31331fa2070
--- /dev/null
+++ b/src/test/java/profplan/model/ProfPlanTest.java
@@ -0,0 +1,108 @@
+package profplan.model;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static profplan.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import profplan.model.task.Task;
+import profplan.model.task.exceptions.DuplicateTaskException;
+import profplan.testutil.Assert;
+import profplan.testutil.TaskBuilder;
+import profplan.testutil.TypicalTasks;
+
+public class ProfPlanTest {
+
+ private final ProfPlan profPlan = new ProfPlan();
+
+ @Test
+ public void constructor() {
+ assertEquals(Collections.emptyList(), profPlan.getTaskList());
+ }
+
+ @Test
+ public void resetData_null_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> profPlan.resetData(null));
+ }
+
+ @Test
+ public void resetData_withValidReadOnlyProfPlan_replacesData() {
+ ProfPlan newData = TypicalTasks.getTypicalProfPlan();
+ profPlan.resetData(newData);
+ assertEquals(newData, profPlan);
+ }
+
+ @Test
+ public void resetData_withDuplicateTasks_throwsDuplicateTaskException() {
+ // Two tasks with the same identity fields
+ Task editedAlice = new TaskBuilder(TypicalTasks.ALICE)
+ .withTags(VALID_TAG_HUSBAND)
+ .build();
+ List newTasks = Arrays.asList(TypicalTasks.ALICE, editedAlice);
+ ProfPlanStub newData = new ProfPlanStub(newTasks);
+
+ Assert.assertThrows(DuplicateTaskException.class, () -> profPlan.resetData(newData));
+ }
+
+ @Test
+ public void hasTask_nullTask_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> profPlan.hasTask(null));
+ }
+
+ @Test
+ public void hasTask_taskNotInProfPlan_returnsFalse() {
+ assertFalse(profPlan.hasTask(TypicalTasks.ALICE));
+ }
+
+ @Test
+ public void hasTask_taskInProfPlan_returnsTrue() {
+ profPlan.addTask(TypicalTasks.ALICE);
+ assertTrue(profPlan.hasTask(TypicalTasks.ALICE));
+ }
+
+ @Test
+ public void hasTask_taskWithSameIdentityFieldsInProfPlan_returnsTrue() {
+ profPlan.addTask(TypicalTasks.ALICE);
+ Task editedAlice = new TaskBuilder(TypicalTasks.ALICE)
+ .withTags(VALID_TAG_HUSBAND)
+ .build();
+ assertTrue(profPlan.hasTask(editedAlice));
+ }
+
+ // @Test
+ // public void getTaskList_modifyList_throwsUnsupportedOperationException() {
+ // Assert.assertThrows(UnsupportedOperationException.class, () -> profPlan.getTaskList().remove(0));
+ // }
+
+ @Test
+ public void toStringMethod() {
+ String expected = ProfPlan.class.getCanonicalName() + "{tasks=" + profPlan.getTaskList() + "}";
+ assertEquals(expected, profPlan.toString());
+ }
+
+ /**
+ * A stub ReadOnlyAddressBook whose tasks list can violate interface constraints.
+ */
+ private static class ProfPlanStub implements ReadOnlyProfPlan {
+ private final ObservableList tasks = FXCollections.observableArrayList();
+
+ ProfPlanStub(Collection tasks) {
+ this.tasks.setAll(tasks);
+ }
+
+ @Override
+ public ObservableList getTaskList() {
+ return tasks;
+ }
+ }
+
+}
diff --git a/src/test/java/profplan/model/UserPrefsTest.java b/src/test/java/profplan/model/UserPrefsTest.java
new file mode 100644
index 00000000000..faf568f2662
--- /dev/null
+++ b/src/test/java/profplan/model/UserPrefsTest.java
@@ -0,0 +1,21 @@
+package profplan.model;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.testutil.Assert;
+
+public class UserPrefsTest {
+
+ @Test
+ public void setGuiSettings_nullGuiSettings_throwsNullPointerException() {
+ UserPrefs userPref = new UserPrefs();
+ Assert.assertThrows(NullPointerException.class, () -> userPref.setGuiSettings(null));
+ }
+
+ @Test
+ public void setProfPlanFilePath_nullPath_throwsNullPointerException() {
+ UserPrefs userPrefs = new UserPrefs();
+ Assert.assertThrows(NullPointerException.class, () -> userPrefs.setProfPlanFilePath(null));
+ }
+
+}
diff --git a/src/test/java/seedu/address/model/tag/TagTest.java b/src/test/java/profplan/model/tag/TagTest.java
similarity index 51%
rename from src/test/java/seedu/address/model/tag/TagTest.java
rename to src/test/java/profplan/model/tag/TagTest.java
index 64d07d79ee2..e8c6be98b11 100644
--- a/src/test/java/seedu/address/model/tag/TagTest.java
+++ b/src/test/java/profplan/model/tag/TagTest.java
@@ -1,26 +1,26 @@
-package seedu.address.model.tag;
-
-import static seedu.address.testutil.Assert.assertThrows;
+package profplan.model.tag;
import org.junit.jupiter.api.Test;
+import profplan.testutil.Assert;
+
public class TagTest {
@Test
public void constructor_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> new Tag(null));
+ Assert.assertThrows(NullPointerException.class, () -> new Tag(null));
}
@Test
public void constructor_invalidTagName_throwsIllegalArgumentException() {
String invalidTagName = "";
- assertThrows(IllegalArgumentException.class, () -> new Tag(invalidTagName));
+ Assert.assertThrows(IllegalArgumentException.class, () -> new Tag(invalidTagName));
}
@Test
public void isValidTagName() {
// null tag name
- assertThrows(NullPointerException.class, () -> Tag.isValidTagName(null));
+ Assert.assertThrows(NullPointerException.class, () -> Tag.isValidTagName(null));
}
}
diff --git a/src/test/java/profplan/model/task/CombinedPredicateTest.java b/src/test/java/profplan/model/task/CombinedPredicateTest.java
new file mode 100644
index 00000000000..a9c41e61b72
--- /dev/null
+++ b/src/test/java/profplan/model/task/CombinedPredicateTest.java
@@ -0,0 +1,49 @@
+package profplan.model.task;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.ArrayList;
+import java.util.function.Predicate;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.model.task.predicates.CombinedPredicate;
+import profplan.model.task.predicates.TaskDueDatePredicate;
+import profplan.model.task.predicates.TaskInMonthPredicate;
+import profplan.model.task.predicates.TaskPriorityPredicate;
+import profplan.model.task.predicates.TaskStatusPredicate;
+import profplan.testutil.TaskBuilder;
+
+public class CombinedPredicateTest {
+ @Test
+ public void equals() {
+ ArrayList> preds = new ArrayList<>();
+ preds.add(new TaskStatusPredicate(new Status("done")));
+ preds.add(new TaskPriorityPredicate(new Priority("3")));
+
+ CombinedPredicate one = new CombinedPredicate(preds);
+ CombinedPredicate two = new CombinedPredicate(preds);
+ assertTrue(one.equals(one));
+ assertTrue(one.equals(two));
+
+ ArrayList> preds2 = new ArrayList<>();
+ preds2.add(new TaskStatusPredicate(new Status("done")));
+
+ CombinedPredicate not = new CombinedPredicate(preds2);
+ assertFalse(one.equals(not));
+ assertFalse(one.equals(new TaskInMonthPredicate()));
+
+ assertTrue(one.toString().equals(two.toString()));
+ }
+
+ @Test
+ public void test() {
+ ArrayList> preds = new ArrayList<>();
+ preds.add(new TaskDueDatePredicate(new DueDate("01-01-2000")));
+ preds.add(new TaskPriorityPredicate(new Priority("3")));
+
+ CombinedPredicate predicate = new CombinedPredicate(preds);
+ assertFalse(predicate.test(new TaskBuilder().withName("Alice").build()));
+ }
+}
diff --git a/src/test/java/profplan/model/task/DueDateTest.java b/src/test/java/profplan/model/task/DueDateTest.java
new file mode 100644
index 00000000000..c4f2e023f03
--- /dev/null
+++ b/src/test/java/profplan/model/task/DueDateTest.java
@@ -0,0 +1,100 @@
+package profplan.model.task;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.testutil.Assert;
+
+public class DueDateTest {
+ private DateTimeFormatter dateTimeFormatter = DueDate.getDateFormat();
+
+ @Test
+ public void constructor_null_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> new DueDate(null));
+ }
+
+ @Test
+ public void isValidDate() {
+ // invalid dates
+ assertFalse(DueDate.isValidDate("01-01-2031")); // max year is 2030
+ assertFalse(DueDate.isValidDate("01-01-1999")); // max year is 2030
+ assertFalse(DueDate.isValidDate("01/01/2000"));
+
+ // valid dates
+ assertTrue(DueDate.isValidDate("01-01-2000"));
+ assertTrue(DueDate.isValidDate("31-02-2000"));
+ assertTrue(DueDate.isValidDate("12-12-2030"));
+ }
+
+ @Test
+ public void isDateBefore() {
+ DueDate before = new DueDate("01-01-2023");
+ DueDate after = new DueDate("01-01-2024");
+ assertTrue(before.isIncludedorBefore(after));
+ }
+
+ @Test
+ public void isDateWithinWeekMonth() {
+
+ LocalDate in4Days = LocalDate.now().plusDays(4);
+
+ DueDate test = new DueDate(dateTimeFormatter.format(in4Days));
+ assertTrue(test.isWithinWeek());
+
+ LocalDate in15Days = LocalDate.now().plusDays(15);
+ DueDate test2 = new DueDate(dateTimeFormatter.format(in15Days));
+ assertFalse(test2.isWithinWeek());
+ assertTrue(test2.isWithinMonth());
+ }
+
+ @Test
+ public void addDays() {
+ DueDate date = new DueDate("01-01-2023");
+ DueDate in5Days = new DueDate("06-01-2023");
+ assertEquals(date.addDays(5), in5Days);
+ }
+
+ @Test
+ public void addMonth() {
+ DueDate date = new DueDate("01-01-2023");
+ DueDate in1Month = new DueDate("01-02-2023");
+ assertEquals(date.addMonth(), in1Month);
+ }
+
+ @Test
+ public void equals() {
+ DueDate date = new DueDate("01-01-2000");
+
+ // same values -> returns true
+ assertTrue(date.equals(new DueDate("01-01-2000")));
+
+ // same object -> returns true
+ assertTrue(date.equals(date));
+
+ // null -> returns false
+ assertFalse(date.equals(null));
+
+ // different types -> returns false
+ assertFalse(date.equals(5.0f));
+
+ // different values -> returns false
+ assertFalse(date.equals(new DueDate("01-01-2030")));
+ }
+
+ @Test
+ public void compareTo() {
+ DueDate date = new DueDate("01-01-2023");
+ DueDate later = new DueDate("01-01-2024");
+ DueDate earlier = new DueDate("01-01-2022");
+
+ assertEquals(date.compareTo(earlier), 1);
+ assertEquals(date.compareTo(later), -1);
+
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java b/src/test/java/profplan/model/task/NameContainsKeywordsPredicateTest.java
similarity index 74%
rename from src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java
rename to src/test/java/profplan/model/task/NameContainsKeywordsPredicateTest.java
index 6b3fd90ade7..d366a86cb57 100644
--- a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java
+++ b/src/test/java/profplan/model/task/NameContainsKeywordsPredicateTest.java
@@ -1,4 +1,4 @@
-package seedu.address.model.person;
+package profplan.model.task;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -10,7 +10,8 @@
import org.junit.jupiter.api.Test;
-import seedu.address.testutil.PersonBuilder;
+import profplan.model.task.predicates.NameContainsKeywordsPredicate;
+import profplan.testutil.TaskBuilder;
public class NameContainsKeywordsPredicateTest {
@@ -35,7 +36,7 @@ public void equals() {
// null -> returns false
assertFalse(firstPredicate.equals(null));
- // different person -> returns false
+ // different task -> returns false
assertFalse(firstPredicate.equals(secondPredicate));
}
@@ -43,35 +44,35 @@ public void equals() {
public void test_nameContainsKeywords_returnsTrue() {
// One keyword
NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Collections.singletonList("Alice"));
- assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build()));
+ assertTrue(predicate.test(new TaskBuilder().withName("Alice Bob").build()));
// Multiple keywords
predicate = new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob"));
- assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build()));
+ assertTrue(predicate.test(new TaskBuilder().withName("Alice Bob").build()));
// Only one matching keyword
predicate = new NameContainsKeywordsPredicate(Arrays.asList("Bob", "Carol"));
- assertTrue(predicate.test(new PersonBuilder().withName("Alice Carol").build()));
+ assertTrue(predicate.test(new TaskBuilder().withName("Alice Carol").build()));
// Mixed-case keywords
predicate = new NameContainsKeywordsPredicate(Arrays.asList("aLIce", "bOB"));
- assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build()));
+ assertTrue(predicate.test(new TaskBuilder().withName("Alice Bob").build()));
}
@Test
public void test_nameDoesNotContainKeywords_returnsFalse() {
// Zero keywords
NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Collections.emptyList());
- assertFalse(predicate.test(new PersonBuilder().withName("Alice").build()));
+ assertFalse(predicate.test(new TaskBuilder().withName("Alice").build()));
// Non-matching keyword
predicate = new NameContainsKeywordsPredicate(Arrays.asList("Carol"));
- assertFalse(predicate.test(new PersonBuilder().withName("Alice Bob").build()));
+ assertFalse(predicate.test(new TaskBuilder().withName("Alice Bob").build()));
- // Keywords match phone, email and address, but does not match name
- predicate = new NameContainsKeywordsPredicate(Arrays.asList("12345", "alice@email.com", "Main", "Street"));
- assertFalse(predicate.test(new PersonBuilder().withName("Alice").withPhone("12345")
- .withEmail("alice@email.com").withAddress("Main Street").build()));
+ // Keywords match priority but does not match name
+ predicate = new NameContainsKeywordsPredicate(Arrays.asList("5"));
+ assertFalse(predicate.test(new TaskBuilder().withName("Alice").withPriority("5")
+ .build()));
}
@Test
diff --git a/src/test/java/seedu/address/model/person/NameTest.java b/src/test/java/profplan/model/task/NameTest.java
similarity index 83%
rename from src/test/java/seedu/address/model/person/NameTest.java
rename to src/test/java/profplan/model/task/NameTest.java
index 94e3dd726bd..b6437553d0c 100644
--- a/src/test/java/seedu/address/model/person/NameTest.java
+++ b/src/test/java/profplan/model/task/NameTest.java
@@ -1,28 +1,29 @@
-package seedu.address.model.person;
+package profplan.model.task;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.testutil.Assert.assertThrows;
import org.junit.jupiter.api.Test;
+import profplan.testutil.Assert;
+
public class NameTest {
@Test
public void constructor_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> new Name(null));
+ Assert.assertThrows(NullPointerException.class, () -> new Name(null));
}
@Test
public void constructor_invalidName_throwsIllegalArgumentException() {
String invalidName = "";
- assertThrows(IllegalArgumentException.class, () -> new Name(invalidName));
+ Assert.assertThrows(IllegalArgumentException.class, () -> new Name(invalidName));
}
@Test
public void isValidName() {
// null name
- assertThrows(NullPointerException.class, () -> Name.isValidName(null));
+ Assert.assertThrows(NullPointerException.class, () -> Name.isValidName(null));
// invalid name
assertFalse(Name.isValidName("")); // empty string
diff --git a/src/test/java/profplan/model/task/PriorityTest.java b/src/test/java/profplan/model/task/PriorityTest.java
new file mode 100644
index 00000000000..c4972096dd1
--- /dev/null
+++ b/src/test/java/profplan/model/task/PriorityTest.java
@@ -0,0 +1,63 @@
+package profplan.model.task;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.testutil.Assert;
+
+public class PriorityTest {
+
+ @Test
+ public void constructor_null_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> new Priority(null));
+ }
+
+ @Test
+ public void constructor_invalidPriority_throwsIllegalArgumentException() {
+ String invalidPriority = "";
+ Assert.assertThrows(IllegalArgumentException.class, () -> new Priority(invalidPriority));
+ }
+
+ @Test
+ public void isValidPriority() {
+ // null priority number
+ Assert.assertThrows(NullPointerException.class, () -> Priority.isValidPriority(null));
+
+ // invalid priority numbers
+ assertFalse(Priority.isValidPriority("")); // empty string
+ assertFalse(Priority.isValidPriority(" ")); // spaces only
+ assertFalse(Priority.isValidPriority("0")); // less than 1
+ assertFalse(Priority.isValidPriority("11")); // more than 10
+ assertFalse(Priority.isValidPriority("priority")); // non-numeric
+ assertFalse(Priority.isValidPriority("9p")); // alphabets within digits
+ assertFalse(Priority.isValidPriority("9 1")); // spaces within digits
+
+ // valid priority numbers
+ assertTrue(Priority.isValidPriority("1")); // minimum valid priority
+ assertTrue(Priority.isValidPriority("10")); // maximum valid priority
+ assertTrue(Priority.isValidPriority("3")); // valid priority within the range
+ assertTrue(Priority.isValidPriority("6"));
+ }
+
+ @Test
+ public void equals() {
+ Priority priority = new Priority("9");
+
+ // same values -> returns true
+ assertTrue(priority.equals(new Priority("9")));
+
+ // same object -> returns true
+ assertTrue(priority.equals(priority));
+
+ // null -> returns false
+ assertFalse(priority.equals(null));
+
+ // different types -> returns false
+ assertFalse(priority.equals(5.0f));
+
+ // different values -> returns false
+ assertFalse(priority.equals(new Priority("5")));
+ }
+}
diff --git a/src/test/java/profplan/model/task/StatusTest.java b/src/test/java/profplan/model/task/StatusTest.java
new file mode 100644
index 00000000000..88caab3d7c9
--- /dev/null
+++ b/src/test/java/profplan/model/task/StatusTest.java
@@ -0,0 +1,63 @@
+package profplan.model.task;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.testutil.Assert;
+
+public class StatusTest {
+
+ @Test
+ public void constructor_null_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> new Status(null));
+ }
+
+ @Test
+ public void constructor_invalidPriority_throwsIllegalArgumentException() {
+ String invalidStatus = "";
+ Assert.assertThrows(IllegalArgumentException.class, () -> new Status(invalidStatus));
+ }
+
+ @Test
+ public void isValidStatus() {
+
+ //checks for invalid status
+ Assert.assertThrows(NullPointerException.class, () -> Status.isValidStatus(null));
+ assertFalse(Status.isValidStatus(""));
+ assertFalse(Status.isValidStatus(" "));
+ assertFalse(Status.isValidStatus("0"));
+ assertFalse(Status.isValidStatus("11"));
+ assertFalse(Status.isValidStatus("status"));
+ assertFalse(Status.isValidStatus("9p"));
+ assertFalse(Status.isValidStatus("9 1"));
+ assertFalse(Status.isValidStatus("1"));
+ assertFalse(Status.isValidStatus("10"));
+ assertFalse(Status.isValidStatus("3"));
+ assertFalse(Status.isValidStatus("6"));
+
+ //checks for valid status
+ assertTrue(Status.isValidStatus("done"));
+ assertTrue(Status.isValidStatus("undone"));
+ }
+
+ @Test
+ public void equals() {
+ Status status = new Status("done");
+
+ // same values -> returns true
+ assertTrue(status.equals(new Status("done")));
+
+ // same object -> returns true
+ assertTrue(status.equals(status));
+
+ // different types -> returns false
+ assertFalse(status.equals(5.0f));
+
+ // different values -> returns false
+ assertFalse(status.equals(new Status("undone")));
+ }
+
+
+}
diff --git a/src/test/java/profplan/model/task/TaskInMonthPredicateTest.java b/src/test/java/profplan/model/task/TaskInMonthPredicateTest.java
new file mode 100644
index 00000000000..9379a93f475
--- /dev/null
+++ b/src/test/java/profplan/model/task/TaskInMonthPredicateTest.java
@@ -0,0 +1,21 @@
+package profplan.model.task;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.model.task.predicates.TaskInMonthPredicate;
+import profplan.model.task.predicates.TaskInWeekPredicate;
+
+public class TaskInMonthPredicateTest {
+ @Test
+ public void equals() {
+ TaskInMonthPredicate one = new TaskInMonthPredicate();
+ TaskInMonthPredicate two = new TaskInMonthPredicate();
+ assertTrue(one.equals(two));
+
+ TaskInWeekPredicate not = new TaskInWeekPredicate();
+ assertFalse(one.equals(not));
+ }
+}
diff --git a/src/test/java/profplan/model/task/TaskInWeekPredicateTest.java b/src/test/java/profplan/model/task/TaskInWeekPredicateTest.java
new file mode 100644
index 00000000000..0826f562f63
--- /dev/null
+++ b/src/test/java/profplan/model/task/TaskInWeekPredicateTest.java
@@ -0,0 +1,21 @@
+package profplan.model.task;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.model.task.predicates.TaskInMonthPredicate;
+import profplan.model.task.predicates.TaskInWeekPredicate;
+
+public class TaskInWeekPredicateTest {
+ @Test
+ public void equals() {
+ TaskInWeekPredicate one = new TaskInWeekPredicate();
+ TaskInWeekPredicate two = new TaskInWeekPredicate();
+ assertTrue(one.equals(two));
+
+ TaskInMonthPredicate not = new TaskInMonthPredicate();
+ assertFalse(one.equals(not));
+ }
+}
diff --git a/src/test/java/profplan/model/task/TaskTest.java b/src/test/java/profplan/model/task/TaskTest.java
new file mode 100644
index 00000000000..f2f30a4f8b2
--- /dev/null
+++ b/src/test/java/profplan/model/task/TaskTest.java
@@ -0,0 +1,129 @@
+package profplan.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 profplan.logic.commands.CommandTestUtil.VALID_NAME_BOB;
+import static profplan.logic.commands.CommandTestUtil.VALID_PRIORITY_BOB;
+import static profplan.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
+
+import java.util.HashSet;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.model.task.Task.RecurringType;
+import profplan.testutil.Assert;
+import profplan.testutil.TaskBuilder;
+import profplan.testutil.TypicalTasks;
+
+public class TaskTest {
+
+ @Test
+ public void asObservableList_modifyList_throwsUnsupportedOperationException() {
+ Task task = new TaskBuilder().build();
+ Assert.assertThrows(UnsupportedOperationException.class, () -> task.getTags().remove(0));
+ }
+
+ @Test
+ public void isSameTask() {
+ // same object -> returns true
+ assertTrue(TypicalTasks.ALICE.isSameTask(TypicalTasks.ALICE));
+
+ // null -> returns false
+ assertFalse(TypicalTasks.ALICE.isSameTask(null));
+
+ // same name, all other attributes different -> returns true
+ Task editedAlice = new TaskBuilder(TypicalTasks.ALICE).withPriority(VALID_PRIORITY_BOB)
+ .withTags(VALID_TAG_HUSBAND).build();
+ assertTrue(TypicalTasks.ALICE.isSameTask(editedAlice));
+
+ // different name, all other attributes same -> returns false
+ editedAlice = new TaskBuilder(TypicalTasks.ALICE).withName(VALID_NAME_BOB).build();
+ assertFalse(TypicalTasks.ALICE.isSameTask(editedAlice));
+
+ // name differs in case, all other attributes same -> returns false
+ Task editedBob = new TaskBuilder(TypicalTasks.BOB).withName(VALID_NAME_BOB.toLowerCase()).build();
+ assertFalse(TypicalTasks.BOB.isSameTask(editedBob));
+
+ // name has trailing spaces, all other attributes same -> returns false
+ String nameWithTrailingSpaces = VALID_NAME_BOB + " ";
+ editedBob = new TaskBuilder(TypicalTasks.BOB).withName(nameWithTrailingSpaces).build();
+ assertFalse(TypicalTasks.BOB.isSameTask(editedBob));
+ }
+
+ @Test
+ public void equals() {
+ // same values -> returns true
+ Task aliceCopy = new TaskBuilder(TypicalTasks.ALICE).build();
+ assertTrue(TypicalTasks.ALICE.equals(aliceCopy));
+
+ // same object -> returns true
+ assertTrue(TypicalTasks.ALICE.equals(TypicalTasks.ALICE));
+
+ // null -> returns false
+ assertFalse(TypicalTasks.ALICE.equals(null));
+
+ // different type -> returns false
+ assertFalse(TypicalTasks.ALICE.equals(5));
+
+ // different task -> returns false
+ assertFalse(TypicalTasks.ALICE.equals(TypicalTasks.BOB));
+
+ // different name -> returns false
+ Task editedAlice = new TaskBuilder(TypicalTasks.ALICE).withName(VALID_NAME_BOB).build();
+ assertFalse(TypicalTasks.ALICE.equals(editedAlice));
+
+ // different priority -> returns false
+ editedAlice = new TaskBuilder(TypicalTasks.ALICE).withPriority(VALID_PRIORITY_BOB).build();
+ assertFalse(TypicalTasks.ALICE.equals(editedAlice));
+
+ // different tags -> returns false
+ editedAlice = new TaskBuilder(TypicalTasks.ALICE).withTags(VALID_TAG_HUSBAND).build();
+ assertFalse(TypicalTasks.ALICE.equals(editedAlice));
+ }
+
+ @Test
+ public void toStringMethod() {
+ String expected = Task.class.getCanonicalName() + "{name=" + TypicalTasks.ALICE.getName() + ", priority="
+ + TypicalTasks.ALICE.getPriority()
+ + ", status=" + TypicalTasks.ALICE.getStatus()
+ + ", tags=" + TypicalTasks.ALICE.getTags()
+ + ", dueDate=" + TypicalTasks.ALICE.getDueDate()
+ + ", link=" + TypicalTasks.ALICE.getLink()
+ + ", description=" + TypicalTasks.ALICE.getDescription()
+ + "}";
+ assertEquals(expected, TypicalTasks.ALICE.toString());
+ }
+
+ @Test
+ public void recurringTask() {
+ Task daily = new Task(new Name("daily"), new Priority("3"),
+ true, RecurringType.DAILY, new HashSet<>(),
+ new DueDate("01-01-2023"), new Link("-"), null);
+
+ daily.setStatus(Status.DONE_STATUS);
+ assertEquals(daily.getDueDate(), new DueDate("02-01-2023"));
+
+
+ Task weekly = new Task(new Name("weekly"), new Priority("3"),
+ true, RecurringType.WEEKLY, new HashSet<>(),
+ new DueDate("01-01-2023"), new Link("-"), null);
+
+ weekly.setStatus(Status.DONE_STATUS);
+ assertEquals(weekly.getDueDate(), new DueDate("08-01-2023"));
+
+ Task monthly = new Task(new Name("monthly"), new Priority("3"),
+ true, RecurringType.MONTHLY, new HashSet<>(),
+ new DueDate("01-01-2023"), new Link("-"), null);
+
+ monthly.setStatus(Status.DONE_STATUS);
+ assertEquals(monthly.getDueDate(), new DueDate("01-02-2023"));
+
+ Task semesterly = new Task(new Name("semly"), new Priority("3"),
+ true, RecurringType.SEMESTERLY, new HashSet<>(),
+ new DueDate("01-01-2023"), new Link("-"), null);
+
+ semesterly.setStatus(Status.DONE_STATUS);
+ assertEquals(semesterly.getDueDate(), new DueDate("30-06-2023"));
+ }
+}
diff --git a/src/test/java/profplan/model/task/UniqueTaskListTest.java b/src/test/java/profplan/model/task/UniqueTaskListTest.java
new file mode 100644
index 00000000000..d577d1f65d5
--- /dev/null
+++ b/src/test/java/profplan/model/task/UniqueTaskListTest.java
@@ -0,0 +1,178 @@
+package profplan.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 profplan.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.model.task.exceptions.DuplicateTaskException;
+import profplan.model.task.exceptions.TaskNotFoundException;
+import profplan.testutil.Assert;
+import profplan.testutil.TaskBuilder;
+import profplan.testutil.TypicalTasks;
+
+public class UniqueTaskListTest {
+
+ private final UniqueTaskList uniqueTaskList = new UniqueTaskList();
+
+ @Test
+ public void contains_nullTask_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> uniqueTaskList.contains(null));
+ }
+
+ @Test
+ public void contains_taskNotInList_returnsFalse() {
+ assertFalse(uniqueTaskList.contains(TypicalTasks.ALICE));
+ }
+
+ @Test
+ public void contains_taskInList_returnsTrue() {
+ uniqueTaskList.add(TypicalTasks.ALICE);
+ assertTrue(uniqueTaskList.contains(TypicalTasks.ALICE));
+ }
+
+ @Test
+ public void contains_taskWithSameIdentityFieldsInList_returnsTrue() {
+ uniqueTaskList.add(TypicalTasks.ALICE);
+ Task editedAlice = new TaskBuilder(TypicalTasks.ALICE)
+ .withTags(VALID_TAG_HUSBAND)
+ .build();
+ assertTrue(uniqueTaskList.contains(editedAlice));
+ }
+
+ @Test
+ public void add_nullTask_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> uniqueTaskList.add(null));
+ }
+
+ @Test
+ public void add_duplicateTask_throwsDuplicateTaskException() {
+ uniqueTaskList.add(TypicalTasks.ALICE);
+ Assert.assertThrows(DuplicateTaskException.class, () -> uniqueTaskList.add(TypicalTasks.ALICE));
+ }
+
+ @Test
+ public void setTask_nullTargetTask_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> uniqueTaskList.setTask(null, TypicalTasks.ALICE));
+ }
+
+ @Test
+ public void setTask_nullEditedTask_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> uniqueTaskList.setTask(TypicalTasks.ALICE, null));
+ }
+
+ @Test
+ public void setTask_targetTaskNotInList_throwsTaskNotFoundException() {
+ Assert.assertThrows(TaskNotFoundException.class, () -> uniqueTaskList.setTask(TypicalTasks.ALICE,
+ TypicalTasks.ALICE));
+ }
+
+ @Test
+ public void setTask_editedTaskIsSameTask_success() {
+ uniqueTaskList.add(TypicalTasks.ALICE);
+ uniqueTaskList.setTask(TypicalTasks.ALICE, TypicalTasks.ALICE);
+ UniqueTaskList expectedUniqueTaskList = new UniqueTaskList();
+ expectedUniqueTaskList.add(TypicalTasks.ALICE);
+ assertEquals(expectedUniqueTaskList, uniqueTaskList);
+ }
+
+ @Test
+ public void setTask_editedTaskHasSameIdentity_success() {
+ uniqueTaskList.add(TypicalTasks.ALICE);
+ Task editedAlice = new TaskBuilder(TypicalTasks.ALICE)
+ .withTags(VALID_TAG_HUSBAND)
+ .build();
+ uniqueTaskList.setTask(TypicalTasks.ALICE, editedAlice);
+ UniqueTaskList expectedUniqueTaskList = new UniqueTaskList();
+ expectedUniqueTaskList.add(editedAlice);
+ assertEquals(expectedUniqueTaskList, uniqueTaskList);
+ }
+
+ @Test
+ public void setTask_editedTaskHasDifferentIdentity_success() {
+ uniqueTaskList.add(TypicalTasks.ALICE);
+ uniqueTaskList.setTask(TypicalTasks.ALICE, TypicalTasks.BOB);
+ UniqueTaskList expectedUniqueTaskList = new UniqueTaskList();
+ expectedUniqueTaskList.add(TypicalTasks.BOB);
+ assertEquals(expectedUniqueTaskList, uniqueTaskList);
+ }
+
+ @Test
+ public void setTask_editedTaskHasNonUniqueIdentity_throwsDuplicateTaskException() {
+ uniqueTaskList.add(TypicalTasks.ALICE);
+ uniqueTaskList.add(TypicalTasks.BOB);
+ Assert.assertThrows(DuplicateTaskException.class, () -> uniqueTaskList.setTask(TypicalTasks.ALICE,
+ TypicalTasks.BOB));
+ }
+
+ @Test
+ public void remove_nullTask_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> uniqueTaskList.remove(null));
+ }
+
+ @Test
+ public void remove_taskDoesNotExist_throwsTaskNotFoundException() {
+ Assert.assertThrows(TaskNotFoundException.class, () -> uniqueTaskList.remove(TypicalTasks.ALICE));
+ }
+
+ @Test
+ public void remove_existingTask_removesTask() {
+ uniqueTaskList.add(TypicalTasks.ALICE);
+ uniqueTaskList.remove(TypicalTasks.ALICE);
+ UniqueTaskList expectedUniqueTaskList = new UniqueTaskList();
+ assertEquals(expectedUniqueTaskList, uniqueTaskList);
+ }
+
+ @Test
+ public void setTasks_nullUniqueTaskList_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> uniqueTaskList.setTasks((UniqueTaskList) null));
+ }
+
+ @Test
+ public void setTasks_uniqueTaskList_replacesOwnListWithProvidedUniqueTaskList() {
+ uniqueTaskList.add(TypicalTasks.ALICE);
+ UniqueTaskList expectedUniqueTaskList = new UniqueTaskList();
+ expectedUniqueTaskList.add(TypicalTasks.BOB);
+ uniqueTaskList.setTasks(expectedUniqueTaskList);
+ assertEquals(expectedUniqueTaskList, uniqueTaskList);
+ }
+
+ @Test
+ public void setTasks_nullList_throwsNullPointerException() {
+ Assert.assertThrows(NullPointerException.class, () -> uniqueTaskList.setTasks((List) null));
+ }
+
+ @Test
+ public void setTasks_list_replacesOwnListWithProvidedList() {
+ uniqueTaskList.add(TypicalTasks.ALICE);
+ List taskList = Collections.singletonList(TypicalTasks.BOB);
+ uniqueTaskList.setTasks(taskList);
+ UniqueTaskList expectedUniqueTaskList = new UniqueTaskList();
+ expectedUniqueTaskList.add(TypicalTasks.BOB);
+ assertEquals(expectedUniqueTaskList, uniqueTaskList);
+ }
+
+ @Test
+ public void setTasks_listWithDuplicateTasks_throwsDuplicateTaskException() {
+ List listWithDuplicateTasks = Arrays.asList(TypicalTasks.ALICE, TypicalTasks.ALICE);
+ Assert.assertThrows(DuplicateTaskException.class, () -> uniqueTaskList.setTasks(
+ listWithDuplicateTasks));
+ }
+
+ @Test
+ public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() {
+ Assert.assertThrows(UnsupportedOperationException.class, ()
+ -> uniqueTaskList.asUnmodifiableObservableList().remove(0));
+ }
+
+ @Test
+ public void toStringMethod() {
+ assertEquals(uniqueTaskList.asUnmodifiableObservableList().toString(), uniqueTaskList.toString());
+ }
+}
diff --git a/src/test/java/profplan/storage/JsonAdaptedTaskTest.java b/src/test/java/profplan/storage/JsonAdaptedTaskTest.java
new file mode 100644
index 00000000000..781610bd971
--- /dev/null
+++ b/src/test/java/profplan/storage/JsonAdaptedTaskTest.java
@@ -0,0 +1,95 @@
+package profplan.storage;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static profplan.storage.JsonAdaptedTask.MISSING_FIELD_MESSAGE_FORMAT;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.junit.jupiter.api.Test;
+
+import profplan.commons.exceptions.IllegalValueException;
+import profplan.model.task.DueDate;
+import profplan.model.task.Name;
+import profplan.model.task.Priority;
+import profplan.testutil.Assert;
+import profplan.testutil.TypicalTasks;
+
+public class JsonAdaptedTaskTest {
+ private static final String INVALID_NAME = "R@chel";
+ private static final String INVALID_PRIORITY = "+651234";
+ private static final String INVALID_TAG = "#friend";
+ private static final String INVALID_DUEDATE = "31/01/3000";
+
+ private static final String VALID_NAME = TypicalTasks.BENSON.getName().toString();
+ private static final String VALID_PRIORITY = TypicalTasks.BENSON.getPriority().toString();
+ private static final List