diff --git a/data/duke.txt b/data/duke.txt index 1a758c99f6..0a0e3316a6 100644 --- a/data/duke.txt +++ b/data/duke.txt @@ -1,12 +1,15 @@ -E | 0 | fsahufias | 30/06/2020 0316 | false -D | 0 | abc | 27/07/1996 2130 | false +E | 0 | fsahufias | 30/06/2020 0316 | ONCE +D | 0 | abc | 24/09/2019 0915 | DAILY P | 0 | abc | 27/07/1996 2130 | 27/06/2029 1630 -D | 0 | abc | 27/07/1996 2130 | false -T | 1 | abc | false -D | 0 | abc | 17/09/2019 2130 | false -E | 0 | werq | 17/09/2019 1500 | false -T | 0 | homework | false -E | 0 | abc | 06/06/2016 1530 | false +D | 0 | abc | 24/09/2019 1145 | DAILY +T | 1 | abc | DAILY +D | 0 | abc | 17/09/2019 2130 | ONCE +E | 0 | werq | 17/09/2019 1500 | ONCE +T | 0 | homework | ONCE +E | 0 | abc | 24/09/2019 1530 | DAILY P | 0 | abc | 27/08/2019 1630 | 29/11/2020 1630 P | 0 | abc | 27/08/2019 1630 | 29/11/2020 1630 P | 0 | abc | 27/08/2019 1630 | 29/11/2020 1630 +E | 1 | dog | 24/09/2019 0001 | MONTHLY +D | 0 | cat | 11/10/2019 0001 | DAILY +E | 0 | rabbit | 23/09/2019 0909 | WEEKLY diff --git a/src/main/java/duke/command/RecurringCommand.java b/src/main/java/duke/command/RecurringCommand.java index ca306367fb..0a6d3cd472 100644 --- a/src/main/java/duke/command/RecurringCommand.java +++ b/src/main/java/duke/command/RecurringCommand.java @@ -14,10 +14,12 @@ public class RecurringCommand extends Command { * Used to identify the task being marked as recurring. */ private int taskIndex; + protected Task.RecurringFrequency frequency; - public RecurringCommand(int taskIndex) { + public RecurringCommand(int taskIndex, Task.RecurringFrequency frequency) { super(); this.taskIndex = taskIndex; + this.frequency = frequency; } /** @@ -43,16 +45,15 @@ public RecurringCommand(int taskIndex) { @Override public void execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { try { - Task recurringTask = tasks.getTask(taskIndex); - if (recurringTask.getDateTime() != null) { - if (!recurringTask.isTaskRecurring()) { - recurringTask.makeTaskRecurring(); - ui.makeRecurring(recurringTask); + Task task = tasks.getTask(taskIndex); + if (task.getDateTime() != null) { + if (!task.isTaskRecurring()) { + task.makeTaskRecurring(this.frequency); + ui.makeRecurring(task); + } else { + System.out.println("This task is already marked as recurring!"); } - recurringTask.recurringTaskTimeUpdate(); storage.save(tasks.fullTaskList()); - } else { - recurringTask.updateLocalDateTime(LocalDateTime.now().toString()); } } catch (DukeException e) { throw new DukeException("I couldn't make the task recurring. " + e); diff --git a/src/main/java/duke/core/Parser.java b/src/main/java/duke/core/Parser.java index 0b16125793..4c7bac90bb 100644 --- a/src/main/java/duke/core/Parser.java +++ b/src/main/java/duke/core/Parser.java @@ -104,8 +104,20 @@ public static Command parse(String ss) throws DukeException { } case "recurring": try { - int index = Integer.parseInt(command[1]); - return new RecurringCommand(index); + String[] parsedInput = command[1].split(" /"); + int index = Integer.parseInt(parsedInput[0]); + if (parsedInput[1] != null) { + parsedInput[1] = parsedInput[1].trim(); + if (parsedInput[1].toLowerCase().contains("weekly")) { + return new RecurringCommand(index, Task.RecurringFrequency.WEEKLY); + } else if (parsedInput[1].toLowerCase().contains("monthly")) { + return new RecurringCommand(index, Task.RecurringFrequency.MONTHLY); + } else if (parsedInput[1].toLowerCase().contains("daily")) { + return new RecurringCommand(index, Task.RecurringFrequency.DAILY); + } else { + return new RecurringCommand(index, Task.RecurringFrequency.DAILY); + } + } } catch (Exception e) { throw new DukeException("Failed to make your task recurring." + e.getMessage()); } diff --git a/src/main/java/duke/core/Storage.java b/src/main/java/duke/core/Storage.java index 7fb6d61f8a..b8f5f4861d 100644 --- a/src/main/java/duke/core/Storage.java +++ b/src/main/java/duke/core/Storage.java @@ -47,8 +47,8 @@ public ArrayList load() throws DukeException { if (newTask[1].equals("1")) { x.markAsDone(); } - if (newTask[3].equals("true")) { - x.makeTaskRecurring(); + if ((newTask[3] != null) && !(newTask[3].equals("ONCE"))) { + x.makeTaskRecurring(giveFrequency(newTask[3])); } tasks.add(x); } @@ -57,8 +57,8 @@ else if (newTask[0].equals("D")) { if (newTask[1].equals("1")) { t.markAsDone(); } - if (newTask[4].equals("true")) { - t.makeTaskRecurring(); + if ((newTask[4] != null) && !(newTask[4].equals("ONCE"))) { + t.makeTaskRecurring(giveFrequency(newTask[4])); } tasks.add(t); } @@ -67,8 +67,8 @@ else if (newTask[0].equals("E")) { if (newTask[1].equals("1")) { t.markAsDone(); } - if (newTask[4].equals("true")) { - t.makeTaskRecurring(); + if ((newTask[4] != null) && !(newTask[4].equals("ONCE"))) { + t.makeTaskRecurring(giveFrequency(newTask[4])); } tasks.add(t); } @@ -84,9 +84,6 @@ else if (newTask[0].equals("P")) { if (newTask[1].equals("1")) { x.markAsDone(); } - if (newTask[4].equals("true")) { - x.makeTaskRecurring(); - } tasks.add(x); } @@ -115,4 +112,17 @@ public void save(ArrayList task) throws DukeException { } } + private Task.RecurringFrequency giveFrequency(String string) { + switch (string) { + case "DAILY": + return Task.RecurringFrequency.DAILY; + case "WEEKLY": + return Task.RecurringFrequency.WEEKLY; + case "MONTHLY": + return Task.RecurringFrequency.MONTHLY; + default: + return Task.RecurringFrequency.ONCE; + } + } + } diff --git a/src/main/java/duke/core/Ui.java b/src/main/java/duke/core/Ui.java index f133b42636..c6a9e47513 100644 --- a/src/main/java/duke/core/Ui.java +++ b/src/main/java/duke/core/Ui.java @@ -198,7 +198,7 @@ public void showWelcome() { + "| |_| | |_| | < __/\n" + "|____/ \\__,_|_|\\_\\___|\n"; System.out.println("Hello from\n" + logo); - System.out.println("Hello! I'm Duke\nWhat can I do for you?"); + System.out.println("Hello! I'm Duke\nWhat can I do for you?\n"); } /** diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java index 826f4e6961..dc9eff5372 100644 --- a/src/main/java/duke/task/Deadline.java +++ b/src/main/java/duke/task/Deadline.java @@ -3,6 +3,8 @@ import duke.core.DateTimeParser; import duke.core.DukeException; +import java.time.format.DateTimeFormatter; + /** * Represents a task with a deadline. It is * extended from the Task class. @@ -39,7 +41,21 @@ public Deadline(String description, String dateTime) throws DukeException { */ @Override public String toString() { - return "[D]" + super.printStatus() + " (by: " + dateTimeEnglish + ")"; + + if (recurringTask != null) { + DateTimeFormatter newDateFormatter = DateTimeFormatter.ofPattern("dd/MM/YYYY HHmm"); + String newDate = recurringTask.recurringTaskTimeUpdate(this).format(newDateFormatter); + this.dateTime = newDate; + try { + this.dateTimeEnglish = DateTimeParser.convertToEnglishDateTime(dateTime); + } catch (DukeException e) { + System.out.println("I couldn't convert your given time. " + e); + } + } + return "[D]" + + super.printStatus() + + " (by: " + + dateTimeEnglish + ")"; } /** @@ -48,6 +64,10 @@ public String toString() { * @return A string in a specific format to be stored in a local file. */ public String writeTxt() { + String frequency = "ONCE"; + if (isTaskRecurring()) { + frequency = recurringTask.getFrequency().toString(); + } return "D | " + (isDone() ? "1" : "0") + " | " @@ -55,7 +75,7 @@ public String writeTxt() { + " | " + dateTime + " | " - + this.isRecurring; + + frequency; } } \ No newline at end of file diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java index ab36625354..32dee0c607 100644 --- a/src/main/java/duke/task/Event.java +++ b/src/main/java/duke/task/Event.java @@ -3,6 +3,9 @@ import duke.core.DateTimeParser; import duke.core.DukeException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + /** * Represents a task with a event. It is * extended from the Task class. @@ -37,7 +40,21 @@ public Event(String description, String dateTime) throws DukeException { */ @Override public String toString() { - return "[E]" + super.printStatus() + " (at: " + dateTimeEnglish + ")"; + + if (recurringTask != null) { + DateTimeFormatter newDateFormatter = DateTimeFormatter.ofPattern("dd/MM/YYYY HHmm"); + String newDate = recurringTask.recurringTaskTimeUpdate(this).format(newDateFormatter); + this.dateTime = newDate; + try { + this.dateTimeEnglish = DateTimeParser.convertToEnglishDateTime(dateTime); + } catch (DukeException e) { + System.out.println("I couldn't convert your given time. " + e); + } + } + return "[E]" + + super.printStatus() + + " (at: " + + dateTimeEnglish + ")"; } /** @@ -46,6 +63,10 @@ public String toString() { * @return A string in a specific format to be stored in a local file. */ public String writeTxt() { + String frequency = "ONCE"; + if (isTaskRecurring()) { + frequency = recurringTask.getFrequency().toString(); + } return "E | " + (isDone() ? "1" : "0") + " | " @@ -53,6 +74,6 @@ public String writeTxt() { + " | " + dateTime + " | " - + this.isRecurring; + + frequency; } } \ No newline at end of file diff --git a/src/main/java/duke/task/RecurringTask.java b/src/main/java/duke/task/RecurringTask.java new file mode 100644 index 0000000000..0b6d2a092a --- /dev/null +++ b/src/main/java/duke/task/RecurringTask.java @@ -0,0 +1,63 @@ +package duke.task; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.format.DateTimeParseException; + +public class RecurringTask { + + public enum RecurringFrequency { DAILY, WEEKLY, MONTHLY}; + public enum TaskType { TODO, DEADLINE, EVENT} + + private LocalDateTime lastRecordedTime; + private RecurringFrequency frequency; + private TaskType taskType; + + public RecurringTask(Task task, RecurringFrequency frequency) { + if (task instanceof Todo) { this.taskType = TaskType.TODO; } + if (task instanceof Deadline) { this.taskType = TaskType.DEADLINE; } + if (task instanceof Event) { this.taskType = TaskType.EVENT; } + + if (task.getDateTime() != null) { + lastRecordedTime = task.getDateTime(); + } + this.frequency = frequency; + } + + public RecurringFrequency getFrequency() { return frequency; } + + /** + * When a task is recurring, method compares current time to listed date. + * If the task's date is outdated, then it will update to be for the next day. + */ + public LocalDateTime recurringTaskTimeUpdate(Task task) { + if (lastRecordedTime != null) { + try { + LocalDateTime currentTime = LocalDateTime.now(); + if (lastRecordedTime.isBefore(currentTime)) { + + switch (this.frequency) { + case DAILY: + while (lastRecordedTime.isBefore(currentTime) || task.isDone()) { + lastRecordedTime = lastRecordedTime.plusDays(1); + if (task.isDone()) { task.isDone = false; } + } + case WEEKLY: + while (lastRecordedTime.isBefore(currentTime) || task.isDone()) { + lastRecordedTime = lastRecordedTime.plusWeeks(1); + if (task.isDone) { task.isDone = false; } + } + case MONTHLY: + while (lastRecordedTime.isBefore(currentTime) || task.isDone()) { + lastRecordedTime = lastRecordedTime.plusMonths(1); + if (task.isDone) { task.isDone = false; } + } + } + } + } catch (DateTimeParseException e) { + System.out.println("I couldn't update your recurring events' times."); + } + } + return lastRecordedTime; + } +} diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java index 2439c44c9b..02bdeea78f 100644 --- a/src/main/java/duke/task/Task.java +++ b/src/main/java/duke/task/Task.java @@ -18,23 +18,37 @@ * instantiated */ public abstract class Task { + /** * A String that represents the description of the task. */ protected String description; + /** * A boolean that represents the status of the task( 1 means done, 0 means not yet) */ protected boolean isDone; + /** * a localDateTime constructor to save the date and time */ protected LocalDateTime ld = null; + /** * A boolean that represents whether or not a task is recurring. True = recurring, False = non-recurring */ protected boolean isRecurring = false; + /** + * An enumerator meant to specify the frequency of a recurring task. + */ + public enum RecurringFrequency { DAILY, WEEKLY, MONTHLY, ONCE; } + + /** + * An extended class that retains information about a recurring task. + */ + protected RecurringTask recurringTask; + /** * Initialises the minimum fields required to setup a Task. * @@ -80,35 +94,31 @@ public void markAsDone() { /** * Marks the task as recurring. */ - public void makeTaskRecurring() { isRecurring = true; } + public void makeTaskRecurring(RecurringFrequency frequency) { + isRecurring = true; + switch (frequency) { + case DAILY: + this.recurringTask = new RecurringTask(this, RecurringTask.RecurringFrequency.DAILY); + break; + case WEEKLY: + this.recurringTask = new RecurringTask(this, RecurringTask.RecurringFrequency.WEEKLY); + break; + case MONTHLY: + this.recurringTask = new RecurringTask(this, RecurringTask.RecurringFrequency.MONTHLY); + break; + case ONCE: + break; + } + if (this.recurringTask != null) { + this.recurringTask.recurringTaskTimeUpdate(this); + } + } /** * Returns boolean stating whether task is recurring. */ public boolean isTaskRecurring() { return isRecurring; } - /** - * When a task is recurring, method compares current time to listed date. - * If the task's date is outdated, then it will update to be for the next day. - */ - public void recurringTaskTimeUpdate() { - if ((ld != null) && this.isRecurring) { - try { - LocalDateTime currentTime = LocalDateTime.now(); - if (this.ld.isBefore(currentTime)) { - Duration dayDifference = Duration.between(currentTime, this.ld); - if (Math.abs(dayDifference.toDays()) > 0 ) { - this.ld = ld.plusDays(Math.abs(dayDifference.toDays())); - - if (!this.isDone) { this.isDone = false; } - } - } - } catch (DateTimeParseException e) { - System.out.println("I couldn't update your recurring events' times."); - } - } - } - /** * Returns a string with the status icon and the description of the task. * @@ -172,7 +182,9 @@ public void updateLocalDateTime(String newDateTime) throws DukeException { */ public LocalDateTime getDateTime() { - if (this.isTaskRecurring()) { this.recurringTaskTimeUpdate(); } + if (recurringTask != null) { + this.ld = recurringTask.recurringTaskTimeUpdate(this); + } return ld; } diff --git a/src/main/java/duke/task/Todo.java b/src/main/java/duke/task/Todo.java index 12e6f88615..ace6f025f6 100644 --- a/src/main/java/duke/task/Todo.java +++ b/src/main/java/duke/task/Todo.java @@ -32,12 +32,16 @@ public String toString() { * @return A string in a specific format to be stored in a local file. */ public String writeTxt() { + String frequency = "ONCE"; + if (isTaskRecurring()) { + frequency = recurringTask.getFrequency().toString(); + } return "T | " + (this.isDone() ? "1" : "0") + " | " + this.description + " | " - + this.isRecurring; + + frequency; } } \ No newline at end of file diff --git a/src/test/java/duke/task/RecurringTest.java b/src/test/java/duke/task/RecurringTest.java new file mode 100644 index 0000000000..a5fcc8eeec --- /dev/null +++ b/src/test/java/duke/task/RecurringTest.java @@ -0,0 +1,57 @@ +package duke.task; +import duke.command.RecurringCommand; +import duke.core.DukeException; +import duke.task.*; +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.time.LocalDateTime; +import static org.junit.jupiter.api.Assertions.*; + +public class RecurringTest { + + @Test + public void recurringDailyDeadlineTest() throws DukeException { + Deadline dummyDeadline = new Deadline("paper", "03/08/2018 1159"); + LocalDateTime oldDateTime = dummyDeadline.getDateTime(); + dummyDeadline.makeTaskRecurring(Task.RecurringFrequency.DAILY); + assert(dummyDeadline.getDateTime().isAfter(oldDateTime)); + + Duration dayDifference = Duration.between(LocalDateTime.now(), dummyDeadline.getDateTime()); + long numberOfDaysDifference = Math.abs(dayDifference.toDays()); + assert(numberOfDaysDifference <= 1); + } + + @Test + public void recurringWeeklyEventTest() throws DukeException { + Event dummyEvent = new Event("presentation", "15/09/2019 1000"); + LocalDateTime oldDateTime = dummyEvent.getDateTime(); + dummyEvent.makeTaskRecurring(Task.RecurringFrequency.WEEKLY); + assert(dummyEvent.getDateTime().isAfter(oldDateTime)); + + Duration dayDifference = Duration.between(LocalDateTime.now(), dummyEvent.getDateTime()); + long numberOfDaysDifference = Math.abs(dayDifference.toDays()); + assert(numberOfDaysDifference <= 7); + } + + @Test public void recurringMonthlyDeadlineTest() throws DukeException { + Deadline dummyDeadline = new Deadline("paper", "03/08/2018 1159"); + LocalDateTime oldDateTime = dummyDeadline.getDateTime(); + dummyDeadline.makeTaskRecurring(Task.RecurringFrequency.MONTHLY); + assert(dummyDeadline.getDateTime().isAfter(oldDateTime)); + + Duration dayDifference = Duration.between(LocalDateTime.now(), dummyDeadline.getDateTime()); + long numberOfDaysDifference = Math.abs(dayDifference.toDays()); + assert(numberOfDaysDifference <= 31); + } + + /* + @Test public void recurringTodoTest() { + Task dummyToDo = new Todo("essay"); + dummyToDo.makeTaskRecurring(Task.RecurringFrequency.DAILY); + assertNotEquals(true, dummyToDo.isTaskRecurring()); + } + + */ + +}