diff --git a/.gitignore b/.gitignore index 2873e189e..397229206 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,6 @@ bin/ /text-ui-test/ACTUAL.TXT text-ui-test/EXPECTED-UNIX.TXT + +# Data files +/data/ diff --git a/docs/README.md b/docs/README.md index 8077118eb..b9eeaa290 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,29 +1,108 @@ -# User Guide +# Python User Guide + +**Python** task manager will be a blessing for users with fast typing speed. You can +add three types of tasks, mark tasks as completed, delete tasks, and if necessary +find tasks using a keyword! ## Features +Python comes with the following core features: +* [Add Todo](#add-todo) +* [Add Deadline](#add-deadline) +* [Add Event](#add-event) +* [Mark Task](#mark-task) +* [Unmark Task](#unmark-task) +* [Delete Task](#delete-task) +* [Find Tasks](#find-tasks) +* [List Tasks](#list-tasks) +* [Exit](#exit-program) + +### Add Todo +Format: +`todo [todo description]` + +Example of usage: +`todo buy groceries` + +Add a simple todo! + +### Add Deadline + +Format: +`deadline [deadline description] /by [due date]` + +Example of usage: +`deadline lab 2 /by friday night` + +Add a deadline with due date! + + +### Add Event + +Format: +`event [event description] /from [start date] /to [end date]` + +Example of usage: +`event math exam /from monday 2pm /to monday 4pm` + +Add an event to keep track of both start and end times of an activity! + +### Mark Task + +Format: +`mark [index of the task - 1 based]` + +Example of usage: +`mark 1` + +Mark the task at index as done! + +### Unmark Task + +Format: +`unmark [index of the task - 1 based]` + +Example of usage: +`unmark 1` + +Unmark the task at index as not done yet! + +### Delete Task + +Format: +`delete [index of the task - 1 based]` + +Example of usage: +`delete 1` + +Delete the task at the index! + +### Find Tasks -### Feature-ABC +Format: +`find [keyword]` -Description of the feature. +Example of usage: +`find exam` -### Feature-XYZ +List the tasks that contains the keyword in the description! -Description of the feature. +### List Tasks -## Usage +Format: +`list` -### `Keyword` - Describe action +Example of usage: +`list` -Describe the action and its outcome. +List all the tasks along with completion status and task type! -Example of usage: +### Exit Program -`keyword (optional arguments)` +Format: +`bye` -Expected outcome: +Example of usage: +`bye` -Description of the outcome. +Greet Python 'bye' to exit the program! -``` -expected output -``` diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java deleted file mode 100644 index 5d313334c..000000000 --- a/src/main/java/Duke.java +++ /dev/null @@ -1,10 +0,0 @@ -public class Duke { - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - } -} diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 000000000..b8735a8ae --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: python.Python + diff --git a/src/main/java/python/Python.java b/src/main/java/python/Python.java new file mode 100644 index 000000000..a1a476184 --- /dev/null +++ b/src/main/java/python/Python.java @@ -0,0 +1,21 @@ +package python; + +import python.exception.PythonException; +import python.ui.Ui; + +public class Python { + static final private Ui ui = new Ui(); + + public static void main(String[] args) { + ui.welcomeUser(); + do { + ui.getUserInput(); + try { + ui.parseLine(); + ui.executeLine(); + } catch (PythonException e) { + ui.displayException(e); + } + } while (!ui.isExit()); + } +} diff --git a/src/main/java/python/exception/PythonException.java b/src/main/java/python/exception/PythonException.java new file mode 100644 index 000000000..4c6d6198f --- /dev/null +++ b/src/main/java/python/exception/PythonException.java @@ -0,0 +1,7 @@ +package python.exception; + +public class PythonException extends Exception { + public PythonException(String errorMessage) { + super(errorMessage); + } +} \ No newline at end of file diff --git a/src/main/java/python/parser/Command.java b/src/main/java/python/parser/Command.java new file mode 100644 index 000000000..54e5d982f --- /dev/null +++ b/src/main/java/python/parser/Command.java @@ -0,0 +1,26 @@ +package python.parser; + +/** + * Defines the commands expected from the user + */ +public class Command { + public static final String COMMAND_BYE = "bye"; + public static final String COMMAND_LIST = "list"; + public static final String COMMAND_MARK = "mark"; + public static final String COMMAND_UNMARK = "unmark"; + public static final String COMMAND_DELETE = "delete"; + public static final String COMMAND_TODO = "todo"; + public static final String COMMAND_DEADLINE = "deadline"; + public static final String COMMAND_EVENT = "event"; + public static final String COMMAND_FIND = "find"; + + /** + * Returns whether the given command is "bye" command or not + * + * @param command The command to check + * @return Returns whether the given command is "bye" command or not + */ + public static boolean isCommandBye(String command) { + return (command.equals(COMMAND_BYE)); + } +} diff --git a/src/main/java/python/parser/Parser.java b/src/main/java/python/parser/Parser.java new file mode 100644 index 000000000..16b717f1f --- /dev/null +++ b/src/main/java/python/parser/Parser.java @@ -0,0 +1,126 @@ +package python.parser; + +/** + * Contains utility functions for parsing of raw input given by the user. All classes here are static + */ +public class Parser { + /** + * Returns the first word as command + * + * @param inputLine The raw user input + * @return Returns the first word as command + */ + public static String extractCommandFromInputLine(String inputLine) { + return inputLine.split("\\s+")[0]; + } + + /** + * Returns the rest of the sentence excluding the first word + * + * @param inputLine The raw user input + * @return Returns the rest of the sentence excluding the first word + */ + public static String extractCommandDescFromInputLine(String inputLine) { + return inputLine.split(" ", 2)[1]; + } + + /** + * Returns the second word as integer + * + * @param inputLine The raw user input + * @return Returns the second word as integer + */ + public static int extractTaskNoFromInputLine(String inputLine) { + return Integer.parseInt(inputLine.split(" ")[1]); + } + + /** + * Returns the second word as keyword + * + * @param inputLine The raw user input + * @return Returns the second word as keyword + */ + public static String extractKeywordFromInputLine(String inputLine) { + return inputLine.split(" ")[1]; + } + + /** + * Returns the rest of the sentence excluding the first word + * + * @param inputLine The raw user input + * @return Returns the rest of the sentence excluding the first word + */ + public static String extractTodoDescFromInputLine(String inputLine) { + return inputLine.split(" ", 2)[1]; + } + + /** + * Returns the rest of the sentence excluding the first word + * + * @param inputLine The raw user input + * @return Returns the rest of the sentence excluding the first word + */ + public static String extractDeadlineDetailsFromInputLine(String inputLine) { + return inputLine.split(" ", 2)[1]; + } + + /** + * Returns the text after the first word + * + * @param deadlineDetails The deadline details + * @return Returns the text after the first word + */ + public static String extractDeadlineDescFromDeadlineDetails(String deadlineDetails) { + return deadlineDetails.split(" /by ")[0]; + } + + /** + * Returns the text after the /by command + * + * @param deadlineDetails The deadline details + * @return Returns the text after the /by command + */ + public static String extractDeadlineByFromDeadlineDetails(String deadlineDetails) { + return deadlineDetails.split(" /by ")[1]; + } + + /** + * Returns the rest of the sentence excluding the first word + * + * @param inputLine The raw user input + * @return Returns the rest of the sentence excluding the first word + */ + public static String extractEventDetailsFromInputLine(String inputLine) { + return inputLine.split(" ", 2)[1]; + } + + /** + * Returns the sentence before /from + * + * @param eventDetails The event details + * @return Returns the sentence before /from + */ + public static String extractEventDescFromEventDetails(String eventDetails) { + return eventDetails.split("\\s+/from\\s+|\\s+/to\\s+", 3)[0]; + } + + /** + * Returns the sentence after /from and before /to + * + * @param eventDetails The event details + * @return Returns the sentence after /from and before /to + */ + public static String extractEventFromFromEventDetails(String eventDetails) { + return eventDetails.split("\\s+/from\\s+|\\s+/to\\s+", 3)[1]; + } + + /** + * Returns the sentence after /to + * + * @param eventDetails The event details + * @return Returns the sentence after /to + */ + public static String extractEventToFromEventDetails(String eventDetails) { + return eventDetails.split("\\s+/from\\s+|\\s+/to\\s+", 3)[2]; + } +} diff --git a/src/main/java/python/task/Deadline.java b/src/main/java/python/task/Deadline.java new file mode 100644 index 000000000..5df1342a1 --- /dev/null +++ b/src/main/java/python/task/Deadline.java @@ -0,0 +1,49 @@ +package python.task; + +/** + * Represents a deadline object using due date + */ +public class Deadline extends Task { + final static private String TYPE_ICON = "[D]"; + private String by; + + /** + * Constructs Deadline object + * + * @param description The description of the task + * @param by The due datetime + */ + public Deadline(String description, String by) { + super(description); + this.by = by; + } + + /** + * Returns the type icon of deadline + * + * @return Returns the type icon of deadline + */ + @Override + public String getTypeIcon() { + return TYPE_ICON; + } + + /** + * Returns the due datetime of deadline + * + * @return Returns the due datetime of deadline + */ + public String getBy() { + return by; + } + + /** + * Returns a human-readable string format + * + * @return Returns a human-readable string format + */ + @Override + public String toString() { + return super.toString() + " (by: " + getBy() + ")"; + } +} diff --git a/src/main/java/python/task/Event.java b/src/main/java/python/task/Event.java new file mode 100644 index 000000000..9f13e3b31 --- /dev/null +++ b/src/main/java/python/task/Event.java @@ -0,0 +1,59 @@ +package python.task; + +/** + * Represents a event object using start and end datetime + */ +public class Event extends Task { + final static private String TYPE_ICON = "[E]"; + private String from, to; + + /** + * Constructs Event object + * + * @param description The description of the task + * @param from The start datetime of the task + * @param to The end datetime of the task + */ + public Event(String description, String from, String to) { + super(description); + this.from = from; + this.to = to; + } + + /** + * Returns the type icon of event + * + * @return Returns the type icon of event + */ + public String getTypeIcon() { + return TYPE_ICON; + } + + /** + * Returns the start datetime of the event + * + * @return Returns the start datetime of the event + */ + public String getFrom() { + return from; + } + + /** + * Returns the end datetime of the event + * + * @return Returns the end datetime of the event + */ + public String getTo() { + return to; + } + + /** + * Returns a human-readable string format + * + * @return Returns a human-readable string format + */ + @Override + public String toString() { + return super.toString() + " (from: " + getFrom() + " to: " + getTo() + ")"; + } +} diff --git a/src/main/java/python/task/Task.java b/src/main/java/python/task/Task.java new file mode 100644 index 000000000..ac6f05cb1 --- /dev/null +++ b/src/main/java/python/task/Task.java @@ -0,0 +1,72 @@ +package python.task; + +/** + * Represents a abstract Task object + */ +public abstract class Task { + private String description; + private boolean isDone; + + /** + * Constructs Task object + * + * @param description The description of the task + */ + public Task(String description) { + this.description = description; + this.isDone = false; + } + + /** + * Returns the status icon of task + * + * @return Returns the status icon of task + */ + public String getStatusIcon() { + return (isDone() ? "[X]" : "[ ]"); // mark done task with X + } + + /** + * Returns the type icon of task + * + * @return Returns the type icon of task + */ + public abstract String getTypeIcon(); + + /** + * Returns the description of the task + * + * @return Returns the description of the task + */ + public String getDescription() { + return description; + } + + /** + * Returns whether the is completed or not + * + * @return Returns whether the is completed or not + */ + public boolean isDone() { + return isDone; + } + + /** + * Sets the status of completion of the task + * + * @param done The status to be set + */ + public void setDone(boolean done) { + isDone = done; + } + + /** + * Returns a human-readable string format + * + * @return Returns a human-readable string format + */ + @Override + public String toString() { + return getTypeIcon() + getStatusIcon() + " " + getDescription(); + } +} diff --git a/src/main/java/python/task/TaskList.java b/src/main/java/python/task/TaskList.java new file mode 100644 index 000000000..61ef84f1e --- /dev/null +++ b/src/main/java/python/task/TaskList.java @@ -0,0 +1,92 @@ +package python.task; + +import java.util.ArrayList; +import java.util.List; + +/** + * Encapsulates the functionality of a collection of tasks + */ +public class TaskList { + final static private List tasks = new ArrayList<>(); + + /** + * Returns a task on the given index + * + * @param taskNo Index of the task + * @return Task at the index + */ + public static Task getTask(int taskNo) { + return tasks.get(taskNo); + } + + /** + * Returns all the tasks as a List + * + * @return Returns all the tasks as a List + */ + public static List getTasks() { + return tasks; + } + + /** + * Returns the number of tasks + * + * @return Returns the number of tasks + */ + public static int getNumberOfTasks() { + return tasks.size(); + } + + /** + * Marks a task + * + * @param taskNo The index of the task + */ + public static void markTask(int taskNo) { + tasks.get(taskNo).setDone(true); + } + + /** + * Unmarks a task + * + * @param taskNo The index of the task + */ + public static void unmarkTask(int taskNo) { + tasks.get(taskNo).setDone(false); + } + + /** + * Deletes a task + * + * @param taskNo The index of the task + */ + public static void deleteTask(int taskNo) { + tasks.remove(taskNo); + } + + /** + * Adds a task + * + * @param task The task to be added + */ + public static void addTask(Task task) { + tasks.add(task); + } + + /** + * Returns a List of Task of the matching tasks. If a task description + * contains the keyword, it is considered a match. + * + * @param keyword The keyword to be searched + * @return Returns a List of Task of the matching tasks + */ + public static List findTask(String keyword) { + List matchedTasks = new ArrayList<>(); + for (int taskNo = 0; taskNo < getNumberOfTasks(); taskNo++) { + if (getTask(taskNo).getDescription().contains(keyword)) { + matchedTasks.add(getTask(taskNo)); + } + } + return matchedTasks; + } +} \ No newline at end of file diff --git a/src/main/java/python/task/Todo.java b/src/main/java/python/task/Todo.java new file mode 100644 index 000000000..a1c4e40b0 --- /dev/null +++ b/src/main/java/python/task/Todo.java @@ -0,0 +1,28 @@ +package python.task; + +/** + * Represents a Todo object with a description + */ +public class Todo extends Task { + + final static private String TYPE_ICON = "[T]"; + + /** + * Constructs Todo object + * + * @param description The description of the task + */ + public Todo(String description) { + super(description); + } + + /** + * Returns the type icon of todo + * + * @return Returns the type icon of todo + */ + @Override + public String getTypeIcon() { + return TYPE_ICON; + } +} diff --git a/src/main/java/python/ui/Message.java b/src/main/java/python/ui/Message.java new file mode 100644 index 000000000..53bb71f9a --- /dev/null +++ b/src/main/java/python/ui/Message.java @@ -0,0 +1,34 @@ +package python.ui; + +/** + * Defines string literals for the chatbot responses. + */ +public class Message { + final static String MESSAGE_INTRODUCTION = "Hello! I am a Java Bot Python!"; + final static String MESSAGE_ASK = "What can I do for you?"; + final static String MESSAGE_BYE = "Bye. See you again when you run the program again!"; + final static String MESSAGE_INT_AFTER_COMMAND = "Command must be followed by an integer (task id)!"; + final static String MESSAGE_KEYWORD_AFTER_COMMAND = "Command must be followed by a keyword!"; + final static String MESSAGE_DESC_AFTER_COMMAND = "Command must be followed by a task description!!"; + final static String MESSAGE_TIME_AFTER_FROM_CLAUSE = + "Command must have /from clause followed by time it starts!"; + final static String MESSAGE_TIME_AFTER_TO_CLAUSE = + "Command must have /to clause followed by time it ends!"; + final static String MESSAGE_TIME_AFTER_BY_CLAUSE = + "Command must have /by clause followed by time its due!"; + final static String MESSAGE_FUTURE_JOKE = "Are you from the future?"; + final static String MESSAGE_PAST_JOKE = "Are you from the past?"; + final static String MESSAGE_UNMARK_TASK_JOKE = "Alas! Only the completed tasks can be unmarked!"; + final static String MESSAGE_EMPTY_COMMAND_JOKE = "Nothing for me?"; + final static String MESSAGE_TASK_ALREADY_DONE = "Task is already done!"; + final static String MESSAGE_CONGRATUALTE = "Good job completing the task!"; + final static String MESSAGE_TASK_IDLE = "Task is already sitting idle. Get started...!!!"; + final static String MESSAGE_MISTAKE_CONSOLATION = "Its okay! To err is human! Unmarked!"; + final static String MESSAGE_DELETE_CONFIRMATION = "Okay. Deleting this task...!"; + final static String MESSAGE_UNKNOWN_COMMAND = "I cannot understand the command!"; + final static String MESSAGE_NEW_TODO = "New Todo! You have added this todo:"; + final static String MESSAGE_NEW_DEADLINE = "New Deadline! You have added this deadline:"; + final static String MESSAGE_NEW_EVENT = "New Event! You have added this event:"; + final static String MESSAGE_NO_MATCH = "No matching tasks found!"; + final static String MESSAGE_MATCHES_FOUND = "Here are the matching tasks:"; +} diff --git a/src/main/java/python/ui/Ui.java b/src/main/java/python/ui/Ui.java new file mode 100644 index 000000000..d74cf563e --- /dev/null +++ b/src/main/java/python/ui/Ui.java @@ -0,0 +1,301 @@ +package python.ui; + +import python.exception.PythonException; +import python.parser.Command; +import python.parser.Parser; +import python.task.*; + +import java.util.List; +import java.util.Scanner; + +/** + * Controls the user-chatbot interactions + */ +public class Ui { + private static final Scanner IN = new Scanner(System.in); + final private static String PYTHON_EMOJI = "\uD83D\uDC0D"; + final private static int HORIZONTAL_LINE_LENGTH = 80; + private String inputLine; + private String inputCommand; + final private static String PYTHON_ASCII_ART = + "\t ____ _ _\n" + + "\t| _ \\ _ _| |_| |__ ___ _ __\n" + + "\t| |_) | | | | __| _ \\ / _ \\| _ \\\n" + + "\t| __/| |_| | |_| | | | (_) | | | |\n" + + "\t|_| \\__, |\\__|_| |_|\\___/|_| |_|\n" + + "\t |___/"; + + private static void printHorizontalLine() { + String horizontalLine = "—".repeat(HORIZONTAL_LINE_LENGTH); + System.out.println("\t" + horizontalLine); + } + + private static void addEmojiAndPrint(String message) { + System.out.printf("\t%s: %s\n", PYTHON_EMOJI, message); + } + + /** + * Welcomes user with some predefined messages + */ + public void welcomeUser() { + System.out.println(PYTHON_ASCII_ART); + printHorizontalLine(); + addEmojiAndPrint(Message.MESSAGE_INTRODUCTION); + addEmojiAndPrint(Message.MESSAGE_ASK); + printHorizontalLine(); + } + + private void greetGoodBye() { + addEmojiAndPrint(Message.MESSAGE_BYE); + } + + private void displayTaskCount() { + addEmojiAndPrint("You have " + TaskList.getNumberOfTasks() + " tasks!"); + } + + private void displayTasks(List tasks) { + for (int taskNo = 0; taskNo < tasks.size(); taskNo++) { + System.out.printf("\t\t\t%d. %s\n", taskNo + 1, tasks.get(taskNo)); + } + } + + private void handleByeCommand() { + greetGoodBye(); + } + + private void handleListCommand() { + displayTaskCount(); + displayTasks(TaskList.getTasks()); + } + + private void handleMarkCommand() throws PythonException { + int taskNo; + try { + taskNo = Parser.extractTaskNoFromInputLine(inputLine) - 1; + } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { + throw new PythonException(Message.MESSAGE_INT_AFTER_COMMAND); + } + + // Handle unintended usage + if (taskNo >= TaskList.getNumberOfTasks()) { + addEmojiAndPrint(Message.MESSAGE_FUTURE_JOKE); + return; + } + if (TaskList.getTask(taskNo).isDone()) { + addEmojiAndPrint(Message.MESSAGE_PAST_JOKE); + addEmojiAndPrint(TaskList.getTask(taskNo).toString()); + addEmojiAndPrint(Message.MESSAGE_TASK_ALREADY_DONE); + return; + } + + TaskList.markTask(taskNo); + + addEmojiAndPrint(Message.MESSAGE_CONGRATUALTE); + addEmojiAndPrint(TaskList.getTask(taskNo).toString()); + } + + private void handleUnmarkCommand() throws PythonException { + int taskNo; + try { + taskNo = Parser.extractTaskNoFromInputLine(inputLine) - 1; + } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { + throw new PythonException(Message.MESSAGE_INT_AFTER_COMMAND); + } + + // Handle unintended usage + if (taskNo >= TaskList.getNumberOfTasks()) { + addEmojiAndPrint(Message.MESSAGE_FUTURE_JOKE); + return; + } + if (!TaskList.getTask(taskNo).isDone()) { + addEmojiAndPrint(Message.MESSAGE_UNMARK_TASK_JOKE); + addEmojiAndPrint(TaskList.getTask(taskNo).toString()); + addEmojiAndPrint(Message.MESSAGE_TASK_IDLE); + return; + } + + TaskList.unmarkTask(taskNo); + + addEmojiAndPrint(Message.MESSAGE_MISTAKE_CONSOLATION); + addEmojiAndPrint(TaskList.getTask(taskNo).toString()); + } + + private void handleDeleteCommand() throws PythonException { + int taskNo; + + try { + taskNo = Parser.extractTaskNoFromInputLine(inputLine) - 1; + } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { + throw new PythonException(Message.MESSAGE_INT_AFTER_COMMAND); + } + + // Handle unintended usage + if (taskNo >= TaskList.getNumberOfTasks()) { + addEmojiAndPrint(Message.MESSAGE_FUTURE_JOKE); + displayTaskCount(); + return; + } + + addEmojiAndPrint(Message.MESSAGE_DELETE_CONFIRMATION); + addEmojiAndPrint(TaskList.getTask(taskNo).toString()); + + TaskList.deleteTask(taskNo); + displayTaskCount(); + } + + private void handleTodoCommand() throws PythonException { + String todoDescription; + try { + todoDescription = Parser.extractTodoDescFromInputLine(inputLine); + } catch (ArrayIndexOutOfBoundsException e) { + throw new PythonException(Message.MESSAGE_DESC_AFTER_COMMAND); + } + addEmojiAndPrint(Message.MESSAGE_NEW_TODO); + + Todo todo = new Todo(todoDescription); + TaskList.addTask(todo); + + addEmojiAndPrint(todo.toString()); + displayTaskCount(); + } + + private void handleDeadlineCommand() throws PythonException { + String deadlineDetails, deadlineDescription, deadlineBy; + try { + deadlineDetails = Parser.extractDeadlineDetailsFromInputLine(inputLine); + deadlineDescription = Parser.extractDeadlineDescFromDeadlineDetails(deadlineDetails); + } catch (ArrayIndexOutOfBoundsException e) { + throw new PythonException(Message.MESSAGE_DESC_AFTER_COMMAND); + } + try { + deadlineBy = Parser.extractDeadlineByFromDeadlineDetails(deadlineDetails); + } catch (ArrayIndexOutOfBoundsException e) { + throw new PythonException(Message.MESSAGE_TIME_AFTER_BY_CLAUSE); + } + + addEmojiAndPrint(Message.MESSAGE_NEW_DEADLINE); + + Deadline deadline = new Deadline(deadlineDescription, deadlineBy); + TaskList.addTask(deadline); + + addEmojiAndPrint(deadline.toString()); + displayTaskCount(); + } + + private void handleEventCommand() throws PythonException { + String eventDetails, eventDescription, eventFrom, eventTo; + try { + eventDetails = Parser.extractEventDetailsFromInputLine(inputLine); + eventDescription = Parser.extractEventDescFromEventDetails(eventDetails); + } catch (ArrayIndexOutOfBoundsException e) { + throw new PythonException(Message.MESSAGE_DESC_AFTER_COMMAND); + } + + try { + eventFrom = Parser.extractEventFromFromEventDetails(eventDetails); + } catch (ArrayIndexOutOfBoundsException e) { + throw new PythonException(Message.MESSAGE_TIME_AFTER_FROM_CLAUSE); + } + + try { + eventTo = Parser.extractEventToFromEventDetails(eventDetails); + } catch (ArrayIndexOutOfBoundsException e) { + throw new PythonException(Message.MESSAGE_TIME_AFTER_TO_CLAUSE); + } + + addEmojiAndPrint(Message.MESSAGE_NEW_EVENT); + + Event event = new Event(eventDescription, eventFrom, eventTo); + TaskList.addTask(event); + + addEmojiAndPrint(event.toString()); + displayTaskCount(); + } + + private void handleFindCommand() throws PythonException { + String keyword; + try { + keyword = Parser.extractKeywordFromInputLine(inputLine); + } catch (ArrayIndexOutOfBoundsException e) { + throw new PythonException(Message.MESSAGE_KEYWORD_AFTER_COMMAND); + } + + List matchedTasks = TaskList.findTask(keyword); + + if (matchedTasks.isEmpty()) { + addEmojiAndPrint(Message.MESSAGE_NO_MATCH); + } else { + addEmojiAndPrint(Message.MESSAGE_MATCHES_FOUND); + displayTasks(matchedTasks); + } + } + + private void handleUnknownCommand() throws PythonException { + if (inputLine.isEmpty()) { + addEmojiAndPrint(Message.MESSAGE_EMPTY_COMMAND_JOKE); + return; + } + addEmojiAndPrint(Message.MESSAGE_UNKNOWN_COMMAND); + } + + /** + * Gets the user given raw input + */ + public void getUserInput() { + this.inputLine = IN.nextLine(); + } + + /** + * Displays the error messages + * + * @param e The error message + */ + public void displayException(Exception e) { + System.out.println("\tError: " + e.getMessage()); + } + + public void parseLine() { + this.inputCommand = Parser.extractCommandFromInputLine(inputLine); + } + + public void executeLine() throws PythonException { + printHorizontalLine(); + switch (inputCommand) { + case Command.COMMAND_BYE: + handleByeCommand(); + break; + case Command.COMMAND_LIST: + handleListCommand(); + break; + case Command.COMMAND_MARK: + handleMarkCommand(); + break; + case Command.COMMAND_UNMARK: + handleUnmarkCommand(); + break; + case Command.COMMAND_DELETE: + handleDeleteCommand(); + break; + case Command.COMMAND_TODO: + handleTodoCommand(); + break; + case Command.COMMAND_DEADLINE: + handleDeadlineCommand(); + break; + case Command.COMMAND_EVENT: + handleEventCommand(); + break; + case Command.COMMAND_FIND: + handleFindCommand(); + break; + default: + handleUnknownCommand(); + break; + } + } + + public boolean isExit() { + return Command.isCommandBye(inputCommand); + } + +} diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 657e74f6e..c034527aa 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,7 +1,190 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| - + ____ _ _ + | _ \ _ _| |_| |__ ___ _ __ + | |_) | | | | __| _ \ / _ \| _ \ + | __/| |_| | |_| | | | (_) | | | | + |_| \__, |\__|_| |_|\___/|_| |_| + |___/ + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Hello! I am a Java Bot Python! + 🐍: What can I do for you? + ———————————————————————————————————————————————————————————————————————————————— + ———————————————————————————————————————————————————————————————————————————————— + 🐍: New Todo! You have added this todo: + 🐍: [T][ ] borrow book + 🐍: You have 1 tasks! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: New Todo! You have added this todo: + 🐍: [T][ ] borrow cycle + 🐍: You have 2 tasks! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: New Todo! You have added this todo: + 🐍: [T][ ] cycle + 🐍: You have 3 tasks! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: New Deadline! You have added this deadline: + 🐍: [D][ ] return book (by: Sunday) + 🐍: You have 4 tasks! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: New Event! You have added this event: + 🐍: [E][ ] project meeting (from: Mon 2pm to: 4pm) + 🐍: You have 5 tasks! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: New Event! You have added this event: + 🐍: [E][ ] group study (from: 1pm to to: 2pm) + 🐍: You have 6 tasks! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: You have 6 tasks! + 1. [T][ ] borrow book + 2. [T][ ] borrow cycle + 3. [T][ ] cycle + 4. [D][ ] return book (by: Sunday) + 5. [E][ ] project meeting (from: Mon 2pm to: 4pm) + 6. [E][ ] group study (from: 1pm to to: 2pm) + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Nothing for me? + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Good job completing the task! + 🐍: [T][X] borrow book + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Are you from the past? + 🐍: [T][X] borrow book + 🐍: Task is already done! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Its okay! To err is human! Unmarked! + 🐍: [T][ ] borrow book + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Alas! Only the completed tasks can be unmarked! + 🐍: [T][ ] borrow book + 🐍: Task is already sitting idle. Get started...!!! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: I cannot understand the command! + ———————————————————————————————————————————————————————————————————————————————— + Error: Command must be followed by an integer (task id)! + ———————————————————————————————————————————————————————————————————————————————— + Error: Command must be followed by an integer (task id)! + ———————————————————————————————————————————————————————————————————————————————— + Error: Command must be followed by an integer (task id)! + ———————————————————————————————————————————————————————————————————————————————— + Error: Command must be followed by an integer (task id)! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Good job completing the task! + 🐍: [T][X] borrow cycle + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Good job completing the task! + 🐍: [T][X] cycle + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Good job completing the task! + 🐍: [D][X] return book (by: Sunday) + ———————————————————————————————————————————————————————————————————————————————— + Error: Command must be followed by a task description!! + ———————————————————————————————————————————————————————————————————————————————— + Error: Command must be followed by a task description!! + ———————————————————————————————————————————————————————————————————————————————— + Error: Command must be followed by a task description!! + ———————————————————————————————————————————————————————————————————————————————— + Error: Command must have /by clause followed by time its due! + ———————————————————————————————————————————————————————————————————————————————— + Error: Command must have /by clause followed by time its due! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: New Deadline! You have added this deadline: + 🐍: [D][ ] return book (by: 2 June) + 🐍: You have 7 tasks! + ———————————————————————————————————————————————————————————————————————————————— + Error: Command must have /from clause followed by time it starts! + ———————————————————————————————————————————————————————————————————————————————— + Error: Command must have /to clause followed by time it ends! + ———————————————————————————————————————————————————————————————————————————————— + Error: Command must have /to clause followed by time it ends! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: New Event! You have added this event: + 🐍: [E][ ] OOP Lecture (from: 12pm to: 2pm) + 🐍: You have 8 tasks! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Here are the matching tasks: + 1. [T][ ] borrow book + 2. [D][X] return book (by: Sunday) + 3. [D][ ] return book (by: 2 June) + ———————————————————————————————————————————————————————————————————————————————— + Error: Command must be followed by a keyword! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: No matching tasks found! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: You have 8 tasks! + 1. [T][ ] borrow book + 2. [T][X] borrow cycle + 3. [T][X] cycle + 4. [D][X] return book (by: Sunday) + 5. [E][ ] project meeting (from: Mon 2pm to: 4pm) + 6. [E][ ] group study (from: 1pm to to: 2pm) + 7. [D][ ] return book (by: 2 June) + 8. [E][ ] OOP Lecture (from: 12pm to: 2pm) + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Its okay! To err is human! Unmarked! + 🐍: [T][ ] cycle + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Its okay! To err is human! Unmarked! + 🐍: [D][ ] return book (by: Sunday) + ———————————————————————————————————————————————————————————————————————————————— + 🐍: You have 8 tasks! + 1. [T][ ] borrow book + 2. [T][X] borrow cycle + 3. [T][ ] cycle + 4. [D][ ] return book (by: Sunday) + 5. [E][ ] project meeting (from: Mon 2pm to: 4pm) + 6. [E][ ] group study (from: 1pm to to: 2pm) + 7. [D][ ] return book (by: 2 June) + 8. [E][ ] OOP Lecture (from: 12pm to: 2pm) + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Okay. Deleting this task...! + 🐍: [T][X] borrow cycle + 🐍: You have 7 tasks! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Are you from the future? + 🐍: You have 7 tasks! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: You have 7 tasks! + 1. [T][ ] borrow book + 2. [T][ ] cycle + 3. [D][ ] return book (by: Sunday) + 4. [E][ ] project meeting (from: Mon 2pm to: 4pm) + 5. [E][ ] group study (from: 1pm to to: 2pm) + 6. [D][ ] return book (by: 2 June) + 7. [E][ ] OOP Lecture (from: 12pm to: 2pm) + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Good job completing the task! + 🐍: [E][X] OOP Lecture (from: 12pm to: 2pm) + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Its okay! To err is human! Unmarked! + 🐍: [E][ ] OOP Lecture (from: 12pm to: 2pm) + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Okay. Deleting this task...! + 🐍: [T][ ] borrow book + 🐍: You have 6 tasks! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Okay. Deleting this task...! + 🐍: [T][ ] cycle + 🐍: You have 5 tasks! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Okay. Deleting this task...! + 🐍: [D][ ] return book (by: Sunday) + 🐍: You have 4 tasks! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Okay. Deleting this task...! + 🐍: [E][ ] project meeting (from: Mon 2pm to: 4pm) + 🐍: You have 3 tasks! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Okay. Deleting this task...! + 🐍: [E][ ] group study (from: 1pm to to: 2pm) + 🐍: You have 2 tasks! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Okay. Deleting this task...! + 🐍: [D][ ] return book (by: 2 June) + 🐍: You have 1 tasks! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Okay. Deleting this task...! + 🐍: [E][ ] OOP Lecture (from: 12pm to: 2pm) + 🐍: You have 0 tasks! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: You have 0 tasks! + ———————————————————————————————————————————————————————————————————————————————— + 🐍: Bye. See you again when you run the program again! diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29bb..bcbbb8412 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1,51 @@ +todo borrow book +todo borrow cycle +todo cycle +deadline return book /by Sunday +event project meeting /from Mon 2pm /to 4pm +event group study /from 1pm to /to 2pm +list + +mark 1 +mark 1 +unmark 1 +unmark 1 +undo +mark a +mark +unmark c +unmark +mark 2 +mark 3 +mark 4 +todo +deadline +event +deadline return book +deadline return book /by +deadline return book /by 2 June +event OOP Lecture +event OOP Lecture /from 12pm +event OOP Lecture /from /to 2pm +event OOP Lecture /from 12pm /to 2pm +find book +find +find exam +list +unmark 3 +unmark 4 +list +delete 2 +delete 10 +list +mark 7 +unmark 7 +delete 1 +delete 1 +delete 1 +delete 1 +delete 1 +delete 1 +delete 1 +list +bye \ No newline at end of file diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh old mode 100644 new mode 100755 index c9ec87003..632867c6a --- a/text-ui-test/runtest.sh +++ b/text-ui-test/runtest.sh @@ -13,14 +13,14 @@ then fi # compile the code into the bin folder, terminates if error occurred -if ! javac -cp ../src/main/java -Xlint:none -d ../bin ../src/main/java/*.java +if ! javac -cp ../src/main/java -Xlint:none -d ../bin ../src/main/java/python/*.java ../src/main/java/python/task/*.java ../src/main/java/python/ui/*.java ../src/main/java/python/exception/*.java ../src/main/java/python/parser/*.java then echo "********** BUILD FAILURE **********" exit 1 fi # run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -java -classpath ../bin Duke < input.txt > ACTUAL.TXT +java -classpath ../bin python.Python < input.txt > ACTUAL.TXT # convert to UNIX format cp EXPECTED.TXT EXPECTED-UNIX.TXT