diff --git a/.gitignore b/.gitignore index a704b9b829f..2ce8f47b893 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ src/main/resources/docs/ /config.json /preferences.json /*.log.* +*.json # Test sandbox files src/test/data/sandbox/ diff --git a/README.adoc b/README.adoc index 1c052b4e365..13571b256e3 100644 --- a/README.adoc +++ b/README.adoc @@ -27,5 +27,3 @@ Technical interviews are hard. Unless you are extraordinarily gifted, you will l * We would like to acknowledge the original source of the code, AddressBook-Level3 project created by SE-EDU initiative at https://se-education.org * Libraries used: https://openjfx.io/[JavaFX], https://github.com/FasterXML/jackson[Jackson], https://github.com/junit-team/junit5[JUnit5] - -== Licence : link:LICENSE[MIT] diff --git a/docs/DeveloperGuide.adoc b/docs/DeveloperGuide.adoc index 2082847f845..1a7a88c1121 100644 --- a/docs/DeveloperGuide.adoc +++ b/docs/DeveloperGuide.adoc @@ -188,18 +188,20 @@ the task management feature. ==== Current Implementation -The task management feature mainly involves four main behaviours: +The task management feature mainly involves six main behaviours: * `AlgoBase#addTask()` - create a new task for a problem and add it to a specified plan. * `AlgoBase#deleteTask()` - delete an existing task from a specified plan. * `AlgoBase#doneTask()` - mark a task as done in a specified plan. +* `AlgoBase#editTask()` - edit the due date of a task in a specified plan. +* `AlgoBase#moveTask()` - move a task from one plan to another. * `AlgoBase#undoneTask()` - mark a task as undone in a specified plan. Given below is an example usage scenario and how the mechanism for adding tasks behaves at each step. The following activity diagram summarizes what happens when a user executes the `AddTask` command: -.Activity Diagram for the Execution of `AddTask` Command +.Activity Diagram for the Execution of `AddTaskCommand` image::task/AddTaskCommandActivityDiagram.png[] Step 1. The user launches the application. @@ -208,23 +210,42 @@ Step 2. AlgoBase displays a list of existing problems and plans in the UI. Step 3. The user executes `addtask plan/1 prob/1` to add the problem with index 1 in the list to the plan with index 1. The `AddTask` command calls `Plan#updateTasks` to create a new plan from the original plan with this additional task. -After that, `Model#setPlan()` is called to replace the original plan with the updated plan in the `PlanList`. +After that, `Model#setPlan` is called to replace the original plan with the updated plan in the `PlanList` +stored in `AlgoBase`. The following sequence diagram shows how the `addtask` operation works: -.Sequence Diagram for the Execution of `AddTask` Command +.Sequence Diagram for the Execution of `AddTaskCommand` image::task/AddTaskSequenceDiagram.png[] ==== Design Considerations ===== Aspect: Data structure to support the task commands. -* Alternative 1 (current choice): Use a `HashSet` in current AlgoBase to save all tasks in a plan. -** Pros: Users can manage the tasks conveniently. -** Cons: Harder to implement for relational storage. -* Alternative 2: Simply keep tasks as a part of a plan. -** Pros: No need to save the task separately in the storage, all tasks are under plans. -** Cons: Does not keep track of relations between tasks and plans. +* Alternative 1 (current choice): Use a `HashSet` to store tasks in a plan. +** Pros: Duplicate tasks can be checked easily. +** Cons: Harder to identify tasks by index. +* Alternative 2: Use an `ArrayList` to store tasks in a plan. +** Pros: Tasks can be identified by index easily. +** Cons: Harder to check for duplicates. + +===== Aspect: How to store problem details within tasks to support the task commands. + +* Alternative 1 (current choice): Store a problem object in each task. +** Pros: Changes in problem details will be reflected in the relevant tasks as well. +** Cons: Relational storage is required to keep track of this relationship. +* Alternative 2: Copy all problem details and store as separate fields in each task. +** Pros: No need to implement relational storage. There will be less coupling between problems and tasks as well. +** Cons: Changes in problem details will not be reflected in the relevant tasks. + +===== Aspect: Relational storage to support the task commands. + +* Alternative 1 (current choice): Use an additional `id` field to identify problems and tasks. +** Pros: Id is immutable over time. +** Cons: An additional field is needed for the models. +* Alternative 2: Use object hash to identify problems and tasks. +** Pros: No need to store another additional field in the models. +** Cons: Object hash can change over time. // end::task[] //@@author @@ -250,6 +271,7 @@ Step 2. The user executes `addtag t/easy` to add a tag named [easy] which have n The following sequence diagram shows how the `deletetag` operation works: +.Sequence Diagram for `AddTagCommand` image::AddTagSequenceDiagram.png[] Step 3. The user decides to execute the command `listtag` to show a tag list in the GUI of algobase. The `listtag` command calls Model#listtag(), causing the taglist shows the current components of uniqueTagList. Commands that do not modify the address book, such as `listtag`, will not call `Model#addTag()`, `Model#deleteTag()` or `Model#editTag()`. Thus the `uniqueTagList` remains unchanged. @@ -262,6 +284,7 @@ Step 5. The user executes `deletetag t/hard` to delete the current tag [easy] in The following activity diagram summarizes what happens when a user executes a new tag modifying command +.Activity Diagram for tag commands image::TagActivityDiagram.png[] ==== Design considerations @@ -624,11 +647,15 @@ Priorities: High (must have) - `* * \*`, Medium (nice to have) - `* \*`, Low (un |`* * *` |user |add tasks to a plan |better prepare for interview -|`* * *` |user |mark tasks as complete/incomplete within plans |better prepare for interview +|`* * *` |user |mark tasks as done/undone within plans |keep track of progress within each plan + +|`* * *` |user |edit due dates of tasks |better manage progress for each plan + +|`* * *` |user |move tasks among plans |better manage progress for each plan |`* * *` |user |import database from <> files |easily transfer data from one computer to another -|`* * *` |user |export data into JSON format |easily transfer data from one computer to another +|`* * *` |user |export data into <> format |easily transfer data from one computer to another |`* *` |advanced user |export data into CSV format |do some manipulation/processing on the data @@ -908,7 +935,67 @@ Use case ends. Use case ends. [discrete] -=== Use Case 11: Switch between View of Items +=== Use Case 11: Edit Due Dates of Tasks + +*MSS* + +1. User requests to edit due date of an existing task +by entering the index of the plan, index of the task and new due date. +2. AlgoBase edits the due date of the specified task in the specified plan. +3. AlgoBase indicates that the existing task is edited successfully. +4. AlgoBase displays details of the task updated. ++ +Use case ends. + +*Extensions* +[none] +* 2a. AlgoBase detects that the index of plan is out of bounds. ++ +[none] +** 2a1. AlgoBase informs user that the update is unsuccessful because the index of plan is out of bounds. ++ +Use case ends. + +[none] +* 2b. AlgoBase detects that the index of task is out of bounds. ++ +[none] +** 2b1. AlgoBase informs user that the update is unsuccessful because the index of task is out of bounds. ++ +Use case ends. + +[discrete] +=== Use Case 12: Move Tasks between Plans + +*MSS* + +1. User requests to move an existing task from one plan to another +by entering the index of the task and the indices of the plans involved. +2. AlgoBase moves the specified task from the specified "from" plan to the "to" plan. +3. AlgoBase indicates that the existing task is moved successfully. +4. AlgoBase displays list of tasks of the "to" plan updated. ++ +Use case ends. + +*Extensions* +[none] +* 2a. AlgoBase detects that the index of either plan is out of bounds. ++ +[none] +** 2a1. AlgoBase informs user that the update is unsuccessful because the index of plan is out of bounds. ++ +Use case ends. + +[none] +* 2b. AlgoBase detects that the index of task is out of bounds. ++ +[none] +** 2b1. AlgoBase informs user that the update is unsuccessful because the index of task is out of bounds. ++ +Use case ends. + +[discrete] +=== Use Case 13: Switch between View of Items *MSS* @@ -927,7 +1014,7 @@ Use case ends. Use case ends. [discrete] -=== Use Case 12: See details of an item +=== Use Case 14: See details of an item *MSS* @@ -936,6 +1023,46 @@ Use case ends. + Use case ends. +[discrete] +=== Use Case 15: Export AlgoBase data + +*MSS* + +1. User requests to export AlgoBase data to a specified path. +2. AlgoBase exports AlgoBase data to a file name `algobase.json` in the specified location. +3. AlgoBase indicates that AlgoBase data are exported successfully. ++ +Use case ends. + +*Extensions* +[none] +* 2a. AlgoBase detects that the specified path is invalid. ++ +[none] +** 2a1. AlgoBase informs user that the export is unsuccessful because the path is invalid. ++ +Use case ends. + +[discrete] +=== Use Case 16: Import AlgoBase data + +*MSS* + +1. User requests to import data from a specified file into AlgoBase. +2. AlgoBase imports data from the specified file into AlgoBase. +3. AlgoBase indicates that the data are imported into AlgoBase successfully. ++ +Use case ends. + +*Extensions* +[none] +* 2a. AlgoBase detects that the specified file does not exist. ++ +[none] +** 2a1. AlgoBase informs user that the import is unsuccessful because the file path is invalid. ++ +Use case ends. + [appendix] == Non Functional Requirements diff --git a/docs/UserGuide.adoc b/docs/UserGuide.adoc index 015d0feb64d..f5cb0296b7a 100644 --- a/docs/UserGuide.adoc +++ b/docs/UserGuide.adoc @@ -314,6 +314,15 @@ Examples: * `donetask plan/1 task/1` +==== Editing due date of a task from a plan: `edittask` + +Edits the due date of a specified task from a specified plan. + +Format: `edittask plan/PLAN_INDEX task/TASK_INDEX due/DUE_DATE` + +Examples: + +* `edittask plan/1 task/1 due/2019-12-12` + ==== Marking a task as undone: `undonetask` Marks a specified task in a specified plan as undone. + @@ -323,6 +332,15 @@ Examples: * `undonetask plan/1 task/1` +==== Moving a task from one plan to another: `movetask` + +Moves a specified task from a specified plan to another. + +Format: `movetask task/TASK_INDEX from/PLAN_INDEX to/PLAN_INDEX` + +Examples: + +* `movetask task/1 from/1 to/2` + === Tabs ==== Switching Tabs: `switchtab` @@ -365,31 +383,27 @@ Examples: ==== Importing data: `import` -Imports external data of a specified format (e.g. CSV, JSON) into local storage. + -Format: `import f/FORMAT p/PATH` +Imports external data of a specified format (e.g. JSON) into local storage. + +Format: `import format/FORMAT path/FILE_PATH` -* Format can be ‘CSV’ or ‘JSON’. -* Directory refers to the full path of the output file. +* Format can only be `JSON`. +* File path refers to the relative path of the input file. Examples: -* `import t/plan p/./steven_halim_secret.json` +* `import format/json path/./steven_halim_secret.json` ==== Exporting data: `export` -Exports data into a specified format (e.g. CSV, JSON). + -Format: `export f/FORMAT p/DIRECTORY` +Exports data into a specified format (e.g. JSON). + +Format: `export format/FORMAT path/DIRECTORY_PATH` -* Format can be ‘CSV’ or ‘JSON’. -* Directory refers to the full path of the output file. +* Format can only be `JSON`. +* Directory path refers to the relative path of the directory to store the output file. Examples: -* `export f/csv p/./` - -=== Encryption - -==== Encrypting data files `[coming in v2.0]` +* `export format/json path/.` === Miscellaneous @@ -432,6 +446,10 @@ Format: `exit` AlgoBase data is saved in the hard disk automatically after any command that changes the data. + There is no need to save manually. +=== Encryption + +==== Encrypting data files `[coming in v2.0]` + == FAQ *Q*: How do I transfer my data to another Computer? + @@ -475,15 +493,19 @@ e.g. `find training set` e.g. `addtask plan/1 prob/2` * *Delete Task from Training Plan* : `deletetask plan/PLAN_INDEX task/TASK_INDEX` + e.g. `deletetask plan/1 task/2` +* *Edit Due Date of Task* : `edittask plan/PLAN_INDEX task/TASK_INDEX due/DUE_DATE` + +e.g. `edittask plan/1 task/2 due/2019-12-12` * *Mark Task as done* : `donetask plan/PLAN_INDEX task/TASK_INDEX` + e.g. `donetask plan/1 task/2` * *Mark Task as undone* : `undonetask plan/PLAN_INDEX task/TASK_INDEX` + e.g. `undonetask plan/1 task/2` +* *Move Tasks among Plans* : `movetask task/TASK_INDEX from/PLAN_INDEX to/PLAN_TASK` + +e.g. `movetask task/1 from/1 to/2` -* *Importing data* : `import f/FORMAT p/PATH` + -e.g. `import t/plan p/./steven_halim_secret.json` -* *Exporting data* : `export f/FORMAT p/DIRECTORY` + -e.g. `export f/csv p/./` +* *Exporting data* : `export format/FORMAT path/DIRECTORY_PATH` + +e.g. `export format/json path/.` +* *Importing data* : `import format/FORMAT path/FILE_PATH` + +e.g. `import format/json path/./steven_halim_secret.json` * *Help* : `help` * *Clear* : `clear` diff --git a/docs/diagrams/task/AddTaskCommandActivityDiagram.puml b/docs/diagrams/task/AddTaskCommandActivityDiagram.puml index 385bbdfa28a..5b14136dae4 100644 --- a/docs/diagrams/task/AddTaskCommandActivityDiagram.puml +++ b/docs/diagrams/task/AddTaskCommandActivityDiagram.puml @@ -2,12 +2,12 @@ start -:User executes an AddTask command; +:User executes an AddTaskCommand; if () then ([command is valid]) - :Instantiate an AddTaskCommand with a corresponding predicate; + :Instantiate an AddTaskCommand with a corresponding AddTaskDescriptor; :Execute AddTaskCommand; - :Update UI with filtered task list; + :Update UI with current plan with corresponding task list; else ([else]) :Throw an exception; :Update UI to notify the user; diff --git a/docs/diagrams/task/AddTaskSequenceDiagram.puml b/docs/diagrams/task/AddTaskSequenceDiagram.puml index 79602eae046..852d9401640 100644 --- a/docs/diagrams/task/AddTaskSequenceDiagram.puml +++ b/docs/diagrams/task/AddTaskSequenceDiagram.puml @@ -4,14 +4,17 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR participant ":AlgoBaseParser" as AlgoBaseParser LOGIC_COLOR -participant "u:AddTaskCommand" as AddTaskCommand LOGIC_COLOR +participant "addTaskCommand:AddTaskCommand" as AddTaskCommand LOGIC_COLOR end box box Model MODEL_COLOR_T1 -participant ":Plan" as Plan MODEL_COLOR +participant "<>\n:Plan" as PlanClass MODEL_COLOR +participant "updatedPlan:Plan" as PlanObject MODEL_COLOR participant ":Model" as Model MODEL_COLOR participant ":AlgoBase" as AlgoBase MODEL_COLOR +participant ":PlanList" as PlanList MODEL_COLOR end box + [-> LogicManager : execute(addTask) activate LogicManager @@ -25,25 +28,37 @@ activate AddTaskCommand AddTaskCommand --> AlgoBaseParser deactivate AddTaskCommand -AlgoBaseParser --> LogicManager : u +AlgoBaseParser --> LogicManager : addTaskCommand deactivate AlgoBaseParser LogicManager -> AddTaskCommand : execute() activate AddTaskCommand -AddTaskCommand -> Plan : updateTasks(planToUpdate, tasks) -activate Plan +AddTaskCommand -> PlanClass : updateTasks(originPlan, tasks) +activate PlanClass + +create PlanObject +PlanClass -> PlanObject +activate PlanObject + +PlanObject --> PlanClass +deactivate PlanObject -Plan --> AddTaskCommand -deactivate Plan +PlanClass --> AddTaskCommand : updatedPlan +deactivate PlanClass -AddTaskCommand -> Model : setPlan(planToUpdate, updatedPlan) +AddTaskCommand -> Model : setPlan(originPlan, updatedPlan) activate Model -Model -> AlgoBase : setPlan(planToUpdate, updatedPlan) +Model -> AlgoBase : setPlan(originPlan, updatedPlan) activate AlgoBase -AlgoBase -> AlgoBase : setPlan(planToUpdate, updatedPlan) +AlgoBase -> PlanList : setPlan(originPlan, updatedPlan) +activate PlanList + +PlanList --> AlgoBase +deactivate PlanList + AlgoBase --> Model deactivate AlgoBase diff --git a/docs/images/AddTagSequenceDiagram.png b/docs/images/AddTagSequenceDiagram.png index 102e312d29e..a3ba6e4871a 100644 Binary files a/docs/images/AddTagSequenceDiagram.png and b/docs/images/AddTagSequenceDiagram.png differ diff --git a/docs/images/MockUi.png b/docs/images/MockUi.png new file mode 100644 index 00000000000..332c01528cf Binary files /dev/null and b/docs/images/MockUi.png differ diff --git a/docs/images/TagActivityDiagram.png b/docs/images/TagActivityDiagram.png index a0477e97892..b27aa82018a 100644 Binary files a/docs/images/TagActivityDiagram.png and b/docs/images/TagActivityDiagram.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 332c01528cf..e07c07d0393 100644 Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ diff --git a/docs/images/task/AddTaskCommandActivityDiagram.png b/docs/images/task/AddTaskCommandActivityDiagram.png index f74cd8c11aa..367668d4954 100644 Binary files a/docs/images/task/AddTaskCommandActivityDiagram.png and b/docs/images/task/AddTaskCommandActivityDiagram.png differ diff --git a/docs/images/task/AddTaskSequenceDiagram.png b/docs/images/task/AddTaskSequenceDiagram.png index 61c6466a3d5..bd95c20a65d 100644 Binary files a/docs/images/task/AddTaskSequenceDiagram.png and b/docs/images/task/AddTaskSequenceDiagram.png differ diff --git a/src/main/java/seedu/algobase/commons/util/FileUtil.java b/src/main/java/seedu/algobase/commons/util/FileUtil.java index f745ff07f71..e8d9ac26e94 100644 --- a/src/main/java/seedu/algobase/commons/util/FileUtil.java +++ b/src/main/java/seedu/algobase/commons/util/FileUtil.java @@ -80,4 +80,10 @@ public static void writeToFile(Path file, String content) throws IOException { Files.write(file, content.getBytes(CHARSET)); } + /** + * Enum for supported formats. + */ + public enum Format { + JSON + } } diff --git a/src/main/java/seedu/algobase/logic/Logic.java b/src/main/java/seedu/algobase/logic/Logic.java index eda34937026..d5a2797e584 100644 --- a/src/main/java/seedu/algobase/logic/Logic.java +++ b/src/main/java/seedu/algobase/logic/Logic.java @@ -2,6 +2,8 @@ import java.nio.file.Path; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.StringProperty; import javafx.collections.ObservableList; import seedu.algobase.commons.core.GuiSettings; import seedu.algobase.logic.commands.CommandResult; @@ -54,6 +56,15 @@ public interface Logic { /** Returns an unmodifiable view of the filtered list of tasks */ ObservableList getProcessedTaskList(); + /** Returns the current plan. */ + StringProperty getCurrentPlan(); + + /** Returns the number of solved tasks in current plan. */ + IntegerProperty getCurrentSolvedCount(); + + /** Returns the number of solved tasks in current plan. */ + IntegerProperty getCurrentUnsolvedCount(); + /** * Returns an unmodifiable view of the filtered list of find rules. */ diff --git a/src/main/java/seedu/algobase/logic/LogicManager.java b/src/main/java/seedu/algobase/logic/LogicManager.java index 42488a502d3..ee9c9f3a05b 100644 --- a/src/main/java/seedu/algobase/logic/LogicManager.java +++ b/src/main/java/seedu/algobase/logic/LogicManager.java @@ -4,6 +4,8 @@ import java.nio.file.Path; import java.util.logging.Logger; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.StringProperty; import javafx.collections.ObservableList; import seedu.algobase.commons.core.GuiSettings; import seedu.algobase.commons.core.LogsCenter; @@ -72,6 +74,7 @@ public GuiState getGuiState() { return model.getGuiState(); } + @Override public ObservableList getProcessedProblemList() { return model.getFilteredProblemList(); } @@ -91,6 +94,21 @@ public ObservableList getProcessedTaskList() { return model.getCurrentTaskList(); } + @Override + public StringProperty getCurrentPlan() { + return model.getCurrentPlan(); + } + + @Override + public IntegerProperty getCurrentSolvedCount() { + return model.getCurrentSolvedCount(); + } + + @Override + public IntegerProperty getCurrentUnsolvedCount() { + return model.getCurrentUnsolvedCount(); + } + @Override public ObservableList getProcessedFindRuleList() { return model.getFilteredFindRuleList(); diff --git a/src/main/java/seedu/algobase/logic/commands/AddCommand.java b/src/main/java/seedu/algobase/logic/commands/AddCommand.java index adf5eab912a..e4420f74901 100644 --- a/src/main/java/seedu/algobase/logic/commands/AddCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/AddCommand.java @@ -21,7 +21,7 @@ public class AddCommand extends Command { public static final String COMMAND_WORD = "add"; public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Adds a Problem to the algobase.\n" + + ": Adds a Problem to AlgoBase.\n" + "Parameters:\n" + PREFIX_NAME + "NAME " + PREFIX_AUTHOR + "AUTHOR " @@ -40,8 +40,8 @@ public class AddCommand extends Command { + PREFIX_DIFFICULTY + "3.0 " + PREFIX_SOURCE + "Kattis"; - public static final String MESSAGE_SUCCESS = "New Problem added: %1$s"; - public static final String MESSAGE_DUPLICATE_PROBLEM = "This Problem already exists in the algobase"; + public static final String MESSAGE_SUCCESS = "New Problem [%1$s] added to AlgoBase."; + public static final String MESSAGE_DUPLICATE_PROBLEM = "Problem [%1$s] already exists in AlgoBase."; private final Problem toAdd; @@ -58,11 +58,12 @@ public CommandResult execute(Model model) throws CommandException { requireNonNull(model); if (model.hasProblem(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PROBLEM); + throw new CommandException(String.format(MESSAGE_DUPLICATE_PROBLEM, toAdd.getName())); } + model.addProblem(toAdd); model.addTags(toAdd.getTags()); - return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd.getName())); } @Override diff --git a/src/main/java/seedu/algobase/logic/commands/AddFindRuleCommand.java b/src/main/java/seedu/algobase/logic/commands/AddFindRuleCommand.java index b5bacb81eed..e0056ea4492 100644 --- a/src/main/java/seedu/algobase/logic/commands/AddFindRuleCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/AddFindRuleCommand.java @@ -20,7 +20,7 @@ public class AddFindRuleCommand extends Command { public static final String COMMAND_WORD = "addfindrule"; public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Adds a find rule to the algobase.\n" + + ": Adds a find rule to AlgoBase.\n" + "Parameters:\n" + "RULE_NAME " + "[" + PREFIX_NAME + "NAME] " @@ -33,10 +33,12 @@ public class AddFindRuleCommand extends Command { + COMMAND_WORD + " MediumDifficulty" + " " + PREFIX_DIFFICULTY + "2.5-3.5\n"; - public static final String MESSAGE_SUCCESS = "New find rule added: %1$s"; - public static final String MESSAGE_DUPLICATE_FIND_RULE = "This find rule already exists in AlgoBase."; + public static final String MESSAGE_SUCCESS = + "New [%1$s] find rule added to AlgoBase."; + public static final String MESSAGE_DUPLICATE_FIND_RULE = + "Find rule [%1$s] already exists in AlgoBase."; public static final String MESSAGE_NO_CONSTRAINTS = - "You should provide at least one constraint for a new find rule"; + "You should provide at least one constraint for a new find rule."; private final ProblemSearchRule toAdd; @@ -50,12 +52,11 @@ public CommandResult execute(Model model) throws CommandException { requireNonNull(model); if (model.hasFindRule(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_FIND_RULE); + throw new CommandException(String.format(MESSAGE_DUPLICATE_FIND_RULE, toAdd.getName())); } model.addFindRule(toAdd); - - return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd.getName())); } @Override diff --git a/src/main/java/seedu/algobase/logic/commands/AddPlanCommand.java b/src/main/java/seedu/algobase/logic/commands/AddPlanCommand.java index a12981a3d82..1b5d1f70697 100644 --- a/src/main/java/seedu/algobase/logic/commands/AddPlanCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/AddPlanCommand.java @@ -20,7 +20,7 @@ public class AddPlanCommand extends Command { public static final String COMMAND_WORD = "addplan"; public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Adds a Plan to the algobase.\n" + + ": Adds a Plan to AlgoBase.\n" + "Parameters:\n" + PREFIX_NAME + "NAME " + PREFIX_DESCRIPTION + "DESCRIPTION " @@ -33,8 +33,8 @@ public class AddPlanCommand extends Command { + PREFIX_START_DATE + "2019-01-01" + PREFIX_END_DATE + "3019-12-12"; - public static final String MESSAGE_SUCCESS = "New Plan added: %1$s"; - public static final String MESSAGE_DUPLICATE_PLAN = "A plan of this name already exists in the algobase."; + public static final String MESSAGE_SUCCESS = "New Plan [%1$s] added to AlgoBase."; + public static final String MESSAGE_DUPLICATE_PLAN = "A plan with name [%1$s] already exists in AlgoBase."; private final Plan toAdd; @@ -51,11 +51,11 @@ public CommandResult execute(Model model) throws CommandException { requireNonNull(model); if (model.hasPlan(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PLAN); + throw new CommandException(String.format(MESSAGE_DUPLICATE_PLAN, toAdd.getPlanName())); } model.addPlan(toAdd); - return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd.getPlanName())); } @Override diff --git a/src/main/java/seedu/algobase/logic/commands/AddTagCommand.java b/src/main/java/seedu/algobase/logic/commands/AddTagCommand.java index 5cf1b7f18ba..a61c6bab239 100644 --- a/src/main/java/seedu/algobase/logic/commands/AddTagCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/AddTagCommand.java @@ -14,13 +14,16 @@ public class AddTagCommand extends Command { public static final String COMMAND_WORD = "addtag"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a Tag to the algobase. " - + "Parameters: " + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Adds a Tag to AlgoBase.\n" + + "Parameters:\n" + PREFIX_TAG + "TAG NAME\n" - + "Example: " + COMMAND_WORD + " " + PREFIX_TAG + "Easy"; + + "Example:\n" + + COMMAND_WORD + " " + + PREFIX_TAG + "Easy"; - public static final String MESSAGE_SUCCESS = "New Tag added: %1$s"; - public static final String MESSAGE_DUPLICATE_TAG = "This Tag already exists in the algobase."; + public static final String MESSAGE_SUCCESS = "New Tag [%1$s] added to AlgoBase."; + public static final String MESSAGE_DUPLICATE_TAG = "Tag [%1$s] already exists in AlgoBase."; private final Tag toAdd; @@ -37,11 +40,11 @@ public CommandResult execute(Model model) throws CommandException { requireNonNull(model); if (model.hasTag(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_TAG); + throw new CommandException(String.format(MESSAGE_DUPLICATE_TAG, toAdd.getName())); } model.addTag(toAdd); - return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd.getName())); } @Override diff --git a/src/main/java/seedu/algobase/logic/commands/AddTaskCommand.java b/src/main/java/seedu/algobase/logic/commands/AddTaskCommand.java index 4fa3751472c..24b7e0caff7 100644 --- a/src/main/java/seedu/algobase/logic/commands/AddTaskCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/AddTaskCommand.java @@ -35,10 +35,11 @@ public class AddTaskCommand extends Command { + "Example:\n" + COMMAND_WORD + " " + PREFIX_PLAN + "1 " - + PREFIX_PROBLEM + "10" + + PREFIX_PROBLEM + "10 " + PREFIX_DUE_DATE + "2019-12-12"; - public static final String MESSAGE_SUCCESS = "New Task [%1$s] added to Plan [%2$s]"; + public static final String MESSAGE_SUCCESS = "New Task [%1$s] added to Plan [%2$s]."; + public static final String MESSAGE_DUPLICATE_TASK = "Task [%1$s] already exists in Plan [%2$s]."; private final AddTaskDescriptor addTaskDescriptor; @@ -78,6 +79,11 @@ public CommandResult execute(Model model) throws CommandException { } Set taskSet = new HashSet<>(planToUpdate.getTasks()); + if (taskSet.contains(task)) { + return new CommandResult( + String.format(MESSAGE_DUPLICATE_TASK, task.getProblem().getName(), planToUpdate.getPlanName())); + } + taskSet.add(task); Plan updatedPlan = Plan.updateTasks(planToUpdate, taskSet); model.setPlan(planToUpdate, updatedPlan); diff --git a/src/main/java/seedu/algobase/logic/commands/ApplyCommand.java b/src/main/java/seedu/algobase/logic/commands/ApplyCommand.java index 1d0b183713e..ba5e710e3d7 100644 --- a/src/main/java/seedu/algobase/logic/commands/ApplyCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/ApplyCommand.java @@ -45,7 +45,7 @@ public CommandResult execute(Model model) throws CommandException { ProblemSearchRule findRuleToApply = lastShownList.get(targetIndex.getZeroBased()); Predicate findProblemPredicate = findRuleToApply.getFindProblemPredicate(); model.updateFilteredProblemList(findProblemPredicate); - return new CommandResult(String.format(MESSAGE_SUCCESS, findRuleToApply)); + return new CommandResult(String.format(MESSAGE_SUCCESS, findRuleToApply.getName())); } @Override diff --git a/src/main/java/seedu/algobase/logic/commands/ClearCommand.java b/src/main/java/seedu/algobase/logic/commands/ClearCommand.java index a46bb8a28cb..ae17086d78f 100644 --- a/src/main/java/seedu/algobase/logic/commands/ClearCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/ClearCommand.java @@ -15,7 +15,7 @@ public class ClearCommand extends Command { + "Clears all entries from AlgoBase.\n" + "Example: \n" + COMMAND_WORD; - public static final String MESSAGE_SUCCESS = "AlgoBase has been cleared!"; + public static final String MESSAGE_SUCCESS = "AlgoBase has been cleared."; @Override diff --git a/src/main/java/seedu/algobase/logic/commands/DeleteCommand.java b/src/main/java/seedu/algobase/logic/commands/DeleteCommand.java index ab4c71c6e72..fcc86f746bf 100644 --- a/src/main/java/seedu/algobase/logic/commands/DeleteCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/DeleteCommand.java @@ -19,10 +19,12 @@ public class DeleteCommand extends Command { public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deletes the Problem identified by the index number used in the displayed Problem list.\n" - + "Parameters: INDEX (must be a positive integer)\n" - + "Example: " + COMMAND_WORD + " 1"; + + "Parameters:\n" + + "INDEX (must be a positive integer)\n" + + "Example:\n" + + COMMAND_WORD + " 1"; - public static final String MESSAGE_DELETE_PROBLEM_SUCCESS = "Deleted Problem: %1$s"; + public static final String MESSAGE_DELETE_PROBLEM_SUCCESS = "Problem [%1$s] deleted."; private final Index targetIndex; @@ -41,7 +43,7 @@ public CommandResult execute(Model model) throws CommandException { Problem problemToDelete = lastShownList.get(targetIndex.getZeroBased()); model.deleteProblem(problemToDelete); - return new CommandResult(String.format(MESSAGE_DELETE_PROBLEM_SUCCESS, problemToDelete)); + return new CommandResult(String.format(MESSAGE_DELETE_PROBLEM_SUCCESS, problemToDelete.getName())); } @Override diff --git a/src/main/java/seedu/algobase/logic/commands/DeleteFindRuleCommand.java b/src/main/java/seedu/algobase/logic/commands/DeleteFindRuleCommand.java index ef8aaa8c5a6..fc68b83a9da 100644 --- a/src/main/java/seedu/algobase/logic/commands/DeleteFindRuleCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/DeleteFindRuleCommand.java @@ -23,7 +23,7 @@ public class DeleteFindRuleCommand extends Command { + "INDEX (must be a positive integer)\n" + "Example:\n" + COMMAND_WORD + " 1\n"; - public static final String MESSAGE_SUCCESS = "Deleted Find Rule: %1$s"; + public static final String MESSAGE_SUCCESS = "Find Rule [%1$s] deleted."; private final Index targetIndex; diff --git a/src/main/java/seedu/algobase/logic/commands/DeletePlanCommand.java b/src/main/java/seedu/algobase/logic/commands/DeletePlanCommand.java index 477eaac23b1..02a08141ac9 100644 --- a/src/main/java/seedu/algobase/logic/commands/DeletePlanCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/DeletePlanCommand.java @@ -20,10 +20,12 @@ public class DeletePlanCommand extends Command { public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deletes the Plan identified by the index number used in the displayed Plan list.\n" - + "Parameters: INDEX (must be a positive integer)\n" - + "Example: " + COMMAND_WORD + " 1"; + + "Parameters:\n" + + "INDEX (must be a positive integer)\n" + + "Example:\n" + + COMMAND_WORD + " 1"; - public static final String MESSAGE_DELETE_PLAN_SUCCESS = "Deleted Plan: %1$s"; + public static final String MESSAGE_DELETE_PLAN_SUCCESS = "Plan [%1$s] deleted."; private final Index targetIndex; @@ -42,7 +44,7 @@ public CommandResult execute(Model model) throws CommandException { Plan planToDelete = lastShownList.get(targetIndex.getZeroBased()); model.deletePlan(planToDelete); - return new CommandResult(String.format(MESSAGE_DELETE_PLAN_SUCCESS, planToDelete)); + return new CommandResult(String.format(MESSAGE_DELETE_PLAN_SUCCESS, planToDelete.getPlanName())); } @Override diff --git a/src/main/java/seedu/algobase/logic/commands/DeleteTagCommand.java b/src/main/java/seedu/algobase/logic/commands/DeleteTagCommand.java index 4a0bcf0cff5..5f59867d8db 100644 --- a/src/main/java/seedu/algobase/logic/commands/DeleteTagCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/DeleteTagCommand.java @@ -19,10 +19,12 @@ public class DeleteTagCommand extends Command { public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deletes the Tag identified by the index number used in the displayed Tag list.\n" - + "Parameters: INDEX (must be a positive integer)\n" - + "Example: " + COMMAND_WORD + " 1"; + + "Parameters:\n" + + "INDEX (must be a positive integer)\n" + + "Example:\n" + + COMMAND_WORD + " 1"; - public static final String MESSAGE_DELETE_TAG_SUCCESS = "Deleted Tag: %1$s"; + public static final String MESSAGE_DELETE_TAG_SUCCESS = "Tag [%1$s] deleted."; private final Index targetIndex; @@ -42,7 +44,7 @@ public CommandResult execute(Model model) throws CommandException { Tag tagToDelete = lastShownList.get(targetIndex.getZeroBased()); model.deleteTag(tagToDelete); model.deleteTags(tagToDelete); - return new CommandResult(String.format(MESSAGE_DELETE_TAG_SUCCESS, tagToDelete)); + return new CommandResult(String.format(MESSAGE_DELETE_TAG_SUCCESS, tagToDelete.getName())); } @Override diff --git a/src/main/java/seedu/algobase/logic/commands/DeleteTaskCommand.java b/src/main/java/seedu/algobase/logic/commands/DeleteTaskCommand.java index fc84d03e61e..2d95131b840 100644 --- a/src/main/java/seedu/algobase/logic/commands/DeleteTaskCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/DeleteTaskCommand.java @@ -34,7 +34,7 @@ public class DeleteTaskCommand extends Command { + PREFIX_PLAN + "1 " + PREFIX_TASK + "10"; - public static final String MESSAGE_DELETE_TASK_SUCCESS = "Task [%1$s] deleted from Plan [%2$s]"; + public static final String MESSAGE_DELETE_TASK_SUCCESS = "Task [%1$s] deleted from Plan [%2$s]."; private final DeleteTaskDescriptor deleteTaskDescriptor; @@ -82,9 +82,9 @@ public static class DeleteTaskDescriptor { private Index planIndex; private Index taskIndex; - public DeleteTaskDescriptor(Index planIndex, Index problemIndex) { + public DeleteTaskDescriptor(Index planIndex, Index taskIndex) { this.planIndex = planIndex; - this.taskIndex = problemIndex; + this.taskIndex = taskIndex; } @Override diff --git a/src/main/java/seedu/algobase/logic/commands/DoneTaskCommand.java b/src/main/java/seedu/algobase/logic/commands/DoneTaskCommand.java index 87380fe32de..9c525550cb0 100644 --- a/src/main/java/seedu/algobase/logic/commands/DoneTaskCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/DoneTaskCommand.java @@ -34,7 +34,7 @@ public class DoneTaskCommand extends Command { + PREFIX_PLAN + "1 " + PREFIX_TASK + "10"; - public static final String MESSAGE_DONE_TASK_SUCCESS = "Task [%1$s] marked as done in Plan [%2$s]"; + public static final String MESSAGE_DONE_TASK_SUCCESS = "Task [%1$s] marked as done in Plan [%2$s]."; private final DoneTaskDescriptor doneTaskDescriptor; @@ -83,9 +83,9 @@ public static class DoneTaskDescriptor { private Index planIndex; private Index taskIndex; - public DoneTaskDescriptor(Index planIndex, Index problemIndex) { + public DoneTaskDescriptor(Index planIndex, Index taskIndex) { this.planIndex = planIndex; - this.taskIndex = problemIndex; + this.taskIndex = taskIndex; } @Override diff --git a/src/main/java/seedu/algobase/logic/commands/EditCommand.java b/src/main/java/seedu/algobase/logic/commands/EditCommand.java index a555417f7be..eb2a50f70d1 100644 --- a/src/main/java/seedu/algobase/logic/commands/EditCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/EditCommand.java @@ -57,9 +57,9 @@ public class EditCommand extends Command { + PREFIX_AUTHOR + "Tung Kam Chuen " + PREFIX_WEBLINK + "https://open.kattis.com/problems/sequences"; - public static final String MESSAGE_EDIT_PROBLEM_SUCCESS = "Edited Problem: %1$s"; + public static final String MESSAGE_EDIT_PROBLEM_SUCCESS = "Problem [%1$s] edited."; public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; - public static final String MESSAGE_DUPLICATE_PROBLEM = "This Problem already exists in the algobase."; + public static final String MESSAGE_DUPLICATE_PROBLEM = "Problem [%1$s] already exists in AlgoBase."; private final Index index; private final EditProblemDescriptor editProblemDescriptor; @@ -89,14 +89,14 @@ public CommandResult execute(Model model) throws CommandException { Problem editedProblem = createEditedProblem(problemToEdit, editProblemDescriptor); if (!problemToEdit.isSameProblem(editedProblem) && model.hasProblem(editedProblem)) { - throw new CommandException(MESSAGE_DUPLICATE_PROBLEM); + throw new CommandException(String.format(MESSAGE_DUPLICATE_PROBLEM, problemToEdit.getName())); } model.setProblem(problemToEdit, editedProblem); model.addTags(editProblemDescriptor.tags); model.updateFilteredProblemList(PREDICATE_SHOW_ALL_PROBLEMS); - return new CommandResult(String.format(MESSAGE_EDIT_PROBLEM_SUCCESS, editedProblem)); + return new CommandResult(String.format(MESSAGE_EDIT_PROBLEM_SUCCESS, editedProblem.getName())); } /** diff --git a/src/main/java/seedu/algobase/logic/commands/EditPlanCommand.java b/src/main/java/seedu/algobase/logic/commands/EditPlanCommand.java index 6708488477b..8a0b811d100 100644 --- a/src/main/java/seedu/algobase/logic/commands/EditPlanCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/EditPlanCommand.java @@ -49,9 +49,9 @@ public class EditPlanCommand extends Command { + PREFIX_START_DATE + "2019/01/01" + PREFIX_END_DATE + "3019/12/12"; - public static final String MESSAGE_EDIT_PLAN_SUCCESS = "Edited Plan: %1$s"; + public static final String MESSAGE_EDIT_PLAN_SUCCESS = "Plan [%1$s] edited."; public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; - public static final String MESSAGE_DUPLICATE_PLAN = "A plan of this name already exists in the algobase."; + public static final String MESSAGE_DUPLICATE_PLAN = "A plan of name [%1$s] already exists in AlgoBase."; private final Index index; private final EditPlanDescriptor editPlanDescriptor; @@ -81,12 +81,12 @@ public CommandResult execute(Model model) throws CommandException { Plan editedPlan = createEditedPlan(planToEdit, editPlanDescriptor); if (!planToEdit.isSamePlan(editedPlan) && model.hasPlan(editedPlan)) { - throw new CommandException(MESSAGE_DUPLICATE_PLAN); + throw new CommandException(String.format(MESSAGE_DUPLICATE_PLAN, editedPlan.getPlanName())); } model.setPlan(planToEdit, editedPlan); model.updateFilteredPlanList(PREDICATE_SHOW_ALL_PLANS); - return new CommandResult(String.format(MESSAGE_EDIT_PLAN_SUCCESS, editedPlan)); + return new CommandResult(String.format(MESSAGE_EDIT_PLAN_SUCCESS, editedPlan.getPlanName())); } /** diff --git a/src/main/java/seedu/algobase/logic/commands/EditTagCommand.java b/src/main/java/seedu/algobase/logic/commands/EditTagCommand.java index 754b2dd2455..5d18cf33de7 100644 --- a/src/main/java/seedu/algobase/logic/commands/EditTagCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/EditTagCommand.java @@ -26,8 +26,8 @@ public class EditTagCommand extends Command { + "Parameters: INDEX (must be a positive integer) " + "[" + PREFIX_TAG + "TAG] \n" + "Example: " + COMMAND_WORD + " 1 " + PREFIX_TAG + "Easy"; - public static final String MESSAGE_EDIT_TAG_SUCCESS = "Edited Tag: %1$s"; - public static final String MESSAGE_DUPLICATE_TAG = "This Tag already exists in the algobase."; + public static final String MESSAGE_EDIT_TAG_SUCCESS = "Tag [%1$s] edited."; + public static final String MESSAGE_DUPLICATE_TAG = "Tag [%1$s] already exists in AlgoBase."; private final Index index; private final String name; @@ -57,14 +57,14 @@ public CommandResult execute(Model model) throws CommandException { Tag editedTag = createEditedTag(tagToEdit, name); if (!tagToEdit.isSameTag(editedTag) && model.hasTag(editedTag)) { - throw new CommandException(MESSAGE_DUPLICATE_TAG); + throw new CommandException(String.format(MESSAGE_DUPLICATE_TAG, tagToEdit.getName())); } model.setTag(tagToEdit, editedTag); model.setTags(tagToEdit, editedTag); model.updateFilteredTagList(PREDICATE_SHOW_ALL_TAGS); - return new CommandResult(String.format(MESSAGE_EDIT_TAG_SUCCESS, editedTag)); + return new CommandResult(String.format(MESSAGE_EDIT_TAG_SUCCESS, editedTag.getName())); } /** diff --git a/src/main/java/seedu/algobase/logic/commands/EditTaskCommand.java b/src/main/java/seedu/algobase/logic/commands/EditTaskCommand.java new file mode 100644 index 00000000000..f4f268f9683 --- /dev/null +++ b/src/main/java/seedu/algobase/logic/commands/EditTaskCommand.java @@ -0,0 +1,109 @@ +package seedu.algobase.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_DUE_DATE; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_PLAN; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_TASK; +import static seedu.algobase.model.Model.PREDICATE_SHOW_ALL_PLANS; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import seedu.algobase.commons.core.Messages; +import seedu.algobase.commons.core.index.Index; +import seedu.algobase.logic.commands.exceptions.CommandException; +import seedu.algobase.model.Model; +import seedu.algobase.model.plan.Plan; +import seedu.algobase.model.task.Task; + +/** + * Marks a Task identified using its index in the Plan and the Plan index as done. + */ +public class EditTaskCommand extends Command { + + public static final String COMMAND_WORD = "edittask"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Edits the due date of the Task identified by the index in the plan.\n" + + "Parameters:\n" + + PREFIX_PLAN + "PLAN_INDEX " + + PREFIX_TASK + "TASK_INDEX " + + PREFIX_DUE_DATE + "DUE_DATE\n" + + "Example:\n" + + COMMAND_WORD + " " + + PREFIX_PLAN + "1 " + + PREFIX_TASK + "10 " + + PREFIX_DUE_DATE + "2019-12-12"; + + public static final String MESSAGE_EDIT_TASK_SUCCESS = "Task [%1$s] set to be due at [%2$s] in Plan [%3$s]."; + + private final EditTaskDescriptor editTaskDescriptor; + + /** + * Creates a EditTaskCommand to change due date of a {@code Task} in the specified {@code Plan} + * + * @param editTaskDescriptor details of the plan and problem involved + */ + public EditTaskCommand(EditTaskDescriptor editTaskDescriptor) { + this.editTaskDescriptor = editTaskDescriptor; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownPlanList = model.getFilteredPlanList(); + + if (editTaskDescriptor.planIndex.getZeroBased() >= lastShownPlanList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + Plan planToUpdate = lastShownPlanList.get(editTaskDescriptor.planIndex.getZeroBased()); + List taskList = new ArrayList<>(planToUpdate.getTasks()); + Task taskToUpdate = taskList.get(editTaskDescriptor.taskIndex.getZeroBased()); + taskList.remove(editTaskDescriptor.taskIndex.getZeroBased()); + Set taskSet = new HashSet<>(taskList); + taskSet.add(Task.updateDueDate(taskToUpdate, editTaskDescriptor.targetDate)); + Plan updatedPlan = Plan.updateTasks(planToUpdate, taskSet); + model.setPlan(planToUpdate, updatedPlan); + model.updateFilteredPlanList(PREDICATE_SHOW_ALL_PLANS); + return new CommandResult( + String.format(MESSAGE_EDIT_TASK_SUCCESS, + taskToUpdate.getProblem().getName(), + editTaskDescriptor.targetDate, + updatedPlan.getPlanName())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EditTaskCommand // instanceof handles nulls + && editTaskDescriptor.equals(((EditTaskCommand) other).editTaskDescriptor)); // state check + } + + /** + * Stores the details of the plan and problem involved. + */ + public static class EditTaskDescriptor { + private Index planIndex; + private Index taskIndex; + private LocalDate targetDate; + + public EditTaskDescriptor(Index planIndex, Index problemIndex, LocalDate targetDate) { + this.planIndex = planIndex; + this.taskIndex = problemIndex; + this.targetDate = targetDate; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EditTaskDescriptor // instanceof handles nulls + && planIndex.equals(((EditTaskDescriptor) other).planIndex) + && taskIndex.equals(((EditTaskDescriptor) other).taskIndex) + && targetDate.equals(((EditTaskDescriptor) other).targetDate)); + } + } +} diff --git a/src/main/java/seedu/algobase/logic/commands/ExportCommand.java b/src/main/java/seedu/algobase/logic/commands/ExportCommand.java new file mode 100644 index 00000000000..7c3fce8d312 --- /dev/null +++ b/src/main/java/seedu/algobase/logic/commands/ExportCommand.java @@ -0,0 +1,66 @@ +package seedu.algobase.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_FORMAT; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_PATH; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import seedu.algobase.commons.util.FileUtil; +import seedu.algobase.commons.util.FileUtil.Format; +import seedu.algobase.commons.util.JsonUtil; +import seedu.algobase.logic.commands.exceptions.CommandException; +import seedu.algobase.model.Model; +import seedu.algobase.storage.JsonSerializableAlgoBase; + +/** + * Exports AlgoBase to specified location. + */ +public class ExportCommand extends Command { + + public static final String COMMAND_WORD = "export"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Exports AlgoBase to the specified location.\n" + + "Parameters:\n" + + PREFIX_FORMAT + "FORMAT " + + PREFIX_PATH + "PATH\n" + + "Example:\n" + + COMMAND_WORD + " " + + PREFIX_FORMAT + "JSON " + + PREFIX_PATH + "./"; + + public static final String MESSAGE_EXPORT_SUCCESS = "AlgoBase data exported to [%1$s]."; + + private final Format format; + private final String path; + + public ExportCommand(Format format, String path) { + this.format = format; + this.path = path; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + Path filePath = Paths.get(path, "algobase.json"); + try { + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonSerializableAlgoBase(model.getAlgoBase()), filePath); + } catch (IOException e) { + return new CommandResult("Filepath invalid."); + } + return new CommandResult(String.format(MESSAGE_EXPORT_SUCCESS, filePath)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ExportCommand // instanceof handles nulls + && format.equals(((ExportCommand) other).format) + && path.equals(((ExportCommand) other).path)); // state check + } + +} diff --git a/src/main/java/seedu/algobase/logic/commands/FindCommand.java b/src/main/java/seedu/algobase/logic/commands/FindCommand.java index 841c68eb97d..52f8f3bb353 100644 --- a/src/main/java/seedu/algobase/logic/commands/FindCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/FindCommand.java @@ -37,11 +37,6 @@ public class FindCommand extends Command { + PREFIX_AUTHOR + " Tung Kam Chuen"; public static final String MESSAGE_NO_CONSTRAINTS = "At least one search constraint should be provided."; - /** - * {@code ALWAYS_TRUE_PROBLEM_PREDICATE} is a non-restrictive predicate that always returns true, which - * is used as a placeholder for predicates not provided by the user. - */ - private static final Predicate ALWAYS_TRUE_PROBLEM_PREDICATE = problem -> true; private final Predicate predicate; private final FindProblemDescriptor descriptor; diff --git a/src/main/java/seedu/algobase/logic/commands/HelpCommand.java b/src/main/java/seedu/algobase/logic/commands/HelpCommand.java index 714a3e414da..dce1a004694 100644 --- a/src/main/java/seedu/algobase/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/HelpCommand.java @@ -15,10 +15,11 @@ 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" - + "Parameter:\n" - + "COMMAND_NAME (can be empty if you want a list of possible commands)\n" - + "Example: " + COMMAND_WORD + " find"; + + ": Shows program usage instructions.\n" + + "Parameter:\n" + + "COMMAND_NAME (can be empty if you want a list of possible commands)\n" + + "Example:\n" + + COMMAND_WORD + " find"; private final boolean isListAllCommands; private final Class commandClass; diff --git a/src/main/java/seedu/algobase/logic/commands/ImportCommand.java b/src/main/java/seedu/algobase/logic/commands/ImportCommand.java new file mode 100644 index 00000000000..fdb2e840fa6 --- /dev/null +++ b/src/main/java/seedu/algobase/logic/commands/ImportCommand.java @@ -0,0 +1,71 @@ +package seedu.algobase.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_FORMAT; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_PATH; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.NoSuchElementException; +import java.util.Optional; + +import seedu.algobase.commons.exceptions.DataConversionException; +import seedu.algobase.commons.exceptions.IllegalValueException; +import seedu.algobase.commons.util.FileUtil.Format; +import seedu.algobase.commons.util.JsonUtil; +import seedu.algobase.logic.commands.exceptions.CommandException; +import seedu.algobase.model.Model; +import seedu.algobase.storage.JsonSerializableAlgoBase; + +/** + * Imports AlgoBase from specified location. + */ +public class ImportCommand extends Command { + + public static final String COMMAND_WORD = "import"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Imports AlgoBase from a specified location.\n" + + "Parameters:\n" + + PREFIX_FORMAT + "FORMAT " + + PREFIX_PATH + "PATH\n" + + "Example:\n" + + COMMAND_WORD + " " + + PREFIX_FORMAT + "JSON " + + PREFIX_PATH + "./"; + + public static final String MESSAGE_EXPORT_SUCCESS = "AlgoBase data successfully imported from [%1$s]."; + + private final Format format; + private final String path; + + public ImportCommand(Format format, String path) { + this.format = format; + this.path = path; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + Path filePath = Paths.get(path); + + try { + Optional jsonAlgoBase = JsonUtil.readJsonFile( + filePath, JsonSerializableAlgoBase.class); + model.setAlgoBase(jsonAlgoBase.get().toModelType()); + } catch (DataConversionException | IllegalValueException | NoSuchElementException e) { + return new CommandResult("Invalid data."); + } + + return new CommandResult(String.format(MESSAGE_EXPORT_SUCCESS, filePath)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ImportCommand // instanceof handles nulls + && format.equals(((ImportCommand) other).format) + && path.equals(((ImportCommand) other).path)); // state check + } + +} diff --git a/src/main/java/seedu/algobase/logic/commands/ListCommand.java b/src/main/java/seedu/algobase/logic/commands/ListCommand.java index d7fe9db5c99..771b91d96f0 100644 --- a/src/main/java/seedu/algobase/logic/commands/ListCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/ListCommand.java @@ -12,14 +12,11 @@ public class ListCommand extends Command { public static final String COMMAND_WORD = "list"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": lists all problems in AlgoBase\n" + "Example: " + COMMAND_WORD; - - public static final String MESSAGE_SUCCESS = "Listed all problems"; - + public static final String MESSAGE_SUCCESS = "All problems listed."; @Override public CommandResult execute(Model model) { diff --git a/src/main/java/seedu/algobase/logic/commands/ListPlanCommand.java b/src/main/java/seedu/algobase/logic/commands/ListPlanCommand.java index 196a37448d8..39b80ea126b 100644 --- a/src/main/java/seedu/algobase/logic/commands/ListPlanCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/ListPlanCommand.java @@ -13,14 +13,11 @@ public class ListPlanCommand extends Command { public static final String COMMAND_WORD = "listplan"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Displays a list of all existing plans.\n" + "Example:\n" + COMMAND_WORD; - - public static final String MESSAGE_SUCCESS = "Listed all plans"; - + public static final String MESSAGE_SUCCESS = "All plans listed."; @Override public CommandResult execute(Model model) { diff --git a/src/main/java/seedu/algobase/logic/commands/ListTagCommand.java b/src/main/java/seedu/algobase/logic/commands/ListTagCommand.java index d68c4b80997..eb218c4682e 100644 --- a/src/main/java/seedu/algobase/logic/commands/ListTagCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/ListTagCommand.java @@ -12,14 +12,11 @@ public class ListTagCommand extends Command { public static final String COMMAND_WORD = "listtag"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Displays a list of all existing tags.\n" + "Example:\n" + COMMAND_WORD; - - public static final String MESSAGE_SUCCESS = "Listed all tags"; - + public static final String MESSAGE_SUCCESS = "All tags listed."; @Override public CommandResult execute(Model model) { diff --git a/src/main/java/seedu/algobase/logic/commands/MoveTaskCommand.java b/src/main/java/seedu/algobase/logic/commands/MoveTaskCommand.java new file mode 100644 index 00000000000..a774a9e1d8c --- /dev/null +++ b/src/main/java/seedu/algobase/logic/commands/MoveTaskCommand.java @@ -0,0 +1,123 @@ +package seedu.algobase.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_PLAN_FROM; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_PLAN_TO; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_TASK; +import static seedu.algobase.model.Model.PREDICATE_SHOW_ALL_PLANS; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import seedu.algobase.commons.core.Messages; +import seedu.algobase.commons.core.index.Index; +import seedu.algobase.logic.commands.exceptions.CommandException; +import seedu.algobase.model.Model; +import seedu.algobase.model.plan.Plan; +import seedu.algobase.model.task.Task; + +/** + * Moves a Task from one Plan to another. + */ +public class MoveTaskCommand extends Command { + + public static final String COMMAND_WORD = "movetask"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Moves the Task identified by the index from one plan to another.\n" + + "Parameters:\n" + + PREFIX_TASK + "TASK_INDEX " + + PREFIX_PLAN_FROM + "PLAN_INDEX " + + PREFIX_PLAN_TO + "PLAN_INDEX\n" + + "Example:\n" + + COMMAND_WORD + " " + + PREFIX_TASK + "10 " + + PREFIX_PLAN_FROM + "1 " + + PREFIX_PLAN_TO + "2"; + + public static final String MESSAGE_MOVE_TASK_SUCCESS = "Task [%1$s] moved from Plan [%2$s] to Plan [%3$s]."; + public static final String MESSAGE_DUPLICATE_TASK = "Task [%1$s] already exists in Plan [%2$s]."; + + private final MoveTaskDescriptor moveTaskDescriptor; + + /** + * Creates a MoveTaskCommand to move a {@code Task} from the specified {@code Plan} to another + * + * @param moveTaskDescriptor details of the plan and problem involved + */ + public MoveTaskCommand(MoveTaskDescriptor moveTaskDescriptor) { + this.moveTaskDescriptor = moveTaskDescriptor; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownPlanList = model.getFilteredPlanList(); + + if (moveTaskDescriptor.planFromIndex.getZeroBased() >= lastShownPlanList.size() + || moveTaskDescriptor.planToIndex.getZeroBased() >= lastShownPlanList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + Plan planFrom = lastShownPlanList.get(moveTaskDescriptor.planFromIndex.getZeroBased()); + List taskListFrom = new ArrayList<>(planFrom.getTasks()); + Task taskToMove = taskListFrom.get(moveTaskDescriptor.taskIndex.getZeroBased()); + taskListFrom.remove(moveTaskDescriptor.taskIndex.getZeroBased()); + Set taskSetFrom = new HashSet<>(taskListFrom); + Plan updatedPlanFrom = Plan.updateTasks(planFrom, taskSetFrom); + + Plan planTo = lastShownPlanList.get(moveTaskDescriptor.planToIndex.getZeroBased()); + List taskListTo = new ArrayList<>(planTo.getTasks()); + Set taskSetTo = new HashSet<>(taskListTo); + if (taskSetTo.contains(taskToMove)) { + return new CommandResult( + String.format(MESSAGE_DUPLICATE_TASK, taskToMove.getProblem().getName(), planTo.getPlanName())); + } + taskSetTo.add(taskToMove); + Plan updatedPlanTo = Plan.updateTasks(planTo, taskSetTo); + + model.setPlan(planFrom, updatedPlanFrom); + model.setPlan(planTo, updatedPlanTo); + model.updateFilteredPlanList(PREDICATE_SHOW_ALL_PLANS); + return new CommandResult( + String.format(MESSAGE_MOVE_TASK_SUCCESS, + taskToMove.getProblem().getName(), + updatedPlanFrom.getPlanName(), + updatedPlanTo.getPlanName() + ) + ); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof MoveTaskCommand // instanceof handles nulls + && moveTaskDescriptor.equals(((MoveTaskCommand) other).moveTaskDescriptor)); // state check + } + + /** + * Stores the details of the plan and problem involved. + */ + public static class MoveTaskDescriptor { + private Index taskIndex; + private Index planFromIndex; + private Index planToIndex; + + public MoveTaskDescriptor(Index taskIndex, Index planFromIndex, Index planToIndex) { + this.taskIndex = taskIndex; + this.planFromIndex = planFromIndex; + this.planToIndex = planToIndex; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof MoveTaskDescriptor // instanceof handles nulls + && taskIndex.equals(((MoveTaskDescriptor) other).taskIndex) + && planFromIndex.equals(((MoveTaskDescriptor) other).planFromIndex) + && planToIndex.equals(((MoveTaskDescriptor) other).planToIndex)); + } + } +} diff --git a/src/main/java/seedu/algobase/logic/commands/OpenTabCommand.java b/src/main/java/seedu/algobase/logic/commands/OpenTabCommand.java index 4724a9b7a8b..c240e9db4fc 100644 --- a/src/main/java/seedu/algobase/logic/commands/OpenTabCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/OpenTabCommand.java @@ -20,8 +20,8 @@ public class OpenTabCommand extends Command { public static final String COMMAND_WORD = "opentab"; - public static final String MESSAGE_SUCCESS = "opened tab %1$s!"; - public static final String MESSAGE_SWITCH_SUCCESS = "switched to tab %1$s!"; + public static final String MESSAGE_SUCCESS = "Tab [%1$s] opened."; + public static final String MESSAGE_SWITCH_SUCCESS = "Switched to tab [%1$s]."; public static final String MESSAGE_USAGE = COMMAND_WORD + ": opens a new Details Tab in the GUI\n" + "Parameters:\n" @@ -32,8 +32,8 @@ public class OpenTabCommand extends Command { + PREFIX_MODEL_TYPE + "problem " + PREFIX_MODEL_INDEX + "1\n"; - public static final String MESSAGE_INVALID_MODEL = "There is no such model!"; - public static final String MESSAGE_INVALID_INDEX = "There is no tab at index %1$s!"; + public static final String MESSAGE_INVALID_MODEL = "Model [%1$s] does not exist."; + public static final String MESSAGE_INVALID_INDEX = "Tab at index [%1$s] does not exist."; private Index modelIndex = Index.fromZeroBased(0); private ModelType modelType; @@ -99,7 +99,7 @@ public CommandResult execute(Model model) throws CommandException { } catch (IndexOutOfBoundsException exception) { throw new CommandException(String.format(MESSAGE_INVALID_INDEX, modelIndex.getOneBased())); } catch (IllegalArgumentException exception) { - throw new IllegalArgumentException(String.format(MESSAGE_INVALID_MODEL)); + throw new IllegalArgumentException(String.format(MESSAGE_INVALID_MODEL, modelType.getTabName())); } } } diff --git a/src/main/java/seedu/algobase/logic/commands/SortCommand.java b/src/main/java/seedu/algobase/logic/commands/SortCommand.java index a48f792cb30..5472bc2ed42 100644 --- a/src/main/java/seedu/algobase/logic/commands/SortCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/SortCommand.java @@ -50,7 +50,7 @@ public enum SortingOrder { + COMMAND_WORD + " " + PREFIX_SORTING_METHOD + "name " + PREFIX_SORTING_ORDER + "ascend"; - public static final String MESSAGE_SUCCESS = "AlgoBase has been sorted!"; + public static final String MESSAGE_SUCCESS = "AlgoBase has been sorted."; public static final Comparator PROBLEM_NAME_COMPARATOR = new Comparator() { @Override diff --git a/src/main/java/seedu/algobase/logic/commands/SwitchTabCommand.java b/src/main/java/seedu/algobase/logic/commands/SwitchTabCommand.java index 6345e894d89..c2b86d707bf 100644 --- a/src/main/java/seedu/algobase/logic/commands/SwitchTabCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/SwitchTabCommand.java @@ -15,7 +15,7 @@ public class SwitchTabCommand extends Command { public static final String COMMAND_WORD = "switchtab"; - public static final String MESSAGE_SUCCESS = "Switched to %1$s tab %2$s!"; + public static final String MESSAGE_SUCCESS = "Switched to [%1$s] tab [%2$s]."; public static final String MESSAGE_USAGE = COMMAND_WORD + ": Switches between Tabs in the GUI\n" + "Parameters:\n" @@ -26,8 +26,8 @@ public class SwitchTabCommand extends Command { + PREFIX_TAB_TYPE + "display " + PREFIX_TAB_INDEX + "1\n"; - public static final String MESSAGE_INVALID_TAB_TYPE = "There is no such tab type!"; - public static final String MESSAGE_INVALID_TAB_INDEX = "There is no tab at index %1$s!"; + public static final String MESSAGE_INVALID_TAB_TYPE = "Tab type [%1$s] does not exist."; + public static final String MESSAGE_INVALID_TAB_INDEX = "Tab at index [%1$s] does not exist."; private Index index = Index.fromZeroBased(0); private TabType tabType; @@ -53,7 +53,7 @@ public CommandResult execute(Model model) throws CommandException { String.format(MESSAGE_SUCCESS, tabType.DETAILS.getName(), index.getOneBased()) ); default: - throw new CommandException(MESSAGE_INVALID_TAB_TYPE); + throw new CommandException(String.format(MESSAGE_INVALID_TAB_TYPE, tabType.getName())); } } catch (IndexOutOfBoundsException exception) { throw new CommandException(String.format(MESSAGE_INVALID_TAB_INDEX, index.getOneBased())); diff --git a/src/main/java/seedu/algobase/logic/commands/UndoneTaskCommand.java b/src/main/java/seedu/algobase/logic/commands/UndoneTaskCommand.java index e6e9f7c22dd..64e3fd32a10 100644 --- a/src/main/java/seedu/algobase/logic/commands/UndoneTaskCommand.java +++ b/src/main/java/seedu/algobase/logic/commands/UndoneTaskCommand.java @@ -34,7 +34,7 @@ public class UndoneTaskCommand extends Command { + PREFIX_PLAN + "1 " + PREFIX_TASK + "10"; - public static final String MESSAGE_UNDONE_TASK_SUCCESS = "Task [%1$s] marked as undone in Plan [%2$s]"; + public static final String MESSAGE_UNDONE_TASK_SUCCESS = "Task [%1$s] marked as undone in Plan [%2$s]."; private final UndoneTaskDescriptor undoneTaskDescriptor; @@ -83,9 +83,9 @@ public static class UndoneTaskDescriptor { private Index planIndex; private Index taskIndex; - public UndoneTaskDescriptor(Index planIndex, Index problemIndex) { + public UndoneTaskDescriptor(Index planIndex, Index taskIndex) { this.planIndex = planIndex; - this.taskIndex = problemIndex; + this.taskIndex = taskIndex; } @Override diff --git a/src/main/java/seedu/algobase/logic/parser/AddTaskCommandParser.java b/src/main/java/seedu/algobase/logic/parser/AddTaskCommandParser.java index 4910a2be268..762d3f8a0a7 100644 --- a/src/main/java/seedu/algobase/logic/parser/AddTaskCommandParser.java +++ b/src/main/java/seedu/algobase/logic/parser/AddTaskCommandParser.java @@ -6,7 +6,6 @@ import static seedu.algobase.logic.parser.CliSyntax.PREFIX_PROBLEM; import java.time.LocalDate; -import java.util.stream.Stream; import seedu.algobase.commons.core.index.Index; import seedu.algobase.logic.commands.AddTaskCommand; @@ -26,8 +25,8 @@ public AddTaskCommand parse(String args) throws ParseException { ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_PLAN, PREFIX_PROBLEM, PREFIX_DUE_DATE); - if (!arePrefixesPresent(argMultimap, PREFIX_PLAN) - || !arePrefixesPresent(argMultimap, PREFIX_PROBLEM) + if (!ParserUtil.arePrefixesPresent(argMultimap, PREFIX_PLAN) + || !ParserUtil.arePrefixesPresent(argMultimap, PREFIX_PROBLEM) || !argMultimap.getPreamble().isEmpty()) { throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTaskCommand.MESSAGE_USAGE)); } @@ -47,7 +46,7 @@ public AddTaskCommand parse(String args) throws ParseException { } LocalDate targetDate; - if (arePrefixesPresent(argMultimap, PREFIX_DUE_DATE)) { + if (ParserUtil.arePrefixesPresent(argMultimap, PREFIX_DUE_DATE)) { targetDate = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DUE_DATE).get()); } else { targetDate = null; @@ -59,12 +58,4 @@ public AddTaskCommand parse(String args) throws ParseException { return new AddTaskCommand(addTaskDescriptor); } - /** - * 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/algobase/logic/parser/AlgoBaseParser.java b/src/main/java/seedu/algobase/logic/parser/AlgoBaseParser.java index 645e9fcdae2..565c243dbe4 100644 --- a/src/main/java/seedu/algobase/logic/parser/AlgoBaseParser.java +++ b/src/main/java/seedu/algobase/logic/parser/AlgoBaseParser.java @@ -24,13 +24,17 @@ import seedu.algobase.logic.commands.EditCommand; import seedu.algobase.logic.commands.EditPlanCommand; import seedu.algobase.logic.commands.EditTagCommand; +import seedu.algobase.logic.commands.EditTaskCommand; import seedu.algobase.logic.commands.ExitCommand; +import seedu.algobase.logic.commands.ExportCommand; import seedu.algobase.logic.commands.FindCommand; import seedu.algobase.logic.commands.FindPlanCommand; import seedu.algobase.logic.commands.HelpCommand; +import seedu.algobase.logic.commands.ImportCommand; import seedu.algobase.logic.commands.ListCommand; import seedu.algobase.logic.commands.ListPlanCommand; import seedu.algobase.logic.commands.ListTagCommand; +import seedu.algobase.logic.commands.MoveTaskCommand; import seedu.algobase.logic.commands.OpenTabCommand; import seedu.algobase.logic.commands.RewindCommand; import seedu.algobase.logic.commands.SortCommand; @@ -110,9 +114,28 @@ public Command parseCommand(String userInput) throws ParseException { case DoneTaskCommand.COMMAND_WORD: return new DoneTaskCommandParser().parse(arguments); + case EditTaskCommand.COMMAND_WORD: + return new EditTaskCommandParser().parse(arguments); + + case MoveTaskCommand.COMMAND_WORD: + return new MoveTaskCommandParser().parse(arguments); + case UndoneTaskCommand.COMMAND_WORD: return new UndoneTaskCommandParser().parse(arguments); + // Tag + case AddTagCommand.COMMAND_WORD: + return new AddTagCommandParser().parse(arguments); + + case DeleteTagCommand.COMMAND_WORD: + return new DeleteTagCommandParser().parse(arguments); + + case ListTagCommand.COMMAND_WORD: + return new ListTagCommand(); + + case EditTagCommand.COMMAND_WORD: + return new EditTagCommandParser().parse(arguments); + // Find Rule case AddFindRuleCommand.COMMAND_WORD: return new AddFindRuleCommandParser().parse(arguments); @@ -123,23 +146,18 @@ public Command parseCommand(String userInput) throws ParseException { case DeleteFindRuleCommand.COMMAND_WORD: return new DeleteFindRuleParser().parse(arguments); - //Rewind + // Rewind case RewindCommand.COMMAND_WORD: return new RewindCommandParser().parse(arguments); - case AddTagCommand.COMMAND_WORD: - return new AddTagCommandParser().parse(arguments); - - case DeleteTagCommand.COMMAND_WORD: - return new DeleteTagCommandParser().parse(arguments); - - case ListTagCommand.COMMAND_WORD: - return new ListTagCommand(); + // Storage + case ExportCommand.COMMAND_WORD: + return new ExportCommandParser().parse(arguments); - case EditTagCommand.COMMAND_WORD: - return new EditTagCommandParser().parse(arguments); + case ImportCommand.COMMAND_WORD: + return new ImportCommandParser().parse(arguments); - // Tab + // UI case SwitchTabCommand.COMMAND_WORD: return new SwitchTabCommandParser().parse(arguments); diff --git a/src/main/java/seedu/algobase/logic/parser/CliSyntax.java b/src/main/java/seedu/algobase/logic/parser/CliSyntax.java index bd1563f01dd..4d61d885b40 100644 --- a/src/main/java/seedu/algobase/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/algobase/logic/parser/CliSyntax.java @@ -5,23 +5,32 @@ */ public class CliSyntax { - /* Prefix definitions */ - public static final Prefix PREFIX_NAME = new Prefix("n/"); + /* Field */ public static final Prefix PREFIX_AUTHOR = new Prefix("a/"); - public static final Prefix PREFIX_WEBLINK = new Prefix("w/"); public static final Prefix PREFIX_DESCRIPTION = new Prefix("d/"); - public static final Prefix PREFIX_TAG = new Prefix("t/"); public static final Prefix PREFIX_DIFFICULTY = new Prefix("diff/"); + public static final Prefix PREFIX_NAME = new Prefix("n/"); public static final Prefix PREFIX_REMARK = new Prefix("r/"); public static final Prefix PREFIX_SOURCE = new Prefix("src/"); + public static final Prefix PREFIX_TAG = new Prefix("t/"); + public static final Prefix PREFIX_WEBLINK = new Prefix("w/"); + /* Sorting */ public static final Prefix PREFIX_SORTING_METHOD = new Prefix("m/"); public static final Prefix PREFIX_SORTING_ORDER = new Prefix("ord/"); + /* Model */ public static final Prefix PREFIX_PLAN = new Prefix("plan/"); + public static final Prefix PREFIX_PLAN_FROM = new Prefix("from/"); + public static final Prefix PREFIX_PLAN_TO = new Prefix("to/"); public static final Prefix PREFIX_PROBLEM = new Prefix("prob/"); public static final Prefix PREFIX_TASK = new Prefix("task/"); + /* Date */ public static final Prefix PREFIX_DUE_DATE = new Prefix("due/"); public static final Prefix PREFIX_START_DATE = new Prefix("start/"); public static final Prefix PREFIX_END_DATE = new Prefix("end/"); + /* Storage */ + public static final Prefix PREFIX_FORMAT = new Prefix("format/"); + public static final Prefix PREFIX_PATH = new Prefix("path/"); + /* UI */ public static final Prefix PREFIX_TAB_TYPE = new Prefix("t/"); public static final Prefix PREFIX_TAB_INDEX = new Prefix("i/"); public static final Prefix PREFIX_MODEL_TYPE = new Prefix("m/"); diff --git a/src/main/java/seedu/algobase/logic/parser/DeleteTaskCommandParser.java b/src/main/java/seedu/algobase/logic/parser/DeleteTaskCommandParser.java index bb017f5a643..8ce8bfd4c95 100644 --- a/src/main/java/seedu/algobase/logic/parser/DeleteTaskCommandParser.java +++ b/src/main/java/seedu/algobase/logic/parser/DeleteTaskCommandParser.java @@ -4,8 +4,6 @@ import static seedu.algobase.logic.parser.CliSyntax.PREFIX_PLAN; import static seedu.algobase.logic.parser.CliSyntax.PREFIX_TASK; -import java.util.stream.Stream; - import seedu.algobase.commons.core.index.Index; import seedu.algobase.logic.commands.DeleteTaskCommand; import seedu.algobase.logic.parser.exceptions.ParseException; @@ -23,8 +21,8 @@ public class DeleteTaskCommandParser implements Parser { public DeleteTaskCommand parse(String args) throws ParseException { ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_PLAN, PREFIX_TASK); - if (!arePrefixesPresent(argMultimap, PREFIX_PLAN) - || !arePrefixesPresent(argMultimap, PREFIX_TASK) + if (!ParserUtil.arePrefixesPresent(argMultimap, PREFIX_PLAN) + || !ParserUtil.arePrefixesPresent(argMultimap, PREFIX_TASK) || !argMultimap.getPreamble().isEmpty()) { throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTaskCommand.MESSAGE_USAGE)); } @@ -51,12 +49,4 @@ public DeleteTaskCommand parse(String args) throws ParseException { return new DeleteTaskCommand(deleteTaskDescriptor); } - /** - * 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/algobase/logic/parser/DoneTaskCommandParser.java b/src/main/java/seedu/algobase/logic/parser/DoneTaskCommandParser.java index 75fa09b7731..edb019c76ef 100644 --- a/src/main/java/seedu/algobase/logic/parser/DoneTaskCommandParser.java +++ b/src/main/java/seedu/algobase/logic/parser/DoneTaskCommandParser.java @@ -4,8 +4,6 @@ import static seedu.algobase.logic.parser.CliSyntax.PREFIX_PLAN; import static seedu.algobase.logic.parser.CliSyntax.PREFIX_TASK; -import java.util.stream.Stream; - import seedu.algobase.commons.core.index.Index; import seedu.algobase.logic.commands.DoneTaskCommand; import seedu.algobase.logic.parser.exceptions.ParseException; @@ -23,8 +21,8 @@ public class DoneTaskCommandParser implements Parser { public DoneTaskCommand parse(String args) throws ParseException { ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_PLAN, PREFIX_TASK); - if (!arePrefixesPresent(argMultimap, PREFIX_PLAN) - || !arePrefixesPresent(argMultimap, PREFIX_TASK) + if (!ParserUtil.arePrefixesPresent(argMultimap, PREFIX_PLAN) + || !ParserUtil.arePrefixesPresent(argMultimap, PREFIX_TASK) || !argMultimap.getPreamble().isEmpty()) { throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DoneTaskCommand.MESSAGE_USAGE)); } @@ -51,12 +49,4 @@ public DoneTaskCommand parse(String args) throws ParseException { return new DoneTaskCommand(doneTaskDescriptor); } - /** - * 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/algobase/logic/parser/EditTaskCommandParser.java b/src/main/java/seedu/algobase/logic/parser/EditTaskCommandParser.java new file mode 100644 index 00000000000..81a0911b45f --- /dev/null +++ b/src/main/java/seedu/algobase/logic/parser/EditTaskCommandParser.java @@ -0,0 +1,57 @@ +package seedu.algobase.logic.parser; + +import static seedu.algobase.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_DUE_DATE; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_PLAN; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_TASK; + +import java.time.LocalDate; + +import seedu.algobase.commons.core.index.Index; +import seedu.algobase.logic.commands.EditTaskCommand; +import seedu.algobase.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new EditTaskCommand object + */ +public class EditTaskCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditTaskCommand + * and returns an EditTaskCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EditTaskCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_PLAN, PREFIX_TASK, PREFIX_DUE_DATE); + + if (!ParserUtil.arePrefixesPresent(argMultimap, PREFIX_PLAN) + || !ParserUtil.arePrefixesPresent(argMultimap, PREFIX_TASK) + || !ParserUtil.arePrefixesPresent(argMultimap, PREFIX_DUE_DATE) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditTaskCommand.MESSAGE_USAGE)); + } + + Index planIndex; + try { + planIndex = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_PLAN).get()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditTaskCommand.MESSAGE_USAGE), pe); + } + + Index taskIndex; + try { + taskIndex = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_TASK).get()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditTaskCommand.MESSAGE_USAGE), pe); + } + + LocalDate targetDate = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DUE_DATE).get()); + + EditTaskCommand.EditTaskDescriptor editTaskDescriptor = + new EditTaskCommand.EditTaskDescriptor(planIndex, taskIndex, targetDate); + + return new EditTaskCommand(editTaskDescriptor); + } + +} diff --git a/src/main/java/seedu/algobase/logic/parser/ExportCommandParser.java b/src/main/java/seedu/algobase/logic/parser/ExportCommandParser.java new file mode 100644 index 00000000000..44a09c242d2 --- /dev/null +++ b/src/main/java/seedu/algobase/logic/parser/ExportCommandParser.java @@ -0,0 +1,41 @@ +package seedu.algobase.logic.parser; + +import static seedu.algobase.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_FORMAT; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_PATH; + +import seedu.algobase.commons.util.FileUtil.Format; +import seedu.algobase.logic.commands.ExportCommand; +import seedu.algobase.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new ExportCommand object + */ +public class ExportCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ExportCommand + * and returns an ExportCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ExportCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_FORMAT, PREFIX_PATH); + + if (!ParserUtil.arePrefixesPresent(argMultimap, PREFIX_FORMAT) + || !ParserUtil.arePrefixesPresent(argMultimap, PREFIX_PATH) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ExportCommand.MESSAGE_USAGE)); + } + + try { + Format format = ParserUtil.parseFileFormat(argMultimap.getValue(PREFIX_FORMAT).get()); + String path = argMultimap.getValue(PREFIX_PATH).get(); + return new ExportCommand(format, path); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ExportCommand.MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/seedu/algobase/logic/parser/ImportCommandParser.java b/src/main/java/seedu/algobase/logic/parser/ImportCommandParser.java new file mode 100644 index 00000000000..65ec76a5adf --- /dev/null +++ b/src/main/java/seedu/algobase/logic/parser/ImportCommandParser.java @@ -0,0 +1,41 @@ +package seedu.algobase.logic.parser; + +import static seedu.algobase.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_FORMAT; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_PATH; + +import seedu.algobase.commons.util.FileUtil.Format; +import seedu.algobase.logic.commands.ImportCommand; +import seedu.algobase.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new ImportCommand object + */ +public class ImportCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ImportCommand + * and returns an ImportCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ImportCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_FORMAT, PREFIX_PATH); + + if (!ParserUtil.arePrefixesPresent(argMultimap, PREFIX_FORMAT) + || !ParserUtil.arePrefixesPresent(argMultimap, PREFIX_PATH) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ImportCommand.MESSAGE_USAGE)); + } + + try { + Format format = ParserUtil.parseFileFormat(argMultimap.getValue(PREFIX_FORMAT).get()); + String path = argMultimap.getValue(PREFIX_PATH).get(); + return new ImportCommand(format, path); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ImportCommand.MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/seedu/algobase/logic/parser/MoveTaskCommandParser.java b/src/main/java/seedu/algobase/logic/parser/MoveTaskCommandParser.java new file mode 100644 index 00000000000..c493dbf058f --- /dev/null +++ b/src/main/java/seedu/algobase/logic/parser/MoveTaskCommandParser.java @@ -0,0 +1,64 @@ +package seedu.algobase.logic.parser; + +import static seedu.algobase.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_PLAN_FROM; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_PLAN_TO; +import static seedu.algobase.logic.parser.CliSyntax.PREFIX_TASK; + +import seedu.algobase.commons.core.index.Index; +import seedu.algobase.logic.commands.MoveTaskCommand; +import seedu.algobase.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteTaskCommand object + */ +public class MoveTaskCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the MoveTaskCommand + * and returns a MoveTaskCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public MoveTaskCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, + PREFIX_PLAN_FROM, PREFIX_PLAN_TO, PREFIX_TASK); + + if (!ParserUtil.arePrefixesPresent(argMultimap, PREFIX_PLAN_FROM) + || !ParserUtil.arePrefixesPresent(argMultimap, PREFIX_PLAN_TO) + || !ParserUtil.arePrefixesPresent(argMultimap, PREFIX_TASK) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + MoveTaskCommand.MESSAGE_USAGE)); + } + + Index planFromIndex; + try { + planFromIndex = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_PLAN_FROM).get()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + MoveTaskCommand.MESSAGE_USAGE), pe); + } + + Index planToIndex; + try { + planToIndex = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_PLAN_TO).get()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + MoveTaskCommand.MESSAGE_USAGE), pe); + } + + Index taskIndex; + try { + taskIndex = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_TASK).get()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + MoveTaskCommand.MESSAGE_USAGE), pe); + } + + MoveTaskCommand.MoveTaskDescriptor moveTaskDescriptor = + new MoveTaskCommand.MoveTaskDescriptor(taskIndex, planFromIndex, planToIndex); + + return new MoveTaskCommand(moveTaskDescriptor); + } + +} diff --git a/src/main/java/seedu/algobase/logic/parser/ParserUtil.java b/src/main/java/seedu/algobase/logic/parser/ParserUtil.java index f49aa8bd9a9..4206787ecb1 100644 --- a/src/main/java/seedu/algobase/logic/parser/ParserUtil.java +++ b/src/main/java/seedu/algobase/logic/parser/ParserUtil.java @@ -18,6 +18,7 @@ import java.util.stream.Stream; import seedu.algobase.commons.core.index.Index; +import seedu.algobase.commons.util.FileUtil.Format; import seedu.algobase.commons.util.StringUtil; import seedu.algobase.logic.commands.OpenTabCommand; import seedu.algobase.logic.commands.SortCommand; @@ -304,7 +305,6 @@ public static Index parseTabIndex(String tabIndex) throws ParseException { } } - /** Parses a {@code String modelType} into an {@code ModelType}. * * @throws ParseException if the given {@code string modelType} is invalid. @@ -456,4 +456,15 @@ public static TagIncludesKeywordsPredicate parseTagPredicate(String arg, String return new TagIncludesKeywordsPredicate(keywords); } + /** Parses a {@code String format} into an {@code Format}. + * + * @throws ParseException if the given {@code string format} is invalid. + */ + public static Format parseFileFormat(String format) throws ParseException { + try { + return Format.valueOf(format.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new ParseException(e.toString()); + } + } } diff --git a/src/main/java/seedu/algobase/logic/parser/UndoneTaskCommandParser.java b/src/main/java/seedu/algobase/logic/parser/UndoneTaskCommandParser.java index 51c5615a693..f78a85c24a0 100644 --- a/src/main/java/seedu/algobase/logic/parser/UndoneTaskCommandParser.java +++ b/src/main/java/seedu/algobase/logic/parser/UndoneTaskCommandParser.java @@ -4,8 +4,6 @@ import static seedu.algobase.logic.parser.CliSyntax.PREFIX_PLAN; import static seedu.algobase.logic.parser.CliSyntax.PREFIX_TASK; -import java.util.stream.Stream; - import seedu.algobase.commons.core.index.Index; import seedu.algobase.logic.commands.UndoneTaskCommand; import seedu.algobase.logic.parser.exceptions.ParseException; @@ -23,8 +21,8 @@ public class UndoneTaskCommandParser implements Parser { public UndoneTaskCommand parse(String args) throws ParseException { ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_PLAN, PREFIX_TASK); - if (!arePrefixesPresent(argMultimap, PREFIX_PLAN) - || !arePrefixesPresent(argMultimap, PREFIX_TASK) + if (!ParserUtil.arePrefixesPresent(argMultimap, PREFIX_PLAN) + || !ParserUtil.arePrefixesPresent(argMultimap, PREFIX_TASK) || !argMultimap.getPreamble().isEmpty()) { throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UndoneTaskCommand.MESSAGE_USAGE)); } @@ -51,12 +49,4 @@ public UndoneTaskCommand parse(String args) throws ParseException { return new UndoneTaskCommand(doneTaskDescriptor); } - /** - * 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/algobase/model/AlgoBase.java b/src/main/java/seedu/algobase/model/AlgoBase.java index 5025715ccae..7fde0af8e0f 100644 --- a/src/main/java/seedu/algobase/model/AlgoBase.java +++ b/src/main/java/seedu/algobase/model/AlgoBase.java @@ -6,6 +6,8 @@ import java.util.Iterator; import java.util.List; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.StringProperty; import javafx.collections.ObservableList; import seedu.algobase.commons.exceptions.IllegalValueException; import seedu.algobase.model.commandhistory.CommandHistory; @@ -273,6 +275,21 @@ public ObservableList getCurrentTaskList() { return plans.getUnmodifiableObservableTaskList(); } + @Override + public StringProperty getCurrentPlan() { + return plans.getCurrentPlan(); + } + + @Override + public IntegerProperty getCurrentSolvedCount() { + return plans.getCurrentSolvedCount(); + } + + @Override + public IntegerProperty getCurrentUnsolvedCount() { + return plans.getCurrentUnsolvedCount(); + } + //========== Find Rules ============================================================= @Override diff --git a/src/main/java/seedu/algobase/model/Model.java b/src/main/java/seedu/algobase/model/Model.java index 475d4b350a2..f35e2cb7c82 100644 --- a/src/main/java/seedu/algobase/model/Model.java +++ b/src/main/java/seedu/algobase/model/Model.java @@ -5,6 +5,8 @@ import java.util.Set; import java.util.function.Predicate; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.StringProperty; import javafx.collections.ObservableList; import seedu.algobase.commons.core.GuiSettings; import seedu.algobase.model.commandhistory.CommandHistory; @@ -55,7 +57,7 @@ public interface Model { */ void setAlgoBaseFilePath(Path algoBaseFilePath); - //=========== GUI state ============================================================= + //=========== UI ==================================================================== /** * Returns the state of the GUI. @@ -205,9 +207,26 @@ public interface Model { //=========== Task ================================================================== - /** Returns an unmodifiable view of the filtered Plan list */ + /** + * Returns an unmodifiable view of the filtered Plan list + */ ObservableList getCurrentTaskList(); + /** + * Returns the current {@code Plan}. + */ + StringProperty getCurrentPlan(); + + /** + * Returns the number of solved tasks in current plan. + */ + IntegerProperty getCurrentSolvedCount(); + + /** + * Returns the number of unsolved tasks in current plan. + */ + IntegerProperty getCurrentUnsolvedCount(); + //========== Find Rules ============================================================= /** diff --git a/src/main/java/seedu/algobase/model/ModelManager.java b/src/main/java/seedu/algobase/model/ModelManager.java index ae9c82d5f34..67cf626ab76 100644 --- a/src/main/java/seedu/algobase/model/ModelManager.java +++ b/src/main/java/seedu/algobase/model/ModelManager.java @@ -9,6 +9,8 @@ import java.util.function.Predicate; import java.util.logging.Logger; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.StringProperty; import javafx.collections.ObservableList; import javafx.collections.transformation.FilteredList; import javafx.collections.transformation.SortedList; @@ -291,27 +293,20 @@ public ObservableList getCurrentTaskList() { return filteredTasks; } - //========== Rewind ================================================================= - - /** - * Returns an unmodifiable view of the list of {@code CommandHistory}. - */ @Override - public ObservableList getCommandHistoryList() { - return filteredCommandHistories; + public StringProperty getCurrentPlan() { + return this.algoBase.getCurrentPlan(); } - /** - * Adds the given {@code CommandHistory}. - * - * @param history the added history - */ @Override - public void addCommandHistory(CommandHistory history) { - requireNonNull(history); - algoBase.addCommandHistory(history); + public IntegerProperty getCurrentSolvedCount() { + return this.algoBase.getCurrentSolvedCount(); } + @Override + public IntegerProperty getCurrentUnsolvedCount() { + return this.algoBase.getCurrentUnsolvedCount(); + } //========== Find Rules ============================================================= @@ -344,6 +339,20 @@ public ObservableList getFilteredFindRuleList() { return filteredFindRules; } + + //========== Rewind ================================================================= + + @Override + public ObservableList getCommandHistoryList() { + return filteredCommandHistories; + } + + @Override + public void addCommandHistory(CommandHistory history) { + requireNonNull(history); + algoBase.addCommandHistory(history); + } + //========== Util =================================================================== @Override diff --git a/src/main/java/seedu/algobase/model/ReadOnlyAlgoBase.java b/src/main/java/seedu/algobase/model/ReadOnlyAlgoBase.java index 249d318b724..aba535471ed 100644 --- a/src/main/java/seedu/algobase/model/ReadOnlyAlgoBase.java +++ b/src/main/java/seedu/algobase/model/ReadOnlyAlgoBase.java @@ -1,5 +1,7 @@ package seedu.algobase.model; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.StringProperty; import javafx.collections.ObservableList; import seedu.algobase.commons.exceptions.IllegalValueException; import seedu.algobase.model.commandhistory.CommandHistory; @@ -15,6 +17,11 @@ */ public interface ReadOnlyAlgoBase { + /** + * Returns the {@code Problem} with the same id in the algobase. + */ + Problem findProblemById(Id problemId) throws IllegalValueException; + /** * Returns an unmodifiable view of the problems list. * This list will not contain any duplicate problems. @@ -22,10 +29,9 @@ public interface ReadOnlyAlgoBase { ObservableList getProblemList(); /** - * Returns the {@code Problem} with the same id in the algobase. + * Returns an unmodifiable view of the tags list. + * This list will not contain any duplicate tags. */ - Problem findProblemById(Id problemId) throws IllegalValueException; - ObservableList getTagList(); /** @@ -49,14 +55,34 @@ public interface ReadOnlyAlgoBase { ObservableList getCurrentTaskList(); /** - * Returns a view of the GuiState. + * Returns an unmodifiable view of the command history. + * Returns current plan name. */ - GuiState getGuiState(); + StringProperty getCurrentPlan(); + + /** + * Returns the number of solved tasks in current plan. + */ + IntegerProperty getCurrentSolvedCount(); + + /** + * Returns the number of solved tasks in current plan. + */ + IntegerProperty getCurrentUnsolvedCount(); + + /** + * Returns an unmodifiable view of the find rule list. + */ + ObservableList getFindRules(); /** * Returns an unmodifiable view of the command history. */ ObservableList getCommandHistoryList(); - ObservableList getFindRules(); + /** + * Returns a view of the GuiState. + */ + GuiState getGuiState(); + } diff --git a/src/main/java/seedu/algobase/model/plan/Plan.java b/src/main/java/seedu/algobase/model/plan/Plan.java index c10cd4ff520..212619d7d0c 100644 --- a/src/main/java/seedu/algobase/model/plan/Plan.java +++ b/src/main/java/seedu/algobase/model/plan/Plan.java @@ -70,6 +70,22 @@ public static Plan updateTasks(Plan planToUpdate, Set taskSet) { return new Plan(id, name, description, startDate, endDate, taskSet); } + /** + * Returns number of solved tasks within plan. + * @return number of solved tasks. + */ + public int getSolvedTaskCount() { + return (int) this.getTasks().stream().filter((task) -> task.getIsSolved()).count(); + } + + /** + * Returns number of unsolved tasks within plan. + * @return number of unsolved tasks. + */ + public int getUnsolvedTaskCount() { + return (int) this.getTasks().stream().filter((task) -> !task.getIsSolved()).count(); + } + public Id getId() { return id; } diff --git a/src/main/java/seedu/algobase/model/plan/PlanList.java b/src/main/java/seedu/algobase/model/plan/PlanList.java index 9418e0e1976..40d5884484b 100644 --- a/src/main/java/seedu/algobase/model/plan/PlanList.java +++ b/src/main/java/seedu/algobase/model/plan/PlanList.java @@ -6,6 +6,10 @@ import java.util.Iterator; import java.util.List; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import seedu.algobase.model.plan.exceptions.PlanNotFoundException; @@ -26,12 +30,18 @@ public class PlanList implements Iterable { private final ObservableList internalTaskList = FXCollections.observableArrayList(); private final ObservableList internalUnmodifiableTaskList = FXCollections.unmodifiableObservableList(internalTaskList); + private final StringProperty currentPlan = new SimpleStringProperty(); + private final IntegerProperty solvedCount = new SimpleIntegerProperty(); + private final IntegerProperty unsolvedCount = new SimpleIntegerProperty(); /** * Adds a Plan to the list. */ public void add(Plan toAdd) { requireNonNull(toAdd); + currentPlan.set(toAdd.getPlanName().fullName); + solvedCount.set(toAdd.getSolvedTaskCount()); + unsolvedCount.set(toAdd.getUnsolvedTaskCount()); internalList.add(toAdd); internalTaskList.setAll(toAdd.getTasks()); } @@ -48,6 +58,9 @@ public void setPlan(Plan target, Plan updatedPlan) { throw new PlanNotFoundException(); } + currentPlan.set(updatedPlan.getPlanName().fullName); + solvedCount.set(updatedPlan.getSolvedTaskCount()); + unsolvedCount.set(updatedPlan.getUnsolvedTaskCount()); internalList.set(index, updatedPlan); internalTaskList.setAll(updatedPlan.getTasks()); } @@ -61,21 +74,57 @@ public void remove(Plan toRemove) { if (!internalList.remove(toRemove)) { throw new PlanNotFoundException(); } + currentPlan.set(""); + solvedCount.set(0); + unsolvedCount.set(0); internalTaskList.setAll(); } - public void setPlans(PlanList replacement) { - requireNonNull(replacement); - internalList.setAll(replacement.internalList); - } - /** * Replaces the contents of this list with {@code plans}. - * {@code plans} must not contain duplicate plans. */ public void setPlans(List plans) { requireAllNonNull(plans); internalList.setAll(plans); + + if (plans.size() > 0) { + // Default to first plan in list + Plan plan = plans.get(0); + currentPlan.set(plan.getPlanName().fullName); + solvedCount.set(plan.getSolvedTaskCount()); + unsolvedCount.set(plan.getUnsolvedTaskCount()); + internalTaskList.setAll(plan.getTasks()); + } + } + + /** + * Replaces the contents of this list with {@code replacement}. + */ + public void setPlans(PlanList replacement) { + requireNonNull(replacement); + List plans = replacement.internalList; + setPlans(plans); + } + + /** + * Returns the current {@code Plan}. + */ + public StringProperty getCurrentPlan() { + return currentPlan; + } + + /** + * Returns the number of solved tasks in current plan. + */ + public IntegerProperty getCurrentSolvedCount() { + return solvedCount; + } + + /** + * Returns the number of solved tasks in current plan. + */ + public IntegerProperty getCurrentUnsolvedCount() { + return unsolvedCount; } /** diff --git a/src/main/java/seedu/algobase/model/task/Task.java b/src/main/java/seedu/algobase/model/task/Task.java index 4e2af159ac2..9eaad78f96f 100644 --- a/src/main/java/seedu/algobase/model/task/Task.java +++ b/src/main/java/seedu/algobase/model/task/Task.java @@ -48,11 +48,11 @@ public Task(Id id, Problem problem, LocalDate targetDate, boolean isSolved) { } /** - * Creates and returns a {@code Plan} with the details of {@code planToUpdate} - * with an updated {@code taskSet}. + * Creates and returns a {@code Task} with the details of {@code taskToUpdate} + * with an updated {@code isSolved}. */ public static Task updateStatus(Task taskToUpdate, boolean isSolved) { - assert taskToUpdate != null; + requireAllNonNull(taskToUpdate, isSolved); Id id = taskToUpdate.id; Problem problem = taskToUpdate.problem; @@ -61,6 +61,20 @@ public static Task updateStatus(Task taskToUpdate, boolean isSolved) { return new Task(id, problem, targetDate, isSolved); } + /** + * Creates and returns a {@code Task} with the details of {@code taskToUpdate} + * with an updated {@code targetDate}. + */ + public static Task updateDueDate(Task taskToUpdate, LocalDate targetDate) { + requireAllNonNull(taskToUpdate, targetDate); + + Id id = taskToUpdate.id; + Problem problem = taskToUpdate.problem; + boolean isSolved = taskToUpdate.isSolved; + + return new Task(id, problem, targetDate, isSolved); + } + public Id getId() { return id; } @@ -138,15 +152,13 @@ public boolean equals(Object other) { } Task otherTask = (Task) other; - return otherTask.getProblem().equals(getProblem()) - && otherTask.getIsSolved().equals(getIsSolved()) - && otherTask.getTargetDate().equals(getTargetDate()); + return otherTask.getProblem().equals(getProblem()); } @Override public int hashCode() { // use this method for custom fields hashing instead of implementing your own - return Objects.hash(problem, isSolved, targetDate); + return Objects.hash(problem); } @Override diff --git a/src/main/java/seedu/algobase/storage/JsonSerializableAlgoBase.java b/src/main/java/seedu/algobase/storage/JsonSerializableAlgoBase.java index 1ca563ba079..2204d239c54 100644 --- a/src/main/java/seedu/algobase/storage/JsonSerializableAlgoBase.java +++ b/src/main/java/seedu/algobase/storage/JsonSerializableAlgoBase.java @@ -20,7 +20,7 @@ * An Immutable AlgoBase that is serializable to JSON format. */ @JsonRootName(value = "algobase") -class JsonSerializableAlgoBase { +public class JsonSerializableAlgoBase { public static final String MESSAGE_DUPLICATE_PROBLEM = "Problems list contains duplicate Problem(s)."; public static final String MESSAGE_DUPLICATE_TAG = "Tags list contains duplicate Tag(s)."; diff --git a/src/main/java/seedu/algobase/ui/FindRuleCard.java b/src/main/java/seedu/algobase/ui/FindRuleCard.java index f08272ed321..d12ff2c36f4 100644 --- a/src/main/java/seedu/algobase/ui/FindRuleCard.java +++ b/src/main/java/seedu/algobase/ui/FindRuleCard.java @@ -7,6 +7,7 @@ import javafx.scene.control.Label; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; +import javafx.scene.text.TextAlignment; import seedu.algobase.model.searchrule.problemsearchrule.DifficultyIsInRangePredicate; import seedu.algobase.model.searchrule.problemsearchrule.ProblemSearchRule; @@ -47,25 +48,35 @@ public FindRuleCard(ProblemSearchRule findRule, int displayedIndex) { super(FXML); this.findRule = findRule; id.setText(displayedIndex + ". "); + id.setWrapText(true); + id.setTextAlignment(TextAlignment.JUSTIFY); ruleName.setText(findRule.getName().name); + ruleName.setWrapText(true); + ruleName.setTextAlignment(TextAlignment.JUSTIFY); if (findRule.getNamePredicate().isEmpty()) { problemName.setText(DEFAULT_PREDICATE); } else { problemName.setText(findRule.getNamePredicate().get().getKeywords().toString()); } + problemName.setWrapText(true); + problemName.setTextAlignment(TextAlignment.JUSTIFY); if (findRule.getAuthorPredicate().isEmpty()) { author.setText(DEFAULT_PREDICATE); } else { author.setText(findRule.getAuthorPredicate().get().getKeyword().keyword); } + author.setWrapText(true); + author.setTextAlignment(TextAlignment.JUSTIFY); if (findRule.getDescriptionPredicate().isEmpty()) { description.setText(DEFAULT_PREDICATE); } else { description.setText(findRule.getDescriptionPredicate().get().getKeywords().toString()); } + description.setWrapText(true); + description.setTextAlignment(TextAlignment.JUSTIFY); if (findRule.getDifficultyPredicate().isEmpty()) { difficulty.setText(DEFAULT_PREDICATE); @@ -76,18 +87,24 @@ public FindRuleCard(ProblemSearchRule findRule, int displayedIndex) { String difficultyString = String.format("%f - %f", lowerBound, upperBound); difficulty.setText(difficultyString); } + difficulty.setWrapText(true); + difficulty.setTextAlignment(TextAlignment.JUSTIFY); if (findRule.getSourcePredicate().isEmpty()) { source.setText(DEFAULT_PREDICATE); } else { source.setText(findRule.getSourcePredicate().get().getKeyword().keyword); } + source.setWrapText(true); + source.setTextAlignment(TextAlignment.JUSTIFY); if (findRule.getTagPredicate().isEmpty()) { tag.setText(DEFAULT_PREDICATE); } else { tag.setText(findRule.getTagPredicate().get().getKeywords().toString()); } + tag.setWrapText(true); + tag.setTextAlignment(TextAlignment.JUSTIFY); } diff --git a/src/main/java/seedu/algobase/ui/MainWindow.java b/src/main/java/seedu/algobase/ui/MainWindow.java index 207c298a336..b9aaecc0d15 100644 --- a/src/main/java/seedu/algobase/ui/MainWindow.java +++ b/src/main/java/seedu/algobase/ui/MainWindow.java @@ -40,10 +40,10 @@ public class MainWindow extends UiPart { // Independent Ui parts residing in this Ui container private DisplayTabPane displayTabPane; private DetailsTabPane detailsTabPane; + private TaskManagementPane taskManagementPane; private ProblemListPanel problemListPanel; private TagListPanel tagListPanel; private PlanListPanel planListPanel; - private TaskListPanel taskListPanel; private FindRuleListPanel findRuleListPanel; private ResultDisplay resultDisplay; private HelpWindow helpWindow; @@ -122,9 +122,17 @@ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { void fillInnerParts() { displayTabPane = getDisplayTabPane(logic.getGuiState().getTabManager()); detailsTabPane = new DetailsTabPane(logic); + taskManagementPane = new TaskManagementPane( + logic.getProcessedTaskList(), + logic.getCurrentPlan(), + logic.getCurrentSolvedCount(), + logic.getCurrentUnsolvedCount() + ); layoutPanePlaceholder.getItems().add(displayTabPane.getRoot()); layoutPanePlaceholder.getItems().add(detailsTabPane.getRoot()); + layoutPanePlaceholder.getItems().add(taskManagementPane.getRoot()); + layoutPanePlaceholder.setDividerPositions(0.33, 0.66); resultDisplay = new ResultDisplay(); resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); @@ -137,24 +145,21 @@ void fillInnerParts() { } private DisplayTabPane getDisplayTabPane(WriteOnlyTabManager writeOnlyTabManager) { - problemListPanel = - new ProblemListPanel(logic.getProcessedProblemList(), writeOnlyTabManager); - planListPanel = - new PlanListPanel(logic.getProcessedPlanList(), writeOnlyTabManager); - tagListPanel = - new TagListPanel(logic.getProcessedTagList()); - taskListPanel = - new TaskListPanel(logic.getProcessedTaskList()); - findRuleListPanel = - new FindRuleListPanel(logic.getProcessedFindRuleList()); + problemListPanel = new ProblemListPanel(logic.getProcessedProblemList(), writeOnlyTabManager); + planListPanel = new PlanListPanel(logic.getProcessedPlanList(), writeOnlyTabManager); + tagListPanel = new TagListPanel(logic.getProcessedTagList()); + findRuleListPanel = new FindRuleListPanel(logic.getProcessedFindRuleList()); DisplayTab problemListPanelTab = new DisplayTab(ModelType.PROBLEM.getTabName(), problemListPanel); DisplayTab tagListPanelTab = new DisplayTab(ModelType.TAG.getTabName(), tagListPanel); DisplayTab planListPanelTab = new DisplayTab(ModelType.PLAN.getTabName(), planListPanel); - DisplayTab taskListPanelTab = new DisplayTab(ModelType.TASK.getTabName(), taskListPanel); DisplayTab findRuleListPaneTab = new DisplayTab(ModelType.FINDRULE.getTabName(), findRuleListPanel); return new DisplayTabPane( - logic.getGuiState(), problemListPanelTab, tagListPanelTab, planListPanelTab, taskListPanelTab, - findRuleListPaneTab); + logic.getGuiState(), + problemListPanelTab, + tagListPanelTab, + planListPanelTab, + findRuleListPaneTab + ); } /** @@ -209,10 +214,6 @@ public PlanListPanel getPlanListPanel() { return planListPanel; } - public TaskListPanel getTaskListPanel() { - return taskListPanel; - } - public FindRuleListPanel getFindRuleListPanel() { return findRuleListPanel; } diff --git a/src/main/java/seedu/algobase/ui/PlanCard.java b/src/main/java/seedu/algobase/ui/PlanCard.java index 6cb0175ef94..2c6a56e3f5c 100644 --- a/src/main/java/seedu/algobase/ui/PlanCard.java +++ b/src/main/java/seedu/algobase/ui/PlanCard.java @@ -10,6 +10,7 @@ import javafx.scene.input.MouseEvent; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; +import javafx.scene.text.TextAlignment; import seedu.algobase.commons.core.LogsCenter; import seedu.algobase.logic.parser.ParserUtil; import seedu.algobase.model.Id; @@ -45,10 +46,20 @@ public PlanCard(Plan plan, int displayedIndex, WriteOnlyTabManager writeOnlyTabM super(FXML); this.plan = plan; id.setText(displayedIndex + ". "); + id.setWrapText(true); + id.setTextAlignment(TextAlignment.JUSTIFY); planName.setText(plan.getPlanName().fullName + " (" + plan.getTasks().size() + " tasks)"); + planName.setWrapText(true); + planName.setTextAlignment(TextAlignment.JUSTIFY); planDescription.setText(plan.getPlanDescription().value); + planDescription.setWrapText(true); + planDescription.setTextAlignment(TextAlignment.JUSTIFY); startDate.setText(plan.getStartDate().format(ParserUtil.FORMATTER)); + startDate.setWrapText(true); + startDate.setTextAlignment(TextAlignment.JUSTIFY); endDate.setText(plan.getEndDate().format(ParserUtil.FORMATTER)); + endDate.setWrapText(true); + endDate.setTextAlignment(TextAlignment.JUSTIFY); addMouseClickListener(writeOnlyTabManager.addDetailsTabConsumer(ModelType.PLAN)); } diff --git a/src/main/java/seedu/algobase/ui/ProblemCard.java b/src/main/java/seedu/algobase/ui/ProblemCard.java index 355f44f151a..80b557dddac 100644 --- a/src/main/java/seedu/algobase/ui/ProblemCard.java +++ b/src/main/java/seedu/algobase/ui/ProblemCard.java @@ -12,6 +12,7 @@ import javafx.scene.layout.FlowPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; +import javafx.scene.text.TextAlignment; import seedu.algobase.commons.core.LogsCenter; import seedu.algobase.model.Id; import seedu.algobase.model.ModelType; @@ -62,13 +63,29 @@ public ProblemCard(Problem problem, int displayedIndex, WriteOnlyTabManager writ super(FXML); this.problem = problem; id.setText(displayedIndex + ". "); + id.setWrapText(true); + id.setTextAlignment(TextAlignment.JUSTIFY); name.setText(problem.getName().fullName); + name.setWrapText(true); + name.setTextAlignment(TextAlignment.JUSTIFY); author.setText(problem.getAuthor().value); + author.setWrapText(true); + author.setTextAlignment(TextAlignment.JUSTIFY); description.setText(problem.getDescription().value); + description.setWrapText(true); + description.setTextAlignment(TextAlignment.JUSTIFY); weblink.setText(problem.getWebLink().value); + weblink.setWrapText(true); + weblink.setTextAlignment(TextAlignment.JUSTIFY); difficulty.setText(problem.getDifficulty().toString()); + difficulty.setWrapText(true); + difficulty.setTextAlignment(TextAlignment.JUSTIFY); remark.setText(problem.getRemark().value); + remark.setWrapText(true); + remark.setTextAlignment(TextAlignment.JUSTIFY); source.setText(problem.getSource().value); + source.setWrapText(true); + source.setTextAlignment(TextAlignment.JUSTIFY); problem.getTags().stream() .sorted(Comparator.comparing(tag -> tag.tagName)) .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); diff --git a/src/main/java/seedu/algobase/ui/TagCard.java b/src/main/java/seedu/algobase/ui/TagCard.java index 297b47c15e2..d7da3de28f3 100644 --- a/src/main/java/seedu/algobase/ui/TagCard.java +++ b/src/main/java/seedu/algobase/ui/TagCard.java @@ -4,6 +4,7 @@ import javafx.scene.control.Label; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; +import javafx.scene.text.TextAlignment; import seedu.algobase.model.tag.Tag; @@ -27,7 +28,11 @@ public TagCard(Tag tag, int displayedIndex) { super(FXML); this.tag = tag; id.setText(displayedIndex + ". "); + id.setWrapText(true); + id.setTextAlignment(TextAlignment.JUSTIFY); tagName.setText(tag.getName()); + tagName.setWrapText(true); + tagName.setTextAlignment(TextAlignment.JUSTIFY); } @Override diff --git a/src/main/java/seedu/algobase/ui/TaskCard.java b/src/main/java/seedu/algobase/ui/TaskCard.java index 4ba637b456c..5e54447a9c4 100644 --- a/src/main/java/seedu/algobase/ui/TaskCard.java +++ b/src/main/java/seedu/algobase/ui/TaskCard.java @@ -7,6 +7,7 @@ import javafx.scene.layout.FlowPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; +import javafx.scene.text.TextAlignment; import seedu.algobase.logic.parser.ParserUtil; import seedu.algobase.model.task.Task; @@ -54,14 +55,32 @@ public TaskCard(Task task, int displayedIndex) { super(FXML); this.task = task; id.setText(displayedIndex + ". "); + id.setWrapText(true); + id.setTextAlignment(TextAlignment.JUSTIFY); name.setText(task.getName().fullName + task.getStatusIcon()); + name.setWrapText(true); + name.setTextAlignment(TextAlignment.JUSTIFY); targetDate.setText(task.getTargetDate().format(ParserUtil.FORMATTER)); + targetDate.setWrapText(true); + targetDate.setTextAlignment(TextAlignment.JUSTIFY); author.setText(task.getAuthor().value); + author.setWrapText(true); + author.setTextAlignment(TextAlignment.JUSTIFY); description.setText(task.getDescription().value); + description.setWrapText(true); + description.setTextAlignment(TextAlignment.JUSTIFY); weblink.setText(task.getWebLink().value); + weblink.setWrapText(true); + weblink.setTextAlignment(TextAlignment.JUSTIFY); difficulty.setText(task.getDifficulty().toString()); + difficulty.setWrapText(true); + difficulty.setTextAlignment(TextAlignment.JUSTIFY); remark.setText(task.getRemark().value); + remark.setWrapText(true); + remark.setTextAlignment(TextAlignment.JUSTIFY); source.setText(task.getSource().value); + source.setWrapText(true); + source.setTextAlignment(TextAlignment.JUSTIFY); task.getTags().stream() .sorted(Comparator.comparing(tag -> tag.tagName)) .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); diff --git a/src/main/java/seedu/algobase/ui/TaskListPanel.java b/src/main/java/seedu/algobase/ui/TaskListPanel.java deleted file mode 100644 index 5719e5ad0c9..00000000000 --- a/src/main/java/seedu/algobase/ui/TaskListPanel.java +++ /dev/null @@ -1,45 +0,0 @@ -package seedu.algobase.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.algobase.commons.core.LogsCenter; -import seedu.algobase.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; - - 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/algobase/ui/TaskManagementPane.java b/src/main/java/seedu/algobase/ui/TaskManagementPane.java new file mode 100644 index 00000000000..e91709c0aeb --- /dev/null +++ b/src/main/java/seedu/algobase/ui/TaskManagementPane.java @@ -0,0 +1,108 @@ +package seedu.algobase.ui; + +import java.util.logging.Logger; + +import javafx.beans.value.ObservableIntegerValue; +import javafx.beans.value.ObservableStringValue; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.chart.PieChart; +import javafx.scene.chart.PieChart.Data; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.Region; +import seedu.algobase.commons.core.LogsCenter; +import seedu.algobase.model.task.Task; + +/** + * Panel containing the list of tasks. + */ +public class TaskManagementPane extends UiPart { + private static final String FXML = "TaskManagementPane.fxml"; + private final Logger logger = LogsCenter.getLogger(TaskManagementPane.class); + private ObservableList taskProgress = FXCollections.observableArrayList(); + + @FXML + private Label currentPlan; + @FXML + private ListView taskListView; + @FXML + private PieChart taskProgressChart; + + public TaskManagementPane(ObservableList taskList, ObservableStringValue plan, + ObservableIntegerValue solvedCount, ObservableIntegerValue unsolvedCount) { + super(FXML); + currentPlan.setText("Current Plan: " + plan.getValue()); + taskListView.setItems(taskList); + taskListView.setCellFactory(listView -> new TaskListViewCell()); + taskProgressChart.setData(getChartData((int) solvedCount.getValue(), (int) unsolvedCount.getValue())); + taskProgressChart.setClockwise(true); + taskProgressChart.setLabelsVisible(false); + taskProgressChart.setLegendVisible(true); + addListenerForPlanName(plan); + addListenerForPieChart(solvedCount, unsolvedCount); + } + + /** + * 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()); + } + } + } + + /** + * Obtains data for pie chart. + */ + private ObservableList getChartData(int solvedCount, int unsolvedCount) { + taskProgress.addAll(new PieChart.Data("Solved", solvedCount), + new PieChart.Data("Unsolved", unsolvedCount)); + return taskProgress; + } + + /** + * Adds a listener to watch for changes for plan name. + * + * @param s The observable plan name string. + */ + private void addListenerForPlanName(ObservableStringValue s) { + s.addListener((observable, oldValue, newValue) -> { + currentPlan.setText("Current Plan: " + newValue); + }); + } + + /** + * Adds a listener to watch for changes for task progress. + * + * @param solvedCount The observable solved count. + * @param unsolvedCount The observable unsolved count. + */ + private void addListenerForPieChart(ObservableIntegerValue solvedCount, ObservableIntegerValue unsolvedCount) { + solvedCount.addListener((observable, oldValue, newValue) -> { + for (Data d : taskProgress) { + if (d.getName().equals("Solved")) { + d.setPieValue((int) newValue); + } + } + }); + unsolvedCount.addListener((observable, oldValue, newValue) -> { + for (Data d : taskProgress) { + if (d.getName().equals("Unsolved")) { + d.setPieValue((int) newValue); + } + } + }); + } + +} diff --git a/src/main/java/seedu/algobase/ui/UiManager.java b/src/main/java/seedu/algobase/ui/UiManager.java index b309ee7437c..82d56bacb71 100644 --- a/src/main/java/seedu/algobase/ui/UiManager.java +++ b/src/main/java/seedu/algobase/ui/UiManager.java @@ -36,6 +36,7 @@ public void start(Stage primaryStage) { //Set the application icon. primaryStage.getIcons().add(getImage(ICON_APPLICATION)); + primaryStage.setMaximized(true); try { mainWindow = new MainWindow(primaryStage, logic); diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 38e1a8ac553..4b0c4cf7811 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -142,6 +142,12 @@ -fx-text-fill: white; } +.cell_big_label_white { + -fx-font-family: "Segoe UI Semibold"; + -fx-font-size: 16px; + -fx-text-fill: #FFFFFF; +} + .cell_big_label { -fx-font-family: "Segoe UI Semibold"; -fx-font-size: 16px; diff --git a/src/main/resources/view/DetailsTabPane.fxml b/src/main/resources/view/DetailsTabPane.fxml index ab8f1b403e0..45a4907fe56 100644 --- a/src/main/resources/view/DetailsTabPane.fxml +++ b/src/main/resources/view/DetailsTabPane.fxml @@ -3,7 +3,6 @@ - diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index c9340b0d75c..032b369b287 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -13,7 +13,7 @@ + title="AlgoBase App" minWidth="1000" minHeight="850" onCloseRequest="#handleExit"> diff --git a/src/main/resources/view/TaskListCard.fxml b/src/main/resources/view/TaskListCard.fxml index 236886f4b66..856a2bdf028 100644 --- a/src/main/resources/view/TaskListCard.fxml +++ b/src/main/resources/view/TaskListCard.fxml @@ -28,6 +28,7 @@