diff --git a/data.txt b/data.txt new file mode 100644 index 000000000..ed3f8fb08 --- /dev/null +++ b/data.txt @@ -0,0 +1,5 @@ +T,false,eat food +D,true,return book , Sunday +T,true,borrow book +E,false,haha , Monday +D,false,120 songs , Tomorrow diff --git a/docs/README.md b/docs/README.md index 8077118eb..2ddb75835 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,29 +1,110 @@ -# User Guide +# User Guide for Duke +**Duke** is a Personal Assistant Chatbot that helps a person to keep track of various tasks via a **Command Line Interface**. ## Features +1. Add a ToDo task +2. Add a Deadline task +3. Add an event task +4. delete a specific task +5. Mark a task as done +6. Mark a task as undone +7. Display the current task list +8. find the tasks with given keyword +9. Load and save the data automatically +10. Exit the program -### Feature-ABC +## Usage -Description of the feature. +### Add a task: `taskType description (time)` -### Feature-XYZ +**Format**: + +- TODO: `TODO description`. **Example**: `TODO borrow book` -Description of the feature. +- DEADLINE: `DEADLINE description /by deadlineTime`. **Example**: `DEADLINE return book /by Sunday` + +- EVENT: `EVENT description /at eventTime`. **Example**: `EVENT project meeting /at Mon 2-4pm` + +**Expected outcome (Deadline as an example)**: + +``` +Got it. I've added this task: + [D][ ] return book (by: Sunday) +Now you have 6 tasks in the list. +``` -## Usage +### Delete a task: `delete` + +Delete certain task based on its index. + +**Format**: `delete taskIndex` + +**Expected outcome**: +``` +Noted. I've removed this task: + [D][ ] return book (by: Sunday) +Now you have 5 tasks in the list. +``` + +### Mark a task as done/undone: `mark/unmark` + +Mark a task as done/undone based on given index. The default status of a task is **undone**. + +**Format**: + +- mark: `mark taskIndex`. **Example**: `mark 2` + +- unmark: `unmark taskIndex`. **Example**: `unmark 2` + +**Expected outcome (mark as example)**: +``` +Nice! I've marked this task as done: +3. [T][X] borrow book +``` + +### Display the current task list: `list` -### `Keyword` - Describe action +**Format**: `list` -Describe the action and its outcome. +**Expected outcome**: +``` +Here are the tasks in your list: +1. [T][ ] eat food +2. [D][X] return book (by: Sunday) +3. [T][X] borrow book +4. [E][ ] study CS2113 (at: Monday) +``` -Example of usage: +### Find the tasks with given keyword: `find` -`keyword (optional arguments)` +**Format**: `find keyword` + +**Example**: `find book` + +**Expected outcome**: +``` +Here are the matching tasks in your list: +1. [D][X] return book (by: Sunday) +2. [T][X] borrow book +``` -Expected outcome: +### Exit the program: `bye` -Description of the outcome. +**Format**: `bye` +**Expected outcome**: ``` -expected output +Bye.Have a nice day! ``` + +## FAQ +Q: How do I know if the data are successfully loaded/saved from/into the file? + +A: Duke will display corresponding message and show error message in case the loading/saving process is unsuccessful. + +Q: What if my input format is wrong? + +A: Duke will give hint message and allow users to type in again. + + + diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 000000000..2f7efbeab --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-minimal \ No newline at end of file diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 5d313334c..3ebdbd8dd 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,10 +1,135 @@ +import Tasks.*; +import java.io.IOException; +import java.util.Scanner; +import java.util.ArrayList; + public class Duke { - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); + private Storage storage; + private ArrayList todolist = new ArrayList(); + private Ui ui; + + /** + * Initialize the Duke. + * @param filePath + */ + public Duke(String filePath){ + ui = new Ui(); + storage = new Storage(filePath); + Storage.create(filePath); + try { + todolist = new ArrayList(storage.load()); + } catch (IOException e) { + ui.showLoadingError(); + todolist = new ArrayList(); + } + } + + public void run() throws IOException { + String task; + ui.greetings(); + Scanner in = new Scanner(System.in); + task=in.nextLine(); + while(true){ + if(task.equalsIgnoreCase("bye")) + break; + String[] words = task.split(" ",2); + switch (words[0].toLowerCase()){ + case "list":{ + ui.displayTasks(todolist); + break; + } + case "mark":{ + if (words.length==1) + ui.incompleteMessage("mark"); + else{ + try{ + int index = Integer.parseInt(words[1]); + if(index>todolist.size()||index<=0) System.out.println("No task found."); + else ui.markAndDisplayTask(todolist,index); + } + catch (NumberFormatException ex){ + ui.showNumberError(); + } + } + break; + } + case "unmark":{ + if (words.length==1) + ui.incompleteMessage("unmark"); + else{ + try{ + int index = Integer.parseInt(words[1]); + if(index>todolist.size()||index<=0) System.out.println("No task found."); + else ui.unmarkAndDisplayTask(todolist,index); + } + catch (NumberFormatException ex){ + ui.showNumberError(); + } + } + break; + } + case "todo": { + if (words.length == 1) ui.incompleteMessage("todo"); + else ui.addToDo(todolist,words[1]); + break; + } + case "deadline": { + if (words.length==1) + ui.incompleteMessage("deadline"); + else{ + try{ + ui.addDeadline(todolist,words[1]); + } + catch(ArrayIndexOutOfBoundsException e){ + ui.showFormattingError("deadline"); + } + } + break; + } + case "event": { + if (words.length==1) + ui.incompleteMessage("event"); + else{ + try{ + ui.addEvent(todolist,words[1]); + } + catch(ArrayIndexOutOfBoundsException e){ + ui.showFormattingError("event"); + } + } + break; + } + case "delete":{ + if (words.length==1) + ui.incompleteMessage("delete"); + else{ + try{ + int index = Integer.parseInt(words[1]); + if(index>todolist.size()||index<=0) System.out.println("No task found."); + else ui.deleteTask(todolist,index); + } + catch (NumberFormatException ex){ + ui.showNumberError(); + } + } + break; + } + case "find": { + if (words.length == 1) ui.incompleteMessage("find"); + else ui.displayFoundTasks(todolist, words[1]); + break; + } + default: + ui.displayDefaultMessage(); + } + task=in.nextLine(); + } + Storage.save(Storage.format(todolist),"data.txt"); + ui.goodBye(); + } + + public static void main(String[] args) throws IOException { + new Duke("data.txt").run(); } } + diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 000000000..d2ffd5b4d --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: Duke + diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java new file mode 100644 index 000000000..6f31ed4d3 --- /dev/null +++ b/src/main/java/Storage.java @@ -0,0 +1,104 @@ +import Tasks.*; +import java.io.*; +import java.util.*; + +/** + * Include all the file operations such as loading tasks from the file and saving tasks in the file. + */ +public class Storage { + protected String filePath; + public Storage(String filePath) { + this.filePath = filePath; + } + + /** + * Find the file according to the given filepath. If the file has not been created, create the file/ + * @param filePath + */ + public static void create(String filePath){ + try { + File data = new File(filePath); + if (data.createNewFile()) { + System.out.println("No data file detected, I have created a new data file for you!"); + } else { + System.out.println("I have found the file and loading the data for you!"); + } + } catch (IOException e) { + System.out.println("An error occurred during file searching."); + } + } + + /** + * Save the information into the file. + * @param textToAdd Texts going to be saved in the file. + * @param filePath + * @throws IOException + */ + + public static void save(String textToAdd,String filePath) throws IOException { + FileWriter fw = new FileWriter(filePath,false); + try { + fw.write(textToAdd+System.lineSeparator()); + } catch (IOException e) { + System.out.println("Something went wrong, the data is not successfully saved in the file."); + } + fw.close(); + } + + /** + * Add the information in the given file into an arraylist. + * @return Arraylist loaded from the file. + * @throws IOException + */ + public ArrayList load() throws IOException { + ArrayList todolist = new ArrayList(); + BufferedReader br = new BufferedReader(new FileReader(this.filePath)); + String taskLine; + while ((taskLine = br.readLine())!=null) { + String[] t = taskLine.split(","); + switch (t[0]) { + case "T": + ToDo todo = new ToDo(t[2]); + todolist.add(todo); + break; + case "D": + Deadline deadline = new Deadline(t[2], t[3]); + todolist.add(deadline); + break; + case "E": + Event event = new Event(t[2], t[3]); + todolist.add(event); + break; + } + if (t.length>1&&t[1].equals("true")) + todolist.get(todolist.size() - 1).markAsDone(); + } + br.close(); + System.out.println("I have successfully loaded the file for you!"); + return todolist; + } + + /** + * format the information in the arraylist into a string. + * @param taskList Arraylist going to be formatted. + * @return String formatted from the tasklist. + */ + + public static String format(ArrayList taskList){ + String taskAllInfo=""; + for(Task k: taskList){ + String taskInfo = k.getType()+","+k.getStatus()+","+k.getDescription(); + if(k.getType()=="D") { + Deadline d = (Deadline) k; + taskInfo = taskInfo + "," + d.getBy(); + } + else if(k.getType()=="E"){ + Event e = (Event) k; + taskInfo = taskInfo + "," + e.getAt(); + } + taskAllInfo+=taskInfo+"\n"; + } + if (taskAllInfo!=null) taskAllInfo=taskAllInfo.substring(0, taskAllInfo.length() - 1); + return taskAllInfo; + } +} diff --git a/src/main/java/Tasks/Deadline.java b/src/main/java/Tasks/Deadline.java new file mode 100644 index 000000000..8f6d817e7 --- /dev/null +++ b/src/main/java/Tasks/Deadline.java @@ -0,0 +1,32 @@ +package Tasks; + +/** + * Represents a kind of task that needs to be done before a specific date/time. + */ +public class Deadline extends Task { + + protected String by; + + public Deadline(String description, String by) { + super(description); + this.by = by; + } + + public void setBy(String by){ + this.by=by; + } + + public String getBy(){ + return by; + } + + @Override + public String toString() { + return "[D]" + super.toString() + " (by:" + by + ")"; + } + + @Override + public String getType() { + return "D"; + } +} diff --git a/src/main/java/Tasks/Event.java b/src/main/java/Tasks/Event.java new file mode 100644 index 000000000..b05bc5a1c --- /dev/null +++ b/src/main/java/Tasks/Event.java @@ -0,0 +1,33 @@ +package Tasks; + +/** + * Represents a kind of task that starts at a specific time and ends at a specific time. + */ +public class Event extends Task { + + protected String at; + + public Event(String description, String at) { + super(description); + this.at = at; + } + + public void setAt(String at){ + this.at=at; + } + + public String getAt(){ + return at; + } + + @Override + public String toString() { + return "[E]" + super.toString() + " (at:" + at + ")"; + } + + @Override + public String getType() { + return "E"; + } +} + diff --git a/src/main/java/Tasks/Task.java b/src/main/java/Tasks/Task.java new file mode 100644 index 000000000..88ec33c75 --- /dev/null +++ b/src/main/java/Tasks/Task.java @@ -0,0 +1,40 @@ +package Tasks; + +/** + * Represents a general concept. + */ +public class Task { + protected String description; + protected boolean isDone; + + public Task(String description) { + this.description = description; + this.isDone = false; + } + + public boolean getStatus(){return isDone;} + + public String getStatusIcon() { + return (isDone ? "X" : " "); // mark done task with X + } + + public String getDescription(){ + return description; + } + + public void setDescription(String description1){ + description=description1; + } + + public void markAsDone(){ + isDone=true; + } + + public void markAsUndone(){ + isDone=false; + } + public String toString(){ + return "["+getStatusIcon()+"] "+description; + } + public String getType(){return null;} +} diff --git a/src/main/java/Tasks/ToDo.java b/src/main/java/Tasks/ToDo.java new file mode 100644 index 000000000..c582687e8 --- /dev/null +++ b/src/main/java/Tasks/ToDo.java @@ -0,0 +1,21 @@ +package Tasks; + +/** + * Represents a kind of task without any date/time attached to it. + */ +public class ToDo extends Task { + + public ToDo(String description) { + super(description); + } + + @Override + public String toString() { + return "[T]" + super.toString(); + } + + @Override + public String getType() { + return "T"; + } +} \ No newline at end of file diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java new file mode 100644 index 000000000..df9d15dd6 --- /dev/null +++ b/src/main/java/Ui.java @@ -0,0 +1,172 @@ +import Tasks.*; +import java.util.ArrayList; + +/** + * Interactions with the user. + */ +public class Ui { + public void greetings(){ + String logo = " ____ _ \n" + + "| _ \\ _ _| | _____ \n" + + "| | | | | | | |/ / _ \\\n" + + "| |_| | |_| | < __/\n" + + "|____/ \\__,_|_|\\_\\___|\n"; + System.out.println("Hello from\n" + logo); + System.out.println("Hello! I'm Duke\nWhat can I do for you?"); + } + + public void goodBye() { + System.out.println("Bye.Have a nice day!"); + } + + /** + * Display the information regarding tasks. + * @param taskList Arraylist containing task information. + */ + public void displayTasks(ArrayList taskList){ + System.out.println("Here are the tasks in your list:"); + for (int i = 0; i < taskList.size(); i++) + System.out.println((i+1)+". "+taskList.get(i).toString()); + } + + /** + * Display the incomplete information message given the input type. + * @param type Type of instruction given by the user. + */ + public void incompleteMessage(String type) { + switch (type) { + case "mark": + System.out.println("☹ OOPS!!! Please tell me the task you want to mark."); + break; + case "unmark": + System.out.println("☹ OOPS!!! Please tell me the task you want to unmark."); + break; + case "todo": + System.out.println("☹ OOPS!!! The description of a todo cannot be empty."); + break; + case "deadline": + System.out.println("OOPS!!! The information about the deadline is incomplete."); + break; + case "event": + System.out.println("☹ OOPS!!! The information about the event is incomplete."); + break; + case "delete": + System.out.println("☹ OOPS!!! Please tell me the task you want to delete."); + break; + case "find": + System.out.println("☹ OOPS!!! Please tell me the task you want to find."); + break; + } + } + + public void addTaskMessage(ArrayList taskList){ + System.out.println("Got it. I've added this task:"); + System.out.println(" " + taskList.get(taskList.size() - 1).toString()); + System.out.println("Now you have " + taskList.size() + " tasks in the list."); + } + + /** + * Mark the corresponding task as done and display the successful message. + * @param taskList Arraylist containing task information. + * @param index Index of the task which will be marked as done. + */ + public void markAndDisplayTask(ArrayList taskList, int index){ + taskList.get(index-1).markAsDone(); + System.out.println("Nice! I've marked this task as done:"); + System.out.println((index)+". "+taskList.get(index-1).toString()); + } + + /** + * Mark the corresponding task as undone and display the successful message. + * @param taskList Arraylist containing task information. + * @param index Index of the task which will be marked as undone. + */ + public void unmarkAndDisplayTask(ArrayList taskList, int index){ + taskList.get(index-1).markAsUndone(); + System.out.println("OK, I've marked this task as not done yet:"); + System.out.println((index)+". "+taskList.get(index-1).toString()); + } + + /** + * Add the task as ToDo. + * @param taskList Arraylist containing task information. + * @param todo Description of the task which will be added as ToDo. + */ + public void addToDo(ArrayList taskList, String todo){ + taskList.add(new ToDo(todo)); + addTaskMessage(taskList); + } + + /** + * Add the task as deadline. + * @param taskList Arraylist containing task information. + * @param deadline Description of the task which will be added as Deadline. + */ + public void addDeadline(ArrayList taskList, String deadline){ + String[] deadline1 = deadline.split("/by", 2); + taskList.add(new Deadline(deadline1[0], deadline1[1])); + addTaskMessage(taskList); + } + + /** + * Add the task as event. + * @param taskList Arraylist containing task information. + * @param event Description of the task which will be added as Event. + */ + public void addEvent(ArrayList taskList, String event){ + String[] event1 = event.split("/at", 2); + taskList.add(new Event(event1[0], event1[1])); + addTaskMessage(taskList); + } + + /** + * Delete the corresponding task. + * @param taskList Arraylist containing task information. + * @param index Index of the task which will be deleted. + */ + public void deleteTask(ArrayList taskList, int index){ + System.out.println("Noted. I've removed this task:"); + System.out.println((index)+". "+taskList.get(index-1).toString()); + taskList.remove(index-1); + System.out.println("Now you have " + taskList.size() + " tasks in the list."); + } + + public void displayDefaultMessage(){ + System.out.println("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); + } + + public void showNumberError(){ + System.out.println("☹ OOPS!!! Please tell me the task number you want to mark/unmark/delete."); + } + + public void showFormattingError(String type){ + switch(type){ + case "deadline": + System.out.println("Could you type in the deadline with format:" + + " deadline {your task} /by {your deadline}?"); + break; + case "event": + System.out.println("Could you type in the event with format:" + + " event {your task} /at {your event time}?"); + break; + } + } + + public void showLoadingError(){ + System.out.println("The loading process is unsuccessful."); + } + + public void displayFoundTasks(ArrayList taskList, String keyword){ + int count=1; + System.out.println("Here are the matching tasks in your list:"); + for(Task t: taskList){ + if(t.getDescription().toUpperCase().contains(keyword.toUpperCase())){ + System.out.println(count+". "+ t); + count++; + } + } + if(count==1) + System.out.println("No matched tasks!"); + } +} +