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