diff --git a/data/AMY.txt b/data/AMY.txt new file mode 100644 index 000000000..c49f32ad1 --- /dev/null +++ b/data/AMY.txt @@ -0,0 +1,2 @@ +T | 0 | bye +T | 1 | helloo diff --git a/docs/README.md b/docs/README.md index 8077118eb..471e76b11 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,29 +1,183 @@ -# User Guide +# AMY User Guide + +AMY is a text based Java app for managing your daily +tasks. Given below is the features list and your user guide. ## Features -### Feature-ABC +Here is your features list: + ++ **todo**, **event**, ++ **deadline**, **mark**, ++ **unmark**, **delete**, ++ **find**, **list**, **bye** + +Users can use these features to handle their tasks + +### ToDo + +Adds a new task to do in the task list. + +#### Usage + +**Format**: todo [task] + +**Examples** + ++ `todo finish my homework` + +**Expected Output**: -Description of the feature. +``` ++ Got it. I've added this task: ++ [T][ ] [finish my homework] +``` + +### Deadline + +Adds a new task with deadline to do in the task list. + +#### Usage -### Feature-XYZ +**Format**: deadline [task] /by [deadline] + +**Example** + ++ `deadline finish my homework /by Sunday` + +**Expected Output**: + +``` ++ Got it. I've added this task:
++ [D][ ] [finish my homework] (by: [Sunday]) +``` + +### Event + +Adds a new Event to the task list. + +#### Usage + +**Format**: event [task] /from [start] /to [end] + +**Example** + ++`finish homework /from 12:00 /to 18:00` + +**Expected Output**: + +``` ++ Got it. I've added this task: ++ [E][ ] [finish homework] (from: [12:00] to: [18:00]) +``` + +### List + +Lists all tasks in the task list. + +#### Usage + +**Format**: list + +**Expected Output**: + +``` +1. [D][ ] [task] (by: [Deadline]) +2. [T][ ] [task] +``` -Description of the feature. +### Mark Task + +Mark the task as done in the task list. + +#### Usage + +**Format**: mark [TASK INDEX] + +**Example** + ++ `mark 1` + +**Expected Output**: + +``` ++ Nice! I've marked this task as done: ++ [T][X] [finish homework] +``` +### Unmark + +Mark the task as not done in the task list. + +#### Usage + +**Format**: unmark [index] + +**Example** + ++ ```unmark 1``` + +**Expected Output**: + +``` ++ Nice! I've marked this task as not done. ++ [T][ ] [task] +``` + +### Delete Task + +Delete a task from the task list. + +#### Usage + +**Format**: delete [index] + +**Example** + ++ ```delete 2``` + +**Expected Output**: + +``` +Noted. I've removed this task: +[task] +Now you have [#] tasks in your list. +``` + +### Find Task + +Find tasks from the task list using either description or timing. + +#### Usage + +**Format**: find [keyword] + +**Example** + ++ ```find /description exam``` ++ ```find /time 21-04-2021 14:30``` + +**Expected Output**: + +``` +Here are the matching tasks in your list: ++ [index]. [task] ++ [index]. [task] +``` -## Usage +### bye -### `Keyword` - Describe action +Exit from the application. -Describe the action and its outcome. +#### Usage -Example of usage: +**Format**: bye -`keyword (optional arguments)` +**Example** -Expected outcome: ++ ```bye``` -Description of the outcome. +**Expected Output**: ``` -expected output ++ Bye. Hope to see you again soon! ``` diff --git a/src/main/java/AMY/AMY.java b/src/main/java/AMY/AMY.java new file mode 100644 index 000000000..87079e7ed --- /dev/null +++ b/src/main/java/AMY/AMY.java @@ -0,0 +1,19 @@ +package AMY; + +public class AMY { + + /** + * Runs the whole program when prompted by the main + */ + public static void run() { + Ui.welcomeMessage(); + Storage.loadTasksFromFile(); + Parser.manageInput(); + Storage.saveTasksToFile(); + Ui.byMessage(); + } + + public static void main(String[] args) { + run(); + } +} diff --git a/src/main/java/AMY/Exceptions/EmptyDeadlineException.java b/src/main/java/AMY/Exceptions/EmptyDeadlineException.java new file mode 100644 index 000000000..b8f8242b9 --- /dev/null +++ b/src/main/java/AMY/Exceptions/EmptyDeadlineException.java @@ -0,0 +1,4 @@ +package AMY.Exceptions; + +public class EmptyDeadlineException extends Exception{ +} diff --git a/src/main/java/AMY/Exceptions/EmptyDeleteException.java b/src/main/java/AMY/Exceptions/EmptyDeleteException.java new file mode 100644 index 000000000..3eb599006 --- /dev/null +++ b/src/main/java/AMY/Exceptions/EmptyDeleteException.java @@ -0,0 +1,4 @@ +package AMY.Exceptions; + +public class EmptyDeleteException extends Exception{ +} diff --git a/src/main/java/AMY/Exceptions/EmptyEventException.java b/src/main/java/AMY/Exceptions/EmptyEventException.java new file mode 100644 index 000000000..c5f52f758 --- /dev/null +++ b/src/main/java/AMY/Exceptions/EmptyEventException.java @@ -0,0 +1,4 @@ +package AMY.Exceptions; + +public class EmptyEventException extends Exception { +} diff --git a/src/main/java/AMY/Exceptions/EmptyFindException.java b/src/main/java/AMY/Exceptions/EmptyFindException.java new file mode 100644 index 000000000..f0df8c57b --- /dev/null +++ b/src/main/java/AMY/Exceptions/EmptyFindException.java @@ -0,0 +1,4 @@ +package AMY.Exceptions; + +public class EmptyFindException extends Exception { +} diff --git a/src/main/java/AMY/Exceptions/EmptyInput.java b/src/main/java/AMY/Exceptions/EmptyInput.java new file mode 100644 index 000000000..e6e76f7ff --- /dev/null +++ b/src/main/java/AMY/Exceptions/EmptyInput.java @@ -0,0 +1,4 @@ +package AMY.Exceptions; + +public class EmptyInput extends Exception { +} diff --git a/src/main/java/AMY/Exceptions/EmptyMarkException.java b/src/main/java/AMY/Exceptions/EmptyMarkException.java new file mode 100644 index 000000000..e136a4d70 --- /dev/null +++ b/src/main/java/AMY/Exceptions/EmptyMarkException.java @@ -0,0 +1,4 @@ +package AMY.Exceptions; + +public class EmptyMarkException extends Exception{ +} diff --git a/src/main/java/AMY/Exceptions/EmptyToDoException.java b/src/main/java/AMY/Exceptions/EmptyToDoException.java new file mode 100644 index 000000000..5b537b141 --- /dev/null +++ b/src/main/java/AMY/Exceptions/EmptyToDoException.java @@ -0,0 +1,4 @@ +package AMY.Exceptions; + +public class EmptyToDoException extends Exception { +} diff --git a/src/main/java/AMY/Exceptions/EmptyUnmarkException.java b/src/main/java/AMY/Exceptions/EmptyUnmarkException.java new file mode 100644 index 000000000..13bd5bf19 --- /dev/null +++ b/src/main/java/AMY/Exceptions/EmptyUnmarkException.java @@ -0,0 +1,4 @@ +package AMY.Exceptions; + +public class EmptyUnmarkException extends Exception { +} diff --git a/src/main/java/AMY/Parser.java b/src/main/java/AMY/Parser.java new file mode 100644 index 000000000..826f52dee --- /dev/null +++ b/src/main/java/AMY/Parser.java @@ -0,0 +1,139 @@ +package AMY; + +import AMY.Exceptions.EmptyDeadlineException; +import AMY.Exceptions.EmptyDeleteException; +import AMY.Exceptions.EmptyEventException; +import AMY.Exceptions.EmptyInput; +import AMY.Exceptions.EmptyMarkException; +import AMY.Exceptions.EmptyToDoException; +import AMY.Exceptions.EmptyUnmarkException; +import AMY.Exceptions.EmptyFindException; + +import AMY.command.Deadline; +import AMY.command.Event; +import AMY.command.Todo; + +import java.util.Scanner; + +public class Parser { + + public static final String LINE = "____________________________________________________________"; + + public static void drawLine() { + System.out.println(LINE); + } + + /** + * Throw exceptions when exceptions are encountered to avoid an error + * @param userInput the user input + * @throws EmptyInput when the input is empty, the exception is thrown + * @throws EmptyToDoException when the todo command is empty, the exception is thrown + * @throws EmptyMarkException when the mark is command empty, the exception is thrown + * @throws EmptyUnmarkException when the unmark command is empty, the exception is thrown + * @throws EmptyDeadlineException when the deadline command is empty, the exception is thrown + * @throws EmptyEventException when the event command is empty, the exception is thrown + * @throws EmptyDeleteException when the delete command is empty, the exception is thrown + */ + public static void manageException(String userInput) throws EmptyInput, EmptyToDoException, + EmptyMarkException, EmptyUnmarkException, EmptyDeadlineException, EmptyEventException, + EmptyDeleteException, EmptyFindException { + + Scanner input = new Scanner(userInput); + String command; + if (!input.hasNext()) { + throw new EmptyInput(); + } else { + command = input.next(); + } + if (command.equals("todo") && !input.hasNext()) { + throw new EmptyToDoException(); + } + if (command.equals("mark") && !input.hasNext()) { + throw new EmptyMarkException(); + } + if (command.equals("unmark") && !input.hasNext()) { + throw new EmptyUnmarkException(); + } + if (command.equals("deadline") && !input.hasNext()) { + throw new EmptyDeadlineException(); + } + if (command.equals("event") && !input.hasNext()) { + throw new EmptyEventException(); + } + if (command.equals("delete") && !input.hasNext()) { + throw new EmptyDeleteException(); + } + if (command.equals("find") && !input.hasNext()) { + throw new EmptyFindException(); + } + } + + /** + * Manage input when input is given to follow order + */ + public static void manageInput() { + Scanner scanner = new Scanner(System.in); + while (true) { + String userInput = scanner.nextLine(); // Read user input + System.out.println(LINE); + try { + manageException(userInput); + if (userInput.equals("bye")) { + break; // Exit the loop if the user enters "bye" + } else if (userInput.equals("list")) { + TaskList.listTasks(); // list tasks + } else if (userInput.startsWith("mark")) { + // Extract the task index from the user input + int taskIndex = Integer.parseInt(userInput.substring(5).trim()); + TaskList.markTaskAsDone(taskIndex); + } else if (userInput.startsWith("unmark")) { + // Extract the task index from the user input + int taskIndex = Integer.parseInt(userInput.substring(7).trim()); + TaskList.unmarkTask(taskIndex); + } else if (userInput.startsWith("todo")) { + String description = userInput.substring(5).trim(); + Todo todo = new Todo(description); + TaskList.addToList(todo); + } else if (userInput.startsWith("deadline")) { + int inputIndex = userInput.indexOf(" /by "); + String description = userInput.substring("deadline ".length(), inputIndex); + String by = userInput.substring(inputIndex + " /by ".length()); + TaskList.addToList(new Deadline(description, by)); + } else if (userInput.startsWith("event")) { + String[] parts = userInput.substring(5).split("/from"); + String description = parts[0].trim(); + String[] dateTimeParts = parts[1].split("/to"); + String from = dateTimeParts[0].trim(); + String to = dateTimeParts[1].trim(); + Event event = new Event(description, from, to); + TaskList.addToList(event); + } else if (userInput.startsWith("delete")) { + int taskIndex = Integer.parseInt(userInput.substring(7).trim()); + TaskList.deleteTask(taskIndex); + } else if (userInput.startsWith("find")) { + String keyword = userInput.substring(5).trim(); + TaskList.findTasks(keyword); + }else { + System.out.println("Invalid command. Please try again."); + } + } catch (EmptyInput exception) { + System.out.println("☹ OOPS!!! The description cannot be empty."); + } catch (EmptyToDoException exception) { + System.out.println("☹ OOPS!!! The description of a todo cannot be empty."); + } catch (EmptyMarkException exception) { + System.out.println("☹ OOPS!!! The description of a mark cannot be empty."); + } catch (EmptyUnmarkException exception) { + System.out.println("☹ OOPS!!! The description of an unmark cannot be empty."); + } catch (EmptyDeadlineException exception) { + System.out.println("☹ OOPS!!! The description of a deadline cannot be empty."); + } catch (EmptyEventException exception) { + System.out.println("☹ OOPS!!! The description of an event cannot be empty."); + } catch (EmptyDeleteException exception) { + System.out.println("☹ OOPS!!! The description of an delete cannot be empty."); + } catch (EmptyFindException exception){ + System.out.println("☹ OOPS!!! The description of an find cannot be empty."); + } + drawLine(); + } + } +} diff --git a/src/main/java/AMY/Storage.java b/src/main/java/AMY/Storage.java new file mode 100644 index 000000000..a1ce87fc1 --- /dev/null +++ b/src/main/java/AMY/Storage.java @@ -0,0 +1,65 @@ +package AMY; + +import AMY.command.Task; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.FileNotFoundException; + +import java.util.Scanner; + +public class Storage { + public static String FILE_PATH = "./data/AMY.txt"; + public static int totalTasks; + public Storage(String filePath) { + FILE_PATH = filePath; + } + + /** + * Load tasks from a file to show saved tasks when the program starts + * Take no parameter + * Load from given file path + */ + public static void loadTasksFromFile() { + try { + File file = new File(FILE_PATH); + if (!file.exists()) { + file.getParentFile().mkdirs(); // Create parent directories if they don't exist + file.createNewFile(); + } + Scanner scanner = new Scanner(file); + int numberOfTasks = 0; // Initialize numberOfTasks + while (scanner.hasNextLine()) { + String taskData = scanner.nextLine(); + Task task = Task.parseFromFileString(taskData); + if (task != null) { + TaskList.addToList(task); + numberOfTasks++; // Increment numberOfTasks + } + } + scanner.close(); + totalTasks = numberOfTasks; // Update numberOfTasks in AMY class + } catch (FileNotFoundException e) { + System.out.println("Data file not found. Creating a new file."); + } catch (IOException e) { + System.out.println("☹ OOPS!!! Error loading tasks from the file."); + } + } + + /** + * Save tasks to a file to load later when command is entered + * Save the tasks to the given file path + */ + public static void saveTasksToFile() { + try { + FileWriter writer = new FileWriter(FILE_PATH); + for (Task task : TaskList.taskList) { + writer.write(task.toFileString() + "\n"); + } + writer.close(); + } catch (IOException e) { + System.out.println("☹ OOPS!!! Error saving tasks to the file."); + } + } +} \ No newline at end of file diff --git a/src/main/java/AMY/TaskList.java b/src/main/java/AMY/TaskList.java new file mode 100644 index 000000000..263f6b0c1 --- /dev/null +++ b/src/main/java/AMY/TaskList.java @@ -0,0 +1,92 @@ +package AMY; + +import AMY.command.Task; + +import java.util.ArrayList; + +public class TaskList { + public static ArrayList taskList = new ArrayList<>(); + + /** + * Add a task to the list when an todo command is entered + */ + public static void addToList(Task task) { + taskList.add(task); + System.out.println("Got it. I've added this task: "); + System.out.println(" " + task); + System.out.println("Now you have " + taskList.size() + " tasks in the list"); + } + + /** + * List all tasks when "list" command is entered + */ + public static void listTasks() { + 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)); + } + } + + /** + * Delete a task from the list when "delete" command is entered + * @param taskIndex the position of task to be deleted + */ + public static void deleteTask(int taskIndex) { + if (taskIndex >= 1 && taskIndex <= taskList.size()) { + Task removedTask = taskList.remove(taskIndex - 1); + System.out.println("Noted. I've removed this task:"); + System.out.println(" " + removedTask); + System.out.println("Now you have " + taskList.size() + " tasks in the list."); + } else { + System.out.println("Invalid task index. Please try again."); + } + } + + /** + * Unmark task as done when "unmark" command is entered + * @param taskIndex the position of task to be unmarked + */ + public static void unmarkTask(int taskIndex) { + if (taskIndex >= 1 && taskIndex <= taskList.size()) { + Task task = taskList.get(taskIndex - 1); + task.markAsNotDone(); + System.out.println("OK, I've marked this task as not done yet:"); + System.out.println(" " + task); + } else { + System.out.println("Invalid task index. Please try again."); + } + } + + /** + * Mark task as done when "mark" command is entered + * @param taskIndex the position of task to be marked + */ + public static void markTaskAsDone(int taskIndex) { + if (taskIndex >= 1 && taskIndex <= taskList.size()) { + Task task = taskList.get(taskIndex - 1); + task.markAsDone(); + System.out.println("Nice! I've marked this task as done:"); + System.out.println(" " + task); + } else { + System.out.println("Invalid task index. Please try again."); + } + } + + /** + * Find tasks containing a specific keyword + * @param keyword the keyword to locate + */ + public static void findTasks(String keyword) { + System.out.println("Here are the matching tasks in your list:"); + int count = 0; + for (Task task : taskList) { + if (task.getDescription().contains(keyword)) { + System.out.println((++count) + ". " + task); + } + } + + if (count == 0) { + System.out.println("No matching tasks found."); + } + } +} diff --git a/src/main/java/AMY/Ui.java b/src/main/java/AMY/Ui.java new file mode 100644 index 000000000..d9a5739b4 --- /dev/null +++ b/src/main/java/AMY/Ui.java @@ -0,0 +1,39 @@ +package AMY; + +import java.util.Scanner; + +public class Ui { + public static final String BOT_NAME = "AMY"; + public static final String LINE = "____________________________________________________________"; + + // Draw a line to separate commands after given inputs + public static void drawLine() { + System.out.println(LINE); + } + + /** + * Display the bot name at the top + * Write a welcome message to welcome user when the program starts + */ + public static void welcomeMessage() { + drawLine(); + String logo = " \n" + + " /\\ |\\ /| \\ / \n" + + " / \\ | \\/ | \\ / \n" + + " /----\\ | | | \n" + + " / \\ | | | \n"; + System.out.println("Hello from\n" + logo); + + drawLine(); + System.out.println("Hello! I'm " + BOT_NAME); + System.out.println("What can I do for you?"); + drawLine(); + } + + // Write a bye message for the user when the program ends + public static void byMessage() { + System.out.println("Bye. Hope to see you again soon!"); + drawLine(); + } + +} diff --git a/src/main/java/AMY/command/Deadline.java b/src/main/java/AMY/command/Deadline.java new file mode 100644 index 000000000..9a4798075 --- /dev/null +++ b/src/main/java/AMY/command/Deadline.java @@ -0,0 +1,17 @@ +package AMY.command; + +public class Deadline extends Task { + + protected String by; + + public Deadline(String description, String by) { + super(description); + this.by = by; + } + + @Override + public String toString() { + return "[D]" + super.toString() + " (by: " + by + ")"; + } +} + diff --git a/src/main/java/AMY/command/Event.java b/src/main/java/AMY/command/Event.java new file mode 100644 index 000000000..7b59b7c2b --- /dev/null +++ b/src/main/java/AMY/command/Event.java @@ -0,0 +1,17 @@ +package AMY.command; + +public class Event extends Task { + protected String from; + protected String to; + + public Event(String description, String from, String to) { + super(description); + this.from = from; + this.to = to; + } + + @Override + public String toString() { + return "[E]" + super.toString() + " (from: " + from + ", to: " + to + ")"; + } +} diff --git a/src/main/java/AMY/command/Task.java b/src/main/java/AMY/command/Task.java new file mode 100644 index 000000000..106e2648a --- /dev/null +++ b/src/main/java/AMY/command/Task.java @@ -0,0 +1,76 @@ +package AMY.command; + +public class Task { + protected String description; + protected boolean isDone; + + public Task(String description) { + this.description = description; + this.isDone = false; + } + + public void markAsDone() { + isDone = true; + } + + public void markAsNotDone() { + isDone = false; + } + + // mark done task with X + public String getStatusIcon() { + return (isDone ? "X" : " "); + } + + public String getDescription() { + return description; + } + + // Convert the task to a file-readable string format + public String toFileString() { + String status = isDone ? "1" : "0"; + return String.format("%s | %s | %s", getTaskType(), status, description); + } + + // Parse a task from a file string + public static Task parseFromFileString(String fileString) { + String[] parts = fileString.split("\\|"); + String taskType = parts[0].trim(); + String status = parts[1].trim(); + String description = parts[2].trim(); + + Task task; + + if (taskType.equals("T")) { + task = new Todo(description); + } else if (taskType.equals("D")) { + String by = parts[3].trim(); + task = new Deadline(description, by); + } else if (taskType.equals("E")) { + String fromTo = parts[3].trim(); + String[] dateTimeParts = fromTo.split("\\|"); + String from = dateTimeParts[0].trim(); + String to = dateTimeParts[1].trim(); + task = new Event(description, from, to); + } else { + task = null; + } + if (task != null) { + if (status.equals("1")) { + task.markAsDone(); + } + } + return task; + } + + protected String getTaskType() { + return "T"; + } + + @Override + public String toString() { + return "[" + this.getStatusIcon() + "] " + description; + } +} + + diff --git a/src/main/java/AMY/command/Todo.java b/src/main/java/AMY/command/Todo.java new file mode 100644 index 000000000..5ba1dd07f --- /dev/null +++ b/src/main/java/AMY/command/Todo.java @@ -0,0 +1,12 @@ +package AMY.command; + +public class Todo extends Task { + public Todo(String description) { + super(description); + } + + @Override + public String toString() { + return "[T]" + super.toString(); + } +} 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..f2985ff8c --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: AMY.AMY +