From 5f51b579de1292637f42d35291b7c5bef0fd36e4 Mon Sep 17 00:00:00 2001 From: Ella-e Date: Thu, 15 Feb 2024 21:40:02 +0800 Subject: [PATCH] Add reminder of due tasks There is no reminder of tasks that are due soon. People might forget their most recent due tasks if the task list contains a lot of tasks. Let's, * Show the most recent due tasks when the user enters the app. It will make the app more useful and userfiendly. --- data/plaudern.txt | 7 +++- src/main/java/duke/Duke.java | 13 ++++-- src/main/java/duke/Main.java | 3 -- src/main/java/duke/control/DialogBox.java | 3 ++ src/main/java/duke/control/MainWindow.java | 35 +++++++++++++--- src/main/java/duke/storage/Storage.java | 11 +++-- src/main/java/duke/tasks/Deadline.java | 20 ++++----- src/main/java/duke/tasks/Event.java | 25 ++++++++--- src/main/java/duke/tasks/TaskList.java | 48 +++++++++++++++++++++- src/main/java/duke/ui/UI.java | 4 +- src/main/java/duke/utils/Parser.java | 43 ++++++++++++++----- src/main/resources/view/MainWindow.fxml | 4 +- 12 files changed, 168 insertions(+), 48 deletions(-) diff --git a/data/plaudern.txt b/data/plaudern.txt index 1bc0d1c748..d21ea1a4cd 100644 --- a/data/plaudern.txt +++ b/data/plaudern.txt @@ -1 +1,6 @@ -T|0|a +D|1|abc |2022-02-20 +D|0|cd |2023-11-01 +D|0|something aa |2024-01-04 +T|1|abc +T|0|abc +T|0|cdd diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java index d1db37b4ae..6cf09d4a1c 100644 --- a/src/main/java/duke/Duke.java +++ b/src/main/java/duke/Duke.java @@ -46,8 +46,8 @@ public Duke(String filePath) { * Runs the chatbot. */ public void run() { - ui.onEnter(); - String response; + System.out.println(ui.onEnter()); + String response = ""; do { response = getResponse(ui.getUserInput()); } while (!Objects.equals(response, "Bye. Hope to see you again soon!")); @@ -69,7 +69,6 @@ public String getResponse(String userInput) { // the parser object contains all the current user input line information Parser parser = new Parser(); parser.parse(userInput); - System.out.println(userInput); // check for end the session if (parser.getCurrentKey().equals(KeyEnum.EXITKEY)) { @@ -120,4 +119,12 @@ public String getResponse(String userInput) { } return response; } + + /** + * Gets next due tasks. + * @return next due tasks + */ + public String getNextDueTasks() { + return tasks.nextDueTasksToString(2); + } } diff --git a/src/main/java/duke/Main.java b/src/main/java/duke/Main.java index 89ee4ae792..1ead41e56c 100644 --- a/src/main/java/duke/Main.java +++ b/src/main/java/duke/Main.java @@ -14,8 +14,6 @@ */ public class Main extends Application { - private Duke duke = new Duke(); - @Override public void start(Stage stage) { try { @@ -23,7 +21,6 @@ public void start(Stage stage) { AnchorPane ap = fxmlLoader.load(); Scene scene = new Scene(ap); stage.setScene(scene); - fxmlLoader.getController().setDuke(duke); stage.show(); } catch (IOException e) { e.printStackTrace(); diff --git a/src/main/java/duke/control/DialogBox.java b/src/main/java/duke/control/DialogBox.java index 0e441931d6..40f23ddd28 100644 --- a/src/main/java/duke/control/DialogBox.java +++ b/src/main/java/duke/control/DialogBox.java @@ -7,6 +7,7 @@ import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; +import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Label; @@ -37,6 +38,8 @@ private DialogBox(String text, Image img) { dialog.setText(text); displayPicture.setImage(img); + Insets padding = new Insets(10); + dialog.setPadding(padding); } /** diff --git a/src/main/java/duke/control/MainWindow.java b/src/main/java/duke/control/MainWindow.java index ce3814e2f5..fe7f67427c 100644 --- a/src/main/java/duke/control/MainWindow.java +++ b/src/main/java/duke/control/MainWindow.java @@ -3,6 +3,9 @@ import java.util.Objects; import duke.Duke; +import duke.exceptions.BaseException; +import duke.ui.UI; +import javafx.animation.PauseTransition; import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.ScrollPane; @@ -10,11 +13,14 @@ import javafx.scene.image.Image; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.VBox; +import javafx.util.Duration; /** * Controller for MainWindow. Provides the layout for the other controls. */ public class MainWindow extends AnchorPane { + private static final String WELCOME_MESSAGE = new UI().onEnter(); + private static final String ERROR_MASSAGE = "Oops, some error occurs"; @FXML private ScrollPane scrollPane; @FXML @@ -31,13 +37,26 @@ public class MainWindow extends AnchorPane { private final Image dukeImage = new Image(Objects.requireNonNull(this.getClass().getResourceAsStream("/images/bot.png"))); + /** + * Initializes the chatbot. + */ @FXML public void initialize() { - scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); - } - - public void setDuke(Duke d) { - duke = d; + try { + this.duke = new Duke(); + String welcomeString = WELCOME_MESSAGE + "\n" + duke.getNextDueTasks(); + scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); + dialogContainer.getChildren().add( + DialogBox.getDukeDialog(welcomeString, dukeImage) + ); + } catch (BaseException e) { + dialogContainer.getChildren().add( + DialogBox.getDukeDialog(ERROR_MASSAGE, dukeImage) + ); + PauseTransition pause = new PauseTransition(Duration.seconds(3)); + pause.setOnFinished(event -> System.exit(0)); + pause.play(); + } } /** @@ -53,6 +72,12 @@ private void handleUserInput() { DialogBox.getDukeDialog(response, dukeImage) ); userInput.clear(); + if (response.equals(new UI().onExit())) { + // Close the window after 2 seconds + PauseTransition pause = new PauseTransition(Duration.seconds(2)); + pause.setOnFinished(event -> System.exit(0)); + pause.play(); + } } } diff --git a/src/main/java/duke/storage/Storage.java b/src/main/java/duke/storage/Storage.java index 4c8baf520c..2f69291f2b 100644 --- a/src/main/java/duke/storage/Storage.java +++ b/src/main/java/duke/storage/Storage.java @@ -4,6 +4,8 @@ import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; +import java.time.LocalDate; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Scanner; @@ -12,6 +14,7 @@ import duke.tasks.Task; import duke.tasks.TaskList; import duke.tasks.Todo; +import duke.utils.Parser; /** * Class represent a file and contains functions processing a file. @@ -95,7 +98,7 @@ public void writeTasksToFile(TaskList taskList) throws IOException { * @param str String format of the task * @return The respective task object */ - private Task stringToTask(String str) throws IOException { + private Task stringToTask(String str) throws IOException, DateTimeParseException { assert str != null : "String should not be null"; String[] strSplit = str.split("\\|"); if (strSplit.length <= 1) { @@ -109,12 +112,12 @@ private Task stringToTask(String str) throws IOException { task = new Todo(status, detail); break; case "D": - String by = strSplit[3]; + LocalDate by = Parser.formatDate(strSplit[3]); task = new Deadline(status, detail, by); break; case "E": - String from = strSplit[3]; - String to = strSplit[4]; + LocalDate from = Parser.formatDate(strSplit[3]); + LocalDate to = Parser.formatDate(strSplit[4]); task = new Event(status, detail, from, to); break; default: diff --git a/src/main/java/duke/tasks/Deadline.java b/src/main/java/duke/tasks/Deadline.java index 6a6fafc5f8..fc4efa3252 100644 --- a/src/main/java/duke/tasks/Deadline.java +++ b/src/main/java/duke/tasks/Deadline.java @@ -3,8 +3,7 @@ import java.time.LocalDate; import java.time.format.DateTimeParseException; -import duke.exceptions.InvalidDateTimeException; -import duke.utils.DukeDateFormater; +import duke.utils.Parser; /** @@ -12,7 +11,6 @@ */ public class Deadline extends Task { private LocalDate by; - private DukeDateFormater formater = new DukeDateFormater(); /** * Initializes a Deadline object with given params. @@ -21,13 +19,9 @@ public class Deadline extends Task { * @param by Task end time. * @throws DateTimeParseException If the end time is invalid. */ - public Deadline(Boolean status, String detail, String by) throws DateTimeParseException { + public Deadline(Boolean status, String detail, LocalDate by) throws DateTimeParseException { super(status, detail); - try { - this.by = this.formater.stringToDate(by); - } catch (DateTimeParseException e) { - throw new InvalidDateTimeException(); - } + this.by = by; } /** @@ -36,11 +30,15 @@ public Deadline(Boolean status, String detail, String by) throws DateTimeParseEx */ @Override public String inFileStringFormat() { - return "D|" + super.inFileStringFormat() + "|" + this.by.toString(); + return "D|" + super.inFileStringFormat() + "|" + by.toString(); } @Override public String toString() { - return "[D]" + super.toString() + "(by: " + this.formater.dateToString(this.by) + ")"; + return "[D]" + super.toString() + "(by: " + Parser.FORMATER.dateToString(this.by) + ")"; + } + + public LocalDate getBy() { + return by; } } diff --git a/src/main/java/duke/tasks/Event.java b/src/main/java/duke/tasks/Event.java index 3deabb1b13..2be9d639d7 100644 --- a/src/main/java/duke/tasks/Event.java +++ b/src/main/java/duke/tasks/Event.java @@ -1,11 +1,16 @@ package duke.tasks; +import java.time.LocalDate; + +import duke.utils.Parser; + + /** * Class represent Task type Event. */ public class Event extends Task { - private String start; - private String by; + private LocalDate start; + private LocalDate by; /** * Initializes an Event object with given params. @@ -15,7 +20,7 @@ public class Event extends Task { * @param start task start time. * @param by task end time. */ - public Event(Boolean status, String detail, String start, String by) { + public Event(Boolean status, String detail, LocalDate start, LocalDate by) { super(status, detail); this.start = start; this.by = by; @@ -28,11 +33,21 @@ public Event(Boolean status, String detail, String start, String by) { */ @Override public String inFileStringFormat() { - return "E|" + super.inFileStringFormat() + "|" + this.start + "|" + this.by; + return "E|" + super.inFileStringFormat() + "|" + start + + "|" + by; } @Override public String toString() { - return "[E]" + super.toString() + "(from: " + start + " to: " + by + ")"; + return "[E]" + super.toString() + "(from: " + Parser.FORMATER.dateToString(start) + + " to: " + Parser.FORMATER.dateToString(by) + ")"; + } + + public LocalDate getStart() { + return start; + } + + public LocalDate getBy() { + return by; } } diff --git a/src/main/java/duke/tasks/TaskList.java b/src/main/java/duke/tasks/TaskList.java index c357770f17..363e5f2601 100644 --- a/src/main/java/duke/tasks/TaskList.java +++ b/src/main/java/duke/tasks/TaskList.java @@ -1,6 +1,8 @@ package duke.tasks; +import java.time.LocalDate; import java.util.ArrayList; +import java.util.Comparator; import duke.exceptions.EmptyBodyException; import duke.exceptions.InvalidDateTimeException; @@ -70,7 +72,7 @@ public String listTask() { * @throws EmptyBodyException If the command body is empty. * @throws InvalidDateTimeException If the due date for deadline is not in recognisable format. */ - public Task addTask(String detail, String from, String to, KeyEnum currentKey) + public Task addTask(String detail, LocalDate from, LocalDate to, KeyEnum currentKey) throws EmptyBodyException, InvalidDateTimeException { Task task = null; switch (currentKey) { @@ -139,7 +141,6 @@ public Task deleteTaskById(Integer id) throws OutOfBoundException, IndexOutOfBou public TaskList findTasks(String word) { ArrayList matchedTasks = new ArrayList<>(); // Iterate through the task list - System.out.println(word); for (Task task : tasks) { if (task.getDetail().contains(word)) { matchedTasks.add(task); @@ -147,4 +148,47 @@ public TaskList findTasks(String word) { } return new TaskList(matchedTasks); } + + /** + * Finds the list of tasks that are due next. + * @param numOfTasks number of tasks to be returned + * @return list of task that most recent due + */ + public TaskList findNextDueTasks(int numOfTasks) { + ArrayList nextTasks = new ArrayList<>(tasks); + nextTasks.sort(new Comparator() { + @Override + public int compare(Task o1, Task o2) { + if (o1 instanceof Deadline && o2 instanceof Deadline) { + return ((Deadline) o1).getBy().compareTo(((Deadline) o2).getBy()); + } else if (o1 instanceof Event && o2 instanceof Event) { + return ((Event) o1).getBy().compareTo(((Event) o2).getBy()); + } else if (o1 instanceof Todo) { + return 1; + } else if (o2 instanceof Todo) { + return -1; + } + return 0; + } + }); + + if (nextTasks.size() <= numOfTasks) { + return new TaskList(nextTasks); + } + + ArrayList tempTasks = new ArrayList<>(); + int counter = numOfTasks; + for (Task task: nextTasks) { + if (counter <= 0) { + break; + } + tempTasks.add(task); + counter--; + } + return new TaskList(tempTasks); + } + + public String nextDueTasksToString(int numOfTasks) { + return "==Reminder==\nThose Tasks are due next:\n" + findNextDueTasks(numOfTasks).listTask(); + } } diff --git a/src/main/java/duke/ui/UI.java b/src/main/java/duke/ui/UI.java index 675e5b47f8..38ac2127ae 100644 --- a/src/main/java/duke/ui/UI.java +++ b/src/main/java/duke/ui/UI.java @@ -14,8 +14,8 @@ public class UI { /** * Displays greeting sentence. */ - public void onEnter() { - System.out.println("Hello! I'm Plaudern\nWhat can I do for you?"); + public String onEnter() { + return "Hello! I'm Plaudern\nWhat can I do for you?\n"; } /** diff --git a/src/main/java/duke/utils/Parser.java b/src/main/java/duke/utils/Parser.java index 1b21c076af..66c0328c47 100644 --- a/src/main/java/duke/utils/Parser.java +++ b/src/main/java/duke/utils/Parser.java @@ -1,6 +1,10 @@ package duke.utils; +import java.time.LocalDate; +import java.time.format.DateTimeParseException; + import duke.exceptions.EmptyBodyException; +import duke.exceptions.InvalidDateTimeException; import duke.exceptions.InvalidKeyException; import duke.exceptions.InvalidNumberException; import duke.exceptions.WrongFormatException; @@ -9,10 +13,11 @@ * Parser to read and understand user inputs. Stores the parsed information inside the object. */ public class Parser { + public static final DukeDateFormater FORMATER = new DukeDateFormater(); private KeyEnum currentKey; private String inputDetail; - private String to; - private String from; + private LocalDate to; + private LocalDate from; private Integer index; /** @@ -25,7 +30,7 @@ public class Parser { * @throws InvalidNumberException If the number in the command is not a number. */ public void parse(String userInput) throws InvalidKeyException, EmptyBodyException, - WrongFormatException, InvalidNumberException { + WrongFormatException, InvalidNumberException, DateTimeParseException { String[] userInputSplit = userInput.split(" "); this.determineCurrentKey(userInputSplit[0]); switch (this.currentKey) { @@ -35,7 +40,7 @@ public void parse(String userInput) throws InvalidKeyException, EmptyBodyExcepti } try { inputDetail = userInput.substring(9, userInput.indexOf("/by")); - to = userInput.substring(userInput.indexOf("/by") + 4); + to = formatDate(userInput.substring(userInput.indexOf("/by") + 4)); } catch (Exception e) { throw new WrongFormatException("\"deadline content /by yyyy-mm-dd\""); } @@ -53,10 +58,10 @@ public void parse(String userInput) throws InvalidKeyException, EmptyBodyExcepti } try { inputDetail = userInput.substring(6, userInput.indexOf("/from")); - from = userInput.substring(userInput.indexOf("/from") + 6, userInput.indexOf("/to") - 1); - to = userInput.substring(userInput.indexOf("/to") + 4); + from = formatDate(userInput.substring(userInput.indexOf("/from") + 6, userInput.indexOf("/to") - 1)); + to = formatDate(userInput.substring(userInput.indexOf("/to") + 4)); } catch (Exception e) { - throw new WrongFormatException("\"deadline content /from time /to time\""); + throw new WrongFormatException("\"event content /from yyyy-mm-dd /to yyyy-mm-dd\""); } break; case MARK: @@ -118,6 +123,24 @@ public void determineCurrentKey(String userInputKey) { } } + /** + * Changes date from string to LocalDate object. + * + * @param str date in string form + * @return date in LocalDate form + */ + public static LocalDate formatDate(String str) { + LocalDate date = null; + System.out.println(str); + try { + date = FORMATER.stringToDate(str); + } catch (DateTimeParseException e) { + throw new InvalidDateTimeException(); + } + assert date != null; + return date; + } + public KeyEnum getCurrentKey() { return currentKey; } @@ -126,15 +149,15 @@ public String getInputDetail() { return inputDetail; } - public String getTo() { + public LocalDate getTo() { return to; } - public void setTo(String to) { + public void setTo(LocalDate to) { this.to = to; } - public String getFrom() { + public LocalDate getFrom() { return from; } diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index a123a96992..b37046c8d1 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -8,11 +8,11 @@ - +