diff --git a/.classpath b/.classpath index 3f05f311a90b..0e145b194171 100644 --- a/.classpath +++ b/.classpath @@ -17,5 +17,17 @@ + + + + + + + + + + + + diff --git a/.project b/.project index 1c9339c5f927..9c40da49c514 100644 --- a/.project +++ b/.project @@ -1,10 +1,15 @@ - addressbook-level4 - Project addressbook-level4 created by Buildship. + main + Project daily planner created by Praveer, Yufeng, Ming Yi. + + org.eclipse.xtext.ui.shared.xtextBuilder + + + org.eclipse.jdt.core.javabuilder @@ -19,5 +24,26 @@ org.eclipse.buildship.core.gradleprojectnature org.eclipse.jdt.core.javanature + org.eclipse.xtext.ui.shared.xtextNature + + + 1475758393731 + + 26 + + org.eclipse.ui.ide.multiFilter + 1.0-projectRelativePath-matches-false-false-build + + + + 1475758393779 + + 26 + + org.eclipse.ui.ide.multiFilter + 1.0-projectRelativePath-matches-false-false-.gradle + + + diff --git a/README.md b/README.md index 249a00b3899c..a325ff19982b 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,13 @@ -[![Build Status](https://travis-ci.org/se-edu/addressbook-level4.svg?branch=master)](https://travis-ci.org/se-edu/addressbook-level4) -[![Coverage Status](https://coveralls.io/repos/github/se-edu/addressbook-level4/badge.svg?branch=master)](https://coveralls.io/github/se-edu/addressbook-level4?branch=master) +[![Build Status](https://travis-ci.org/CS2103AUG2016-F11-C4/main.svg?branch=master)](https://travis-ci.org/CS2103AUG2016-F11-C4/main) +[![Coverage Status](https://coveralls.io/repos/github/CS2103AUG2016-F11-C4/main/badge.svg?branch=master)](https://coveralls.io/github/CS2103AUG2016-F11-C4/main?branch=master) -# Address Book (Level 4) +# Daily Planner -
+
-* This is a desktop Address Book application. It has a GUI but most of the user interactions happen using - a CLI (Command Line Interface). -* It is a Java sample application intended for students learning Software Engineering while using Java as - the main programming language. -* It is **written in OOP fashion**. It provides a **reasonably well-written** code example that is - **significantly bigger** (around 6 KLoC)than what students usually write in beginner-level SE modules. -* What's different from [level 3](https://github.com/se-edu/addressbook-level3): - * A more sophisticated GUI that includes a list panel and an in-built Browser. - * More test cases, including automated GUI testing. - * Support for *Build Automation* using Gradle and for *Continuous Integration* using Travis CI. +* This is a desktop Daily Planner application. It has a GUI but most of the user interactions happen using a CLI (Command Line Interface). +* It is a To-Do List application that helps the user plan and organise their daily schedule automatically. - #### Site Map * [User Guide](docs/UserGuide.md) * [Developer Guide](docs/DeveloperGuide.md) @@ -27,8 +18,8 @@ #### Acknowledgements -* Some parts of this sample application were inspired by the excellent - [Java FX tutorial](http://code.makery.ch/library/javafx-8-tutorial/) by *Marco Jakob*. +The application is based off a sample project created by created by the SE-EDU initiative +at https://github.com/se-edu/. #### Licence : [MIT](LICENSE) diff --git a/build.gradle b/build.gradle index 46b06c1e42ec..c3510a64be1b 100644 --- a/build.gradle +++ b/build.gradle @@ -23,6 +23,10 @@ allprojects { mavenCentral() maven { url "https://repo.eclipse.org/content/repositories/egit-releases/" } maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } + + flatDir { + dirs 'libs' + } } // This part is similar to global variables @@ -52,6 +56,8 @@ allprojects { compile "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion" compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonDataTypeVersion" compile "com.google.guava:guava:$guavaVersion" + + compile fileTree(dir: 'libs', include: ['*.jar']) testCompile "junit:junit:$junitVersion" testCompile "org.testfx:testfx-core:$testFxVersion" diff --git a/collated/docs/A0139102U.md b/collated/docs/A0139102U.md new file mode 100644 index 000000000000..74bbfc1c1f62 --- /dev/null +++ b/collated/docs/A0139102U.md @@ -0,0 +1,187 @@ +# A0139102U +###### \DeveloperGuide.md +``` md +## Appendix A : User Stories + +Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` + + +Priority | As a ... | I want to ... | So that I can... +-------- | :-------- | :--------- | :----------- +`* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App +`* * *` | user | add new task | find out tasks are urgently due +`* * *` | user | set deadlines | be reminded of my due dates for my respective tasks +`* * *` | user | delete a task | remove entries that are completed +`* * *` | user | find a task by name | search for a task without having to go through the entire list +`* * *` | user | edit a task and its particulars | update instantly if there are any changes in the task +`* *` | user | mark my tasks | know which ones are completed and uncompleted +`* *` | user | undo my last action | amend my mistakes immediately or put lost information back completely +`* *` | user | view the tasks | take a quick glance or have my tasks presented according to my needs and preference +`* *` | busy user | sort my tasks | rank the importance and urgency(time and date) of my activities +`*` | advanced user | pin my tasks on a new list | remind myself which are the tasks that require my constant attention +`*` | advanced user | use natural language| specify my dates and time without using a fixed format + +## Appendix B : Use Cases + +(For all use cases below, the **System** is the `DailyPlanner` and the **Actor** is the `user`, unless specified otherwise) + +#### Use case: Add task + +**MSS** + +1. User requests to add task with minimum input `TASKNAME` +2. Task Manager adds task and displays the new list of tasks +Use case ends. + +**Extensions** + +1a. Name of task missing + +>1a1. Daily Planner shows an error message
+ Use case resumes back to step 1 + +1b. Time slot for task is already filled +>1b1. Daily Planner warns user that time slot clashes
+ Use case resumes + +1c. Format is invalid +> 3b1. Daily Planner shows an error message
+ Use case resumes + + +#### Use case: Delete task + +**MSS** + +1. User requests to view tasks for specific time period or completed list +2. Daily Planner displays all tasks during time period or the completed tasks +3. User requests to delete a specific task in the list or the clear the completed task list +4. Daily Planner deletes the task(s) +Use case ends + +**Extensions** + +2a. The list is empty +>Use case ends + +3a. Given index is invalid +>3b1. Task manager shows error message
+ Use case resumes at step 2 + + +#### Use case: Completing task + +**MSS** + +1. User requests to view tasks for specific time period +2. Daily Planner displays all tasks during time period +3. User requests to mark a specific task in the list as completed +4. Daily Planner mark the task as completed +Use case ends + +**Extensions** + +2a. The list is empty +>Use case ends + +3a. Given index is invalid +>3b1. Task manager shows error message
+ Use case resumes at step 2 + + +#### Use case: Pin a task + +**MSS** + +1. User requests to view tasks for specific time period +2. Task Manager displays all tasks during time period +3. User requests to pin a specific task in the current schedule list to the pinned list +4. Task Manager puts the task as on pinned list and displays it there +Use case ends + +**Extensions** + +2a. The list is empty +>Use case ends + +3a. Given index is invalid +>3b1. Task manager shows error message
+ Use case resumes at step 2 + + +#### Use case: Unpin a task + +**MSS** + +1. User refers to the INDEX on the pinned list +2. User requests to unpin a specific task in the pinned list to the tasks list +3. Task Manager removes the task from the pinned list +Use case ends + +**Extensions** + +2a. The list is empty +>Use case ends + +2a. Given index is invalid +>3b1. Task manager shows error message
+Use case resumes at step 2 + +``` +###### \UserGuide.md +``` md +### Exiting the program : `exit` +Exits the program.
+Format: `exit`
+ + +### Saving the data +Daily Planner data is saved in the hard disk automatically after any command that changes the data.
+There is no need to save manually. + +
+ + + + + +## Descriptions and Usage +### Tasks +The daily planner stores all of the users events as a `Task`. + +A `Task` can be viewed, added, searched, edited or deleted with the right command word as can be found in [Commands](#commands). + +**Every `Task` must consist of a mandatory `TASKNAME` field and optional `START`, `END`, `CATEGORY` fields.** + +#### `START` and `END` Field +The `START` and `END` field, if specified, tells the Daily Planner when a task should start and end. +
+Both `START` and `END` fields consist of either a `DATE` field, a `TIME` field or both `DATE` `TIME` fields, separated by a whitespace. +
+This allows for natural descriptors of starting and ending times. +
+The `TIME` field must be given in 12hr am or pm format. +
+The following are all valid `START` and `END` fields: +``` + +11/11/2016 5pm +The 31st of April in the year 2008 +Fri, 21 Nov 1997 +Jan 21, '97 +Sun, Nov 21 5am +jan 1st 9pm +february twenty-eighth +next thursday +last wednesday +today +tomorrow 6pm +yesterday 3pm +next week +next month +next year +3 days from now +three weeks ago +``` + +``` diff --git a/collated/docs/A0140124B.md b/collated/docs/A0140124B.md new file mode 100644 index 000000000000..73d7162a97bf --- /dev/null +++ b/collated/docs/A0140124B.md @@ -0,0 +1,148 @@ +# A0140124B +###### \DeveloperGuide.md +``` md +#### Use case: View task + +**MSS** + +1. User requests to view tasks for specific time period or completed tasks +2. Task Manager displays all tasks during time period or tasks that are completed + +Use case ends + + +#### Use case: Edit task + +**MSS** + +1. User requests to list tasks for a specific time period +2. Task Manager displays all tasks during time period +3. User requests to edit a specific task in the list +4. Task manager makes the edits
+Use case ends. + + +**Extensions** + +2a. The list is empty + +> Use case ends + +3a. The given index is invalid + +> 3a1. Task Manager shows an error message
+> Use case resumes at step 2 + +3b. Format is invalid +> 3b1. Task Manager shows an error message
+> Use case resumes at step 2 + + + + +
+ +``` +###### \UserGuide.md +``` md +### Editing a task: `edit` + +Edits a particular task's details
+Format: +``` +edit [INDEX] [NEWTASKNAME](optional) [NEWSTART](optional) [NEWEND](optional) [NEWCATEGORY](optional) +``` +Examples:
+`edit 2 tomorrow`
+`s/wednesday 4pm e/6pm` (only changes date and time)
+ + +### Deleting a task : `delete` + +Description: Deletes a task from the planner.
+ +Format: `delete [INDEX]` + +> Deletes the task at the specified `INDEX`. + The index refers to the index number shown on the list that is currently being viewed
+ The index **must be a positive integer** 1, 2, 3, ... + +Examples:
+```delete 5``` +>Deletes task 5 of current list being viewed + +### Completing a task : `complete` + +Description: Marks a task as completed from the planner.
+ +Format: `complete [INDEX]` + +> Marks the task at the specified `INDEX` as completed. + The index refers to the index number shown on the list that is currently being viewed
+ The index **must be a positive integer** 1, 2, 3, ... + +Examples:
+```complete 5``` +>Task 5 of current list being viewed is marked as completed
+ +### Pin important task on the pinned task list: `pin` + +Description: Pins task on the list on the left.
+ +Format: `pin [INDEX]` + +> Pins the task at the specified `INDEX`. + The index refers to the index number shown on the list that is currently being viewed
+ The index **must be a positive integer** 1, 2, 3, ... + + +Examples:
+```pin 5``` +>Task 5 of current list is being pinned on the pinned list
+ + +### Remove important task on the pinned task list: `unpin` + +Description: Unpins task on the list from the pinned tasks list.
+ +Format: `unpin INDEX` + +> Unpins the task at the specified `INDEX`. + The index refers to the index number shown on the pinned tasks list(left)
+ The index **must be a positive integer** 1, 2, 3, ... + + +Examples:
+```unpin 5``` +>Task 5 of pinned list is being removed and put back to the schedule list
+ + +### Undo last command: `undo` + +Description: Undo the latest command.
+ +Format: `undo` + +>Undo the last command. If the last command was `add`, the task added will be removed if `undo` is invoked
+ + +Examples:
+`undo` + +Undo previous command
+ + +### Delete every task: `clear` + +Description: Clears all tasks.
+ +Format: `clear` + +>Clears all task from the list
+ + +Examples:
+`clear` +>The list is now empty
+ +``` diff --git a/collated/docs/A0146749N.md b/collated/docs/A0146749N.md new file mode 100644 index 000000000000..7a7701616dbe --- /dev/null +++ b/collated/docs/A0146749N.md @@ -0,0 +1,165 @@ +# A0146749N +###### \DeveloperGuide.md +``` md +## Appendix C : Non Functional Requirements + +1. Should work on any [mainstream OS](#mainstream-os) as long as it has Java `1.8.0_60` or higher installed. +2. Should be able to hold up to 1000 tasks. +3. Should come with automated unit tests and open source code. +4. Should favor DOS style commands over Unix-style commands. +5. Task input should not be more than 200 characters to be concise. + +{More to be added} + +## Appendix D : Glossary + +##### Mainstream OS + +> Windows, Linux, Unix, OS-X + +##### Private contact detail + +> A contact detail that is not meant to be shared with others + +## Appendix E : Product Survey +Our product survey is researched and presented from our client's point of view - Jim does not use cloud-based platforms and prefers to key in his tasks in a single command line to clicking. + +| | Wunderlist | Google Calendar | Todoist | +|---|---|---|---| +| Strengths |
  • Elegant and simple
  • CLI input but graphical display
  • Add notes and timestamps to tasks
  • Ability to create email with list of tasks automatically
  • Organize tasks in different lists
|
  • Quick add feature to minimize use of mouse
  • Ability to allocate tasks to specific timeslots
  • Ability to view free timeslots
  • Ability to set reminders
|
  • CLI input but graphical display
  • Keyboard shortcuts to minimize use of mouse
  • Ability to set deadlines and reminders for tasks
  • Ability to set priorities for tasks to decide which task to do next
| +| Irrelevant features |
  • Cross-platform functionality
  • Syncs between devices
  • Collaboration with others
|
  • Collaboration with others
|
  • Cross-platform functionality
  • Collaboration with others
| +| Weaknesses |
  • No way to allocate tasks to a specific timeslot
  • No way to view free timeslots
|
  • Does not provide a desktop software for easy summoning using keyboard shortcuts
  • No easy way to block multiple timeslots when the exact timing of a task is uncertain and release the blocked slots when the time is finalized
  • Not good with capturing tasks that need to be done before a specific date/time, or after a specific date/time, and items without specific times
  • No support for offline access
|
  • No way to allocate tasks to a specific timeslot
  • No way to view free timeslots
| + + + + + + + + + + + + + + +``` +###### \UserGuide.md +``` md +1. Ensure that you have the latest Java version ‘1.8.0_60’ or later installed in your Computer.
+ > Having any Java 8 version is not enough
+ > The application will not work for any earlier Java versions +2. Find the project in the `Project Explorer` or `Package Explorer` (usually located at the left side) +3. Right click on the project +4. Click `Run As` > `Java Application` and choose the `Main` class. The GUI should appear within split second. +5. Type the command into the command box and press Enter to execute the command.
+ List of commands:
+ **`help`** : opens instruction
+ **`add`** : adds a task
+ **`show`** : shows all tasks
+ **`find`** : searches for a task
+ **`edit`** : edits a task
+ **`delete`** : deletes a task
+ **`complete`** : marks a task as completed
+ **`pin`** : pin tasks
+ **`unpin`** : removes tasks from the pinned list
+ **`undo`** : reverts the task list back to the previous state
+ **`clear`** : deletes every task on the list
+ +7. Refer to the [Commands](#commands) section below for details of each command.
+ + +# Features + + + +## Commands +### Viewing help : `help` +Format: + +``` +help +``` + +> Help is also shown if you enter an incorrect command e.g. `abcd` + +### Adding a task : `add` + +Adds a task to the planner

+Format:
+``` +1. add [TASKNAME] s/[START](optional) e/[END](optional) c/[CATEGORY]...(optional) + +``` + +Examples:
+ +1. `add travel`
+> Task with no specified timing is added to today's schedule
+ +2. `add meeting s/tomorrow 2pm e/4pm`
+> Fixed task is added from 2pm to 4pm the next day
+ +3. `add math homework e/7 nov 6pm`
+> Task with a deadline is added
+ + +### Viewing a schedule : `show` + +Shows tasks based on query(date or completion) + +Format: + +``` +show [DATE]/[COMPLETION](optional) +``` + +Examples:
+`show`
+>Shows all tasks
+ +`show today`
+>Shows schedule for today
+ +`show complete`
+>Shows completed tasks
+ +`show not complete`
+>Shows uncompleted tasks
+ +`show next wednesday`
+>Shows schedule for next wednesday
+ + +
+ +The show function will sort the users's schedule and display it. + +Tasks are sorted by their urgency(how close it is to its deadline). + +The user can then use this recommended schedule to follow the order of the tasks and worryless-ly go about their day. + + +### Searching for a task: `find` + +Searches for a particular task and displays more information about it.
+ +Format: + +``` +find TASKNAME +``` + +Examples: + +``` +find cs lecture +``` + +> * The search is case insensitive. +> * The order of the keywords does not matter. e.g. `math assignment` will match `assignment math` +> * Only the name is searched. +> * Task matching at least one keyword will be returned (i.e. `OR` search). + e.g. `math` will match `math lecture` + +``` diff --git a/collated/main/A0139102U.md b/collated/main/A0139102U.md new file mode 100644 index 000000000000..c1b23dab548d --- /dev/null +++ b/collated/main/A0139102U.md @@ -0,0 +1,796 @@ +# A0139102U +###### \java\seedu\dailyplanner\commons\util\ArgumentFormatUtil.java +``` java +public class ArgumentFormatUtil { + + public static boolean isValidAddArgumentFormat(String trimmedArgs) { + if (trimmedArgs.length() != 1 && trimmedArgs.charAt(1) == '/') { + return false; + } + for (int k = 0; k < trimmedArgs.length(); k++) { + if (trimmedArgs.charAt(k) == '/') { + if (!(k + 1 == trimmedArgs.length())) { + if (trimmedArgs.charAt(k + 1) == ' ') { + return false; + } + } else { + if (trimmedArgs.charAt(k) == '/') + return false; + } + + } + } + return true; + } + + public static boolean isValidEditArgumentFormat(String args) { + args = args.trim(); + //If no index, return invalid + if(noIndexInArgs(args)) { + return false; + } + + //Return false if string is only a number + if (StringUtils.isNumeric(args)) { + return false; + } + + //Return false if no space before s/ + if(args.contains("s/")) { + if (ifNoWhiteSpaceBeforeParameter(args, "s/")) { + return false; + } + } + + //Return false if no space before e/ + if(args.contains("e/")) { + if (ifNoWhiteSpaceBeforeParameter(args, "e/")) { + return false; + } + } + //Return false if no space before c/ + if(args.contains("c/")) { + if (ifNoWhiteSpaceBeforeParameter(args, "c/")) { + return false; + } + } + return true; + } + + private static boolean ifNoWhiteSpaceBeforeParameter(String args, String parameter) { + return args.charAt(args.indexOf(parameter)-1) != ' '; + } + + private static boolean noIndexInArgs(String args) { + return !Character.isDigit(args.charAt(0)); + } +} +``` +###### \java\seedu\dailyplanner\commons\util\StringUtil.java +``` java + public static final String EMPTY_STRING = ""; + + public static boolean containsIgnoreCase(String source, String query) { + String[] split = source.toLowerCase().split("\\s+"); + List strings = Arrays.asList(split); + return strings.stream().filter(s -> s.equals(query.toLowerCase())).count() > 0; + } + + /** + * Returns a detailed message of the t, including the stack trace. + */ + public static String getDetails(Throwable t) { + assert t != null; + StringWriter sw = new StringWriter(); + t.printStackTrace(new PrintWriter(sw)); + return t.getMessage() + "\n" + sw.toString(); + } + + /** + * Returns true if s represents an unsigned integer e.g. 1, 2, 3, ...
+ * Will return false for null, empty string, "-1", "0", "+1", and " 2 " + * (untrimmed) "3 0" (contains whitespace). + * + * @param s + * Should be trimmed. + */ + public static boolean isUnsignedInteger(String s) { + return s != null && s.matches("^0*[1-9]\\d*$"); + } + + + public static boolean stringContainsAmOrPm(String startString) { + return startString.contains("am") || startString.contains("pm"); + } + + /* + * Loops through arguments, adds them to hashmap if valid + */ +``` +###### \java\seedu\dailyplanner\history\HistoryManager.java +``` java +public class HistoryManager { + + private Stack recordCommand = new Stack(); + + public Instruction getLastInstruction() { + return recordCommand.pop(); + } + + public void stackAddInstruction(ReadOnlyTask toPush) { + recordCommand.push(new Instruction("A", toPush)); + } + + public void stackDeleteInstruction(ReadOnlyTask toPush) { + recordCommand.push(new Instruction("D", toPush)); + } + + public void stackEditInstruction(ReadOnlyTask originalTask, ReadOnlyTask editedTask) { + recordCommand.push(new Instruction("EA", originalTask)); + recordCommand.push(new Instruction("ED", editedTask)); + } + + public void stackUnpinInstruction(ReadOnlyTask taskToUnpin) { + recordCommand.push(new Instruction("UP", taskToUnpin)); + } + + public void stackPinInstruction(ReadOnlyTask taskToUnpin) { + recordCommand.push(new Instruction("P", taskToUnpin)); + } + + public void stackUncompleteInstruction(ReadOnlyTask taskToUncomplete) { + recordCommand.push(new Instruction("UC", taskToUncomplete)); + } + + public void stackCompleteInstruction(ReadOnlyTask taskToComplete) { + recordCommand.push(new Instruction("C", taskToComplete)); + } +} +``` +###### \java\seedu\dailyplanner\history\Instruction.java +``` java +public class Instruction { + + private String reverseCommand; + private ReadOnlyTask task; + + public Instruction(String cmd, ReadOnlyTask task) { + this.reverseCommand = cmd; + this.task = task; + } + + public String getReverse() { + return reverseCommand; + } + + public void setReverse(String cmd) { + this.reverseCommand = cmd; + } + + public ReadOnlyTask getTask() { + return this.task; + } +} +``` +###### \java\seedu\dailyplanner\logic\commands\AddCommand.java +``` java + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a task to the daily planner. " + + "Format: add [TASKNAME] s/[STARTDATE] [STARTTIME] e/[ENDDATE] [ENDTIME] c/CATEGORY...\n" + "Example: " + + COMMAND_WORD + " CS2103 Assignment s/today 10pm e/11pm c/urgent c/important"; + + public static final String MESSAGE_SUCCESS = "New task added: %1$s"; + public static final String MESSAGE_DUPLICATE_TASK = "This task already exists in the daily planner"; + public static final String MESSAGE_WARNING_CLASH = "Warning! Current timeslot clashes with the following task: %1$s"; + private List taskList; + private final Task toAdd; + + /** + * Convenience constructor using raw values. + * + * @param endDate + * + * @throws IllegalValueException + * if any of the raw values are invalid + */ + + public AddCommand(String taskName, DateTime start, DateTime end, Set cats) throws IllegalValueException { + final Set catSet = new HashSet<>(); + for (String catName : cats) { + catSet.add(new Category(catName)); + } + this.toAdd = new Task(taskName, start, end, false, false, new UniqueCategoryList(catSet)); + + } +``` +###### \java\seedu\dailyplanner\logic\commands\AddCommand.java +``` java + /** + * Returns the index of the task clashing with argument, returns -1 if no + * clash + */ + public int getIndexOfClashingTask(Task toCheck) { + + if (!(DateUtil.hasStartandEndTime(toCheck))) { + return -1; + } + Time toAddStartTiming = toCheck.getStart().getTime(); + Time toAddEndTiming = toCheck.getEnd().getTime(); + + for (int i = 0; i < taskList.size(); i++) { + ReadOnlyTask storedTask = taskList.get(i); + if (DateUtil.hasStartandEndTime(storedTask)) { + if (notSameTask(toCheck, storedTask)) { + if (isSameStartDate(toCheck, storedTask)) { + Time tasksEndTiming = storedTask.getEnd().getTime(); + Time tasksStartTiming = storedTask.getStart().getTime(); + + if (isStartTimeClashing(toAddStartTiming, tasksEndTiming, tasksStartTiming)) { + return i; + } + if (isEndTimeClashing(toAddEndTiming, tasksEndTiming, tasksStartTiming)) { + return i; + } + if (timingSpansEntireTask(toAddStartTiming, toAddEndTiming, tasksEndTiming, tasksStartTiming)) { + return i; + } + } + } + + } + } + return -1; + } + + private boolean notSameTask(Task toCheck, ReadOnlyTask storedTask) { + return !(toCheck == storedTask); + } + + private boolean timingSpansEntireTask(Time toAddStartTiming, Time toAddEndTiming, Time tasksEndTiming, + Time tasksStartTiming) { + return ((toAddEndTiming.compareTo(tasksEndTiming) > 0) || (toAddEndTiming.compareTo(tasksEndTiming) == 0)) + && ((toAddStartTiming.compareTo(tasksStartTiming) < 0) + || (toAddStartTiming.compareTo(tasksStartTiming) == 0)); + } + + private boolean isEndTimeClashing(Time toAddEndTiming, Time tasksEndTiming, Time tasksStartTiming) { + return (toAddEndTiming.compareTo(tasksStartTiming) > 0) && (toAddEndTiming.compareTo(tasksEndTiming) < 0); + } + + private boolean isStartTimeClashing(Time toAddStartTiming, Time tasksEndTiming, Time tasksStartTiming) { + return (toAddStartTiming.compareTo(tasksEndTiming) < 0) && (toAddStartTiming.compareTo(tasksStartTiming) > 0); + } + + private boolean isSameStartDate(Task toCheck, ReadOnlyTask storedTask) { + return toCheck.getStart().getDate().compareTo(storedTask.getStart().getDate()) == 0; + } + +} +``` +###### \java\seedu\dailyplanner\logic\commands\ClearCommand.java +``` java + @Override + public CommandResult execute() { + assert model != null; + model.resetData(DailyPlanner.getEmptyDailyPlanner()); + model.resetPinBoard(); + model.setLastTaskAddedIndex(0); + return new CommandResult(MESSAGE_SUCCESS); + } +} +``` +###### \java\seedu\dailyplanner\logic\commands\CompleteCommand.java +``` java + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Marks the task identified by the index number shown in current list\n" + + "Format: complete [INDEX] (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_COMPLETED_TASK_SUCCESS = "Completed Task: %1$s"; + + public CompleteCommand(int targetIndex) { + this.targetIndex = targetIndex; + } +``` +###### \java\seedu\dailyplanner\logic\commands\DeleteCommand.java +``` java + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the task identified by the index number shown in current list\n" + + "Format: [INDEX] (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_TASK_SUCCESS = "Deleted Task: %1$s"; + + public final int targetIndex; + + public DeleteCommand(int targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + ReadOnlyTask taskToDelete = lastShownList.get(targetIndex - 1); + + try { + model.getHistory().stackAddInstruction(taskToDelete); + model.deleteTask(taskToDelete); + model.updatePinBoard(); + + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + + return new CommandResult(String.format(MESSAGE_DELETE_TASK_SUCCESS, taskToDelete)); + } + +} +``` +###### \java\seedu\dailyplanner\logic\commands\DeleteCompletedCommand.java +``` java +public static final String MESSAGE_USAGE = DeleteCommand.COMMAND_WORD + ": Deletes all completed tasks.\n" + + "Format: [INDEX] (must be a positive integer)\n" + "Example: " + DeleteCommand.COMMAND_WORD + + " completed"; + + public static final String MESSAGE_DELETE_TASK_SUCCESS = "Deleted Task: %1$s"; + + public DeleteCompletedCommand() { + } +``` +###### \java\seedu\dailyplanner\logic\commands\DeleteCompletedCommand.java +``` java + @Override + public CommandResult execute() { + + final Set keywordSet = new HashSet<>(Arrays.asList(new String[] { "complete" })); + model.updateFilteredTaskListByCompletion(keywordSet); + UnmodifiableObservableList completedList = model.getFilteredTaskList(); + + int size = completedList.size(); + for (int i = 0; i < size; i++) { + ReadOnlyTask taskToDelete = completedList.get(0); + + try { + model.getHistory().stackAddInstruction(taskToDelete); + model.deleteTask(taskToDelete); + model.updatePinBoard(); + + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + } + + model.updateFilteredListToShowAll(); + model.setLastShowDate(StringUtil.EMPTY_STRING); + return new CommandResult(String.format(MESSAGE_DELETE_TASK_SUCCESS, "all completed")); + } +} +``` +###### \java\seedu\dailyplanner\logic\commands\EditCommand.java +``` java + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Edits the task identified by the index number shown in current list.\n" + + "Format: edit [INDEX] (must be a positive integer)[TASKNAME] s/[STARTDATE] [STARTTIME] e/[ENDDATE] [ENDTIME]\n" + + "Example: " + COMMAND_WORD + " 2 s/3pm"; + + public static final String MESSAGE_DUPLICATE_TASK = "This task already exists in the daily planner"; + public static final String MESSAGE_EDIT_TASK_SUCCESS = "Edited Task: %1$s"; + + public final int targetIndex; + private final Optional taskName; + private final Optional start; + private final Optional end; + private Optional categoriesSet; + + public EditCommand(int targetIndex, String taskName, DateTime start, DateTime end, Set cats) + throws IllegalValueException { + this.targetIndex = targetIndex; + this.taskName = Optional.ofNullable(taskName); + this.start = Optional.ofNullable(start); + this.end = Optional.ofNullable(end); + this.categoriesSet = Optional.empty(); + + if (cats.size() != 0) { + final Set catSet = new HashSet<>(); + + for (String catName : cats) { + catSet.add(new Category(catName)); + } + this.categoriesSet = Optional.of(new UniqueCategoryList(catSet)); + } + } + +``` +###### \java\seedu\dailyplanner\logic\commands\FindCommand.java +``` java + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all tasks whose names contain any of " + + "the specified keywords (case-sensitive) and displays them as a list with index numbers.\n" + + "Format: KEYWORD [MORE_KEYWORDS]...\n" + "Example: " + COMMAND_WORD + " homework lecture"; + + private final Set keywords; + + public FindCommand(Set keywords) { + this.keywords = keywords; + } + + @Override + public CommandResult execute() { + model.updateFilteredTaskList(keywords); + return new CommandResult(getMessageForTaskListShownSummary(model.getFilteredTaskList().size())); + } + +} +``` +###### \java\seedu\dailyplanner\logic\commands\PinCommand.java +``` java + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Pins the task identified by the index number used in the last task listing on the pin board.\n" + + "Format: pin [INDEX] (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_PINNED_TASK_SUCCESS = "Pinned Task: %1$s"; + public static final String MESSAGE_DUPLICATE_PINNED_TASK = "Task is already pinned."; +``` +###### \java\seedu\dailyplanner\logic\commands\SelectCommand.java +``` java + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Selects the task identified by the index number used in the last task listing.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_SELECT_TASK_SUCCESS = "Selected Task: %1$s"; + + public SelectCommand(int targetIndex) { + this.targetIndex = targetIndex; + } +``` +###### \java\seedu\dailyplanner\logic\commands\UncompleteCommand.java +``` java + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Unmarks a completed task identified by the index number used in the last task listing as incomplete.\n" + + "Format: uncomplete [INDEX] (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_UNCOMPLETED_TASK_SUCCESS = "Uncompleted Task: %1$s"; + + public UncompleteCommand(int targetIndex) { + this.targetIndex = targetIndex; + } + +``` +###### \java\seedu\dailyplanner\logic\commands\UndoCommand.java +``` java + @Override + public CommandResult execute() { + Instruction undoInstruction = model.getHistory().getLastInstruction(); + ReadOnlyTask taskToUndo = null; + + taskToUndo = undoInstruction.getTask(); + + if (undoInstruction.getReverse().equals("A")) { + try { + model.addTask((Task) taskToUndo); + } catch (IllegalValueException e) { + e.printStackTrace(); + } + } + + if (undoInstruction.getReverse().equals("D")) { + try { + model.deleteTask(taskToUndo); + } catch (TaskNotFoundException e) { + e.printStackTrace(); + } + } + + if (undoInstruction.getReverse().equals("ED")) { + try { + model.deleteTask(taskToUndo); + + } catch (TaskNotFoundException e) { + e.printStackTrace(); + } + + // Get next instruction from stack to generate and add the old task + // back + // The instruction is guaranteed to be an "EA" instruction + undoInstruction = model.getHistory().getLastInstruction(); + taskToUndo = null; + + taskToUndo = undoInstruction.getTask(); + + try { + model.addTask((Task) taskToUndo); + } catch (DuplicateTaskException e) { + e.printStackTrace(); + } + } + + if (undoInstruction.getReverse().equals("UP")) { + int indexInPinBoard = model.getPinnedTaskList().indexOf(taskToUndo); + try { + model.unpinTask(indexInPinBoard); + } catch (TaskNotFoundException e) { + e.printStackTrace(); + } + } + + if (undoInstruction.getReverse().equals("P")) { + try { + model.pinTask(taskToUndo); + } catch (TaskNotFoundException e) { + e.printStackTrace(); + } + } + + if (undoInstruction.getReverse().equals("UC")) { + int indexInTaskList = model.getFilteredTaskList().indexOf(taskToUndo); + model.uncompleteTask(indexInTaskList); + } + + if (undoInstruction.getReverse().equals("C")) { + try { + model.markTaskAsComplete(taskToUndo); + } catch (TaskNotFoundException e) { + e.printStackTrace(); + } + } + + model.updatePinBoard(); + + return new CommandResult(String.format(MESSAGE_SUCCESS)); + + } + +} +``` +###### \java\seedu\dailyplanner\logic\parser\Parser.java +``` java + case CompleteCommand.COMMAND_WORD: + return prepareComplete(arguments); + + case UncompleteCommand.COMMAND_WORD: + return prepareUncomplete(arguments); + + case UndoCommand.COMMAND_WORD: + return new UndoCommand(); + + case PinCommand.COMMAND_WORD: + return preparePin(arguments); + + case UnpinCommand.COMMAND_WORD: + return prepareUnpin(arguments); + + case ShowCommand.COMMAND_WORD: + if (arguments.equals("")) + return new ShowCommand(); + else + return prepareShow(arguments); + + default: + return new IncorrectCommand(MESSAGE_UNKNOWN_COMMAND); + } + } + +``` +###### \java\seedu\dailyplanner\logic\parser\Parser.java +``` java + /** + * Parses arguments in the context of the edit task command. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command prepareEdit(String arguments) { + + int index = 0; + String taskName = null; + DateTime formattedStart = null, formattedEnd = null; + Set categories = new HashSet(); + + String trimmedArgs = arguments.trim(); + + if (!(ArgumentFormatUtil.isValidEditArgumentFormat(arguments))) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + + HashMap mapArgs = parseEdit(trimmedArgs); + + // If arguments are in hashmap, pass them to editCommand, if not pass + // them as empty string + // Change date to "dd/mm/yy/", time to "hh:mm" + + nattyParser natty = new nattyParser(); + + if (mapArgs.containsKey("index")) { + index = Integer.parseInt(mapArgs.get("index")); + } + if (mapArgs.containsKey("taskName")) { + taskName = mapArgs.get("taskName"); + } + if (mapArgs.containsKey("start")) { + formattedStart = extractStart(mapArgs, natty); + } + if (mapArgs.containsKey("end")) { + formattedEnd = extractEnd(formattedStart, mapArgs, natty); + } + if (mapArgs.containsKey("cats")) { + categories = extractCategories(mapArgs); + } + + try { + return new EditCommand(index, taskName, formattedStart, formattedEnd, categories); + } catch (IllegalValueException ive) { + return new IncorrectCommand(ive.getMessage()); + } + } + + /** + * Parses arguments in the context of the add task command. + * + * @param args + * full command args string + * @return the prepared command + */ + +``` +###### \java\seedu\dailyplanner\logic\parser\Parser.java +``` java + /** + * Parses arguments in the context of the select task command. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command prepareSelect(String args) { + String trimmedArg = args.trim(); + Optional index = parseIndex(trimmedArg); + if (!index.isPresent()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, SelectCommand.MESSAGE_USAGE)); + } + return new SelectCommand(index.get()); + } + + /** + * Returns the specified index in the {@code command} IF a positive unsigned + * integer is given as the index. Returns an {@code Optional.empty()} + * otherwise. + */ + private Optional parseIndex(String command) { + final Matcher matcher = TASK_INDEX_ARGS_FORMAT.matcher(command.trim()); + if (!matcher.matches()) { + return Optional.empty(); + } + + String index = matcher.group("targetIndex"); + if (!StringUtil.isUnsignedInteger(index)) { + return Optional.empty(); + } + return Optional.of(Integer.parseInt(index)); + + } + + /** + * Parses arguments in the context of the find task command. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command prepareFind(String args) { + String trimmedArg = args.trim(); + final Matcher matcher = KEYWORDS_ARGS_FORMAT.matcher(trimmedArg); + if (!matcher.matches()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + } + + // keywords delimited by whitespace + final String[] keywords = matcher.group("keywords").split("\\s+"); + final Set keywordSet = new HashSet<>(Arrays.asList(keywords)); + return new FindCommand(keywordSet); + } + +} +``` +###### \java\seedu\dailyplanner\model\Model.java +``` java + /** Marks the given task as complete */ + void markTaskAsComplete(ReadOnlyTask taskToComplete) throws TaskNotFoundException; + + /** Unmarks the given task as incomplete */ + void markTaskAsIncomplete(ReadOnlyTask taskToIncomplete) throws TaskNotFoundException; + + /** Pins the given task. */ + void pinTask(ReadOnlyTask taskToPin) throws TaskNotFoundException; + + /** Unpins the given task. */ + void unpinTask(int i) throws TaskNotFoundException; + + /** Returns the filtered task list as an {@code UnmodifiableObservableList} */ + UnmodifiableObservableList getFilteredTaskList(); + + /** Returns the list of pinned task as an {@code UnmodifiableObservableList} */ + UnmodifiableObservableList getPinnedTaskList(); + + /** Updates the filter of the filtered task list to show all tasks */ + void updateFilteredListToShowAll(); + + /** Updates the filter of the filtered task list to filter by the given keywords*/ + void updateFilteredTaskList(Set keywords); + + /** Updates the filter of the filtered task list to filter by the given date*/ + void updateFilteredTaskListByDate(Set keywords); + + /** Updates the filter of the filtered task list to show only completed tasks*/ + void updateFilteredTaskListByCompletion(Set keywords); + + /** Returns the index of the last task that was added to the task list */ + public int getLastTaskAddedIndex(); + + /** Sets the stored index of the last task added */ + public void setLastTaskAddedIndex(int index); + + /** Returns the last task added index as the property itself */ + public IntegerProperty getLastTaskAddedIndexProperty(); + + /** Returns last shown date command */ + public String getLastShowDate(); + + /** Sets last shown date given by show command, this date is dislayed in green beside 'Your Tasks' in GUI*/ + public void setLastShowDate(String showInput); + + /** Returns the StringProperty holding the last shown date command */ + public StringProperty getLastShowDateProperty(); + + /** Resets the pinboard to an empty pinboard */ + public void resetPinBoard(); + + /** Uncompletes task with given index in taskList */ + public void uncompleteTask(int indexInTaskList); + + /** Refreshes the pin board after a command is carried out */ + public void updatePinBoard(); + +} +``` +###### \java\seedu\dailyplanner\model\ModelManager.java +``` java + public synchronized void markTaskAsComplete(ReadOnlyTask taskToComplete) throws TaskNotFoundException { + dailyPlanner.markTaskAsComplete(taskToComplete); + setLastTaskAddedIndex(dailyPlanner.indexOf((Task) taskToComplete)); + indicateDailyPlannerChanged(); + } + + public synchronized void markTaskAsIncomplete(ReadOnlyTask taskToIncomplete) throws TaskNotFoundException { + int targetIndex = dailyPlanner.indexOf((Task) taskToIncomplete); + uncompleteTask(targetIndex); + setLastTaskAddedIndex(targetIndex); + indicateDailyPlannerChanged(); + } + +``` +###### \java\seedu\dailyplanner\ui\HelpWindow.java +``` java + private void configure(){ + Scene scene = new Scene(mainPane); + //Null passed as the parent stage to make it non-modal. + dialogStage = createDialogStage(TITLE, null, scene); + dialogStage.setMaximized(true); //TODO: set a more appropriate initial size + setIcon(dialogStage, ICON); + + WebView browser = new WebView(); + browser.getEngine().loadContent("

Help

add

add [TASKNAME] s/[START] e/[END] c/[CATEGORY]..

Except TASKNAME, all the fields above are optional

Examples:

add Math Assignment s/today

add Music Lesson s/12 nov 2pm e/5pm

add Sleepover s/today 9pm e/tomorrow 9am

add CS1020 Revision


delete

delete [INDEX] or delete[COMPLETED]

Examples:

delete 1

delete completed (deletes all completed tasks)


edit

edit [INDEX] [TASKNAME] s/[START] e/[END]

Except INDEX, only one of the other fields has to be entered.

Examples:

edit 1 Math Assignment

edit 5 Music Lesson s/6pm e/7pm

edit 9 Freshmen Camp s/18Dec 1pm e/17112016 9am

edit 11 CS1020 Revision s/tomorrow


find

find [TASKNAME]

Examples:

find CS1020


complete

complete[INDEX]

Examples:

complete 5


show

show [DATE]/[COMPLETED]

Examples:

show complete

show (shows all tasks)

show today

show not complete


undo

undo

Examples:

undo


pin

pin[INDEX]

Examples:

pin 5 (pins a task to the pin board)

" ); + FxViewUtil.applyAnchorBoundaryParameters(browser, 0.0, 0.0, 0.0, 0.0); + mainPane.getChildren().add(browser); + } + + public void show() { + dialogStage.showAndWait(); + } +} +``` diff --git a/collated/main/A0140124B.md b/collated/main/A0140124B.md new file mode 100644 index 000000000000..32d2b15e435a --- /dev/null +++ b/collated/main/A0140124B.md @@ -0,0 +1,894 @@ +# A0140124B +###### \java\seedu\dailyplanner\commons\util\StringUtil.java +``` java + public static void argumentArrayToHashMap(HashMap mapArgs, String[] splitArgs) { + for (int i = 0; i < splitArgs.length; i++) { + if (splitArgs[i].substring(0, 2).equals("s/")) { + extractArgument(mapArgs, splitArgs, i, "start"); + } + + if (splitArgs[i].substring(0, 2).equals("e/")) { + extractArgument(mapArgs, splitArgs, i, "end"); + } + + if (splitArgs[i].substring(0, 2).equals("c/")) { + extractArgument(mapArgs, splitArgs, i, "cats"); + + } + } + } + + private static void extractArgument(HashMap mapArgs, String[] splitArgs, int i, String type) { + int j = i + 1; + String arg = splitArgs[i].substring(2); + while (j < splitArgs.length && !splitArgs[j].contains("/")) { + arg += " " + splitArgs[j]; + j++; + } + i = j; + mapArgs.put(type, arg); + } + } + +``` +###### \java\seedu\dailyplanner\logic\commands\AddCommand.java +``` java + @Override + public CommandResult execute() { + assert model != null; + try { + taskList = model.getDailyPlanner().getTaskList(); + model.getHistory().stackDeleteInstruction(toAdd); + model.addTask(toAdd); + model.updatePinBoard(); + + int indexOfClashingTask = getIndexOfClashingTask(toAdd); + + if (isClash(indexOfClashingTask)) + return new CommandResult( + String.format(MESSAGE_WARNING_CLASH, taskList.get(indexOfClashingTask))); + + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } catch (UniqueTaskList.DuplicateTaskException e) { + return new CommandResult(MESSAGE_DUPLICATE_TASK); + } + +``` +###### \java\seedu\dailyplanner\logic\parser\nattyParser.java +``` java + private com.joestelmach.natty.Parser nattyParserPackage; + + public nattyParser() { + nattyParserPackage = new com.joestelmach.natty.Parser(); + } + + public String parse(String dateAndTime) { + Date parsedDateAndTime = getDateObjectFromNatty(dateAndTime); + DateFormat df = new SimpleDateFormat("dd/MM/yyyy hh.mma"); + return df.format(parsedDateAndTime); + } + + public String parseDate(String date) { + + if (DateUtil.isValidDayMonthAnd4DigitYearFormat(date)) { + return getDDMMYYYYFormat(date); + } + + if (DateUtil.isValidDayMonthAnd2DigitYearFormat(date)) { + return convertFrom2DigitYearto4DigitYearFormat(date); + } + + Date parsedDate = getDateObjectFromNatty(date); + DateFormat df = new SimpleDateFormat("dd/MM/yyyy"); + return df.format(parsedDate); + } + + public String parseTime(String time) { + Date parsedTime = getDateObjectFromNatty(time); + DateFormat df = new SimpleDateFormat("hh.mma"); + return df.format(parsedTime); + } + + private Date getDateObjectFromNatty(String dateAndTime) { + List groups = nattyParserPackage.parse(dateAndTime); + Date parsedDateAndTime = new Date(); + for (DateGroup group : groups) { + parsedDateAndTime = group.getDates().get(0); + break; + } + return parsedDateAndTime; + } + + private String convertFrom2DigitYearto4DigitYearFormat(String date) { + String dateWithAddedZero; + if (date.charAt(2) != '/') { + dateWithAddedZero = "0" + date; + } else { + dateWithAddedZero = date; + } + dateWithAddedZero = DateUtil.convertTo4DigitYearFormat(dateWithAddedZero); + + return dateWithAddedZero; + } + + private String getDDMMYYYYFormat(String date) { + if (date.charAt(2) != '/') { + return "0" + date; + } else { + return date; + } + } + +} +``` +###### \java\seedu\dailyplanner\logic\parser\Parser.java +``` java + private Command prepareAdd(String args) { + String taskName = ""; + DateTime formattedStart = DateUtil.getEmptyDateTime(); + DateTime formattedEnd = DateUtil.getEmptyDateTime(); + Set cats = new HashSet(); + + String trimmedArgs = args.trim(); + + if (!(ArgumentFormatUtil.isValidAddArgumentFormat(trimmedArgs))) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } + + HashMap mapArgs = parseAdd(trimmedArgs); + nattyParser natty = new nattyParser(); + + if (mapArgs.containsKey("taskName")) { + taskName = mapArgs.get("taskName"); + } + if (mapArgs.containsKey("start")) { + formattedStart = extractStart(mapArgs, natty); + } + if (mapArgs.containsKey("end")) { + formattedEnd = extractEnd(formattedStart, mapArgs, natty); + } + if (mapArgs.containsKey("cats")) { + cats = extractCategories(mapArgs); + } + try { + return new AddCommand(taskName, formattedStart, formattedEnd, cats); + } catch (IllegalValueException ive) { + return new IncorrectCommand(ive.getMessage()); + } + + } + + private boolean hasEndDate(String endString) { + return endString.length() >= 7 && !Character.isDigit(endString.charAt(0)); + } + + private DateTime extractStart(HashMap mapArgs, nattyParser natty) { + DateTime formattedStart; + String startString = mapArgs.get("start"); + // if field is empty, return empty DateTime + if (startString.equals("")) { + formattedStart = DateUtil.getEmptyDateTime(); + } + // if start time is given + if (StringUtil.stringContainsAmOrPm(startString)) { + String start = natty.parse(startString); + formattedStart = DateUtil.getDateTimeFromString(start); + } else { + String start = natty.parseDate(startString); + Date startDate = new Date(start); + formattedStart = new DateTime(startDate, new Time("")); + } + return formattedStart; + } + + private DateTime extractEnd(DateTime formattedStart, HashMap mapArgs, nattyParser natty) { + DateTime formattedEnd; + String endString = mapArgs.get("end"); + // if field is empty, return empty DateTime + if (endString.equals("")) { + formattedEnd = DateUtil.getEmptyDateTime(); + } + // if end time is given + if (StringUtil.stringContainsAmOrPm(endString)) { + // if end date is given + if (hasEndDate(endString)) { + String end = natty.parse(endString); + formattedEnd = DateUtil.getDateTimeFromString(end); + } else { + Date endDate; + // if no start date, infer end date as today + if (!mapArgs.containsKey("start")) { + endDate = DateUtil.todayAsDate(); + } + // if start date present, infer end date as start date + else { + endDate = formattedStart.getDate(); + } + Time endTime = new Time(natty.parseTime(endString)); + formattedEnd = new DateTime(endDate, endTime); + } + } else { + String end = natty.parseDate(endString); + Date endDate = new Date(end); + formattedEnd = new DateTime(endDate, new Time("")); + } + return formattedEnd; + } + + private Set extractCategories(HashMap mapArgs) { + Set cats; + if (mapArgs.get("cats").equals("")) { + cats = new HashSet(); + } + String[] catArray = mapArgs.get("cats").split(" "); + cats = new HashSet(Arrays.asList(catArray)); + return cats; + } + + /** + * Parses the arguments given by the user in the add command and returns it + * to prepareAdd in a HashMap with keys taskName, date, startTime, endTime, + */ + + private HashMap parseAdd(String arguments) { + HashMap mapArgs = new HashMap(); + String taskName = getTaskNameFromArguments(arguments); + mapArgs.put("taskName", taskName); + if (arguments.contains("/")) { + String[] splitArgs = arguments.substring(taskName.length() + 1).split(" "); + // loop through rest of arguments, add them to hashmap if valid + + StringUtil.argumentArrayToHashMap(mapArgs, splitArgs); + } + + return mapArgs; + } + + /** + * Parses the arguments given by the user in the add command and returns it + * to prepareEdit in a HashMap with keys taskName, date, startTime, endTime, + */ + private HashMap parseEdit(String arguments) { + + HashMap mapArgs = new HashMap(); + + // Extract index + String[] splitArgs1 = arguments.split(" ", 2); + int indexStringLength = splitArgs1[0].length(); + String index = arguments.substring(0, indexStringLength); + mapArgs.put("index", index); + + arguments = arguments.substring(indexStringLength + 1); + if (hasTaskName(arguments)) { + String taskName = getTaskNameFromArguments(arguments); + mapArgs.put("taskName", taskName); + if (arguments.contains("/")) { + String[] splitArgs = arguments.substring(taskName.length() + 1).split(" "); + StringUtil.argumentArrayToHashMap(mapArgs, splitArgs); + } + } else if (arguments.contains("/")) { + String[] splitArgs = arguments.split(" "); + StringUtil.argumentArrayToHashMap(mapArgs, splitArgs); + } + + return mapArgs; + } + + + /** + * Checks if argument given contains a task name + */ + private boolean hasTaskName(String arguments) { + String trimmedArgs = arguments.trim(); + // if first parameter is a start, end or category field + if (trimmedArgs.length() >= 2 && trimmedArgs.charAt(1) == '/') { + return false; + } + return true; + } +``` +###### \java\seedu\dailyplanner\model\ModelManager.java +``` java + public ModelManager(DailyPlanner src, UserPrefs userPrefs) { + super(); + assert src != null; + assert userPrefs != null; + + logger.fine("Initializing with daily planner: " + src + " and user prefs " + userPrefs); + + dailyPlanner = new DailyPlanner(src); + filteredTasks = new FilteredList<>(dailyPlanner.getTasks()); + pinnedTasks = new FilteredList<>(dailyPlanner.getPinnedTasks()); + history = new HistoryManager(); + lastTaskAddedIndex = new SimpleIntegerProperty(0); + lastShowDate = new SimpleStringProperty(); + } + + public ModelManager() { + this(new DailyPlanner(), new UserPrefs()); + } + + public ModelManager(ReadOnlyDailyPlanner initialData, UserPrefs userPrefs) { + dailyPlanner = new DailyPlanner(initialData); + filteredTasks = new FilteredList<>(dailyPlanner.getTasks()); + pinnedTasks = new FilteredList<>(dailyPlanner.getPinnedTasks()); + history = new HistoryManager(); + lastTaskAddedIndex = new SimpleIntegerProperty(0); + lastShowDate = new SimpleStringProperty(); + } + +``` +###### \java\seedu\dailyplanner\model\task\Date.java +``` java + +/** + * Represents a Task's start date or end date in the daily planner. + */ +public class Date implements Comparable { + + private final String m_value; + private final int m_day; + private final int m_month; + private final int m_year; + + /** + * Guaranteed that value is either an empty string or in the format: + * DD/MM/YYYY + */ + public Date(String value) { + assert value != null; + m_value = value; + if (!value.equals("")) { + m_day = Integer.parseInt(value.substring(0, 2)); + m_month = Integer.parseInt(value.substring(3, 5)); + m_year = Integer.parseInt(value.substring(6)); + } + // default values when date not present + else { + m_day = 0; + m_month = 0; + m_year = 3000; + } + } + + @Override + public String toString() { + return m_value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Date // instanceof handles nulls + && m_value.equals(((Date) other).m_value)); // state + // check + } + + @Override + public int hashCode() { + return m_value.hashCode(); + } + + public int getDay() { + return m_day; + } + + public int getMonth() { + return m_month; + } + + public int getYear() { + return m_year; + } + + @Override + public int compareTo(Date o) { + + if (m_year != o.m_year) { + return m_year - o.m_year; + } + if (m_month != o.m_month) { + return m_month - o.m_month; + } + if (m_day != o.m_day) { + return m_day - o.m_day; + } + + return 0; + } +} +``` +###### \java\seedu\dailyplanner\model\task\Task.java +``` java + private String taskName; + private DateTime start; + private DateTime end; + private boolean isComplete; + private boolean isPinned; + private UniqueCategoryList tags; + + /** + * Every field must be present and not null. + */ + public Task(String name, DateTime start, DateTime end, boolean isComplete, boolean isPinned, UniqueCategoryList tags) { + assert !CollectionUtil.isAnyNull(name, start, end, isComplete, isPinned, tags); + this.taskName = name; + this.start = start; + this.end = end; + this.tags = new UniqueCategoryList(tags); // protect internal tags from + // changes in the arg list + this.isComplete = isComplete; + this.isPinned = isPinned; + } + + /** + * Copy constructor. + */ + public Task(ReadOnlyTask source) { + this(source.getName(), source.getStart(), source.getEnd(), source.isComplete(), source.isPinned(), + source.getCats()); + } + + @Override + public void setName(String name) { + this.taskName = name; + } + + @Override + public void setStart(DateTime startDate) { + this.start = startDate; + } + + @Override + public void setEnd(DateTime endDate) { + this.end = endDate; + } + + @Override + public void markAsComplete() { + this.isComplete = true; + } + + @Override + public void markAsNotComplete() { + this.isComplete = false; + } + + public void pin() { + this.isPinned = true; + } + + public void unpin() { + this.isPinned = false; + } + + public static String calculateDueStatus(DateTime end) { + // if there is no end date, return empty string + if (end.getDate().equals("")) { + return ""; + } + DateTime nowAsDateTime = DateUtil.nowAsDateTime(); + + // if end date is strictly before current date + if (end.getDate().compareTo(nowAsDateTime.getDate()) < 0 ) { + return "OVERDUE"; + } + + // if end date is today or later but there is no end time + if (end.getTime().toString().equals("")) { + return ""; + } + + // if end date is same as current date + else if (end.getDate().equals(nowAsDateTime.getDate())) { + // if end time is before or equal to current time + if (end.getTime().compareTo(nowAsDateTime.getTime()) <= 0) { + return "OVERDUE"; + } else { + int endHour = DateUtil.convertTo24HrFormat(end.getTime()); + int nowHour = DateUtil.convertTo24HrFormat(nowAsDateTime.getTime()); + int overDueHours = endHour - nowHour; + if (overDueHours <= 3) { + return "DUE SOON"; + } + } + } + return ""; + } + + @Override + public String getName() { + return taskName; + } + + @Override + public DateTime getStart() { + return start; + } + + @Override + public DateTime getEnd() { + return end; + } + + @Override + public String getCompletion() { + return (isComplete) ? "COMPLETE" : "NOT COMPLETE"; + } + + @Override + public String getDueStatus() { + return calculateDueStatus(end); + } + + @Override + public boolean isComplete() { + return isComplete; + } + + @Override + public void setCompletion(boolean completion) { + isComplete = completion; + } + + public boolean isPinned() { + return isPinned; + } + + @Override + public UniqueCategoryList getCats() { + return new UniqueCategoryList(tags); + } + + /** + * Replaces this task's tags with the tags in the argument tag list. + */ + public void setCategories(UniqueCategoryList replacement) { + tags.setTags(replacement); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ReadOnlyTask // instanceof handles nulls + && this.isSameStateAs((ReadOnlyTask) other)); + } + + @Override + public int hashCode() { + return Objects.hash(taskName, start, end, isComplete, isPinned, tags); + } + + @Override + public String toString() { + return getAsText(); + } + + @Override + public int compareTo(Task o) { + if (!start.equals(o.start)) { + return start.compareTo(o.start); + } else if (!end.equals(o.end)) { + return end.compareTo(o.end); + } else + return taskName.compareTo(o.taskName); + } + +} +``` +###### \java\seedu\dailyplanner\ui\PinnedTaskPanel.java +``` java +public class PinnedTaskPanel extends UiPart { + private final Logger logger = LogsCenter.getLogger(PinnedTaskPanel.class); + private static final String FXML = "PinnedTaskPanel.fxml"; + private VBox panel; + private AnchorPane placeHolderPane; + + @FXML + private ListView pinListView; + + public PinnedTaskPanel() { + super(); + } + + @Override + public void setNode(Node node) { + panel = (VBox) node; + } + + @Override + public String getFxmlPath() { + return FXML; + } + + @Override + public void setPlaceholder(AnchorPane pane) { + this.placeHolderPane = pane; + } + + public static PinnedTaskPanel load(Stage primaryStage, AnchorPane PinnedTaskPlaceholder, + ObservableList pinnedTaskList) { + PinnedTaskPanel pinnedTaskPanel = + UiPartLoader.loadUiPart(primaryStage, PinnedTaskPlaceholder, new PinnedTaskPanel()); + pinnedTaskPanel.configure(pinnedTaskList); + return pinnedTaskPanel; + } + + private void configure(ObservableList personList) { + setConnections(personList); + addToPlaceholder(); + } + + private void setConnections(ObservableList personList) { + pinListView.setItems(personList); + pinListView.setCellFactory(listView -> new PersonListViewCell()); + setEventHandlerForSelectionChangeEvent(); + } + + private void addToPlaceholder() { + SplitPane.setResizableWithParent(placeHolderPane, false); + placeHolderPane.getChildren().add(panel); + } + + private void setEventHandlerForSelectionChangeEvent() { + pinListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { + if (newValue != null) { + logger.fine("Selection in person list panel changed to : '" + newValue + "'"); + raise(new TaskPanelSelectionChangedEvent(newValue)); + } + }); + } + + public void scrollTo(int index) { + Platform.runLater(() -> { + pinListView.scrollTo(index); + pinListView.getSelectionModel().clearAndSelect(index); + }); + } + + class PersonListViewCell extends ListCell { + + public PersonListViewCell() { + } + + @Override + protected void updateItem(ReadOnlyTask person, boolean empty) { + super.updateItem(person, empty); + + if (empty || person == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(TaskCard.load(person, getIndex() + 1).getLayout()); + } + } + } + +} +``` +###### \java\seedu\dailyplanner\ui\ResultDisplay.java +``` java + public void configure() { + resultDisplayArea = new TextArea(); + resultDisplayArea.setEditable(false); + resultDisplayArea.setId(RESULT_DISPLAY_ID); + resultDisplayArea.getStyleClass().removeAll(); + resultDisplayArea.getStyleClass().add(STATUS_BAR_STYLE_SHEET); + resultDisplayArea.setWrapText(true); + resultDisplayArea.setText(""); + resultDisplayArea.textProperty().bind(displayed); + displayed.setValue(INITIAL_RESULT_MESSAGE); + FxViewUtil.applyAnchorBoundaryParameters(resultDisplayArea, 0.0, 0.0, 0.0, 0.0); + mainPane.getChildren().add(resultDisplayArea); + FxViewUtil.applyAnchorBoundaryParameters(mainPane, 0.0, 0.0, 0.0, 0.0); + placeHolder.getChildren().add(mainPane); + } + + @Override + public void setNode(Node node) { + mainPane = (AnchorPane) node; + } + + @Override + public void setPlaceholder(AnchorPane placeholder) { + this.placeHolder = placeholder; + } + + @Override + public String getFxmlPath() { + return FXML; + } + + public void postMessage(String message) { + displayed.setValue(message); + } + +} +``` +###### \java\seedu\dailyplanner\ui\TaskCard.java +``` java + @FXML + public void initialize() { + name.setText(task.getName()); + name.setWrapText(true); + id.setText(displayedIndex + ". "); + startDate.setText(task.getStart().getDate().toString()); + startTime.setText(task.getStart().getTime().toString()); + endDate.setText(task.getEnd().getDate().toString()); + endTime.setText(task.getEnd().getTime().toString()); + tags.setText(task.tagsString()); + if (task.isComplete()) { + isComplete.setText(task.getCompletion()); + isComplete.setVisible(true); + } else { + String dueStatus = task.getDueStatus(); + if(dueStatus.equals("")) { + isComplete.setVisible(false); + } else { + isComplete.setText(dueStatus); + if (dueStatus.equals("DUE SOON")) { + isComplete.setStyle(DUE_SOON_LABEL_STYLE); + } else if (dueStatus.equals("OVERDUE")) { + isComplete.setStyle(OVERDUE_LABEL_STYLE); + } + + } + } + + if (task.getStart().getDate().toString().equals("")) { + startAtLabel.setVisible(false); + } else { + startAtLabel.setText("Starts at: "); + } + + if (task.getEnd().getDate().toString().equals("")) { + endAtLabel.setVisible(false); + } else { + endAtLabel.setText("Ends at: "); + } + + } +``` +###### \resources\view\DarkTheme.css +``` css +.fordate-label { + -fx-font-family: Verdana; + -fx-font-size: 15px; + -fx-highlight-fill: #00ff00; + -fx-highlight-text-fill: #000000; + -fx-text-fill: #00ff00; + +``` +###### \resources\view\MainWindow.fxml +``` fxml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` +###### \resources\view\TaskListCard.fxml +``` fxml + + + + + + + + + + + + + + + + + + + + + + + + +``` +###### \resources\view\TaskListCard.fxml +``` fxml + + + + + + + + + + +``` diff --git a/collated/main/A0146749N.md b/collated/main/A0146749N.md new file mode 100644 index 000000000000..723a1b7af44a --- /dev/null +++ b/collated/main/A0146749N.md @@ -0,0 +1,859 @@ +# A0146749N +###### \java\seedu\dailyplanner\commons\util\DateUtil.java +``` java +public class DateUtil { + + private static final String STRING_REPRESENTING_NOW = "now"; + + /** + * Checks if given task has both start time and end time + * + * @return true if task has both, false otherwise + */ + public static boolean hasStartandEndTime(ReadOnlyTask storedTask) { + Time storedStartTime = storedTask.getStart().getTime(); + Time storedEndTime = storedTask.getEnd().getTime(); + if (!(storedStartTime.toString().equals("")) && !(storedEndTime.toString().equals(""))) { + return true; + } + return false; + } + + /** + * Checks if date given by keyword falls within a task's start and end dates + * Guaranteed that keyword is of the format DD/MM/YYYY + */ + public static boolean withinDateRange(ReadOnlyTask task, String keyword) { + int keyDate = SubStringOfStringAsInt(0, 2, keyword); + int keyMonth = SubStringOfStringAsInt(3, 5, keyword); + int keyYear = subStringOfStringAsInt(6, keyword); + Date taskStart = task.getStart().getDate(); + Date taskEnd = task.getEnd().getDate(); + if (startDateTimeAndEndDateTimeIsEmpty(taskStart, taskEnd)) { + return false; + } else if (dateTimeIsEmpty(taskStart)) { + taskStart = taskEnd; + } else if (dateTimeIsEmpty(taskEnd)) { + taskEnd = taskStart; + } + return isKeyBetweenStartAndEnd(keyDate, keyMonth, keyYear, taskStart, taskEnd); + } + + /** Helper method for withinDateRang method */ + private static boolean isKeyBetweenStartAndEnd(int keyDate, int keyMonth, int keyYear, Date taskStart, + Date taskEnd) { + Calendar start = Calendar.getInstance(); + start.set(taskStart.getYear() + 1900, taskStart.getMonth(), taskStart.getDay()); + Calendar searchKey = Calendar.getInstance(); + searchKey.set(keyYear + 1900, keyMonth, keyDate); + Calendar end = Calendar.getInstance(); + end.set(taskEnd.getYear() + 1900, taskEnd.getMonth(), taskEnd.getDay()); + return (start.compareTo(searchKey) <= 0 && end.compareTo(searchKey) >= 0); + } + + private static boolean dateTimeIsEmpty(Date taskStart) { + return taskStart.toString().equals(""); + } + + private static boolean startDateTimeAndEndDateTimeIsEmpty(Date taskStart, Date taskEnd) { + return dateTimeIsEmpty(taskStart) && dateTimeIsEmpty(taskEnd); + } + + private static int SubStringOfStringAsInt(int start, int end, String keyword) { + return Integer.parseInt(keyword.substring(start, end)); + } + + private static int subStringOfStringAsInt(int start, String keyword) { + return Integer.parseInt(keyword.substring(start)); + } + + /** Returns current time as DateTime object */ + public static DateTime nowAsDateTime() { + nattyParser natty = new nattyParser(); + String dateTimeAsString = natty.parse(STRING_REPRESENTING_NOW); + return getDateTimeFromString(dateTimeAsString); + } + + /** + * Converts given string into DateTime object. Guaranteed that String is in + * DD/MM/YYYY HH.MMam or HH.MMpm format + */ + public static DateTime getDateTimeFromString(String dateTimeAsString) { + String[] dateTimeArray = dateTimeAsString.split(" "); + Date nowDate = new Date(dateTimeArray[0]); + Time nowTime = new Time(dateTimeArray[1]); + return new DateTime(nowDate, nowTime); + } + + /** + * Converts time from 12HR to 24HR format + */ + public static int convertTo24HrFormat(Time firstTime) { + if (firstTime.m_meridiem.equals("AM") && firstTime.m_hour == 12) { + return 0; + } else if (firstTime.m_meridiem.equals("PM") && firstTime.m_hour != 12) { + return firstTime.m_hour + 12; + } else { + return firstTime.m_hour; + } + } + + /** + * Checks if string is in dd/mm/yyyy format + */ + public static boolean isValidDayMonthAnd4DigitYearFormat(String date) { + return (date.matches("([0-9]{2})/([0-9]{2})/([0-9]{4})") || date.matches("([0-9]{1})/([0-9]{2})/([0-9]{4})")); + } + + /** + * Checks if string is in dd/mm/yy format + */ + public static boolean isValidDayMonthAnd2DigitYearFormat(String date) { + return (date.matches("([0-9]{2})/([0-9]{2})/([0-9]{2})") || date.matches("([0-9]{1})/([0-9]{2})/([0-9]{2})")); + + } + + /** Converts string from dd/mm/yy to dd/mm/20yy format */ + public static String convertTo4DigitYearFormat(String date) { + String dayAndMonth = date.substring(0, 6); + String yy = date.substring(6); + return dayAndMonth + "20" + yy; + } + + /** Returns the date today as a Date object */ + public static Date todayAsDate() { + nattyParser natty = new nattyParser(); + return new Date(natty.parseDate("today")); + } + + /** Returns an empty DateTime object */ + public static DateTime getEmptyDateTime() { + return new DateTime(new Date(""), new Time("")); + } + +} +``` +###### \java\seedu\dailyplanner\logic\commands\CompleteCommand.java +``` java + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + ReadOnlyTask taskToComplete = lastShownList.get(targetIndex - 1); + + try { + model.getHistory().stackUncompleteInstruction(taskToComplete); + model.markTaskAsComplete(taskToComplete); + model.updatePinBoard(); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + return new CommandResult(String.format(MESSAGE_COMPLETED_TASK_SUCCESS, taskToComplete)); + } + +} +``` +###### \java\seedu\dailyplanner\logic\commands\EditCommand.java +``` java + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + ReadOnlyTask originalTask = lastShownList.get(targetIndex - 1); + + // if any of the fields passed in by user are empty, take it from the + // original task + String toAddName = (taskName.isPresent()) ? taskName.get() : originalTask.getName(); + DateTime toAddStart = (start.isPresent()) ? start.get() : originalTask.getStart(); + DateTime toAddEnd = (end.isPresent()) ? end.get() : originalTask.getEnd(); + UniqueCategoryList toAddCats = (categoriesSet.isPresent()) ? categoriesSet.get() : originalTask.getCats(); + Task toAdd = new Task(toAddName, toAddStart, toAddEnd, originalTask.isComplete(), originalTask.isPinned(), + toAddCats); + + try { + model.getHistory().stackEditInstruction(originalTask, toAdd); + model.deleteTask(originalTask); + model.addTask(toAdd); + model.updatePinBoard(); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } catch (UniqueTaskList.DuplicateTaskException e) { + return new CommandResult(MESSAGE_DUPLICATE_TASK); + } + return new CommandResult(String.format(MESSAGE_EDIT_TASK_SUCCESS, originalTask)); + } +} +``` +###### \java\seedu\dailyplanner\logic\commands\PinCommand.java +``` java + public PinCommand(int targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + ReadOnlyTask taskToPin = lastShownList.get(targetIndex - 1); + + if (model.getPinnedTaskList().contains(taskToPin)) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(MESSAGE_DUPLICATE_PINNED_TASK); + } + + try { + model.getHistory().stackUnpinInstruction(taskToPin); + model.pinTask(taskToPin); + model.updatePinBoard(); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + return new CommandResult(String.format(MESSAGE_PINNED_TASK_SUCCESS, taskToPin)); + } + +} +``` +###### \java\seedu\dailyplanner\logic\commands\SelectCommand.java +``` java + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + EventsCenter.getInstance().post(new JumpToListRequestEvent(targetIndex - 1)); + return new CommandResult(String.format(MESSAGE_SELECT_TASK_SUCCESS, targetIndex)); + + } + +} +``` +###### \java\seedu\dailyplanner\logic\commands\ShowCommand.java +``` java +public class ShowCommand extends Command { + + public static final String COMMAND_WORD = "show"; + public static final String KEYWORD_SHOW_COMPLETED = "complete"; + public static final String KEYWORD_SHOW_NOT_COMPLETED = "not complete"; + + public static final String MESSAGE_SUCCESS = "Showing %1$s tasks"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Shows all tasks whose completion status" + + "matches the keyword or whose start and end date range falls within the keyword date" + + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" + "Example: " + COMMAND_WORD + " completed"; + + private final Set keywords; + + public ShowCommand() { + keywords = null; + } + +``` +###### \java\seedu\dailyplanner\logic\commands\ShowCommand.java +``` java + public ShowCommand(Set keywords) { + this.keywords = keywords; + } + + @Override + public CommandResult execute() { + if (keywords == null) { + model.updateFilteredListToShowAll(); + model.setLastShowDate(StringUtil.EMPTY_STRING); + return new CommandResult(String.format(MESSAGE_SUCCESS, "all")); + } else { + // if command is a show completed command + if (keywords.contains(KEYWORD_SHOW_COMPLETED)) { + model.updateFilteredTaskListByCompletion(keywords); + model.setLastShowDate("completed"); + } + // command is a show not completed command + else if (keywords.contains(KEYWORD_SHOW_NOT_COMPLETED)) { + model.updateFilteredTaskListByCompletion(keywords); + model.setLastShowDate("not completed"); + } + // command is a show by date command + else { + model.updateFilteredTaskListByDate(keywords); + model.setLastShowDate((String) keywords.toArray()[0]); + } + return new CommandResult(String.format(MESSAGE_SUCCESS, model.getFilteredTaskList().size())); + } + } +} +``` +###### \java\seedu\dailyplanner\logic\commands\UncompleteCommand.java +``` java + @Override + public CommandResult execute() { + + UnmodifiableObservableList lastShownList = model.getFilteredTaskList(); + + if (lastShownList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + ReadOnlyTask taskToUncomplete = lastShownList.get(targetIndex - 1); + + try { + model.getHistory().stackCompleteInstruction(taskToUncomplete); + model.markTaskAsIncomplete(taskToUncomplete); + model.updatePinBoard(); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + return new CommandResult(String.format(MESSAGE_UNCOMPLETED_TASK_SUCCESS, taskToUncomplete)); + } + +} +``` +###### \java\seedu\dailyplanner\logic\commands\UnpinCommand.java +``` java + +/** + * Unpins a task identified using it's last displayed index from the daily + * planner pinned task board. + */ +public class UnpinCommand extends Command { + + public final int targetIndex; + + public static final String COMMAND_WORD = "unpin"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Unpins the task identified by the index number used in the last task listing on the pin board.\n" + + "Parameters: INDEX (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_PIN_TASK_SUCCESS = "Unpinned Task: %1$s"; + + public UnpinCommand(int targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute() { + + UnmodifiableObservableList pinnedList = model.getPinnedTaskList(); + + if (pinnedList.size() < targetIndex) { + indicateAttemptToExecuteIncorrectCommand(); + return new CommandResult(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + ReadOnlyTask taskToUnpin = pinnedList.get(targetIndex - 1); + + try { + model.getHistory().stackPinInstruction(taskToUnpin); + model.unpinTask(targetIndex - 1); + } catch (TaskNotFoundException pnfe) { + assert false : "The target task cannot be missing"; + } + return new CommandResult(String.format(MESSAGE_PIN_TASK_SUCCESS, taskToUnpin)); + } + +} +``` +###### \java\seedu\dailyplanner\logic\LogicManager.java +``` java + @Override + public ObservableList getPinnedTaskList() { + return model.getPinnedTaskList(); + } + + @Override + public IntegerProperty getLastTaskAddedIndexProperty() { + return model.getLastTaskAddedIndexProperty(); + } + + @Override + public StringProperty getLastShowDateProperty() { + return model.getLastShowDateProperty(); + } + +} +``` +###### \java\seedu\dailyplanner\logic\parser\Parser.java +``` java + + /** + * Parses arguments in the context of the pin task command. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command preparePin(String arguments) { + String trimmedArg = arguments.trim(); + Optional index = parseIndex(trimmedArg); + if (!index.isPresent()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, PinCommand.MESSAGE_USAGE)); + } + return new PinCommand(index.get()); + } + + /** + * Parses arguments in the context of the unpin task command. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command prepareUnpin(String arguments) { + String trimmedArg = arguments.trim(); + Optional index = parseIndex(trimmedArg); + if (!index.isPresent()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UnpinCommand.MESSAGE_USAGE)); + } + return new UnpinCommand(index.get()); + } + + /** + * Parses arguments in the context of the complete task command. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command prepareComplete(String arguments) { + String trimmedArg = arguments.trim(); + Optional index = parseIndex(trimmedArg); + if (!index.isPresent()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, CompleteCommand.MESSAGE_USAGE)); + } + return new CompleteCommand(index.get()); + } + + /** + * Parses arguments in the context of the uncomplete task command. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command prepareUncomplete(String arguments) { + String trimmedArg = arguments.trim(); + Optional index = parseIndex(trimmedArg); + if (!index.isPresent()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UncompleteCommand.MESSAGE_USAGE)); + } + return new UncompleteCommand(index.get()); + } + +``` +###### \java\seedu\dailyplanner\logic\parser\Parser.java +``` java + /** + * Extracts the task name from the rest of the arguments + */ + private String getTaskNameFromArguments(String arguments) { + if (arguments.contains("/")) { + String[] splitArgs = arguments.split("/"); + return splitArgs[0].substring(0, splitArgs[0].length() - 2); + } else { + return arguments; + } + } + + /** + * Parses arguments in the context of the delete task command. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command prepareDelete(String args) { + String trimmedArgs = args.trim(); + + if (trimmedArgs.contains("complete")) { + return new DeleteCompletedCommand(); + } else { + Optional index = parseIndex(trimmedArgs); + if (!index.isPresent()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE)); + } + return new DeleteCommand(index.get()); + } + } + + /** + * Parses arguments in the context of the show tasks command. + * + * @param args + * full command args string + * @return the prepared command + */ + private Command prepareShow(String args) { + final Matcher matcher = KEYWORDS_ARGS_FORMAT.matcher(args.trim()); + if (!matcher.matches()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ShowCommand.MESSAGE_USAGE)); + } + + // keywords delimited by whitespace + final String keyword = matcher.group("keywords"); + + String[] keywords = new String[1]; + + // if command is a show by completion status command + if (keyword.contains("complete")) { + if (keyword.contains(ShowCommand.KEYWORD_SHOW_NOT_COMPLETED)) { + keywords[0] = "not complete"; + } else { + keywords[0] = "complete"; + } + } + // command is a show by date command + else { + nattyParser natty = new nattyParser(); + keywords[0] = natty.parseDate(keyword); + } + final Set keywordSet = new HashSet<>(Arrays.asList(keywords)); + return new ShowCommand(keywordSet); + } + +``` +###### \java\seedu\dailyplanner\model\DailyPlanner.java +``` java + public ObservableList getPinnedTasks() { + return tasks.getInternalPinnedList(); + } +``` +###### \java\seedu\dailyplanner\model\DailyPlanner.java +``` java + public void markTaskAsComplete(ReadOnlyTask key) throws UniqueTaskList.TaskNotFoundException { + tasks.complete(key); + } + + + public void pinTask(ReadOnlyTask taskToPin) throws TaskNotFoundException { + tasks.pin(taskToPin); + } + + public void unpinTask(int targetIndex) { + tasks.unpin(targetIndex); + } + + public void uncompleteTask(int targetIndex) { + tasks.uncomplete(targetIndex); + + } + + public void resetPinBoard() { + tasks.resetPinBoard(); + } + + public int indexOf(Task task) { + return tasks.getIndexOf(task); + } + + public void updatePinBoard() { + tasks.updatePinBoard(); + } +``` +###### \java\seedu\dailyplanner\model\ModelManager.java +``` java + @Override + public void pinTask(ReadOnlyTask taskToPin) throws TaskNotFoundException { + dailyPlanner.pinTask(taskToPin); + indicateDailyPlannerChanged(); + } + + @Override + public void unpinTask(int targetIndex) throws TaskNotFoundException { + dailyPlanner.unpinTask(targetIndex); + indicateDailyPlannerChanged(); + } + + @Override + public void uncompleteTask(int targetIndex) { + dailyPlanner.uncompleteTask(targetIndex); + indicateDailyPlannerChanged(); + } + + @Override + public void updatePinBoard() { + dailyPlanner.updatePinBoard(); + } + +``` +###### \java\seedu\dailyplanner\model\ModelManager.java +``` java + @Override + public void updateFilteredTaskListByDate(Set keywords) { + updateFilteredTaskList(new PredicateExpression(new DateQualifier(keywords))); + } + + @Override + public void updateFilteredTaskListByCompletion(Set keywords) { + updateFilteredTaskList(new PredicateExpression(new CompletionQualifier(keywords))); + } + + @Override + public int getLastTaskAddedIndex() { + return lastTaskAddedIndex.get(); + } + + @Override + public void setLastTaskAddedIndex(int index) { + if (index == lastTaskAddedIndex.get()) { + lastTaskAddedIndex.set(-1); + } + lastTaskAddedIndex.set(index); + } + + @Override + public IntegerProperty getLastTaskAddedIndexProperty() { + return lastTaskAddedIndex; + } + + @Override + public String getLastShowDate() { + return lastShowDate.get(); + } + + @Override + public void setLastShowDate(String showInput) { + lastShowDate.set(showInput); + } + + @Override + public StringProperty getLastShowDateProperty() { + return lastShowDate; + } +``` +###### \java\seedu\dailyplanner\model\ModelManager.java +``` java + private class CompletionQualifier implements Qualifier { + private Set completionKeywords; + + CompletionQualifier(Set completionKeyword) { + this.completionKeywords = completionKeyword; + } + + @Override + public boolean run(ReadOnlyTask task) { + return completionKeywords.contains(task.getCompletion().toLowerCase()); + } + + @Override + public String toString() { + return "completion=" + String.join(", ", completionKeywords); + } + } + + private class DateQualifier implements Qualifier { + private Set dateKeyWords; + + DateQualifier(Set dateKeyWords) { + this.dateKeyWords = dateKeyWords; + } + + @Override + public boolean run(ReadOnlyTask task) { + return dateKeyWords.stream().filter(keyword -> DateUtil.withinDateRange(task, keyword)).findAny() + .isPresent(); + } + + @Override + public String toString() { + return "date=" + String.join(", ", dateKeyWords); + } + } + + private class NameQualifier implements Qualifier { + private Set nameKeyWords; + + NameQualifier(Set nameKeyWords) { + this.nameKeyWords = nameKeyWords; + } + + @Override + public boolean run(ReadOnlyTask task) { + return nameKeyWords.stream().filter(keyword -> StringUtil.containsIgnoreCase(task.getName(), keyword)) + .findAny().isPresent(); + } + + @Override + public String toString() { + return "name=" + String.join(", ", nameKeyWords); + } + } + + @Override + public HistoryManager getHistory() { + + return history; + } + +} +``` +###### \java\seedu\dailyplanner\model\task\DateTime.java +``` java +/** + * Contains a Date object and a Time object. In the daily planner, a task's + * start and end are instances of this class + * + */ +public class DateTime implements Comparable { + + private final Date m_date; + private final Time m_time; + + public DateTime(Date date, Time time) { + m_date = date; + m_time = time; + } + + @Override + public String toString() { + // if date field is empty, time field is also empty + // so return empty string + if (m_date.toString().equals("")) { + return ""; + } + // if date is present, but time is not present + else if (m_time.toString().equals("")) { + return m_date.toString(); + } else { + return m_date.toString() + " " + m_time.toString(); + } + } + + public Date getDate() { + return m_date; + } + + public Time getTime() { + return m_time; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DateTime // instanceof handles nulls + && m_date.equals(((DateTime) other).m_date) && m_time.equals(((DateTime) other).m_time)); // state + // check + } + + @Override + public int hashCode() { + return m_date.hashCode(); + } + + @Override + public int compareTo(DateTime o) { + if (!m_date.equals(o.m_date)) { + return m_date.compareTo(o.m_date); + } else { + return m_time.compareTo(o.m_time); + } + } +} +``` +###### \java\seedu\dailyplanner\model\task\Time.java +``` java + +/** + * Represents a task's start or end time in the daily planner. + */ +public class Time implements Comparable