diff --git a/README.md b/README.md index 8715d4d91..f92dca5a7 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,136 @@ -# 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. - -## Setting up in Intellij - -Prerequisites: JDK 11, update Intellij to the most recent version. - -1. Open Intellij (if you are not in the welcome screen, click `File` > `Close Project` to close the existing project first) -1. Open the project into Intellij as follows: - 1. Click `Open`. - 1. Select the project directory, and click `OK`. - 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: - ``` - Hello from - ____ _ - | _ \ _ _| | _____ - | | | | | | | |/ / _ \ - | |_| | |_| | < __/ - |____/ \__,_|_|\_\___| - ``` +# User Guide +Duke is a **desktop app for tracking tasks, +optimized for use via a Command Line Interface** (CLI). +As Duke is designed to be use in a CLI, this +application would benefit users who are able to type fast. + +## Quick Start +1. Ensure you have Java 11 or above installed on your Computer +2. Download the latest JAR file from GitHub. +3. Copy the file to the folder you want to use as the home folder for your Duke application. + Note that the data file would also be created in the same folder. +4. Open the CLI at where you have placed the jar file and run it using the command + `java -jar ip.jar` +5. Type the commands in the box and press enter to execute it. + Here are some commands you can try: + - `list` : List all current tasks. + - `todo Read Book` : Adds a todo task of reading book. + - `delete 1` : Deletes the first task from list. +### Feature list: +* Listing all tasks: `list` +* Adding a new task: + * Todo task: `todo` + * Deadline task: `deadline` + * Event task: `event` +* Marking a task as done: `mark` +* Marking a task as not done: `unmark` +* Deleting a task: `delete` +* Finding a task: `find` +* Exiting the program: `bye` + +## Feature +### Adding a Task +There are three kinds of Tasks: Todos, Deadlines, and Events. +### Adding a Todo +Adds a Todo to the list of tasks.
+Format: `todo DESCRIPTION`
+Words in `UPPER_CASE` are the parameters.
+Examples: +* todo buy sunscreen +* todo buy bubble tea + +### Adding a Deadline +Adds a Deadline to the list of tasks.
+Format: `deadline DESCRIPTION /by DEADLINE`
+Words in `UPPER_CASE` are the parameters.
+Examples:
+* deadline finish econometrics problem set /by Saturday +* deadine return book /by Sunday 12:30PM + +### Adding an Event +Adds an Event to the list of tasks.
+Format: `event DESCRIPTION /at TIME`
+Words in `UPPER_CASE` are the parameters.
+Examples:
+* event project meeting /at Saturday 8:00PM +* event attend CS2113 lecture /at Friday 4:00PM + +### Listing all Tasks +Displays all tasks that the user has added to their list.
+Format: `list`
+Output could look like the following:
+``` + 1: [T][ ] go shopping + 2: [D][ ] finish 2113 iP (by: Friday) + 3: [E][ ] 2113 project meeting (at: Monday 1:00PM) + ``` +`T` denotes a task of type Todo, `D` denotes a task of type Deadline, and `E` denotes a task of type Event. + +### Marking a Task as complete +You have the option of marking (i.e. checking off) a task that has been completed.
+Format: `mark TASK_NUMBER`
+`TASK_NUMBER` is the numerical label assigned to the task (that you can find from the output of the `list` command).
+Example:
+Assuming the following task list:
+``` + 1: [T][ ] go shopping + 2: [D][ ] finish 2113 iP (by: Friday) + 3: [E][ ] 2113 project meeting (at: Monday 1:00PM) +``` +`mark 2` would result in the following list:
+``` + 1: [T][ ] go shopping + 2: [D][X] finish 2113 iP (by: Friday) + 3: [E][ ] 2113 project meeting (at: Monday 1:00PM) +``` + +### Unmarking a Task +Alternatively, you may also unmark a task that has previously been marked.
+Format: `unmark TASK_NUMBER`
+Example:
+Assuming the following task list:
+``` + 1: [T][ ] go shopping + 2: [D][ ] finish 2113 iP (by: Friday) + 3: [E][X] 2113 project meeting (at: Monday 1:00PM) +``` +`unmark 3` would result in the following list:
+``` + 1: [T][ ] go shopping + 2: [D][ ] finish 2113 iP (by: Friday) + 3: [E][ ] 2113 project meeting (at: Monday 1:00PM) +``` + +### Deleting a Task +Users may also delete a task from the list of tasks.
+Format `delete TASK_NUMBER`
+Example:
+Assuming the following task list:
+``` + 1: [T][ ] go shopping + 2: [D][ ] finish 2113 iP (by: Friday) + 3: [E][ ] 2113 project meeting (at: Monday 1:00PM) +``` +`delete 2` would result in the following list:
+``` + 1: [T][ ] go shopping + 2: [E][ ] 2113 project meeting (at: Monday 1:00PM) +``` + + +### Finding a Task +Users can search for Tasks in their task list with a specific keywords.
+Format `find KEYWORD`
+Example:
+Assuming the following task list:
+``` + 1: [T][ ] go shopping + 2: [D][ ] finish 2113 iP (by: Friday) + 3: [E][X] 2113 project meeting (at: Monday 1:00PM) +``` +`find 2113` would result in the following list:
+``` + 1: [T][ ] go shopping + 2: [D][ ] finish 2113 iP (by: Friday) + 3: [E][ ] 2113 project meeting (at: Monday 1:00PM) + ``` \ No newline at end of file diff --git a/data/duke.txt b/data/duke.txt new file mode 100644 index 000000000..1413de65a --- /dev/null +++ b/data/duke.txt @@ -0,0 +1,3 @@ +T | 1 | go shopping +D | 1 | finish 2113 iP | Friday +E | 0 | 2113 project meeting | Monday 1:00PM diff --git a/docs/README.md b/docs/README.md index 8077118eb..326ae15e3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,29 +1,135 @@ # User Guide +Duke is a **desktop app for tracking tasks, +optimized for use via a Command Line Interface** (CLI). +As Duke is designed to be use in a CLI, this +application would benefit users who are able to type fast. -## Features +## Quick Start +1. Ensure you have Java 11 or above installed on your Computer +2. Download the latest JAR file from GitHub. +3. Copy the file to the folder you want to use as the home folder for your Duke application. + Note that the data file would also be created in the same folder. +4. Open the CLI at where you have placed the jar file and run it using the command + `java -jar ip.jar` +5. Type the commands in the box and press enter to execute it. + Here are some commands you can try: + - `list` : List all current tasks. + - `todo Read Book` : Adds a todo task of reading book. + - `delete 1` : Deletes the first task from list. +### Feature list: +* Listing all tasks: `list` +* Adding a new Todo task: `todo` +* Adding a new Deadline task: `deadline` +* Adding a new Event task: `event` +* Marking a task as done: `mark` +* Marking a task as not done: `unmark` +* Deleting a task: `delete` +* Finding a task: `find` +* Exiting the program: `bye` -### Feature-ABC +## Feature +### Adding a Task +There are three kinds of Tasks: Todos, Deadlines, and Events. +### Adding a Todo +Adds a Todo to the list of tasks.
+Format: `todo DESCRIPTION`
+Words in `UPPER_CASE` are the parameters.
+Examples: +* todo go to class +* todo buy fruits -Description of the feature. +### Adding a Deadline +Adds a Deadline to the list of tasks.
+Format: `deadline DESCRIPTION /by DEADLINE`
+Words in `UPPER_CASE` are the parameters.
+Examples:
+* deadline finish problem set /by Saturday +* deadine return book /by Sunday -### Feature-XYZ +### Adding an Event +Adds an Event to the list of tasks.
+Format: `event DESCRIPTION /at TIME`
+Words in `UPPER_CASE` are the parameters.
+Examples:
+* event project meeting /at Saturday 8:00PM +* event attend CS2113 lecture /at Friday 4:00PM -Description of the feature. - -## Usage - -### `Keyword` - Describe action - -Describe the action and its outcome. +### Listing all Tasks +Displays all tasks that the user has added to their list.
+Format: `list`
+Output could look like the following:
+``` + 1: [T][ ] go shopping + 2: [D][ ] finish 2113 iP (by: Friday) + 3: [E][ ] 2113 project meeting (at: Monday 1:00PM) + ``` +`T` denotes a task of type Todo, `D` denotes a task of type Deadline, and `E` denotes a task of type Event. -Example of usage: +### Marking a Task as complete +You have the option of marking (i.e. checking off) a task that has been completed.
+Format: `mark TASK_NUMBER`
+`TASK_NUMBER` is the numerical label assigned to the task (that you can find from the output of the `list` command).
+Example:
+Assuming the following task list:
+``` + 1: [T][ ] go shopping + 2: [D][ ] finish 2113 iP (by: Friday) + 3: [E][ ] 2113 project meeting (at: Monday 1:00PM) +``` +`mark 2` would result in the following list:
+``` + 1: [T][ ] go shopping + 2: [D][X] finish 2113 iP (by: Friday) + 3: [E][ ] 2113 project meeting (at: Monday 1:00PM) +``` -`keyword (optional arguments)` +### Unmarking a Task +Alternatively, you may also unmark a task that has previously been marked.
+Format: `unmark TASK_NUMBER`
+Example:
+Assuming the following task list:
+``` + 1: [T][ ] go shopping + 2: [D][ ] finish 2113 iP (by: Friday) + 3: [E][X] 2113 project meeting (at: Monday 1:00PM) +``` +`unmark 3` would result in the following list:
+``` + 1: [T][ ] go shopping + 2: [D][ ] finish 2113 iP (by: Friday) + 3: [E][ ] 2113 project meeting (at: Monday 1:00PM) +``` -Expected outcome: +### Deleting a Task +Users may also delete a task from the list of tasks.
+Format `delete TASK_NUMBER`
+Example:
+Assuming the following task list:
+``` + 1: [T][ ] go shopping + 2: [D][ ] finish 2113 iP (by: Friday) + 3: [E][ ] 2113 project meeting (at: Monday 1:00PM) +``` +`delete 2` would result in the following list:
+``` + 1: [T][ ] go shopping + 2: [E][ ] 2113 project meeting (at: Monday 1:00PM) +``` -Description of the outcome. +### Finding a Task +Users can search for Tasks in their task list with a specific keywords.
+Format `find KEYWORD`
+Example:
+Assuming the following task list:
+``` + 1: [T][ ] go shopping + 2: [D][ ] finish 2113 iP (by: Friday) + 3: [E][X] 2113 project meeting (at: Monday 1:00PM) ``` -expected output +`find 2113` would result in the following list:
``` + 1: [T][ ] go shopping + 2: [D][ ] finish 2113 iP (by: Friday) + 3: [E][ ] 2113 project meeting (at: Monday 1:00PM) + ``` \ No newline at end of file diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 000000000..9da9a0291 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-dinky \ No newline at end of file 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..2c9a9745c --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: duke.Duke + diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java new file mode 100644 index 000000000..069b51b36 --- /dev/null +++ b/src/main/java/duke/Duke.java @@ -0,0 +1,35 @@ +package duke; +import duke.tasks.TaskList; +import java.io.IOException; + +/** + * Entry point of the Duke application. + * Initializes the application and starts the interaction with the user. + */ +public class Duke { + + private Storage storage; + private static TaskList taskList; + private Ui ui; + + public Duke(String filePath) throws IOException { + storage = new Storage(filePath); + ui = new Ui(); + taskList = new TaskList(storage); + taskList.loadTaskList(); + } + + public void run() throws IOException { + Ui.greeting(); + startBot(); + Ui.farewell(); + } + + private static void startBot() throws IOException { + taskList.processTasks(); + } + + public static void main(String[] args) throws IOException { + new Duke("data/duke.txt").run(); + } +} diff --git a/src/main/java/duke/Storage.java b/src/main/java/duke/Storage.java new file mode 100644 index 000000000..af3f5d8ef --- /dev/null +++ b/src/main/java/duke/Storage.java @@ -0,0 +1,90 @@ +package duke; + +import duke.tasks.*; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Scanner; + +/** + * Represents a Storage object that contains support for + * saving tasks/loading tasks from storage (ensures persistent storage + * of tasks) + */ + +public class Storage { + protected static String filePath; + protected static final char TODO = 'T'; + protected static final char EVENT = 'E'; + protected static final char DEADLINE = 'D'; + + public Storage(String filePath) { + this.filePath = filePath; + } + + /** + * Helper for loadTaskList + * Loads tasks from duke.txt (file of saved tasks) + * Returns ArrayList of Task objects + */ + public static ArrayList loadFile() throws IOException { + try { + File f = new File(filePath); + f.getParentFile().mkdirs(); + f.createNewFile(); + Scanner s = new Scanner(f); + ArrayList listArray = new ArrayList<>(); + while (s.hasNext()) { + boolean isDone = true; + String message = s.nextLine(); + String[] splitMessage = message.split(" \\| "); + char type = splitMessage[0].charAt(0); + int status = Integer.parseInt(splitMessage[1]); + if (status == 0) { + isDone = false; + } + String description = splitMessage[2]; + switch (type) { + case TODO: + listArray.add(new ToDo(description, isDone)); + break; + case EVENT: + String at = splitMessage[3]; + listArray.add(new Event(description, isDone, at)); + break; + case DEADLINE: + String by = splitMessage[3]; + listArray.add(new Deadline(description, isDone, by)); + break; + default: + break; + } + } + s.close(); + return listArray; + } catch (IOException e) { + System.out.println("IO exception"); + } + return null; + } + /** + * Helper for processTasks + * Saves the list of tasks to the filepath of the Storage object + * @param listArray an ArrayList of tasks to write to file + */ + public static void saveFile(ArrayList listArray) throws IOException { + try { + FileWriter fw = new FileWriter("data/duke.txt"); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < listArray.size(); i++) { + sb.append(listArray.get(i).saveTasks()); + } + fw.write(sb.toString()); + fw.close(); + } catch (IOException e) { + System.out.println("IO exception"); + } + } +} diff --git a/src/main/java/duke/Ui.java b/src/main/java/duke/Ui.java new file mode 100644 index 000000000..f0188c385 --- /dev/null +++ b/src/main/java/duke/Ui.java @@ -0,0 +1,56 @@ +package duke; + +import java.util.Scanner; + +/** + * Create a UI for displaying message + */ +public class Ui { + + public static void greeting() { + String greeting = "----------------------------------------------\n" + + "Hello! Duke here :)\n" + + "What can I do for you?\n" + + "----------------------------------------------\n"; + System.out.println(greeting); + } + + public static void farewell() { + String bye = "Bye. Hope to see you again soon!\n"; + System.out.println(bye); + } + + public static String getInput() { + Scanner sc = new Scanner(System.in); + String userInput = sc.nextLine(); + return userInput; + } + + public static void printDeadlineErrorMsg() { + System.out.println("Invalid input format. Please use /by for deadline."); + } + + public static void printEventErrorMsg() { + System.out.println("Invalid input format. Please use /at for event."); + } + + public static void printCommandErrorMsg() { + System.out.println("I don't understand D: Please key in your command again!"); + } + + public static void printIndexOutOfBoundMsg() { + System.out.println("This index is not within the range :( Try again!"); + } + + public static void printKeyWordMsg() { + System.out.println("The keyword doesn't exist in task descriptions, try other keyword please :)"); + } + + public static void printNumberFormatMsg() { + System.out.println("You didn't input a valid number, please try again!"); + } + + public static void printDescriptionMissingMsg() { + System.out.println("Please input some description for this Task!"); + } +} diff --git a/src/main/java/duke/tasks.txt b/src/main/java/duke/tasks.txt new file mode 100644 index 000000000..fcab3e740 --- /dev/null +++ b/src/main/java/duke/tasks.txt @@ -0,0 +1,2 @@ +task1 +ddl1 diff --git a/src/main/java/duke/tasks/Deadline.java b/src/main/java/duke/tasks/Deadline.java new file mode 100644 index 000000000..a7692c2ae --- /dev/null +++ b/src/main/java/duke/tasks/Deadline.java @@ -0,0 +1,26 @@ +package duke.tasks; +/** + * Represents an Deadline object on a person's list. + * This class inherits from the Task class and supports a slightly different toString() method + * that contains a marker for the Deadline object (an "D" for "Deadline"). Also + * allows users to set the time/date the Deadline task should be completed by. + */ +public class Deadline extends Task { + + protected String by; + + public Deadline(String description, boolean isDone, String by) { + super(description, isDone); + this.by = by; + } + + @Override + public String toString() { + return "[D]" + super.toString() + " (by: " + by + ")"; + } + + @Override + public String saveTasks() { + return "D " + super.saveTasks() + " | " + this.by + System.lineSeparator(); + } +} diff --git a/src/main/java/duke/tasks/Event.java b/src/main/java/duke/tasks/Event.java new file mode 100644 index 000000000..be80d2b1c --- /dev/null +++ b/src/main/java/duke/tasks/Event.java @@ -0,0 +1,24 @@ +package duke.tasks; + +/** + * Represents an Event Task + */ +public class Event extends Task { + + protected String at; + + public Event(String description, boolean isDone, String at) { + super(description, isDone); + this.at = at; + } + + @Override + public String toString() { + return "[E]" + super.toString() + " (at: " + at + ")"; + } + + @Override + public String saveTasks() { + return "E " + super.saveTasks() + " | " + this.at + System.lineSeparator(); + } +} diff --git a/src/main/java/duke/tasks/Task.java b/src/main/java/duke/tasks/Task.java new file mode 100644 index 000000000..2327abb4e --- /dev/null +++ b/src/main/java/duke/tasks/Task.java @@ -0,0 +1,45 @@ +package duke.tasks; +/** + * Represents a Task object on a person's list of tasks that they must complete. + * A Task corresponds to a Task represented by a description of the + * task as well as a flag for whether it has been completed. + */ +public class Task { + protected String description; + protected boolean isDone; + + public Task(String description, boolean isDone) { + this.description = description; + this.isDone = isDone; + } + + public String getDescription() { + return description; + } + + public boolean getStatus() { return isDone; } + + public String getStatusIcon() { + return (isDone ? "X" : " "); // mark done task with X + } + + public void markAsDone(){ + this.isDone = true; + } + + public void markAsNotDone(){ + this.isDone = false; + } + + public String toString() { + return "[" + getStatusIcon() + "] " + description; + } + + public String saveTasks() { + int isMarked = 0; + if(this.isDone) { + isMarked = 1; + } + return "| " + isMarked + " | " + this.description ; + } +} \ No newline at end of file diff --git a/src/main/java/duke/tasks/TaskList.java b/src/main/java/duke/tasks/TaskList.java new file mode 100644 index 000000000..0cc94c2cc --- /dev/null +++ b/src/main/java/duke/tasks/TaskList.java @@ -0,0 +1,206 @@ +package duke.tasks; + +import duke.Storage; +import duke.Ui; + +import java.io.IOException; +import java.util.ArrayList; + +/** + * Represents a person's list of Tasks that they must complete. + * This class supports the modification of Tasks. + */ +public class TaskList { + private static ArrayList tasks; + private Storage storage; + private Ui ui; + + public TaskList(Storage storage) { + tasks = new ArrayList<>(); + this.storage = storage; + } + + /** + * Load the Task List from the file. + * @throws IOException + */ + public void loadTaskList() throws IOException { + try { + tasks = storage.loadFile(); + } catch (IOException e) { + throw e; + } + } + + /** + * Process Tasks after parsing it and use the command accordingly. + * @throws IOException + */ + public void processTasks() throws IOException { + String by = new String(); + String at = new String(); + + while(true){ + String userInput = Ui.getInput(); + String choice = userInput.split(" ")[0]; + System.out.println("----------------------------------------------"); + if (choice.equals("bye")) { + Storage.saveFile(tasks); + break; + } + switch (choice) { + case "list": + listTask(); + break; + case "delete": + try{ + int taskIndexDelete = Integer.parseInt(userInput.replace("delete","").trim()); + deleteTask(taskIndexDelete); + } + catch (NumberFormatException e) { + Ui.printNumberFormatMsg(); + } + break; + case "find": + String keyWord = userInput.replace("find","").trim(); + findTask(keyWord); + break; + case "mark": + try { + int taskIndexMark = Integer.parseInt(userInput.replace("mark","").trim()); + markTask(taskIndexMark); + } + catch (NumberFormatException e) { + Ui.printNumberFormatMsg(); + } + break; + case "unmark": + try { + int taskIndexUnmark = Integer.parseInt(userInput.replace("unmark","").trim()); + unmarkTask(taskIndexUnmark); + } + catch (NumberFormatException e) { + Ui.printNumberFormatMsg(); + } + break; + case "todo": + String toDoDescription = userInput.replace("todo","").trim(); + if (toDoDescription.equals("")) { + Ui.printDescriptionMissingMsg(); + } + else { + Task t = new ToDo(toDoDescription, false); + addTask(t); + } + break; + case "deadline": + String deadlineDescription = userInput.replace("deadline","").trim(); + String[] parsedDeadlineInput = deadlineDescription.split("/by"); + try { + by = parsedDeadlineInput[1].trim(); + deadlineDescription = parsedDeadlineInput[0].trim(); + if (deadlineDescription.equals("")) { + Ui.printDescriptionMissingMsg(); + } + else { + Task d = new Deadline(deadlineDescription, false, by); + addTask(d); + } + } + catch (ArrayIndexOutOfBoundsException e) { + Ui.printDeadlineErrorMsg(); + } + break; + case "event": + String eventDescription = userInput.replace("event","").trim(); + String[] parsedEventInput = eventDescription.split("/at"); + try { + at = parsedEventInput[1].trim(); + eventDescription = parsedEventInput[0].trim(); + if (eventDescription.equals("")) { + Ui.printDescriptionMissingMsg(); + } + else { + Task e = new Event(eventDescription, false, at); + addTask(e); + } + } + catch (ArrayIndexOutOfBoundsException e) { + Ui.printEventErrorMsg(); + } + break; + default: + Ui.printCommandErrorMsg(); + break; + } + Storage.saveFile(tasks); + System.out.println("----------------------------------------------"); + } + } + + public void listTask() { + System.out.println("Here are the tasks in your list:"); + for(int i = 0; i ACTUAL.TXT +java -classpath ..\bin duke.duke < input.txt > ACTUAL.TXT REM compare the output to the expected output FC ACTUAL.TXT EXPECTED.TXT