Skip to content

Commit

Permalink
Merge pull request #104 from CS2103AUG2016-T14-C2/V0.5rc-Implement-Re…
Browse files Browse the repository at this point in the history
…curring-Tasks

Merge V0.5rc implement recurring tasks
  • Loading branch information
qhng authored Oct 29, 2016
2 parents 68aeeb2 + 90e3a39 commit 9675f25
Show file tree
Hide file tree
Showing 12 changed files with 266 additions and 59 deletions.
2 changes: 1 addition & 1 deletion docs/UserGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Format: `add TASK_NAME [s/START_DATE] [e/END_DATE] [l/LOCATION] [p/PRIORITY_LEVE
> LOCATION | `Optional` Specifies the location where the task happens.
> PRIORITY_LEVEL | `Optional` Specifies the priority level of the task.<br>`Accepts` values `low`, `medium`, `high`<br>`Defaults` to `???`
> RECURRING_TYPE | `Optional` Specifies the recurring type of the task.<br>`Accepts` values `none`, `daily`, `weekly`, `monthly`, `yearly`<br>`Defaults` to `none`
> NUMBER_OF_RECURRENCE | `Optional` Specifies the number of times the task recurrs. A value of 0 specifies a never-ending recurrence.<br>`Defaults` to `0`<br>`Ignored` if RECURRING_TYPE is `none`
> NUMBER_OF_RECURRENCE | `Optional` Specifies the number of times the task recurrs. <br>`Defaults` to `1`<br>`Ignored` if RECURRING_TYPE is `none`
> CATEGORY | `Optional` Specifies a custom category for the task. This can be used for keeping track of similar tasks.
> DESCRIPTION | `Optional` Describes the task.
Expand Down
55 changes: 38 additions & 17 deletions src/main/java/seedu/savvytasker/logic/commands/AddCommand.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package seedu.savvytasker.logic.commands;

import java.util.Iterator;
import java.util.LinkedList;

import seedu.savvytasker.commons.core.EventsCenter;
import seedu.savvytasker.commons.core.UnmodifiableObservableList;
import seedu.savvytasker.commons.events.ui.JumpToListRequestEvent;
Expand Down Expand Up @@ -40,6 +43,7 @@ public class AddCommand extends ModelRequiringCommand {
private final String description;

private Task toAdd;
private LinkedList<Task> tasksAdded;

//@@author A0139915W
/**
Expand All @@ -57,26 +61,45 @@ public AddCommand(String taskName, InferredDate startDateTime, InferredDate endD
this.numberOfRecurrence = numberOfRecurrence;
this.category = category;
this.description = description;
tasksAdded = new LinkedList<Task>();
}

private void createTask() {
final boolean isArchived = false; // all tasks are first added as active tasks
final int taskId = 0; // taskId to be assigned by ModelManager, leave as 0
final int groupId = 0; // groupId to be assigned by ModelManager, leave as 0

this.toAdd = new Task(taskId, taskName, startDateTime, endDateTime,
this.toAdd = new Task(taskId, groupId, taskName, startDateTime, endDateTime,
location, priority, recurringType, numberOfRecurrence,
category, description, isArchived);
}

private void addToListOfTasksAdded(Task... tasks) {
for (Task t : tasks) {
tasksAdded.add(t);
}
}

@Override
public CommandResult execute() {
assert model != null;
createTask();

try {
Task taskAdded = model.addTask(toAdd);
Task taskAdded = null;
if (toAdd.getRecurringType() == RecurrenceType.None) {
// not a recurring task, add a single task
taskAdded = model.addTask(toAdd);
addToListOfTasksAdded(taskAdded);
} else {
// a recurring task, add a group of recurring tasks
LinkedList<Task> tasksAdded = model.addRecurringTask(toAdd);
taskAdded = tasksAdded.peekFirst();
addToListOfTasksAdded(tasksAdded.toArray(new Task[tasksAdded.size()]));
}

int targetIndex = getIndexOfTask(taskAdded);
if (targetIndex > 0) {
if (targetIndex >= 0) {
EventsCenter.getInstance().post(new JumpToListRequestEvent(targetIndex));
} else {
// GUI should never ever get here
Expand Down Expand Up @@ -119,7 +142,7 @@ public boolean canUndo() {
@Override
public boolean redo() {
execute();
return false;
return true;
}

/**
Expand All @@ -128,20 +151,18 @@ public boolean redo() {
*/
@Override
public boolean undo() {

UnmodifiableObservableList<Task> lastShownList = model.getFilteredTaskListTask();

for (int i = 0; i < lastShownList.size(); i++) {
if (lastShownList.get(i) == toAdd){
ReadOnlyTask taskToDelete = lastShownList.get(i);
try {
model.deleteTask(taskToDelete);
} catch (TaskNotFoundException e) {
e.printStackTrace();
}
Iterator<Task> itr = tasksAdded.iterator();
while (itr.hasNext()) {
try {
model.deleteTask(itr.next());
} catch (TaskNotFoundException e) {
// do nothing.
}
}
return false;
}
// clears the list of tasks added.
// if redo is performed, the list will be populated again.
tasksAdded.clear();
return true;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public CommandResult execute() {
originalTask = (Task)taskToModify;
Task taskModified = model.modifyTask(taskToModify, replacement);
int targetIndex = getIndexOfTask(taskModified);
if (targetIndex > 0) {
if (targetIndex >= 0) {
EventsCenter.getInstance().post(new JumpToListRequestEvent(targetIndex));
} else {
// GUI should never ever get here
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/seedu/savvytasker/model/Model.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package seedu.savvytasker.model;

import java.util.LinkedList;

import seedu.savvytasker.commons.core.UnmodifiableObservableList;
import seedu.savvytasker.model.alias.AliasSymbol;
import seedu.savvytasker.model.alias.DuplicateSymbolKeywordException;
Expand Down Expand Up @@ -43,6 +45,13 @@ public interface Model {
* @return Returns a Task if the add operation is successful, an exception is thrown otherwise.
* */
Task addTask(Task task) throws DuplicateTaskException, InvalidDateException;

/** Adds the given Task as a recurring task. The task's recurrence type must not be null.
* @throws {@link DuplicateTaskException} if a duplicate is found
* @throws {@link InvalidDateException} if the end date is earlier than the start date
* @return Returns the list of Tasks added if the add operation is successful, an exception is thrown otherwise.
* */
LinkedList<Task> addRecurringTask(Task task) throws DuplicateTaskException, InvalidDateException;

/** Returns the filtered task list as an {@code UnmodifiableObservableList<ReadOnlyTask>} */
UnmodifiableObservableList<ReadOnlyTask> getFilteredTaskList();
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/seedu/savvytasker/model/ModelManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
Expand Down Expand Up @@ -115,6 +116,14 @@ public synchronized Task addTask(Task t) throws DuplicateTaskException, InvalidD
indicateSavvyTaskerChanged();
return taskAdded;
}

@Override
public synchronized LinkedList<Task> addRecurringTask(Task recurringTask) throws DuplicateTaskException, InvalidDateException {
LinkedList<Task> recurringTasks = savvyTasker.addRecurringTasks(recurringTask);
updateFilteredListToShowActive();
indicateSavvyTaskerChanged();
return recurringTasks;
}
//@@author

//@@author A0139916U
Expand Down
126 changes: 124 additions & 2 deletions src/main/java/seedu/savvytasker/model/SavvyTasker.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package seedu.savvytasker.model;

import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
Expand All @@ -12,6 +16,7 @@
import seedu.savvytasker.model.alias.DuplicateSymbolKeywordException;
import seedu.savvytasker.model.alias.SymbolKeywordNotFoundException;
import seedu.savvytasker.model.task.ReadOnlyTask;
import seedu.savvytasker.model.task.RecurrenceType;
import seedu.savvytasker.model.task.Task;
import seedu.savvytasker.model.task.TaskList;
import seedu.savvytasker.model.task.TaskList.DuplicateTaskException;
Expand Down Expand Up @@ -70,13 +75,130 @@ public void resetData(ReadOnlySavvyTasker newData) {

/**
* Adds a task to savvy tasker.
* @throws DuplicateTaskException if an equivalent task already exists.
* @throws InvalidDateException if the end date is earlier than the start date.
* @throws {@link DuplicateTaskException} if a duplicate is found
* @throws {@link InvalidDateException} if the end date is earlier than the start date
* @return Returns the task added if the operation succeeds, an exception is thrown otherwise.
*/
public Task addTask(Task t) throws DuplicateTaskException, InvalidDateException {
t.setId(tasks.getNextId());
return tasks.add(t);
}

/**
* Adds a group of recurring tasks to savvy tasker.
* @throws {@link DuplicateTaskException} if a duplicate is found
* @throws {@link InvalidDateException} if the end date is earlier than the start date
* @return Returns the list of recurring tasks if the operation succeeds, an exception is thrown otherwise
*/
public LinkedList<Task> addRecurringTasks(Task recurringTask) throws DuplicateTaskException, InvalidDateException {
LinkedList<Task> tasksToAdd =
createRecurringTasks(recurringTask, recurringTask.getRecurringType(),
recurringTask.getNumberOfRecurrence());
Iterator<Task> itr = tasksToAdd.iterator();

while(itr.hasNext()) {
// this will be an atomic operation
// guaranteed no duplicates
// if the start/end dates are invalid,
// the first task to be added will fail immediately,
// subsequent tasks will not be added
tasks.add(itr.next());
}
return tasksToAdd;
}

/**
* Creates a list of recurring tasks to be added into savvy tasker.
* @param recurringTask the task that recurs
* @param recurringType the type of recurrence
* @param numberOfRecurrences the number of recurrences
* @return A list of tasks that represents a recurring task.
*/
private LinkedList<Task> createRecurringTasks(Task recurringTask, RecurrenceType recurringType, int numberOfRecurrences) {
assert recurringTask.getRecurringType() != null;
assert recurringTask.getNumberOfRecurrence() > 0;

LinkedList<Task> listOfTasks = new LinkedList<Task>();
recurringTask.setGroupId(tasks.getNextGroupId());

for (int i = 0; i < numberOfRecurrences; ++i) {
Task t = recurringTask.clone();
t.setId(tasks.getNextId());
listOfTasks.add(setDatesForRecurringType(t, recurringType, i));
}

return listOfTasks;
}

/**
* Helper function for createRecurringTasks(). Sets the respective start/end datetime for the
* i-th recurring task to be added
* @param t The first recurring task
* @param recurringType the type of recurrence
* @param index the index of the loop
* @return A task with its respective datetime set.
*/
private Task setDatesForRecurringType(Task t, RecurrenceType recurringType, int index) {
Date startDate = t.getStartDateTime();
Date endDate = t.getEndDateTime();
Calendar calendar = Calendar.getInstance();
switch (recurringType) {
case Daily: // add one day to the i-th task
if (startDate != null) {
calendar.setTime(startDate);
calendar.add(Calendar.DATE, 1 * index);
startDate = calendar.getTime();
}
if (endDate != null) {
calendar.setTime(endDate);
calendar.add(Calendar.DATE, 1 * index);
endDate = calendar.getTime();
}
break;
case Weekly: // add 7 days to the i-th task
if (startDate != null) {
calendar.setTime(startDate);
calendar.add(Calendar.DATE, 7 * index);
startDate = calendar.getTime();
}
if (endDate != null) {
calendar.setTime(endDate);
calendar.add(Calendar.DATE, 7 * index);
endDate = calendar.getTime();
}
break;
case Monthly: // add 1 month to the i-th task
if (startDate != null) {
calendar.setTime(startDate);
calendar.add(Calendar.MONTH, 1 * index);
startDate = calendar.getTime();
}
if (endDate != null) {
calendar.setTime(endDate);
calendar.add(Calendar.MONTH, 1 * index);
endDate = calendar.getTime();
}
break;
case Yearly: // add 1 year to the i-th task
if (startDate != null) {
calendar.setTime(startDate);
calendar.add(Calendar.YEAR, 1 * index);
startDate = calendar.getTime();
}
if (endDate != null) {
calendar.setTime(endDate);
calendar.add(Calendar.YEAR, 1 * index);
endDate = calendar.getTime();
}
break;
case None:
default:
assert false; // should not come here
}
t.setStartDateTime(startDate);
t.setEndDateTime(endDate);
return t;
}

/**
* Removes a task from savvy tasker.
Expand Down
7 changes: 2 additions & 5 deletions src/main/java/seedu/savvytasker/model/task/ReadOnlyTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
public interface ReadOnlyTask {

int getId();
int getGroupId();
boolean isMarked();
boolean isArchived();
String getTaskName();
Expand Down Expand Up @@ -53,11 +54,7 @@ default String getAsText() {
.append(getLocation());
}
builder.append(" Priority: ")
.append(getPriority())
.append(" Recurring Type: ")
.append(getRecurringType())
.append(" Nr. Recurrence: ")
.append(getNumberOfRecurrence());
.append(getPriority());
if (getCategory() != null && !getCategory().isEmpty()) {
builder.append(" Category: ")
.append(getCategory());
Expand Down
Loading

0 comments on commit 9675f25

Please sign in to comment.