diff --git a/README.md b/README.md index 8715d4d91..3fe1f9bd9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Duke project template +# chatbot.Duke project template This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it. @@ -13,7 +13,7 @@ Prerequisites: JDK 11, update Intellij to the most recent version. 1. If there are any further prompts, accept the defaults. 1. Configure the project to use **JDK 11** (not other versions) as explained in [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk).
In the same dialog, set the **Project language level** field to the `SDK default` option. -3. After that, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output: +3. After that, locate the `src/main/java/chatbot.Duke.java` file, right-click it, and choose `Run chatbot.Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output: ``` Hello from ____ _ diff --git a/data.txt b/data.txt new file mode 100644 index 000000000..0dccabe2d --- /dev/null +++ b/data.txt @@ -0,0 +1,3 @@ +true| concert | aer | thurs +false| Homework | Friday +false|event concert |now| thwn diff --git a/docs/README.md b/docs/README.md index 8077118eb..4436e9420 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,28 +2,117 @@ ## Features -### Feature-ABC +### Feature: Add Todo, Deadline or Event -Description of the feature. +Use app to record todos, deadlines and events with a custom user description for each added entry. Deadlines support due dates and events support start and end dates. Todos, deadlines and events can be removed as well. -### Feature-XYZ +Can also search for tasks with keywords from their task description. + +### Feature: Mark/unmark + +Use app to mark/unmark todos/deadlines/events as done or not done yet. + +### Feature: Find + +Use app to find todos/deadlines/events by searching the task list for the relavent keyword in their descriptions + +### Feature: List + +List all todos, deadlines and events. + +### Feature: Save + +Todos, deadlines and events are saved in between running the program. -Description of the feature. ## Usage -### `Keyword` - Describe action +### `todo`/`deadline`/`event` - Add todo, deadline or event + +How to add a Todo: + +todo [description] + + +How to add a Deadline: + +deadline [description] /by [due date] + + +How to add an Event: + +event [description] /from [start date] /to [end date] -Describe the action and its outcome. Example of usage: -`keyword (optional arguments)` +`deadline Homework /by Friday` + +Expected outcome: + +Adds a new deadline with description "go to school" with due date of "Friday" + +``` +Created new Deadline: + Homework (by: Friday) +``` + +### `mark`/`unmark` - Mark/unmark todo, deadline or event + +Mark/unmark a todo/event/deadline as done or not done yet using the mark/unmark keyword: + +mark [item position in list] + +unmark [item position in list] + + +Example of usage: + +`mark 1` + +Expected outcome: + +Marks todo/deadline/event in position 1 of list as done + +``` +Marked as done: + Homework +``` + +### `list` - Lists all todos, deadlines and events + +Example of usage: + +`list` Expected outcome: -Description of the outcome. +``` +[D][X] Homework (by: Friday) +``` + +### `find` - Lists all todos, deadlines and events that contain search term in the description + +find [search term] + +Example of usage: + +`find Homework` + +Expected outcome: + +``` +[D][X] Homework (by: Friday) +``` + +### `bye` - Exits program + +Example of usage: + +`bye` + +Expected outcome: ``` -expected output +Bye. Hope to see you again soon! ``` 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..70897dd5d --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: chatbot.Duke + diff --git a/src/main/java/chatbot/Duke.java b/src/main/java/chatbot/Duke.java new file mode 100644 index 000000000..aff7fcc81 --- /dev/null +++ b/src/main/java/chatbot/Duke.java @@ -0,0 +1,157 @@ +package chatbot; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Scanner; + +import exception.FattyException; +import tasks.Task; + +/** + * This is the chatbot class. This contains an array of Task objects and contains functions to handle them. + */ + +public class Duke { + public static ArrayList taskList; + private static Storage storage; + private static Ui ui; + private static Parser parser; + + public Duke() { + taskList = new ArrayList<>(); + ui = new Ui(); + parser = new Parser(); + storage = new Storage(); + } + + /** + * Main function creates new Duke object and calls run method + * @param args + * @throws FattyException + */ + public static void main(String[] args) throws FattyException, IOException { + new Duke().run(); + } + + /** + * Run function executes the main while loop of the program + * @throws FattyException + */ + public static void run() throws FattyException, IOException { + + try { + storage.load(); + } catch (IOException e) { + System.out.println(e.toString()); + } + + ui.printWelcomeMessage(); + + Scanner scanner = new Scanner(System.in); + + while(true) { + String inputMessage = scanner.nextLine(); + + if (inputMessage.equals("bye")) { + ui.printGoodbyeMessage(); + break; + } + + try { + String keyword = parser.getKeyword(inputMessage); + + switch (keyword) { + case "list": + list(); + break; + case "mark": + mark(parser.getIndex(inputMessage)); + break; + case "unmark": + unmark(parser.getIndex(inputMessage)); + break; + case "delete": + delete(parser.getIndex(inputMessage)); + break; + case "find": + find(parser.getSearchField(inputMessage)); + break; + case "create": + Task newTask = parser.getTask(inputMessage); + taskList.add(parser.getTask(inputMessage)); + ui.printNewTaskMessage(); + newTask.show(); + ui.printDivider(); + break; + } + + storage.save(); + + } catch (FattyException e) { + ui.printDivider(); + System.out.println(e.toString()); + ui.printDivider(); + continue; + } + } + } + + private static void add(Task task) { + taskList.add(task); + } + + private static void delete(int index) throws FattyException { + if(index <= 0 || index > taskList.size()) { + throw new FattyException ("Please enter a valid index"); + } + + ui.printDeleteMessage(); + taskList.get(index - 1).show(); + ui.printDivider(); + + taskList.remove(index - 1); + } + + private static void list() { + ui.printListMessage(); + + for(int i = 0; i < taskList.size(); i++) { + taskList.get(i).show(); + } + + ui.printDivider(); + } + + private static void mark(int index) throws FattyException { + if(index <= 0 || index > taskList.size()) { + throw new FattyException ("Please enter a valid index"); + } + + taskList.get(index - 1).mark(); + } + + private static void unmark(int index) throws FattyException { + if(index <= 0 || index > taskList.size()) { + throw new FattyException ("Please enter a valid index"); + } + + taskList.get(index - 1).unmark(); + } + + private static void find(String keyword) { + boolean found = false; + + for(int i = 0; i < taskList.size(); i++) { + if(taskList.get(i).getDescription().contains(keyword)) { + taskList.get(i).show(); + found = true; + } + } + + if(found) { + ui.printFoundMessage(); + } else { + ui.printNotFoundMessage(); + } + } +} diff --git a/src/main/java/chatbot/Parser.java b/src/main/java/chatbot/Parser.java new file mode 100644 index 000000000..c2f801336 --- /dev/null +++ b/src/main/java/chatbot/Parser.java @@ -0,0 +1,111 @@ +package chatbot; + +import exception.FattyException; +import tasks.Task; +import tasks.Deadline; +import tasks.Event; +import tasks.ToDo; + +/** + * This is the parser class. + * It handles all the user inputs and interprets them for the chat bot. + */ + +public class Parser { + /** + * This is the function that returns the function type + * Functions that are implemented as private methods in the Duke class are returned as is + * If tasks are added, "create" is returned instead + * @param inputMessage + * @return + * @throws FattyException + */ + public String getKeyword(String inputMessage) throws FattyException { + String keyword = inputMessage.split(" ")[0]; + + switch(keyword){ + case "list": + case "mark": + case "unmark": + case "delete": + case "find": + return keyword; + case "todo": + case "deadline": + case "event": + return "create"; + default: + throw new FattyException("Please enter a valid keyword"); + } + } + + /** + * Returns the index for commands that target a specific task + * @param inputMessage + * @return + * @throws FattyException + * @throws NumberFormatException + */ + public int getIndex(String inputMessage) throws FattyException, NumberFormatException { + String[] parts = inputMessage.split(" "); + + if(parts.length != 2) { + throw new FattyException("Please have correct input format"); + } + + try { + return Integer.parseInt(parts[1]); + } catch (NumberFormatException e) { + throw new FattyException("Please have correct input format"); + } + } + + public String getSearchField(String inputMessage) { + return inputMessage.substring(4).trim(); + } + + /** + * Returns a Task object to the main Duke chatbot class. + * Handles the creation of the task and parsing of the input message + * @param inputMessage + * @return + * @throws FattyException + */ + public Task getTask(String inputMessage) throws FattyException { + String keyword = inputMessage.split(" ")[0]; + + switch(keyword){ + case "todo": + return parseToDo(inputMessage); + case "deadline": + return parseDeadline(inputMessage); + case "event": + return parseEvent(inputMessage); + default: + return new Task("dummy", false); + } + } + private Task parseToDo(String inputMessage){ + return new ToDo(inputMessage, false); + } + + private Task parseDeadline(String inputMessage) throws FattyException { + String[] parts = inputMessage.split("/by"); + + if(parts.length != 2) { + throw new FattyException("Missing fields detected"); + } + + return new Deadline(parts, false); + } + + private Task parseEvent(String inputMessage) throws FattyException { + String[] parts = inputMessage.split("/from | /to"); + + if(parts.length != 3) { + throw new FattyException("Missing fields detected"); + } + + return new Event(parts, false); + } +} diff --git a/src/main/java/chatbot/Storage.java b/src/main/java/chatbot/Storage.java new file mode 100644 index 000000000..5aaf350e8 --- /dev/null +++ b/src/main/java/chatbot/Storage.java @@ -0,0 +1,93 @@ +package chatbot; + +import java.io.IOException; +import java.io.File; +import java.io.FileWriter; +import java.io.FileReader; +import java.io.BufferedReader; + +import exception.FattyException; +import tasks.Task; +import tasks.ToDo; +import tasks.Event; +import tasks.Deadline; + +/** + * This is the storage class. + * It handles the storing of data between sessions. + */ + +public class Storage { + private File file; + + public Storage() { + this.file = new File("data.txt"); + } + + /** + * Method to save taskList information to file + * Called every time there is an update to the taskList + */ + public void save() { + try (FileWriter fileWriter = new FileWriter("data.txt")) { + for (int i = 0; i < Duke.taskList.size(); i++) { + Task task = Duke.taskList.get(i); + String description = task.getDescription(); + String isDone = task.getDone(); + + if (task instanceof ToDo) { + fileWriter.write(isDone + "|" + description + "\n"); + continue; + } + + String time = task.getTime(); + fileWriter.write(isDone + "|" + description + "|" + time + "\n"); + } + } catch (IOException e) { + System.out.println("Error writing file"); + } + } + + /** + * Loads information from the save file to the program + * Called on program startup + * Creates new file if the save file does not exist + * Throws format exceptions if the save file has been corrupted + * @throws IOException + * @throws FattyException + */ + public void load() throws IOException, FattyException { + if (!this.file.exists()) { + try { + this.file.createNewFile(); + } catch (IOException e) { + System.out.println("File creation failed"); + } + } else { + try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) { + String line; + while ((line = br.readLine()) != null) { + String [] words = line.split("\\|"); + + for(int i = 0; i < words.length; i++) { + words[i] = words[i].trim(); + } + + boolean isDone = words[0].equals("true"); + + if (words.length == 2) { + Duke.taskList.add(new ToDo(words[1], isDone)); + } else if (words.length == 3) { + String [] args = {words[1], words[2]}; + Duke.taskList.add(new Deadline(args, isDone)); + } else if(words.length == 4) { + String [] args = {words[1], words[2], words[3]}; + Duke.taskList.add(new Event(args, isDone)); + } else { + throw new FattyException("Save file corrupted."); + } + } + } + } + } +} diff --git a/src/main/java/chatbot/Ui.java b/src/main/java/chatbot/Ui.java new file mode 100644 index 000000000..14fcdffe6 --- /dev/null +++ b/src/main/java/chatbot/Ui.java @@ -0,0 +1,60 @@ +package chatbot; + +import java.util.ArrayList; + +import tasks.Task; + +/** + * This is the user interface class. + * It handles the printing of all cosmetics and user messages. + * Each type of user message is implemented in its own public method + */ + +public class Ui { + public void printDivider() { + System.out.println("------------------------------------"); + } + + public void printWelcomeMessage() { + printDivider(); + System.out.println("Welcome to TheChattyFatty chatbot!"); + printDivider(); + } + + public void printGoodbyeMessage() { + printDivider(); + System.out.println("Goodbye!"); + printDivider(); + } + + public void printNotFoundMessage() { + printDivider(); + System.out.println("Task not found."); + printDivider(); + } + + public void printFoundMessage() { + printDivider(); + System.out.println("Found the above tasks."); + printDivider(); + } + + public void printListMessage() { + printDivider(); + System.out.println("Here are your results:"); + } + + public void printNewTaskMessage() { + printDivider(); + System.out.println("Successfully created:"); + } + + public void printDeleteMessage() { + printDivider(); + System.out.println("Successfully deleted:"); + } + + public void printTask(Task t) { + t.show(); + } +} diff --git a/src/main/java/exception/FattyException.java b/src/main/java/exception/FattyException.java new file mode 100644 index 000000000..b827c5348 --- /dev/null +++ b/src/main/java/exception/FattyException.java @@ -0,0 +1,12 @@ +package exception; + +/** + * This is the exception class. + * It contains the custom exception that is thrown by the chatbot. + */ + +public class FattyException extends Exception{ + public FattyException(String errorMessage) { + super(errorMessage); + } +} diff --git a/src/main/java/tasks/Deadline.java b/src/main/java/tasks/Deadline.java new file mode 100644 index 000000000..fed7eb893 --- /dev/null +++ b/src/main/java/tasks/Deadline.java @@ -0,0 +1,30 @@ +/** + * This is the deadline class which contains a description as well as a due date. It inherits from the task class + */ + +package tasks; + +public class Deadline extends Task{ + private String deadline; + public Deadline(String[] parts, boolean isDone){ + super(parts[0], isDone); + this.deadline = parts[1]; + } + + @Override + public String getTime(){ + return deadline; + } + + @Override + public void show(){ + System.out.print("[D]["); + if(isDone){ + System.out.print("X"); + } + else{ + System.out.print(" "); + } + System.out.println("] " + description + " (by: " + deadline + ")"); + } +} diff --git a/src/main/java/tasks/Event.java b/src/main/java/tasks/Event.java new file mode 100644 index 000000000..645e79554 --- /dev/null +++ b/src/main/java/tasks/Event.java @@ -0,0 +1,34 @@ +/** + * This is the Event class which contains a task description, event start time and event end time. + * It inherits from the task class. + */ + +package tasks; + +public class Event extends Task{ + private String start; + private String end; + + public Event(String[] parts, boolean isDone){ + super(parts[0], isDone); + this.start = parts[1]; + this.end = parts[2]; + } + + @Override + public String getTime(){ + return start + "|" + end; + } + + @Override + public void show(){ + System.out.print("[D]["); + if(isDone){ + System.out.print("X"); + } + else{ + System.out.print(" "); + } + System.out.println("] " + description + " (from:" + start + " to:" + end + ")"); + } +} diff --git a/src/main/java/tasks/Task.java b/src/main/java/tasks/Task.java new file mode 100644 index 000000000..76a49e262 --- /dev/null +++ b/src/main/java/tasks/Task.java @@ -0,0 +1,51 @@ +/** + * This is the task class which is the base class for todo, deadline and event classes. + * All task class objects can be marked as done or not done yet + */ + +package tasks; + +public class Task{ + protected String description; + protected boolean isDone; + + public Task(String description, boolean isDone){ + this.description = description; + this.isDone = isDone; + } + + public void show(){ + System.out.print("[?]["); + if(isDone){ + System.out.print("X"); + } + else{ + System.out.print(" "); + } + System.out.println("] " + description); + } + + public void mark(){ + isDone = true; + System.out.println("Marked as done:"); + System.out.println(description); + } + + public void unmark(){ + isDone = false; + System.out.println("Marked as not done yet:"); + System.out.println(description); + } + + public String getDescription() { + return description; + } + + public String getDone() { + return Boolean.toString(isDone); + } + + public String getTime(){ + return ""; + } +} \ No newline at end of file diff --git a/src/main/java/tasks/ToDo.java b/src/main/java/tasks/ToDo.java new file mode 100644 index 000000000..26957268e --- /dev/null +++ b/src/main/java/tasks/ToDo.java @@ -0,0 +1,23 @@ +/** + * This is the ToDo class which contains task description only. It inherits from the Task class. + */ + +package tasks; + +public class ToDo extends Task{ + public ToDo(String description, boolean isDone){ + super(description, isDone); + } + + @Override + public void show(){ + System.out.print("[T]["); + if(isDone){ + System.out.print("X"); + } + else{ + System.out.print(" "); + } + System.out.println("] " + description); + } +} diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index 087374464..4be53bdd5 100644 --- a/text-ui-test/runtest.bat +++ b/text-ui-test/runtest.bat @@ -15,7 +15,7 @@ IF ERRORLEVEL 1 ( REM no error here, errorlevel == 0 REM 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 chatbot.Duke < input.txt > ACTUAL.TXT REM compare the output to the expected output FC ACTUAL.TXT EXPECTED.TXT diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh index c9ec87003..d1673aa74 100644 --- a/text-ui-test/runtest.sh +++ b/text-ui-test/runtest.sh @@ -13,7 +13,7 @@ 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 /Users/apple/Desktop/CS2113/ip/src/main/java -Xlint:none -d ../bin ../src/main/java/*.java then echo "********** BUILD FAILURE **********" exit 1