diff --git a/.gitignore b/.gitignore index 2873e189e1..58b16a3a79 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ bin/ /text-ui-test/ACTUAL.TXT text-ui-test/EXPECTED-UNIX.TXT +data/savedata.txt diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..b2d2c29e06 --- /dev/null +++ b/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: seedu.duck.Duck + diff --git a/build.gradle b/build.gradle index d5e548e85f..f5ef89863f 100644 --- a/build.gradle +++ b/build.gradle @@ -43,4 +43,5 @@ checkstyle { run{ standardInput = System.in + enableAssertions = true } diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953ea..7b551d5bb3 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -2,8 +2,8 @@ Display | Name | Github Profile | Portfolio --------|:----:|:--------------:|:---------: -![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +![](https://via.placeholder.com/100.png?text=Photo) | Thant Aung Htet Nyan | [Github](https://github.com/thant) | [Portfolio](team/thant.md) +![](https://via.placeholder.com/100.png?text=Photo) | Jeremiah Ong | [Github](https://github.com/miahjerry) | [Portfolio](team/miahjerry.md) +![](https://via.placeholder.com/100.png?text=Photo) | Yan Zaiya | [Github](https://github.com/skyanzy) | [Portfolio](team/skyanzy.md) +![](https://via.placeholder.com/100.png?text=Photo) | Gau Kiat Lok Jerald | [Github](https://github.com/jeraldgau) | [Portfolio](team/jeraldgau.md) +![](https://via.placeholder.com/100.png?text=Photo) | Liang Ting-Yu | [Github](https://github.com/michelleliang0116) | [Portfolio](team/michelleliang0116.md) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 64e1f0ed2b..670dc494bb 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -2,37 +2,275 @@ ## Acknowledgements -{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +Reused iP to serve as base code from [jeraldgau/ip](https://github.com/jeraldgau/ip) + +## Architecture + +![image](https://user-images.githubusercontent.com/1620654/230882687-7c5f75d1-9bba-4e3b-8aed-4dd432f165ea.png) + +The **Architecture Diagram** given above explains the high-level design of Duck. + +Given below is a quick overview of main components and how they interact with each other. + +
+ +**Main components of the architecture** + +```Duck``` is responsible for: + +**At app launch:**  + +- Executes the ```Ui#greetingMessage()``` operation. + +- Initializes a new ArrayList tasks  + +- Populates the ArrayList by executing the ```Storage#tryLoad()``` operation. + +- Begins scanning for user inputs, and passing these inputs to ```Parser``` class. + +- At shut down: Shuts down the components and invokes the ```Ui#exitMessage()``` operation. + +```Commons``` represents a collection of classes used by multiple other components, such as ```java.time.LocalDateTime``` + +The rest of the App consists of four components. + +- ```UI```: The UI of the App. + +- ```Parser```: The command executor. + +- ```Storage```: Reads data from, and writes data to, the hard disk. + +- ```TaskList```: Holds the data of Duck in memory. + +```TaskList``` stores items that belong to 4 subclasses that draw upon a shared superclass. + +```Task```: The skeleton superclass of all Tasks.  + +- ```ToDo```: Basic form of ```Task``` + +- ```Deadline```: Expands upon ```Task``` by storing a ```by``` variable + +- ```Event```: Expands upon ```Task``` by storing ```start``` and ```end``` variable + +- ```SchoolClass```: Expands upon ```Task``` by storing ```start``` and ```end``` variable, along with a ```className``` variable ## Design & implementation -{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.} +This section describes how Duck is implemented and how its commands are executed. + +### Purge feature + +**Implementation** + +The ```TaskList#purge()``` command will be implemented to facilitate the removal of expired ```Task``` items from the ```TaskList```. This will be executed once upon Duck's startup, and can be further executed by inputting the term ```purge``` into the CLI when Duck is running. The following is the new operation to be implemented. + +- ```TaskList#purge()``` - Navigates through the ```TaskList``` array, and checks the date/time of each Task item within. If the date/time of a given ```Task``` has passed, it will be removed from the ```TaskList``` array. The user will be then able to view a total of how many ```Tasks``` were purged, along with what ```Task``` they were. + +
+ +**Given below is an example usage scenario for ```purge()```** + +**Step 1.** The user launches the application for the first time. The ```TaskList``` will be initialized with the data from a given pre-existing datafile if it exists, and the ```Task``` items will be inserted into the ```TaskList```.![image](https://user-images.githubusercontent.com/1620654/230883139-f664bde2-6c65-45f5-b060-06ec9708c620.png) + + +**Step 2.** The ```TaskList#purge()``` command will be executed. The process is illustrated through two steps: + +**Identifying all expired ```Task``` items in ```TaskList```.** + +![image](https://user-images.githubusercontent.com/1620654/230883393-ef0d4e33-96c9-4e32-a6f6-f30121b953dd.png) + +**Removing all expired ```Task``` items in ```TaskList```.** +![image](https://user-images.githubusercontent.com/1620654/230883437-d1c3d47b-ef8b-451a-804e-cc2ce5a74e1b.png) + +The user will see this on his terminal after the ```purge``` command has executed. + +``` +---------------------------------------------------------------------- +Quack! Tasks have expired! Purge proceeding... +2. [X if , " " if !] +4. [X if , " " if !] + +Purge Completed! Now we are one quack closer to finishing all tasks! +---------------------------------------------------------------------- +``` + + +**Step 3.** The ```Duck``` will continue to run as per usual. The user can choose to manually input the keyword purge into the CLI to repeat Step 2 above at any given time. + +
+ +### Clear feature + +**Implementation** + +The ```Storage#clear()``` command was implemented to facilitate the removal of all ```Task``` items from the ```TaskList```. Upon the entry of the keyword ```clear```, the new operation ```Ui#doubleCheck``` will prompt the user for an input of ```Y/N```. + +- If a ```Y``` is inputted, the operation ```Storage#clear()``` executes and removes all ```Task``` items from the ```TaskList```. Additionally, the ```savedata``` file located in ```\data\savedata.txt``` will also be deleted and recreated as a blank slate. + +- If a ```N``` is inputted, the operation ```Storage#clear()``` executes and removes all ```Task``` items from the ```TaskList```. Additionally, the ```savedata``` file located in ```\data\savedata.txt``` will also be deleted and recreated as a blank slate. +
+ The following are the new operations implemented. + +- ```Storage#clear()``` - Removes all ```Task``` items from the ```TaskList```. Additionally, the ```savedata``` file located in ```\data\savedata.txt``` will also be deleted and recreated as a blank slate. + +- ```Ui#doubleCheck``` - Prompts the user for further input. If a ```Y``` is inputted, the ```Storage#clear()``` operation is executed. Else if a ```N``` is inputted, the operation is cancelled. + +
+ +Given below is an example usage scenario for ```Storage#clear()``` + +**Step 1.** The user launches the application for the first time. The ```TaskList``` will be initialised with the data from a given pre-existing datafile if it exists, and the ```Task``` items will be inserted into the ```TaskList```.![image](https://user-images.githubusercontent.com/1620654/230883139-f664bde2-6c65-45f5-b060-06ec9708c620.png) + +**Step 2.** The user wants to start afresh. The ```Storage#clear()```  command is then executed. The user will see an output in the following format: + +``` +THIS IS AN IRREVERSIBLE PROCESS. ARE YOU SURE? Y/N +``` + +**Step 3.1.** The user decides against clearing the ```TaskList```, and inputs a ```N```. The ```TaskList``` has no changes. ```Duck``` resumes after the output in the following format has been displayed: + +``` + +Process cancelled. + +``` + +**Step 3.2.** The user decides upon clearing the ```TaskList```, and inputs a ```Y```. The ```TaskList``` has been cleared. + +![image](https://user-images.githubusercontent.com/1620654/230883611-5f7c49bd-9e8a-405e-8c96-e55241fa9cca.png) + +```Duck``` resumes after the output in the following format has been displayed: + +``` + +Got it, all tasks have been cleared. + + ``` + +### SchoolClass `list_classes` feature + +**Implementation** + +The ```SchoolClass``` Class is implemented to facilitate the adding of students' classes to a separate schedule, which is a priority queue. It extends the ```Task``` Class with additional String attributes to store the class name, day of week, start time, and end time. It also overrides the ```toString()``` method to have its own specialised output when being printed, as well as overriding the ```toSaveString()``` method to correctly save its details to the save file. The ```SchoolClass``` Class will also facilitate the implementation of automatic time tracking of whether the class is over for the week, as well as sorting the SchoolClasses based on their day of week and start/end time. These two features are part of the ```list_classes``` feature, which lists out all the SchoolClasses stored in Duck. + +The following are the new operations implemented. + +- ```TaskList#addSchoolClass()``` - Adds a SchoolClass to the schedule. + +- ```TaskList#deleteClass()``` - Removes a SchoolClass from the schedule. + +- ```Ui#listClasses()``` - Lists out all currently stored SchoolClasses according to day of week, start and end time in ascending order. + +- ```Storage#loadSchoolClass()``` - Adds a SchoolClass to the task list without generating a successfully added message, to be used when loading from the save data. + +
+ +**Given below is an example usage scenario for TaskList#addSchoolClass().** + +The user inputs a command following the proper formatting for adding a ```SchoolClass```. The sequence diagram for adding a SchoolClass is shown below + +![image](https://user-images.githubusercontent.com/88079008/230120692-d4771d07-b228-400e-9968-aed978a323e6.png) + +
+ +**Given below is an example usage scenario for Ui#listClasses.** + +The user inputs the command for ```list_classes```. The program then lists out all the SchoolClasses stored in Duck according to chronological order. The sequence diagram for Ui#listClasses is shown below. + +![image](https://user-images.githubusercontent.com/88079008/230112915-fd04ca83-4d30-47c0-a3a4-c7a3c82e0b17.png) + +
+ +### Recurring deadlines and events feature + +**Implementation** + +The `RecurringDeadline` and `RecurringEvent` classes are implemented to facilitate the adding of deadlines and events that happen every week to the `TaskList`. They extend `Deadline` and `Event` respectively with an additional `DayOfWeek` attribute `day` to indicate which day of the week the task recurs on. They both override the `toString()` and `toSaveString()` methods to return the correct `String` representation for output to the user and storage. `RecurringDeadline` and `RecurringEvent` also have a public method `getDay()` to return the private attribute `day`. + +**The following new operations are implemented. ** + +- `TaskList#addRecurrDeadline()` -- adds a `RecurringDeadline` object to the task list + +- `Storage#loadRecurrDeadline()` -- loads a `RecurringDeadline` from the saved data file to the task list + +- `TaskList#addRecurrEvent()` -- adds a `RecurringEvent` object to the task list + +- `Storage#loadRecurrEvent()` -- loads a `RecurringEvent` from the saved data file to the task list + +Below is a class diagram for the implementation + + image + + +
+ ## Product scope ### Target user profile -{Describe the target user profile} +Duck is a program developed for students with multiple tasks such as deadlines and events, as well as many different classes who prefer CLI applications. ### Value proposition -{Describe the value proposition: what problem does it solve?} +Duck will be able to allow students who can type fast to quickly record down the tasks that they need to do, as well as helping them keep track of their different classes in an organised manner. By inputting short commands, students will be able to retrieve information on their upcoming tasks and classes and any related notes easily. ## User Stories |Version| As a ... | I want to ... | So that I can ...| |--------|----------|---------------|------------------| -|v1.0|new user|see usage instructions|refer to them when I forget how to use the application| -|v2.0|user|find a to-do item by name|locate a to-do without having to go through the entire list| +|v1.0|user|have a help command that will display all possible commands and their appropriate input format|understand how to use Duck easily and conveniently| +|v1.0|student|be able to add tasks and events|remind myself constantly| +|v1.0|user|have a command to close the program|exit the program when I want to| +|v1.0|user|have a find filter|find the tasks in the list that contain a specified input keyword| +|v1.0|user|add a priority tag to each task such that they will be easily identified|be aware of urgent tasks to completed| +|v1.0|user|clear function with double check confirmation|delete all tasks from the list, with a confirmation message before clearing| +|v1.0|user|have priority variables for tasks|add priority levels to individual tasks| +|v1.0|student|be able to add school classes|keep track of my classes| +|v2.0|student|receive notifications about deadlines|not miss out on any tasks| +|v2.0|student with different classes|view my next class with its specified time|| +|v2.0|student with multiple classes|record recurring classes at fixed times throughout the week|view my weekly class schedule without needing to record them down again each week| +|v2.0|student|create recurring tasks that will reset their 'Done' status when appropriate|not have to manually unmark these tasks| +|v2.0|student|easily access my schedule|plan my daily life| +|v2.0|user|automatically remove my expired tasks|not deal with the hassle of having to manually remove expired tasks| +|v2.0|user|add notes to my tasks|record down additional details| +|v2.0|user|edit my tasks|change previously recorded information| +|v2.0|student|delete my classes|remove classes that are no longer necessary| ## Non-Functional Requirements -{Give non-functional requirements} +1. Duck should be able to run on Macos, Windows and Linux with at least Java 11 installed. +2. Recorded tasks and classes should not be lost when the program or the system is shut down. +3. Users who are proficient in typing should be able to accomplish the different tasks faster than a typical user trying to accomplish the same tasks with a GUI instead. ## Glossary -* *glossary item* - Definition +* *Duck* - The name of the program +* *Task* - A task that can be recorded in Duck (Todo, Deadline, Event) +* *Command* - Lines of input that when typed into the terminal, will execute appropriate functions as indicated in the User Guide ## Instructions for manual testing {Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing} + +### Launching the program +1. Ensure that Java 11 or above is installed +2. Download the latest version of Duck +3. Launch the terminal and navigate to the folder where Duck is located in +4. Type in the command ```java -jar [CS2113-T11-1][DUCK].jar``` to lauch Duck + - Expected Outcome: Duck launches in the terminal with its start message displayed. + - If the file was sourced from our Github Releases, type in the command ```java -jar CS2113-T11-1.DUCK.jar``` to lauch Duck instead! + +### Closing the program +1. Type ```bye``` in the terminal to exit the program + - Expected Outcome: The exit message is displayed in the terminal, and the program closes. + +### Storage and Save Data +- If Duck is run for the first time, the save file will be generated automatically along with the ```data``` folder + - Expected Outcome: The ```data``` folder will be created, and the ```savedata.txt``` file will be created inside this folder + +- After any changes are made to the list of tasks or schedule, the changes will be stored in to the save file located in ```data/savedata.txt``` + - Expected Outcome: Any newly added tasks or school classes or other changes made will be reflected in the ```savedata.txt``` file + +- If any edits or changes are made directly to the ```savedata.txt``` file (which users are strongly recommended not to do so) which leads to the save file being invalid, the ```savedata.txt``` file will have all its data wiped upon starting up Duck. + - Expected Outcome: An error message will be displayed in the terminal when Duck is lauched, with none of the previously saved tasks or classes being loaded. The ```savedata.txt``` file will have all of its contents erased. diff --git a/docs/README.md b/docs/README.md index bbcc99c1e7..544e12fd12 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ -# Duke +# Duck -{Give product intro here} +Duck is a desktop task and class tracker for users to keep track of their schedule and the things they have to do. The user interacts with it using the CLI. It is written in Java, and has about 3.5kLoc. Useful links: * [User Guide](UserGuide.md) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index abd9fbe891..85a416b26c 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -1,42 +1,637 @@ -# User Guide +# Duck User Guide(v 2.0) -## Introduction +Duck is a **desktop app for managing tasks and deadlines, as well as a school class scheduler. It is optimised for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). Duck will have a number of quality-of-life features for busy students seeking a simple solution to task management. These include automated task removal upon expiration, or limiting the number of tasks displayed to within a date range. Duck also has recurring task functionality to automate routine tasks so users do not have to manually re-add expired tasks. -{Give a product intro} -## Quick Start -{Give steps to get started quickly} +* [**Features**](#features) + * [Viewing help :](#viewing-help--help) `help` + * [Listing all tasks :](#listing-all-tasks--list) `list` + * [Listing all tasks and classes happening today :](#listing-all-tasks--list) `list_today` + * [Listing all tasks up to X days into the future :](#listing-all-tasks-up-to-x-days-in-the-future--list-x) `list X` + * [Displaying class schedule :](#displaying-class-schedule--list_classes) `list_classes` + * [Displaying upcoming class :](#displaying-upcoming-class--upcoming_class) `upcoming_class` + * [Displaying upcoming deadline :](#displaying-upcoming-deadline--upcoming_deadline) `upcoming_deadline` + * [Displaying upcoming event :](#displaying-upcoming-event--upcoming_event) `upcoming_event` + * [Mark a specified task as done :](#marking-a-task--mark-task_number) `mark ` + * [Unmark a specified task as not done :](#unmarking-a-task--unmark-task_number) `unmark ` + * [Edit a specific piece of information for a given task :](#editing-a-task--edit-task_number) `edit ` + * [Deleting a task :](#deleting-a-task--delete) `delete ` + * [Deleting a school class :](#deleting-a-school-class--remove-class) `remove /class /description /day /from /to ` + * [Designate a priority to a given task :](#designate-a-task-priority--priority-task_number-priority) `priority ` + * [Adding notes for a specific task :](#adding-notes-for-a-specific-task--add_notes) `add_notes ` + * [Deleting notes for a specific task :](#deleting-notes-for-a-specific-task--delete_notes) `delete_notes ` + * [Editing notes for a specific task :](#editing-notes-for-a-specific-task--edit_notes) `edit_notes ` + * [Viewing notes for a specific task :](#printing-notes-for-a-specific-task--view_notes) `view_notes ` + * [List tasks of low/medium/high priority :](#listing-all-low-priority-tasks--low_priority) `low_priority`/`medium_priority`/`high_priority` + * [List tasks in priority order:](#listing-all-tasks-arranged-by-priority--priority_list) `priority_list` + * [Purge expired tasks :](#purge-expired-tasks--purge) `purge` + * [Clearing all tasks (including datafile) :](#clearing-tasks-from-storage-clear) `clear` + * [Find tasks matching a given keyword :](#finding-tasks-from-storage-that-match-a-keyword-find-keyword) `find ` + * [Add tasks that can be broken down into the following 6 types:](#adding-a-todo-task--todo-description) -1. Ensure that you have Java 11 or above installed. -1. Down the latest version of `Duke` from [here](http://link.to/duke). + * Add ToDo: /todo + * Add Deadline: /by + * Add RecurringDeadline: /re /by /day + * Add Event: /from /to + * Add RecurringEvent: /re /from /to /day + * Add Class: /class /day /from /to + * [Motivational quotes :](#printing-a-motivational-quote--motivation) `motivation` + * [Terminate the program :](#exiting-the-program--bye) `bye` -## Features -{Give detailed description of each feature} +# Features -### Adding a todo: `todo` -Adds a new item to the list of todo items. -Format: `todo n/TODO_NAME d/DEADLINE` +## **Viewing help : `help`** -* The `DEADLINE` can be in a natural language format. -* The `TODO_NAME` cannot contain punctuation. +Displays all available commands and their input format on the terminal. -Example of usage: +**Input:** `help` -`todo n/Write the rest of the User Guide d/next week` +**Output:** -`todo n/Refactor the User Guide to remove passive voice d/13/04/2020` -## FAQ +``` +Here are the commands you can give me: +COMMAND_FORMAT : COMMAND_FUNCTIONALITY EXPLANATION +. +. +. +How else may I assist you today, human? +``` -**Q**: How do I transfer my data to another computer? -**A**: {your answer here} -## Command Summary +## **Listing all tasks : `list`** -{Give a 'cheat sheet' of commands here} +Displays all tasks currently stored in the application. -* Add todo `todo n/TODO_NAME d/DEADLINE` +**Input:** `list` + +**Output: Demonstrated with 1 of each type currently in the stored list of tasks** + + +``` +Here are the tasks in your list: + 1. [T][ ] todo (Low priority.) + 2. [D][ ] deadline (by: 2023-03-25 2359) (Low priority.) + 3. [E][ ] event (from: 2023-03-25 1200 to: 2023-03-26 2359) (Low priority.) +``` + +## **Listing all tasks and classes happening today : `list_today`** + +Displays all deadlines, events and classes happening today. + +**Input:** `list_today` + +**Output: Demonstrated with 1 of each type currently in the stored data array** + + +``` + ____________________________________________________________ + Here is your class schedule for today + [WEDNESDAY][X] cs2113 (from: 1200 to: 1300) + + Here are your tasks today + [D][ ] sleep (by: 2023-03-29 2000) (Low priority.) + [D][ ] shower (by: 2000) (every WEDNESDAY) (Low priority.) + [E][ ] study (from: 2023-03-29 2100 to: 2023-04-23 2300) (Low priority.) + [E][ ] study (from: 0800 to: 2300) (every WEDNESDAY) (Low priority.) + ____________________________________________________________ +``` + +## **Displaying class schedule : `list_classes`** + +Displays all School Classes currently stored in the application. Classes will be automatically sorted according to chronological order. Classes will also automatically be marked as done (represented by a cross) if the current time is past the ending time of the class, and their 'done' status will be reset at the start of each week. + +**Input:** `list_classes` + +**Output: Demonstrated with classes with different names and start/end times, with some already past their end time** + + +``` +Here is your class schedule: + + [MONDAY][X] eg2501 (from: 1600 to: 1800) + [TUESDAY][X] ee2026 (from: 0900 to: 1200) + [TUESDAY][ ] cs2113 (from: 1600 to: 1700) + +``` +## **Displaying upcoming class : `upcoming_class`** + +Displays the next upcoming class stored in the application. Classes will be automatically sorted according to chronological order. Classes will also automatically be marked as done (represented by a cross) if the current time is past the ending time of the class, and their 'done' status will be reset at the start of each week. + +**Input:** `upcoming_class` + +**Output: The next upcoming class with names and start/end times** + + +``` +Here is your next upcoming class: + [TUESDAY][ ] cs2113 (from: 1600 to: 1700) + +``` +## **Displaying upcoming deadline : `upcoming_deadline`** + +Displays the next upcoming deadline stored in the application. + +**Input:** `upcoming_deadline` + +**Output: The next upcoming deadline with names and due time** + + +``` +Here are your next upcoming event: + [D][ ] Deadlines Eat bread (by: 2023-04-05 2015) (Medium priority.) + +``` + +## **Displaying upcoming event : `upcoming_event`** + +Displays the next upcoming event stored in the application. + +**Input:** `upcoming_event` + +**Output: The next upcoming event with names and start/end time** + + +``` +Here are your next upcoming event: + [E][ ] Meeting (from: 2023-04-15 2015 to: 2023-04-15 2215) (Low priority.) + +``` + +## **Listing all low priority tasks : `low_priority`** +Displays all tasks that have been assigned low priority +If there are no tasks in the low priority list, another message will be shown + +**Input:** `low_priority` +**Output: Demonstrated with 1 of each data type currently in the stored data array** +``` +Quack! + You have 3 tasks that are low in priority! + 1. [T][ ] Water plants (Low priority.) + 2. [D][ ] Submit CS2113 quiz (by: 2023-03-31 2359) (Low priority.) + 3. [E][ ] CS2113 lecture (from: 2023-03-30 1100 to: 2023-03-30 1200) (Low priority.) +``` +**If no tasks in low priority list:** +``` +There are no tasks that are low in priority! + ____________________________________________________________ +``` + +## **Listing all medium priority tasks : `medium_priority`** +Displays all tasks that have been assigned medium priority +If there are no tasks in the medium priority list, another message will be shown + +**Input:** `medium_priority` +**Output: Demonstrated with 1 of each data type currently in the stored data array** +``` +QUACK QUACK!! + You have 3 tasks that are medium in priority! + 1. [T][ ] Water plants (Medium priority.) + 2. [D][ ] Submit CS2113 quiz (by: 2023-03-31 2359) (Medium priority.) + 3. [E][ ] CS2113 lecture (from: 2023-03-30 1100 to: 2023-03-30 1200) (Medium priority.) + ____________________________________________________________ +``` +**If no tasks in medium priority list:** +``` +There are no tasks that are medium in priority! + ____________________________________________________________ +``` + +## **Listing all high priority tasks : `high_priority`** +Displays all tasks that have been assigned high priority +If there are no tasks in the high priority list, another message will be shown + +**Input:** `high_priority` +**Output: Demonstrated with 1 of each data type currently in the stored data array** +``` +QUACK QUACK QUACK!!! + You have 3 tasks that are high in priority! + 1. [T][ ] Water plants (High priority.) + 2. [D][ ] Submit CS2113 quiz (by: 2023-03-31 2359) (High priority.) + 3. [E][ ] CS2113 lecture (from: 2023-03-30 1100 to: 2023-03-30 1200) (High priority.) + ____________________________________________________________ +``` +**If no tasks in high priority list:** +``` +There are no tasks that are high in priority! + ____________________________________________________________ +``` + +## **Listing all tasks arranged by priority : `priority_list`** +Displays all tasks arranged by priority + +**Input:** `priority_list` +**Output: Demonstrated with 1 task in each priority list** +``` +____________________________________________________________ + Here are the tasks in your list arranged by priority: + ____________________________________________________________ + QUACK QUACK QUACK!!! + You have 1 tasks that are high in priority! + 1. [E][ ] CS2113 lecture (from: 2023-03-30 1100 to: 2023-03-30 1200) (High priority.) + ____________________________________________________________ + QUACK QUACK!! + You have 1 tasks that are medium in priority! + 1. [T][ ] Water plants (Medium priority.) + ____________________________________________________________ + Quack! + You have 1 tasks that are low in priority! + 1. [D][ ] Submit CS2113 quiz (by: 2023-03-31 2359) (Low priority.) + ____________________________________________________________ +``` + + +## **Listing all tasks up to X days in the future : `list X`** + +Displays all tasks currently stored in the application, up to X days into the future. + +For instance list 0returns all tasks that are starting within 24 hours. + +**Input:** `list X ` + +**Output: Demonstrated by inputting list 0 on 24/3/2023 12:40, using the same array as above** + + +``` + Here are your tasks in 0 days: + 1. [E][ ] event (from: 2023-03-25 1200 to: 2023-03-26 2359) (Low priority.) + 2. [D][ ] Submit CS2113 quiz (by: 2023-03-31 2359) (Low priority.) +``` + + + +## **Marking a task : `mark `** + +Marks a task from the tasklist as complete. + +**Input: `mark `** + +**Output: Demonstrated with input `mark 1`** + + +``` +Understood. I've marked this task as done: + [T][X] todo (Low priority.) +``` + + + +## **Unmarking a task : `unmark `** + +Unmarks a task from the tasklist as not complete. + +**Input:** `unmark ` + +**Output: Demonstrated with input `unmark 1`** + + +``` + Understood. I've marked this task as not done yet: + [T][ ] todo (Low priority.) +``` + +## **Editing a task : `edit `** + +Edits a specific piece of information of a task. + +**Input:** `edit ` + +**Output: Demonstrated with input `edit 2`** + +``` + ____________________________________________________________ + Please edit one of the following: + For non-recurring deadlines: /description or /deadline + For recurring deadlines: /description or /deadline or /day + Please follow the format: + /description or /deadline or /day + e.g. /deadline 2023-06-30 1200 or /deadline 1200 (for recurring deadlines) +``` +**Following output: Demonstrated with input `/deadline 2023-04-01 2000`** + +``` + ____________________________________________________________ + Quack! + I have changed your task to: + [D][ ] deadline (by: 2023-04-01 2000) (Low priority.) + ____________________________________________________________ +``` + +## **Deleting a task : `delete`** + +Removes a task from the tasklist. + +**Input:** `delete ` + +**Output: Demonstrated with removing the following task** + + +``` +Understood. I have removed this task: + [T][ ] todo (Low priority.) +You now have 3 tasks in your list. +``` + + + +## **Deleting a school class : `remove class`** + +Removes a school class from the schedule. + +**Input:** `remove /class /description /day /from /to ` + +`` can be left empty if the class has no description. + +eg. `remove /class cs2113 /description /day THURSDAY /from 1100 /to 1200` + +**Output: Demonstrated with input `remove class /class CS2113 /description bring laptop /day THURSDAY /from 1100 /to 1200`** + + +``` +Class has been deleted successfully. +``` + + + +## **Designate a task priority : `priority `** + + + +Specifies a priority for a given task, with the following assignments: +* 1 - Low +* 2 - Medium +* 3 - High + +By default, all tasks are low priority. + +**Input:** `priority ` + +**Output: Demonstrated with input `priority 1 1`** + + +``` +Understood. The task's new priority is: + Low priority. +``` + +## **Adding notes for a specific task : `add_notes`** +Adds a note to the specified task + +**Input:** `add_notes ` +**Output: Demonstrated below adding "Bring own recyclable bag"** +``` + What note would you like to add to the following task? + [T][ ] Buy groceries (Low priority.) + ____________________________________________________________ +Bring own recyclable bag + The note has been added! + ____________________________________________________________ +``` + +## **Deleting notes for a specific task : `delete_notes`** +Deletes the specified note for the task + +**Input:** `delete_notes ` +**Output: Demonstrated below to delete 1 task note** + +``` + ____________________________________________________________ + Deleting note: + Bring own recyclable bag + ____________________________________________________________ +``` +## **Editing notes for a specific task : `edit_notes`** +Edits the specified note for a specific task. + +**Input:** `edit_notes ` +**Output: Demonstrated below to edit "Bring recyclable bag" to "Bring tote bag"** +``` + ____________________________________________________________ + What would you like to change the note to? + Bring recyclable bag +Bring tote bag + The specified note has been edited! + ____________________________________________________________ +``` + +## **Printing notes for a specific task : `view_notes`** +Prints the notes for a specific task if they exist. +Otherwise, a message stating that there are no notes for that task will be shown + +**Input:** `view_notes ` +**Output: Demonstrated below for a task with 1 note** +``` + ____________________________________________________________ + Here are the notes for that task quack! + [T][ ] Buy groceries (Low priority.) + 1. Bring own recyclable bag + ____________________________________________________________ +``` +**Output: Demonstrated below for a task with no notes** +``` + ____________________________________________________________ + Here are the notes for that task quack! + [T][ ] Buy groceries (Low priority.) + There are no notes for this task! + ____________________________________________________________ +``` + +## **Purge Expired Tasks : `purge`** + +Prompts the user for confirmation. Proceeds to remove all expired tasks from storage upon confirmation. This operation is automatically executed once upon Duck’s startup. + +**Input:** `purge` + +**Output: Demonstrated by having an expired deadline task in the array** + + +``` + Displaying all expired tasks below... + + [D][ ] deadline (by: 2023-03-24 1100) (Low priority.) +____________________________________________________________ + Quack! A total of 1 task has expired! + Should I remove these tasks from the pending list human? +____________________________________________________________ + THIS IS AN IRREVERSIBLE PROCESS. ARE YOU SURE? Y/N + +``` + + + +* **If the user inputs ‘Y’:** +``` +Expired Tasks have been purged from the list! +I love purging things,human... +``` + + +* **If the user inputs ‘N’:** +``` +Quack! Expired tasks have not been purged. +``` + +## **Clearing tasks from storage: `clear`** + +Prompts the user for confirmation. Proceeds to clear all tasks from storage upon confirmation. + +**Input:** `clear` + +**Output:** + + +``` +THIS IS AN IRREVERSIBLE PROCESS. ARE YOU SURE? Y/N +``` + + + +* **If the user inputs ‘Y’:** `Got it, all tasks have been cleared.` +* **If the user inputs ‘N’:** `Quack! Process cancelled.` + +## **Finding tasks from storage that match a keyword: `find `** + +Traverses through the stored tasks to return all tasks that contain the keyword + +**Input:** `find ` + +**Output: Demonstrated by using the keyword `todo`** + + +``` +Here are the matching tasks in your list: +1. [T][ ] todo (Low priority.) || The index of this item is 1 +``` + + + +## **Adding a ToDo Task : `/todo `** + +Adds a ToDo task to the storage of Duck + +**Input:** `/todo ` + +**Output: Demonstrated by inputting `/todo todo`** + + +``` +Alright, I have added this task: + [T][ ] todo (Low priority.) +You now have 2 tasks in your list. +``` + + + +## **Adding a Deadline Task : ` /by `** + +Adds a Deadline task to the storage of Duck + +**Input:** ` /by ` + +**Output: Demonstrated by inputting `deadline /by 2023-03-25 2359`** + + +``` +Alright, I have added this task: + [D][ ] deadline (by: 2023-03-25 2359) (Low priority.) +You now have 3 tasks in your list. +``` + +## **Adding a RecurringDeadline Task : `/re /by /day `** + +Adds a RecurringDeadline task to the storage of Duck + +**Input:** `/re /by /day ` + +**Output: Demonstrated by inputting `/re new_deadline /by 2359 /day MONDAY`** + + +``` +Alright, I have added this task: + [D][ ] new_deadline (by: 2359) (every MONDAY) (Low priority.) +You now have 4 tasks in your list. +``` + + +## **Adding an Event Task : ` /from /to `** + +Adds an Event task to the storage of Duck + +**Input:** ` /from /to ` + +**Output: Demonstrated by inputting `event /from 2023-03-25 2359 /to 2023-03-26 1100`** + + +``` +Alright, I have added this task: + [E][ ] event (from: 2023-03-25 2359 to: 2023-03-26 1100) (Low priority.) +You now have 5 tasks in your list. +``` + +## **Adding an RecurringEvent Task : `/re /from /to /day `** + +Adds a RecurringEvent task to the storage of Duck + +**Input:** `/re /from /to /day ` + +**Output: Demonstrated by inputting `/re new_event /from 2000 /to 2300 /day MONDAY`** + + +``` +Alright, I have added this task: + [E][ ] new_event (from: 2000 to: 2300) (every MONDAY) (Low priority.) +You now have 6 tasks in your list. +``` + + + +## **Adding a School Class : ` /class /day /from /to `** + +Adds a Class task to the storage of Duck + +**Input:** ` /class /day /from /to ` + +**Output: Demonstrated by inputting `Bring laptop /class CS2113 /day THURSDAY /from 1100 /to 1200`** + + +``` +Alright, I have added this task: + [THURSDAY][ ] CS2113: Bring laptop (from: 1100 to: 1200) +You now have 1 class in your schedule. +``` + +## **Printing a motivational quote : `motivation`** + +Prints a random motivational quote. + +**Input:** `motivation` + +**Output:** + + +``` +Success is not how high you have climbed, but how you make a positive difference to the world. -Roy T. Bennett +____________________________________________________________ +``` + +## **Exiting the program : `bye`** + +Exits the program. + +**Input:** `bye` + +**Output:** + + +``` +Bye. Hope to see you again soon! +``` + + +## **Saving the data :** + +Duck’s data is saved in the hard disk automatically after any command that changes the data. There is no need to save manually. diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000000..13866cfa24 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,3 @@ +remote_theme: pages-themes/cayman@v0.2.0 +plugins: +- jekyll-remote-theme # add this line to the plugins list if you already have one diff --git a/docs/tP Diagram Editable.pptx b/docs/tP Diagram Editable.pptx new file mode 100644 index 0000000000..8ed2f4b2c4 Binary files /dev/null and b/docs/tP Diagram Editable.pptx differ diff --git a/docs/team/jeraldgau.md b/docs/team/jeraldgau.md new file mode 100644 index 0000000000..c60b51dbb7 --- /dev/null +++ b/docs/team/jeraldgau.md @@ -0,0 +1,57 @@ +# Jerald Gau - Project Portfolio Page + +## Overview + +Duck is a desktop task and class tracker for users to keep track of their schedule and the things they have to do. The user interacts with it using the CLI. It is written in Java, and has about 3.5kLoc. + + + +## Summary of Contributions + +### Base Code: Contributed code from iP Duke bot to serve as a base to work upon. +- Justification: Duck is an improved version of the Duke bot, building upon the pre-existing functionality of Duke with enhanced features like date/time tracking, as well as a new school class scheduler. As such, it made sense to reuse code from the iP and add in features from there. + + +### New Feature: Implementation of the ```SchoolClass``` Task +- Makes use of the jave.time package for registering valid start/end timings, as well as java.time.DayOfWeek to register valid days in a week. This facilitates the time tracking feature. +- Revamped how saving and loading from savefile works from the iP base code, in order to account for the new SchoolClass Task. The saving and loading of task priorities was also tweaked in order for it to work with adding SchoolClasses to the savefile. + + +### New Feature: Class schedule with automatic time tracking +- SchoolClasses will be added to a separate list which automatically sorts them based on their registered day of week, and starting/ending time. +- The SchoolClasses will be automatically marked as done when the current time is past their registered ending time, and their done status will be reset to not done at the start of a new week. + + +### New Feature: ```list_classes``` +- Displays all the currently registered SchoolClasses saved in Duck, and lists them out based on their day of week, starting and ending time. +- Shows whether the class is over for the week, with a "done" marker [X] shown on the left of each class. + + +### New Feature: Adding a SchoolClass +- By following the specified format as indicated in the User Guide, users will be able to add SchoolClasses to their schedule which are saved to the savefile. + + +### New Feature: Removing a SchoolClass +- By Following the specified format as indicated in the User Guide, users will be able to remove unwanted SchoolClasses from their schedule. + + +### Code Contributed: [RepoSense Link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=jeraldgau&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + + +### Project Management: +- Managed the releases ```v1.0``` - ```v2.0``` (2 releases) on GitHub + + +### Documentation: +- User Guide: + - Added documentation for the features ```list_classes```, ```remove class``` and ```Adding a School Class``` + +- Developer Guide: + - Added design and implementation details of the SchoolClass feature + - Added the sequence diagram for the ```Adding a School Class``` function + - Added the sequence diagram for the ```list_classes``` function + - Added Product Scope, User Stories, Non-Functional Requirements, Glossary, and Instructions for Manual Testing + + +### Community: +- Reported bugs and suggestions for other teams (examples: [1](https://github.com/jeraldgau/ped/issues/9), [2](https://github.com/jeraldgau/ped/issues/7), [3](https://github.com/jeraldgau/ped/issues/5)) diff --git a/docs/team/miahjerry.md b/docs/team/miahjerry.md new file mode 100644 index 0000000000..718e113ed7 --- /dev/null +++ b/docs/team/miahjerry.md @@ -0,0 +1,56 @@ +# Jeremiah Ong - Project Portfolio Page + +## Overview +Duck is a desktop task and class tracker for users to keep track of their schedule and the things they have to do. The user interacts with it using the CLI. It is written in Java, and has about 3.5kLoc. + +## Summary of Contributions + +### New Feature: Implementation of ```print_priority``` feature + +- Added functions to print all tasks arranged by priority, rather than by the order in which they were added + +### New Feature: Implementation of ```notes``` feature + +- This feature allows users to add additional notes to each task that they will want to take note of +- Notes will also be saved to be viewed again on next start up of Duck + +### New Feature: Adding notes to tasks + +- Allows the user to add a note to the specified task +- By following the steps outlined by the user guide, a note can be added to a task. + +### New Feature: Deleting notes under tasks + +- This feature allows the user to delete notes that have been created for whatever reason +- By following the steps outlined by the user guide, existing notes can be deleted under their respective tasks. + +### New Feature: Editing notes under tasks + +- This feature allows the user to change an existing note under a specified task +- By following the steps outlined by the user guide, existing notes can be edited. + +### New Feature: Implementation of ```motivation``` feature +- Prints a motivational quote for the user +- Also prints on start up to greet the user + +### Cleaning up of code + +- Helped to make the code more defensive to prevent bad inputs from crashing Duck, specifically indexes that accessed out of bounds +- Helped to refactor code to better fulfill coding standard + +### Code Contributed: [RepoSense Link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=miahjerry&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + +### Project Management: + +- Helped to host group meetings and organize them to ensure group was on task and on the same page +- Liased with other groups to carry out mutual stress-testing of each other's product +- Updated team of new merges and PRs whenever necessary + +### Documentation: +- User Guide: + - Added documentation for the features ```print_priority```, ```low_priority```,```medium_priority```, ```high_priority```, ```add_notes```, ```edit_notes```, ```delete_notes```,and ```motivation``` + - Helped to fix bugs with the hyperlink to enable easier navigation of the UG + +### Community: +- Helped to connect to other teams for more feedback on Duck (examples: [1](https://ginger-vicuna-3c7.notion.site/Duck-jar-62da4045517945f880b94392f6c0478e),[2](https://docs.google.com/document/d/1Um9IQ-UzBePFyVlHz3TH4wGpermMRMWOpBLmJe6c0IM/edit?usp=sharing)) +- Helped to give other teams feedback on their product (examples: [1](https://docs.google.com/document/d/1UVnMxMm19w6zJRVTrtQq-U1MtQGjcguNfLYa4f1Uh28/edit?usp=sharing),[2](https://docs.google.com/document/d/1JXlZDlqaf9X08aNsDAeK3y8I08eVEqsbz_Pqo6yOFTo/edit?usp=sharing),[3](https://github.com/miahjerry/ped/issues/4)) \ No newline at end of file diff --git a/docs/team/michelleliang0116.md b/docs/team/michelleliang0116.md new file mode 100644 index 0000000000..6fb7f2e525 --- /dev/null +++ b/docs/team/michelleliang0116.md @@ -0,0 +1,38 @@ +# LIANG TING YU - Project Portfolio Page + + +## Overview + +Duck is a desktop task and class tracker for users to keep track of their schedule and the things they have to do. The user interacts with it using the CLI. It is written in Java, and has about 3.5kLoc. + + + +## Summary of Contributions + +### New Feature: Implementation of displaying upcoming deadline/event when starting duck. +- Displays upcoming deadline stored in the application when starting the application. +- Calculate the remaining time before the deadline + + +### New Feature: Implementation of displaying next upcoming deadline/event/class. +- Displays the next upcoming class stored in the application. Classes will be automatically sorted according to chronological order. Classes will also automatically be marked as done (represented by a cross) if the current time is past the ending time of the class, and their 'done' status will be reset at the start of each week. +- This is utilized in the ```upcoming_deadline```,```upcoming_class```,```upcoming_event```commands. + + +### Code Contributed: [RepoSense Link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=liang&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + + +### Project Management: +- Group meetings/notes. +- Complete assigned task and double check typo/bugs of others. + + +### Documentation: +- User Guide: + - Added documentation for the features `upcoming_deadline` , `upcoming_event`, `upcoming_class` , and feature displaying upcoming deadline/event. + - fix inconsistent format. + - Added missing links to the table of contents. + + +### Community: +- Reported bugs and suggestions for other teams (examples: [1](https://github.com/MichelleLiang0116/ped/issues/3), [2](https://github.com/MichelleLiang0116/ped/issues/2), [3](https://github.com/MichelleLiang0116/ped/issues/1)) diff --git a/docs/team/skyanzy.md b/docs/team/skyanzy.md new file mode 100644 index 0000000000..b8e3082b17 --- /dev/null +++ b/docs/team/skyanzy.md @@ -0,0 +1,50 @@ +# Yan Zaiya - Project Portfolio Page + +## Overview + +Duck is a desktop task and class tracker for users to keep track of their schedule and the things they have to do. The user interacts with it using the CLI. It is written in Java, and has about 3.5kLoc. + +## Summary of Contributions + +### New Feature: Implementation of the `RecurringDeadline` Task +- Allows users to keep track of deadlines that happen every week by only adding the deadline once instead of every week. +- `Storage` class was modified to recognise a `RecurringDeadline`. + +### New Feature: Implementation of the `RecurringEvent` Task +- Allows users to keep track of events that happen every week by only adding the event once instead of every week. +- `Storage` class was modified to recognise a `RecurringEvent`. + +### New Feature: `list_today` +- Displays all deadlines and events(recurring tasks included) saved in Duck that take place on the same day as the local time. + +### New Feature: Adding a `RecurringDeadline` +- By following the specified format as indicated in the User Guide, users will be able to add RecurringDeadline to their schedule which are saved to the savefile. + +### New Feature: Adding a `RecurringEvent` +- By following the specified format as indicated in the User Guide, users will be able to add RecurringEvent to their schedule which are saved to the savefile. + +### New Feature: `list X` +- Lists all the non-recurring deadlines and events that would happen within X+1 days. +- Justification: this allows users to plan their schedule better by easily referring to the upcoming tasks in the future. + +### New Feature: `edit ` +- Allows uers to edit a specific piece of information regarding a stored task and then updates the savefile. +- Justification: this is done so that the if the users want to modify/ save wrong information about a certain task, they do not have to delete the task and add a new one back in. + +### Code Contributed: [RepoSense Link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=T11&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2023-02-17&tabOpen=true&tabType=authorship&tabAuthor=skyanzy&tabRepo=AY2223S2-CS2113-T11-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) + +### Project Management: +- Integration of different parts when a new feature is implemented. +- Seting up milestone v2.1 and dividing issues among the team members. + +### Documentation: +- User Guide: + - Added documentation for the features `edit ` , `list_today`, adding a `RecurringDeadline and RecurringEvnet`. + - Added some missing links to the table of contents and cleared some issues regarding inconsistent format.. + +- Developer Guide: + - Added design and the implementation of RecurringDeadline and RecurringEvent + - Added a class diagram to illustrate the implementation + +### Community: +- Reported bugs and suggestions for other teams(e.g. [1](https://github.com/skyanzy/ped/issues/5), [2](https://github.com/skyanzy/ped/issues/2), [3](https://github.com/skyanzy/ped/issues/1)) diff --git a/docs/team/thant.md b/docs/team/thant.md new file mode 100644 index 0000000000..fe9b183700 --- /dev/null +++ b/docs/team/thant.md @@ -0,0 +1,55 @@ +# Thant Aung Htet Nyan - Project Portfolio Page + +## Overview + +Duck is a desktop task and class tracker for users to keep track of their schedule and the things they have to do. The user interacts with it using the CLI. It is written in Java, and has about 3.5kLoc. + + + +## Summary of Contributions + +### New Feature: Implementation of Date-Time tracking across Duck +- Elaboration: Duck is an improved version of the Duke bot, building upon the pre-existing functionality of Duke with enhanced features like date/time tracking, which was added by me. This date/time tracking is crucial in a number of new Duck features. + + +### New Feature: Implementation of the ```priority``` variables +- Adds a new priority variable to the Task class that ranges from 1-3, being LOW MEDIUM HIGH respectively. +- This is utilized in the ```priority_list``` new feature as well as the ```high_priority```,```medium_priority```,```low_priority```commands. + + +### New Feature: Implementation of the ```purge``` feature +- Checks across the ```TaskList``` array to identify all ```Deadline``` and ```Event``` objects. +- Checks for if the ```by``` variable for ```Deadline```objects or the ```end```variable for ```Event``` objects have passed the localtime of the local machine where Duck is running on. +- Displays all expired tasks, as well as the total count of expired tasks. +- Prompts user for a ```Y/N``` to confirm the removal of expired tasks. +- This feature helps keep the list of tasks clean, and is automatically run once on Duck's startup. + + +### New Feature: Implementation of the ```clear``` feature +- Wipes the ```TaskList``` array clean and wipes the local ```savedata``` file clean as well. +- User is prompted for a ```Y/N``` confirmation as this process is irreversible. +- Justification: During debugging / demonstrations, I continually manually deleted and recreated the ```savedata``` file to reset Duck to a blank slate. Hence, I decided to just make it an easily accessible feature for Duck users and it became highly useful. + + +### Code Contributed: [RepoSense Link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=thant&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + + +### Project Management: +- Initiated meetings / kept group on track to meet specific deadlines. +- Specified and broke down which issues should be done by v1.0, v2.0 and so forth to breakdown work timelines. + + +### Documentation: +- User Guide: + - Did up most of the initial User Guide. + - Updated respective features as new features were added / input methods were updated. + - Added hyperlinks to the feature lists. + +- Developer Guide: + - Did the ```architecture``` section. + - Did the design and implementation for ```purge``` and ```clear``` features + - Added an editable class diagram powerpoint skeleton located at ```docs/tP Diagram Editable.pptx``` + + +### Community: +- Reported bugs and suggestions for other teams (examples: [1](https://github.com/thant/ped/issues/5), [2](https://github.com/thant/ped/issues/6), [3](https://github.com/thant/ped/issues/8)) diff --git a/src/main/java/seedu/duck/Duck.java b/src/main/java/seedu/duck/Duck.java new file mode 100644 index 0000000000..0f229430fb --- /dev/null +++ b/src/main/java/seedu/duck/Duck.java @@ -0,0 +1,34 @@ +package seedu.duck; + +import seedu.duck.task.SchoolClass; +import seedu.duck.task.Task; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Scanner; +import java.util.PriorityQueue; + +public class Duck { + /** Runs the Duck bot */ + private static void runDuck() throws IOException { + Ui.greetingMessage(); + + PriorityQueue classes = new PriorityQueue<>(); + ArrayList tasks = new ArrayList<>(); + Storage.tryLoad(tasks, classes); + TaskList.purge(tasks, classes); + Ui.displayUpcomingDeadline(tasks); + Ui.displayUpcomingEvent(tasks); + String line; + Scanner in = new Scanner(System.in); + line = in.nextLine(); + + Parser.processCommand(tasks, classes, line, in); + + Ui.exitMessage(); + } + + public static void main(String[] args) throws IOException { + runDuck(); + } +} diff --git a/src/main/java/seedu/duck/Parser.java b/src/main/java/seedu/duck/Parser.java new file mode 100644 index 0000000000..af5e2a3d63 --- /dev/null +++ b/src/main/java/seedu/duck/Parser.java @@ -0,0 +1,244 @@ +package seedu.duck; + +import seedu.duck.task.SchoolClass; +import seedu.duck.task.Task; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.PriorityQueue; +import java.util.Scanner; + +/** + * Deals with making sense of the user command + */ +public class Parser { + + /** + * Returns boolean value of true if input String is an integer, + * else returns boolean value of false + * + * @param word String input to check if it is an integer + * @return true if input String is an integer, otherwise false + */ + public static boolean isNumeric(String word) { + int valueToConvert; + try { + valueToConvert = Integer.parseInt(word); + return true; + } catch (NumberFormatException e) { + // Empty catch block, since the only purpose is to return false + // if try block fails. + } + return false; + } + + /** + * Processes the user input and executes the appropriate command + * + * @param tasks The array list of tasks + * @param line The line of user input + * @param in The input from scanner + */ + static void processCommand(ArrayList tasks, PriorityQueue classes, String line, Scanner in) throws IOException { + while (!line.trim().equals("bye")) { + line = line.trim().replaceAll("\\s{2,}", " "); + String[] words = line.split(" "); + if (line.isBlank()) { + Ui.emptyCommandMessage(); + line = in.nextLine(); + } else { + switch (words[0]) { + case "list": + if (words.length == 1) { + Ui.list(tasks); + } else if (words.length == 2 && isNumeric(words[1])) { + Ui.printUpcomingTasks(tasks, words[1]); + } else { + Ui.unknownCommandMessage(); + } + break; + case "event": + if(words.length==2 && isNumeric(words[1])){ + Ui.printUpcomingEvents(tasks,words[1]); + } else{ + TaskList.addTask(line, tasks, classes); + Storage.trySave(tasks, classes); + } + break; + case "deadline": + if(words.length==2 && isNumeric(words[1])){ + Ui.printUpcomingDeadline(tasks,words[1]); + } else{ + TaskList.addTask(line, tasks, classes); + Storage.trySave(tasks, classes); + } + break; + case "list_today": + Ui.listToday(tasks, classes); + break; + case "priority_list": + Ui.printPriorityList(tasks); + break; + case "low_priority": + Ui.printLowPriority(tasks); + break; + case "medium_priority": + Ui.printMediumPriority(tasks); + break; + case "high_priority": + Ui.printHighPriority(tasks); + break; + case "list_classes": + Ui.listClasses(classes, tasks); + break; + case "help": + Ui.help(); + break; + case "upcoming_class": + Ui.displayNextUpcomingClass(classes); + break; + case "upcoming_event": + Ui.displayNextUpcomingEvent(tasks); + break; + case "upcoming_deadline": + Ui.displayNextUpcomingDeadline(tasks); + break; + case "upcoming_task": + Ui.displayNextUpcomingTask(tasks); + break; + case "unmark": + if (words.length == 2 && isNumeric(words[1])) { + TaskList.unmarkTask(tasks, words); + Storage.trySave(tasks, classes); + } else { + Ui.unknownCommandMessage(); + } + break; + case "mark": + if (words.length == 2 && isNumeric(words[1])) { + TaskList.markTask(tasks, words); + Storage.trySave(tasks, classes); + } else { + Ui.unknownCommandMessage(); + } + break; + case "delete": + if (words.length == 2 && isNumeric(words[1])) { + TaskList.deleteTask(tasks, words); + Storage.trySave(tasks, classes); + } else { + Ui.unknownCommandMessage(); + } + break; + case "remove": + if (words.length > 1 && words[1].equals("/class")) { + TaskList.tryDeleteClass(classes, line); + Storage.trySave(tasks, classes); + } else { + Ui.unknownCommandMessage(); + } + break; + case "edit": + if (words.length == 2 && isNumeric(words[1])) { + TaskList.tryEditTask(tasks, words); + Storage.trySave(tasks, classes); + } else { + Ui.unknownCommandMessage(); + } + break; + case "find": + if (words.length > 1) { + Ui.find(tasks, words); + } else { + Ui.unknownCommandMessage(); + } + break; + case "purge": + TaskList.purge(tasks, classes); + break; + case "priority": + if (words.length == 3) { + TaskList.setPriority(tasks, words); + Storage.trySave(tasks, classes); + } else { + Ui.unknownCommandMessage(); + } + break; + case "add_notes": + if(words.length == 2 && isNumeric(words[1])){ + TaskList.addNote(tasks, words); + Storage.trySave(tasks, classes); + }else{ + Ui.unknownCommandMessage(); + } + break; + case "delete_notes": + if(words.length == 3){ + TaskList.deleteNotes(tasks,words); + Storage.trySave(tasks, classes); + } else{ + Ui.unknownCommandMessage(); + } + break; + case "view_notes": + if(words.length == 2 && isNumeric(words[1])) { + Ui.printNotes(tasks, words); + }else{ + Ui.unknownCommandMessage(); + } + break; + case "edit_notes": + if(words.length == 3){ + TaskList.editNote(tasks, words); + Storage.trySave(tasks, classes); + }else{ + Ui.unknownCommandMessage(); + } + break; + case "motivation": + Ui.printMotivationalQuote(); + Ui.borderLine(); + break; + case "clear": + if (Ui.doubleCheck()) { + // Find tasks that contain a keyword + tasks.clear(); + Task.clearCount(); + classes.clear(); + Ui.borderLine(); + System.out.println("\t Got it, all tasks have been cleared."); + Ui.borderLine(); + Storage.clearTask(); + break; + } else { + Ui.borderLine(); + System.out.println("\t Quack! Process cancelled."); + Ui.borderLine(); + break; + } + default: + TaskList.addTask(line, tasks, classes); + Storage.trySave(tasks, classes); + break; + } + line = in.nextLine(); + } + } + } + + /** + * Process the array of words from the user input and extracts the + * keywords into a single string to use for the find function + * + * @param words The array of words generated from the user input + * @return The keywords string to use for the find function + */ + static String processKeywords(String[] words,int index) { + String rawKeyword = ""; + for (int i = index; i < words.length; i++) { + rawKeyword += (" " + words[i]); + } + String keyword = rawKeyword.trim(); + return keyword; + } +} \ No newline at end of file diff --git a/src/main/java/seedu/duck/Storage.java b/src/main/java/seedu/duck/Storage.java new file mode 100644 index 0000000000..f5e7238945 --- /dev/null +++ b/src/main/java/seedu/duck/Storage.java @@ -0,0 +1,300 @@ +package seedu.duck; + + +import seedu.duck.task.*; + +import java.io.File; +import java.io.FileWriter; +import java.io.PrintWriter; +import java.io.IOException; +import java.time.DayOfWeek; +import java.util.ArrayList; +import java.util.PriorityQueue; +import java.util.Scanner; + +/** + * Deals with loading tasks from the file and saving tasks in the file + */ +public class Storage { + static final String SAVEPATH = "data/savedata.txt"; + static final String SAVEFOLDER = "data"; + + /** + * Load tasks and the notes under the task from save data into the list + * + * @param line The line of input from the save file + * @param tasks The array list of tasks + */ + static void loadTask(String line, ArrayList tasks, PriorityQueue classes, String doneStatus) + throws IndexOutOfBoundsException { + if (line.contains("/by")) { + if (line.contains("/day")) { + loadRecurringDeadline(line, tasks); + } else { + loadDeadline(line, tasks); + } + loadTaskStatus(tasks, doneStatus); + Task.incrementCount(); + } else if (line.contains("/class")) { + loadSchoolClass(line, classes); + } else if (line.contains("/from") || line.contains("/to")) { + if (line.contains("/day")) { + loadRecurringEvent(line, tasks); + } else { + loadEvent(line, tasks); + } + loadTaskStatus(tasks, doneStatus); + Task.incrementCount(); + } else { + loadTodo(line, tasks); + loadTaskStatus(tasks, doneStatus); + Task.incrementCount(); + } + } + + /** + * Deletes the existing savedata file then recreates a new blank slate. + */ + static void clearTask() throws IOException { + { + try { + FileWriter fw = new FileWriter(SAVEPATH, false); + PrintWriter pw = new PrintWriter(fw, false); + pw.flush(); + pw.close(); + fw.close(); + } catch (Exception exception) { + System.out.println("Exception have been caught"); + } + } + } + + /** + * Adds a _Todo_ into the list without generating messages, + * to be used when loading from save data. + * + * @param line The line of input from the save file + * @param tasks The array list of tasks + */ + static void loadTodo(String line, ArrayList tasks) { + String description = line.substring(0, line.indexOf("

")).trim(); + Todo currTodo = new Todo(description); + String priority = line.substring(line.indexOf("

") + 3,line.indexOf("")).trim(); + currTodo.setPriority(priority); + tasks.add(currTodo); + String savedNotes = line.substring(line.indexOf("") + 3); + if (savedNotes.charAt(0) == '1'){ + savedNotes = savedNotes.substring(1); + String[] notesToBeAdded; + notesToBeAdded = savedNotes.split("@",0); + for (String item : notesToBeAdded) { + tasks.get(tasks.size() - 1).addNotes(item); + } + } + } + + + /** + * Adds an event into the list without generating messages, + * to be used when loading from save data. + * + * @param line The line of input from the save file + * @param tasks The array list of tasks + */ + static void loadEvent(String line, ArrayList tasks) { + String description = line.substring(0, line.indexOf("/from")).trim(); + String start = line.substring(line.indexOf("/from") + 5, line.indexOf("/to")).trim(); + String end = line.substring(line.indexOf("/to") + 3, line.indexOf("

")).trim(); + String priority = line.substring(line.indexOf("

") + 3, line.indexOf("

") + 4).trim(); + Event currEvent = new Event(description, start, end); + currEvent.setPriority(priority); + tasks.add(currEvent); + String savedNotes = line.substring(line.indexOf("") + 3); + if (savedNotes.charAt(0) == '1'){ + savedNotes = savedNotes.substring(1); + String[] notesToBeAdded; + notesToBeAdded = savedNotes.split("@",0); + for (String item : notesToBeAdded) { + tasks.get(tasks.size() - 1).addNotes(item); + } + } + } + + static void loadRecurringEvent(String line, ArrayList tasks) { + String description = line.substring(0, line.indexOf("/from")).trim(); + String start = line.substring(line.indexOf("/from") + 5, line.indexOf("/to")).trim(); + String end = line.substring(line.indexOf("/to") + 3, line.indexOf("

")).trim(); + String priority = line.substring(line.indexOf("

") + 3, line.indexOf("/day")).trim(); + DayOfWeek day = DayOfWeek.valueOf(line.substring(line.indexOf("/day") + 4).trim()); + RecurringEvent currEvent = new RecurringEvent(description, start, end, day); + currEvent.setPriority(priority); + tasks.add(currEvent); + } + + /** + * Adds a schoolClass to the list without generating messages, + * to be used when loading from save data. + * + * @param line The line of input from the user + * @param classes The priority queue of school classes + */ + static void loadSchoolClass(String line, PriorityQueue classes) { + String description = line.substring(0, line.indexOf("/class")).trim(); + String className = line.substring(line.indexOf("/class") + 6, line.indexOf("/day")).trim(); + DayOfWeek day = DayOfWeek.valueOf(line.substring(line.indexOf("/day") + 4, line.indexOf("/from")).trim()); + String startString = line.substring(line.indexOf("/from") + 5, line.indexOf("/to")).trim(); + String endString = line.substring(line.indexOf("/to") + 3).trim(); + SchoolClass currSchoolClass = new SchoolClass(className, description, day, startString, endString); + + TaskList.checkClassOver(day, endString, currSchoolClass); + classes.add(currSchoolClass); + } + + /** + * Adds a deadline into the list without generating messages, + * to be used when loading from save data. + * + * @param line The line of input from the save file + * @param tasks The array list of tasks + */ + static void loadDeadline(String line, ArrayList tasks) { + String description = line.substring(0, line.indexOf("/by")).trim(); + String deadline = line.substring(line.indexOf("/by") + 3, line.indexOf("

")).trim(); + String priority = line.substring(line.indexOf("

") + 3, line.indexOf("

") + 4).trim(); + Deadline currDeadline = new Deadline(description, deadline); + currDeadline.setPriority(priority); + tasks.add(currDeadline); + String savedNotes = line.substring(line.indexOf("") + 3); + if (savedNotes.charAt(0) == '1'){ + savedNotes = savedNotes.substring(1); + String[] notesToBeAdded; + notesToBeAdded = savedNotes.split("@",0); + for (String item : notesToBeAdded) { + tasks.get(tasks.size() - 1).addNotes(item); + } + } + } + + /** + * Adds a RecurringDeadline to the list when loading from save data + * + * @param line The line of input from save file + * @param tasks the array list of tasks + */ + static void loadRecurringDeadline(String line, ArrayList tasks) { + String description = line.substring(0, line.indexOf("/by")).trim(); + String deadline = line.substring(line.indexOf("/by") + 3, line.indexOf("

")).trim(); + String priority = line.substring(line.indexOf("

") + 3, line.indexOf("/day")).trim(); + DayOfWeek day = DayOfWeek.valueOf(line.substring(line.indexOf("/day") + 4).trim()); + RecurringDeadline currDeadline = new RecurringDeadline(description, deadline, day); + currDeadline.setPriority(priority); + tasks.add(currDeadline); + } + + /** + * Load the task status of a task from the save data + * + * @param tasks The array list of tasks + * @param doneStatus The done status of the current task + */ + static void loadTaskStatus(ArrayList tasks, String doneStatus) { + int taskNumber = Task.getTaskCount(); + if (doneStatus.equals("1")) { + tasks.get(taskNumber).markAsDone(); + } else { + tasks.get(taskNumber).markAsNotDone(); + } + } + + /** + * Saves the tasks in the list to the save file + * + * @param tasks The array list of tasks + */ + static void save(ArrayList tasks, PriorityQueue classes) throws IOException { + File f = new File(SAVEPATH); + if (f.exists()) { + f.delete(); + } + f.createNewFile(); + + // Saving the task list to the save file + FileWriter fw = new FileWriter(SAVEPATH); + for (Task currTask : tasks) { + fw.write(currTask.toSaveString()); + } + fw.close(); + + // Saving the class schedule to the save file + FileWriter fw2 = new FileWriter(SAVEPATH, true); + PriorityQueue temp = new PriorityQueue(classes); + while (!temp.isEmpty()) { + fw2.write(temp.poll().toSaveString()); + } + fw2.close(); + } + + /** + * Try to save, shows error message if saving fails + * + * @param tasks The array list of tasks + */ + static void trySave(ArrayList tasks, PriorityQueue classes) { + try { + save(tasks, classes); + } catch (IOException e) { + System.out.println("Saving error."); + } + } + + /** + * Load the save data + * + * @param tasks The array list of tasks + */ + static void load(ArrayList tasks, PriorityQueue classes) throws IOException, + IndexOutOfBoundsException { + File folder = new File(SAVEFOLDER); + if (!folder.exists()) { + new File(SAVEFOLDER).mkdir(); + } + + File f = new File(SAVEPATH); + if (!f.exists()) { + f.createNewFile(); + } + + Scanner s = new Scanner(f); + while (s.hasNext()) { + String line = s.nextLine(); + String[] formattedInput = line.split(" "); + String doneStatus = formattedInput[0]; + String command = ""; + for (int i = 1; i < formattedInput.length; i++) { + command += formattedInput[i]; + command += " "; + } + try { + loadTask(command, tasks, classes, doneStatus); + } catch (IndexOutOfBoundsException e) { + FileWriter fw = new FileWriter(SAVEPATH); + throw new IndexOutOfBoundsException(); + } + } + } + + /** + * Try to load the save data, shows error message if loading fails + * + * @param tasks The array list of tasks + */ + static void tryLoad(ArrayList tasks, PriorityQueue classes) { + try { + load(tasks, classes); + } catch (IOException e) { + Ui.loadingErrorMessage(); + } catch (IndexOutOfBoundsException e) { + Ui.loadingErrorMessage(); + } + } +} diff --git a/src/main/java/seedu/duck/TaskList.java b/src/main/java/seedu/duck/TaskList.java new file mode 100644 index 0000000000..0f04b15f83 --- /dev/null +++ b/src/main/java/seedu/duck/TaskList.java @@ -0,0 +1,799 @@ +package seedu.duck; + +import seedu.duck.exception.*; +import seedu.duck.task.*; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.DayOfWeek; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Date; +import java.util.PriorityQueue; +import java.util.Scanner; + +/** + * Contains operations to make changes to the list of tasks or the class schedule + */ +public class TaskList { + private static final int DESCRIPTION_OFFSET = 12; + private static final int DEADLINE_OFFSET = 9; + private static final int FROM_OFFSET = 5; + private static final int TO_OFFSET = 3; + private static final int DAY_OFFSET = 4; + private static final int BY_OFFSET = 3; + + static void addTask(String line, ArrayList tasks, PriorityQueue classes) { + if (line.contains("/by")) { + // Adding a Deadline + if (line.contains("/re")) { + try { + addRecurringDeadline(line, tasks); + Task.incrementCount(); + } catch(IllegalDeadlineException | StringIndexOutOfBoundsException e) { + Ui.deadlineErrorMessage(); + } catch (IllegalArgumentException e) { + Ui.invalidDayMessage(); + } catch (DateTimeParseException e) { + Ui.invalidDateTimeMessage(); + } + } else { + try { + addDeadline(line, tasks); + Task.incrementCount(); + } catch (IllegalDeadlineException e) { + Ui.deadlineErrorMessage(); + } catch (expiredDateException e) { + Ui.expiredErrorMessage(); + } catch (DateTimeException e) { + Ui.invalidDateTimeMessage(); + } + } + } else if (line.contains("/class")) { + // Adding a SchoolClass + try { + addSchoolClass(line, classes); + } catch (IllegalSchoolClassException | IndexOutOfBoundsException e) { + Ui.eventErrorMessage(); + } catch (expiredDateException e) { + Ui.expiredErrorMessage(); + } catch (startAfterEndException e) { + Ui.startAfterEndErrorMessage(); + } catch (DateTimeException e) { + Ui.invalidDateTimeMessage(); + } + } else if (line.contains("/from") && line.contains("/to")) { + // Adding an Event + if (line.contains("/re")) { + try { + addRecurringEvent(line, tasks); + Task.incrementCount(); + } catch (IllegalEventException | StringIndexOutOfBoundsException e) { + Ui.eventErrorMessage(); + } catch (IllegalArgumentException e) { + Ui.invalidDayMessage(); + } catch (DateTimeParseException e) { + Ui.invalidDateTimeMessage(); + } + } else { + try { + addEvent(line, tasks); + Task.incrementCount(); + } catch (IllegalEventException | IndexOutOfBoundsException e) { + Ui.eventErrorMessage(); + } catch (expiredDateException e) { + Ui.expiredErrorMessage(); + } catch (startAfterEndException e) { + Ui.startAfterEndErrorMessage(); + } catch (DateTimeException e) { + Ui.invalidDateTimeMessage(); + } + } + } else if (line.trim().split(" ")[0].equals("/todo")){ + // Adding a _Todo_ + try { + addTodo(line, tasks); + Task.incrementCount(); + } catch (IllegalTodoException e) { + Ui.todoErrorMessage(); + } + } else { + Ui.unknownCommandMessage(); + } + } + + /** + * Adds a _Todo_ to the list + * + * @param line The line of input from the user + * @param tasks The array list of tasks + */ + static void addTodo(String line, ArrayList tasks) throws IllegalTodoException { + line = line.trim(); + String description = line.substring(5).trim(); + if (description.isBlank()) { + throw new IllegalTodoException(); + } else { + Todo currTodo = new Todo(description); + tasks.add(currTodo); + Ui.addedTaskMessage(currTodo); + } + } + + /** + * Sets a priority to the specified task + * + * @param words The input variable from the user, consisting of an index and a priority (from 1 to 3) + * @param tasks The array list of tasks + */ + static void setPriority(ArrayList tasks, String[] words) { + if (!words[2].equals("1") && !words[2].equals("2") && !words[2].equals("3")) { + Ui.priorityErrorMessage(); + } else if (!Parser.isNumeric(words[1])) { + Ui.unknownCommandMessage(); + } else { + int taskNumber = Integer.parseInt(words[1]); + int taskCount = Task.getTaskCount(); + if (taskNumber > taskCount || taskNumber <= 0) { + // Input task number exceeds the number of tasks in the list + Ui.exceedTaskNumberMessage(taskNumber); + } else { + tasks.get(taskNumber - 1).setPriority(words[2]); + // Printing out marked as done message + Ui.borderLine(); + System.out.println("\t Understood. The task's new priority is:"); + System.out.println("\t " + tasks.get(taskNumber - 1).getPriority()); + Ui.borderLine(); + } + } + } + + /** + * Adds an event to the list + * + * @param line The line of input from the user + * @param tasks The array list of tasks + */ + static void addEvent(String line, ArrayList tasks) throws IllegalEventException, startAfterEndException, + expiredDateException { + String description = line.substring(0, line.indexOf("/from")).trim(); + String startString = line.substring(line.indexOf("/from") + FROM_OFFSET, line.indexOf("/to")).trim(); + String endString = line.substring(line.indexOf("/to") + 3).trim(); + DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm"); + LocalDateTime start = LocalDateTime.parse(startString, dateFormat); + LocalDateTime end = LocalDateTime.parse(endString, dateFormat); + if (start.isAfter(end)) { + throw new startAfterEndException(); + } else if (start.isBefore(LocalDateTime.now()) || end.isBefore(LocalDateTime.now())) { + throw new expiredDateException(); + } else if (description.isBlank() || startString.isBlank() || endString.isBlank()) { + throw new IllegalEventException(); + } else { + Event currEvent = new Event(description, startString, endString); + tasks.add(currEvent); + Ui.addedTaskMessage(currEvent); + } + } + + /** + * Adds a RecurringEvent to the list + * @param line input from user + * @param tasks the array list of tasks + * @throws IllegalEventException handles incorrect event format + */ + static void addRecurringEvent(String line, ArrayList tasks) throws IllegalEventException{ + String description = line.substring(4, line.indexOf("/from")).trim(); + String start = line.substring(line.indexOf("/from") + FROM_OFFSET, line.indexOf("/to")).trim(); + String end = line.substring(line.indexOf("/to") + TO_OFFSET, line.indexOf("/day")).trim(); + DayOfWeek day = DayOfWeek.valueOf(line.substring(line.indexOf("/day") + DAY_OFFSET).trim()); + DateTimeFormatter timeFormat = DateTimeFormatter.ofPattern("HHmm"); + //check whether start and end are in the correct format + LocalTime.parse(start, timeFormat); + LocalTime.parse(end, timeFormat); + if (description.isBlank() || start.isBlank() || end.isBlank()) { + throw new IllegalEventException(); + } else { + RecurringEvent currEvent = new RecurringEvent(description, start, end, day); + tasks.add(currEvent); + Ui.addedTaskMessage(currEvent); + } + } + + /** + * Adds a schoolClass to the list + * + * @param line The line of input from the user + * @param classes The priority queue of school classes + */ + static void addSchoolClass(String line, PriorityQueue classes) throws IllegalSchoolClassException, + startAfterEndException, expiredDateException, IllegalArgumentException, NullPointerException { + String description = line.substring(0, line.indexOf("/class")).trim(); + String className = line.substring(line.indexOf("/class") + 6, line.indexOf("/day")).trim(); + try { + DayOfWeek day = DayOfWeek.valueOf(line.substring(line.indexOf("/day") + 4, line.indexOf("/from")).trim()); + String startString = line.substring(line.indexOf("/from") + 5, line.indexOf("/to")).trim(); + String endString = line.substring(line.indexOf("/to") + 3).trim(); + DateTimeFormatter timeFormat = DateTimeFormatter.ofPattern("HHmm"); + LocalTime start = LocalTime.parse(startString, timeFormat); + LocalTime end = LocalTime.parse(endString, timeFormat); + if (start.isAfter(end)) { + throw new startAfterEndException(); + } else if (className.isBlank() || startString.isBlank() || endString.isBlank()) { + throw new IllegalSchoolClassException(); + } else { + SchoolClass currSchoolClass = new SchoolClass(className, description, day, startString, endString); + classes.add(currSchoolClass); + Ui.addedSchoolClassMessage(currSchoolClass, classes); + } + } catch (IllegalArgumentException e) { + Ui.invalidDayMessage(); + } catch (NullPointerException e) { + Ui.emptyDayErrorMessage(); + } + } + + /** + * Adds a deadline to the list + * + * @param line The line of input from the user + * @param tasks The array list of tasks + */ + static void addDeadline(String line, ArrayList tasks) throws IllegalDeadlineException, expiredDateException { + String description = line.substring(0, line.indexOf("/by")).trim(); + String deadlineString = line.substring(line.indexOf("/by") + 3).trim(); + DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm"); + LocalDateTime deadline = LocalDateTime.parse(deadlineString, dateFormat); + //System.out.println(description.isBlank()); + if (description.isBlank() || deadlineString.isBlank()) { + throw new IllegalDeadlineException(); + } else if (deadline.isBefore(LocalDateTime.now())) { + throw new expiredDateException(); + } else { + Deadline currDeadline = new Deadline(description, deadlineString); + tasks.add(currDeadline); + Ui.addedTaskMessage(currDeadline); + } + } + + + /** + * adds a recurringDeadline to the list + * + * @param line the line of input from the user + * @param tasks the array list of tasks + */ + static void addRecurringDeadline(String line, ArrayList tasks) throws IllegalDeadlineException, + IllegalArgumentException { + String description = line.substring(4, line.indexOf("/by")).trim(); + String deadline = line.substring(line.indexOf("/by") + BY_OFFSET, line.indexOf("/day")).trim(); + DateTimeFormatter timeFormat = DateTimeFormatter.ofPattern("HHmm"); + LocalTime.parse(deadline, timeFormat); + DayOfWeek day = DayOfWeek.valueOf(line.substring(line.indexOf("/day") + DAY_OFFSET).trim()); + if (description.isBlank() || deadline.isBlank()) { + throw new IllegalDeadlineException(); + } else { + RecurringDeadline currDeadline = new RecurringDeadline(description, deadline, day); + tasks.add(currDeadline); + Ui.addedTaskMessage(currDeadline); + } + } + + /** + * Marks a task as done + * + * @param tasks The array list of tasks + * @param words The array of words generated from the user input + */ + static void markTask(ArrayList tasks, String[] words) { + int taskNumber = Integer.parseInt(words[1]); + int taskCount = Task.getTaskCount(); + if (taskNumber > taskCount || taskNumber <= 0) { + // Input task number exceeds the number of tasks in the list + Ui.exceedTaskNumberMessage(taskNumber); + } else { + tasks.get(taskNumber - 1).markAsDone(); + // Printing out marked as done message + Ui.borderLine(); + System.out.println("\t Understood. I've marked this task as done:"); + System.out.println("\t " + tasks.get(taskNumber - 1)); + Ui.borderLine(); + } + } + + /** + * Marks a task as not done + * + * @param tasks The array list of tasks + * @param words The array of words generated from the user input + */ + static void unmarkTask(ArrayList tasks, String[] words) { + int taskNumber = Integer.parseInt(words[1]); + int taskCount = Task.getTaskCount(); + if (taskNumber > taskCount || taskNumber <= 0) { + // Input task number exceeds the number of tasks in the list + Ui.exceedTaskNumberMessage(taskNumber); + } else { + tasks.get(taskNumber - 1).markAsNotDone(); + // Printing out marked as not done message + Ui.borderLine(); + System.out.println("\t Understood. I've marked this task as not done yet:"); + System.out.println("\t " + tasks.get(taskNumber - 1)); + Ui.borderLine(); + } + } + + /** + * edits the attributes of a specific task + * @param tasks The array list of tasks + * @param words The array of words generated from the user input + */ + static void editTask(ArrayList tasks, String[] words) throws expiredDateException, + startAfterEndException, EmptyDescriptionException { + int taskNumber = Integer.parseInt(words[1]); + int taskCount = Task.getTaskCount(); + DateTimeFormatter timeFormat = DateTimeFormatter.ofPattern("HHmm"); + DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm"); + if (taskNumber > taskCount || taskNumber <= 0) { + Ui.exceedTaskNumberMessage(taskNumber); + return; + } + Task taskToEdit = tasks.get(taskNumber - 1); + if (taskToEdit instanceof Todo) { + editTodo(taskToEdit); + } else if (taskToEdit instanceof Deadline) { + editDeadline(words, timeFormat, dateFormat, taskToEdit); + } else if (taskToEdit instanceof Event) { + editEvent(timeFormat, dateFormat, taskToEdit); + } + Ui.printEditedTask(taskToEdit); + } + + /** + * edits an attribute of an event + * @param timeFormat time format for event + * @param dateFormat date format for event + * @param taskToEdit the event to edit + * @throws EmptyDescriptionException if the new description is empty + * @throws startAfterEndException if the start-end time is incorrect + * @throws expiredDateException if the task is expired + */ + private static void editEvent(DateTimeFormatter timeFormat, DateTimeFormatter dateFormat, Task taskToEdit) throws + EmptyDescriptionException, startAfterEndException, expiredDateException { + Ui.editEventMessage(); + String editLine = Ui.askForEditMessage().trim(); + String[] editWords = editLine.split(" "); + if (taskToEdit instanceof RecurringEvent) { + if (editWords.length > 1 && editWords[0].equals("/description")) { + String newDescription = editLine.substring(DESCRIPTION_OFFSET).trim(); + if (newDescription.isBlank()) { + throw new EmptyDescriptionException(); + } + taskToEdit.setDescription(newDescription); + } else if (editWords.length > 1 && editWords[0].equals("/from")){ + String start = editLine.substring(FROM_OFFSET).trim(); + LocalTime.parse(start, timeFormat); + ((RecurringEvent) taskToEdit).setStart(start); + } else if (editWords.length > 1 && editWords[0].equals("/to")) { + String end = editLine.substring(TO_OFFSET).trim(); + LocalTime.parse(end, timeFormat); + ((RecurringEvent) taskToEdit).setEnd(end); + } else if(editWords.length > 1 && editWords[0].equals("/day")) { + DayOfWeek day = DayOfWeek.valueOf(editLine.substring(DAY_OFFSET).trim()); + ((RecurringEvent) taskToEdit).setDay(day); + } else { + Ui.unknownCommandMessage(); + } + } else { + LocalDateTime start = LocalDateTime.parse(((Event) taskToEdit).getStart(), dateFormat); + LocalDateTime end = LocalDateTime.parse(((Event) taskToEdit).getEnd(), dateFormat); + String newStartString = null; + String newEndString = null; + LocalDateTime newStart = null; + LocalDateTime newEnd = null; + if (editWords.length > 1 && editWords[0].equals("/description")) { + String newDescription = editLine.substring(DESCRIPTION_OFFSET).trim(); + if (newDescription.isBlank()) { + throw new EmptyDescriptionException(); + } + taskToEdit.setDescription(newDescription); + } else if (editWords.length > 1 && editWords[0].equals("/from")){ + newStartString = editLine.substring(FROM_OFFSET).trim(); + newStart = LocalDateTime.parse(newStartString, dateFormat); + } else if (editWords.length > 1 && editWords[0].equals("/to")) { + newEndString = editLine.substring(TO_OFFSET).trim(); + newEnd= LocalDateTime.parse(newEndString, dateFormat); + } else { + Ui.unknownCommandMessage(); + } + if (newStart != null) { + if (newStart.isAfter(end)) { + throw new startAfterEndException(); + } else if (newStart.isBefore(LocalDateTime.now()) || end.isBefore(LocalDateTime.now())) { + throw new expiredDateException(); + } else { + ((Event) taskToEdit).setStart(newStartString); + } + } else if (newEnd != null) { + if (start.isAfter(newEnd)) { + throw new startAfterEndException(); + } else if (start.isBefore(LocalDateTime.now()) || newEnd.isBefore(LocalDateTime.now())) { + throw new expiredDateException(); + } else { + ((Event) taskToEdit).setEnd(newEndString); + } + } + } + } + + /** + * edits an attribute of a stored deadline + * + * @param words input split into an array of string + * @param timeFormat time format for deadline + * @param dateFormat date format for deadline + * @param taskToEdit the task to edit + * @throws EmptyDescriptionException if the new description is empty + * @throws expiredDateException if the deadline has expired + */ + private static void editDeadline(String[] words, DateTimeFormatter timeFormat, DateTimeFormatter dateFormat, + Task taskToEdit) throws EmptyDescriptionException, expiredDateException { + Ui.editDeadlineMessage(); + String editLine = Ui.askForEditMessage().trim(); + String[] editWords = editLine.split(" "); + if (taskToEdit instanceof RecurringDeadline) { + if (editWords.length > 1 && editWords[0].equals("/description")) { + String newDescription = editLine.substring(12).trim(); + if (newDescription.isBlank()) { + throw new EmptyDescriptionException(); + } + taskToEdit.setDescription(newDescription); + } else if (editWords.length > 1 && editWords[0].equals("/deadline")){ + String deadline = editLine.substring(DEADLINE_OFFSET).trim(); + LocalTime.parse(deadline, timeFormat); + ((RecurringDeadline) taskToEdit).setDeadline(deadline); + } else if (editWords.length > 1 && editWords[0].equals("/day")) { + DayOfWeek day = DayOfWeek.valueOf(editLine.substring(DAY_OFFSET).trim()); + ((RecurringDeadline) taskToEdit).setDay(day); + } else { + Ui.unknownCommandMessage(); + } + } else { + if (editWords.length > 1 && editWords[0].equals("/description")) { + String newDescription = editLine.substring(editLine.indexOf(words[1])); + if (newDescription.isBlank()) { + throw new EmptyDescriptionException(); + } + taskToEdit.setDescription(newDescription); + } else if (editWords.length > 1 && editWords[0].equals("/deadline")) { + String deadlineString = editLine.substring(DEADLINE_OFFSET).trim(); + System.out.println(deadlineString); + LocalDateTime deadline = LocalDateTime.parse(deadlineString, dateFormat); + if (deadline.isBefore(LocalDateTime.now())){ + throw new expiredDateException(); + } else { + ((Deadline) taskToEdit).setDeadline(deadlineString); + } + } else { + Ui.unknownCommandMessage(); + } + } + } + + /** + * edits the todd + * @param taskToEdit the todo to edit + * @throws EmptyDescriptionException empty description + */ + private static void editTodo(Task taskToEdit) throws EmptyDescriptionException { + Ui.editTodoMessage(); + String editLine = Ui.askForEditMessage().trim(); + if (editLine.isBlank()) { + throw new EmptyDescriptionException(); + } + taskToEdit.setDescription(editLine); + } + + /** + * tries editTask and handles exceptions + * + * @param tasks The array list of tasks + * @param words The array of words generated from the user input + */ + static void tryEditTask(ArrayList tasks, String[] words) { + try { + editTask(tasks, words); + } catch (DateTimeParseException e) { + Ui.invalidDateTimeMessage(); + } catch (IllegalArgumentException e) { + Ui.invalidDayMessage(); + } catch (expiredDateException e) { + Ui.expiredErrorMessage(); + } catch (startAfterEndException e) { + Ui.startAfterEndErrorMessage(); + } catch (EmptyDescriptionException e) { + Ui.emptyDescriptionErrorMessage(); + } + } + + /** + * Deletes a task from the list + * + * @param tasks The array list of tasks + * @param words The array of words generated from the user input + */ + static void deleteTask(ArrayList tasks, String[] words) { + int taskNumber = Integer.parseInt(words[1]); + int taskCount = Task.getTaskCount(); + if (taskNumber > taskCount || taskNumber <= 0) { + // Input task number exceeds the number of tasks in the list + Ui.exceedTaskNumberMessage(taskNumber); + } else { + Task taskToDelete = tasks.get(taskNumber - 1); + tasks.remove(taskNumber - 1); + Task.decrementCount(); + Ui.deleteTaskMessage(taskToDelete); + } + } + + /** + * Deletes a SchoolClass from the priority queue + * + * @param classes The priority queue of SchoolClasses + * @param line The line of user input + * @throws IllegalArgumentException handle IllegalArgumentException + * @throws NullPointerException handle NullPointerException + * @throws StringIndexOutOfBoundsException handle StringIndexOutOfBoundsException + */ + static void deleteClass(PriorityQueue classes, String line) throws + IllegalArgumentException, NullPointerException, StringIndexOutOfBoundsException{ + try { + // Buffer holds the string "remove class" and is redundant + String buffer = line.substring(0, line.indexOf("/class")).trim(); + String className = line.substring(line.indexOf("/class") + 6, line.indexOf("/description")).trim(); + String description = line.substring(line.indexOf("/description") + 12, line.indexOf("/day")).trim(); + DayOfWeek day = DayOfWeek.valueOf(line.substring(line.indexOf("/day") + 4, line.indexOf("/from")).trim()); + String startString = line.substring(line.indexOf("/from") + 5, line.indexOf("/to")).trim(); + String endString = line.substring(line.indexOf("/to") + 3).trim(); + SchoolClass toDelete = new SchoolClass(className, description, day, startString, endString); + if (classes.remove(toDelete)) { + Ui.deleteClassMessage(); + } else { + Ui.unsuccessfulDeleteClassMessage(); + } + } catch (IllegalArgumentException e) { + Ui.invalidDayMessage(); + } catch (NullPointerException e) { + Ui.emptyDayErrorMessage(); + } catch (StringIndexOutOfBoundsException e) { + Ui.invalidRemoveClassMessage(); + } + } + + /** + * Tries to delete a SchoolClass from the priority queue, and throws an error message + * if unsuccessful. This method should be used instead of directly invoking + * the deleteClass() method. + * + * @param classes The priority queue of SchoolClasses + * @param line The line of user input + */ + static void tryDeleteClass(PriorityQueue classes, String line) { + if (!line.contains("/class") || !line.contains("/description") || !line.contains("/day") || + !line.contains("/from") || !line.contains("/to")) { + Ui.invalidRemoveClassMessage(); + } else { + deleteClass(classes, line); + } + } + + /** + * Checks if the current day and time is past the day and end time of the SchoolClass. + * If SchoolClass is over, mark as done, otherwise mark as not done. + * + * @param day The enum for the day of week registered in the current SchoolClass to check + * @param endString The end timing for the current SchoolClass to check + * @param currSchoolClass The current SchoolClass + */ + static void checkClassOver(DayOfWeek day, String endString, SchoolClass currSchoolClass) { + LocalDate today = LocalDate.now(); + DayOfWeek dayToday = today.getDayOfWeek(); + if (dayToday.getValue() > day.getValue()) { // day of week passed + currSchoolClass.markAsDone(); + } else if (dayToday.getValue() < day.getValue()) { // day of week not passed + currSchoolClass.markAsNotDone(); + } else { // same day of week + DateTimeFormatter timeFormat = DateTimeFormatter.ofPattern("HHmm"); + LocalTime classEndTime = LocalTime.parse(endString, timeFormat); + LocalTime currTime = LocalTime.now(); + if (currTime.isAfter(classEndTime)) { + currSchoolClass.markAsDone(); + } else { + currSchoolClass.markAsNotDone(); + } + } + } + + /** + * Clears the task list and SchoolClass priority queue, and reloads from save file. + * This function is mainly used to update the done status of SchoolClasses in the schedule. + * + * @param tasks The ArrayList of tasks + * @param classes The priority queue of SchoolClasses + */ + static void refresh(ArrayList tasks, PriorityQueue classes) { + tasks.clear(); + Task.clearCount(); + classes.clear(); + Storage.tryLoad(tasks, classes); + } + + static void purge(ArrayList tasks, PriorityQueue classes) { + Ui.borderLine(); + System.out.println("\t Displaying all expired tasks below..."); + System.out.println(); + if (tasks.size() > 0) { + int expiredCount = 0; + ArrayList expiredTasks = new ArrayList<>(); + for (Task task : tasks) { + if (task instanceof Deadline && !(task instanceof RecurringDeadline)) { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HHmm"); + String deadline = ((Deadline) task).getDeadline(); + Date d = null; + Date n = new Date(); + try { + d = format.parse(deadline); + } catch (ParseException e) { + e.printStackTrace(); + } + assert d != null; + long diff = d.getTime() - n.getTime(); + if (diff < 0) { + expiredCount++; + System.out.println(task); + expiredTasks.add(task); + } + } else if (task instanceof Event && !(task instanceof RecurringEvent)) { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HHmm"); + String end = ((Event) task).getEnd(); + Date d = null; + Date n = new Date(); + try { + d = format.parse(end); + } catch (ParseException e) { + e.printStackTrace(); + } + assert d != null; + long diff = d.getTime() - n.getTime(); + if (diff < 0) { + expiredCount++; + System.out.println(task); + expiredTasks.add(task); + } + } else if (task instanceof SchoolClass) { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HHmm"); + String end = ((SchoolClass) task).getEnd(); + Date d = null; + Date n = new Date(); + try { + d = format.parse(end); + } catch (ParseException e) { + e.printStackTrace(); + } + assert d != null; + long diff = d.getTime() - n.getTime(); + if (diff < 0) { + expiredCount++; + System.out.println(task); + expiredTasks.add(task); + } + } + } + if (expiredCount > 0) { + Ui.borderLine(); + System.out.println("\t Quack! A total of " + expiredCount + " tasks have expired!"); + System.out.println("\t Should I remove these tasks from the pending list human?"); + Ui.borderLine(); + if (Ui.doubleCheck()) { + for (Task expiredTask : expiredTasks) { + tasks.removeIf(task -> task == expiredTask); + Task.decrementCount(); + } + Storage.trySave(tasks, classes); + Ui.borderLine(); + System.out.println("\t Expired Tasks have been purged from the list!"); + System.out.println("\t I love purging things, human..."); + Ui.borderLine(); + } else { + Ui.borderLine(); + System.out.println("\t Quack! Expired tasks have not been purged."); + Ui.borderLine(); + } + } else { + System.out.println("\t Quack! No tasks have expired!"); + Ui.borderLine(); + } + } else { + System.out.println("\t Quack! No tasks currently pending!"); + Ui.borderLine(); + } + } + + /** + * Takes in the task number and adds a note to the list of notes under that task + * + * @param tasks The arraylist of tasks + * @param words The array of strings from user input + */ + static void addNote(ArrayList tasks, String[] words){ + int index = Integer.parseInt(words[1]); + if (index-1 < tasks.size() && index >= 1) { + System.out.println("\t What note would you like to add to the following task?"); + System.out.println(tasks.get(index - 1).toString()); + Ui.borderLine(); + Scanner userInput = new Scanner(System.in); + String noteToAdd = userInput.nextLine(); + tasks.get(index - 1).addNotes(noteToAdd); + System.out.println("\t The note has been added!"); + Ui.borderLine(); + } else { + Ui.exceedTaskNumberMessage(index); + } + } + + /** + * Takes in the task number and index of the note to be deleted and then deletes it + * + * @param tasks The arraylist of tasks + * @param words The array of strings from user input + */ + static void deleteNotes(ArrayList tasks, String[] words) { + int index = Integer.parseInt(words[1]); + int indexOfNoteToBeDeleted = Integer.parseInt(words[2]); + if (index-1 < tasks.size() && index >= 1) { + if (indexOfNoteToBeDeleted-1 < tasks.get(index-1).numberOfNotes() && indexOfNoteToBeDeleted >= 1) { + Ui.borderLine(); + System.out.println("\t Deleting note: "); + ArrayList noteToBeDeleted = tasks.get(index - 1).getAdditionalNotes(); + System.out.println("\t \t" + noteToBeDeleted.get(indexOfNoteToBeDeleted - 1)); + tasks.get(index - 1).deleteNote(indexOfNoteToBeDeleted); + Ui.borderLine(); + } else { + Ui.exceedNoteNumberMessage(indexOfNoteToBeDeleted); + } + } else { + Ui.exceedTaskNumberMessage(index); + } + } + + /** + * Takes in the task number and index of the note to be edited and then changes it + * + * @param tasks The arraylist of tasks + * @param words The array of strings from user input + */ + static void editNote(ArrayList tasks, String[] words) { + int index = Integer.parseInt(words[1]); + int indexOfNoteToBeEdited = Integer.parseInt(words[2]); + Scanner userInput = new Scanner(System.in); + if (index-1 < tasks.size() && index >= 1) { + if (indexOfNoteToBeEdited-1 < tasks.get(index-1).numberOfNotes() && indexOfNoteToBeEdited >= 1) { + Ui.borderLine(); + System.out.println("\t What would you like to change the note to? "); + System.out.println("\t" + "\t" + tasks.get(index - 1).getNote(indexOfNoteToBeEdited - 1)); + String editedNote = userInput.nextLine(); + tasks.get(index - 1).editNote(indexOfNoteToBeEdited - 1, editedNote); + System.out.println("\t" + "The specified note has been edited!"); + Ui.borderLine(); + } else { + Ui.exceedNoteNumberMessage(indexOfNoteToBeEdited); + } + } else { + Ui.exceedTaskNumberMessage(index); + } + } +} diff --git a/src/main/java/seedu/duck/Ui.java b/src/main/java/seedu/duck/Ui.java new file mode 100644 index 0000000000..2fa76b88eb --- /dev/null +++ b/src/main/java/seedu/duck/Ui.java @@ -0,0 +1,997 @@ +package seedu.duck; + +import seedu.duck.task.Deadline; +import seedu.duck.task.Event; +import seedu.duck.task.RecurringDeadline; +import seedu.duck.task.RecurringEvent; +import seedu.duck.task.SchoolClass; +import seedu.duck.task.Task; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.Objects; +import java.util.PriorityQueue; +import java.util.Random; +import java.util.Scanner; + + +/** + * Deals with interactions with the user + */ +public class Ui { + static void printDuck() { + System.out.println(" ,-.\n" + + " ,--' ~.).\n" + + " ,' `.\n" + + " ; (((__ __)))\n" + + " ; ( (#) ( (#)\n" + + " | \\_/___\\_/\n" + + " ,\" ,-' `__\".\n" + + " ( ( ._ ____`.)--._ _\n" + + " `._ `-.`-' \\(`-' _ `-. _,-' `-/`.\n" + + " ,') `.`._)) ,' `. `. ,',' ;\n" + + " .' . `--' / ). `. ;\n" + + " ; `- / ' ) ;\n" + + " \\ ') ,'\n" + + " \\ ,' ;\n" + + " \\ `~~~' ,'\n" + + " `. _,'\n" + + " `-._________,--'"); + } + + /** + * Prints a random motivational quote + */ + static void printMotivationalQuote() { + String[] quotes = { + "Believe you can and you're halfway there. -Theodore Roosevelt", + "Start where you are. Use what you have. Do what you can. -Arthur Ashe", + "You miss 100% of the shots you don't take. -Wayne Gretzky", + "Success is not final, failure is not fatal: it is the courage to continue that counts. -Winston Churchill", + "You are never too old to set another goal or to dream a new dream. -C.S. Lewis", + "Believe in yourself and all that you are. Know that there is something \n\t " + + "inside you that is greater than any obstacle. -Christian D. Larson", + "Success is not how high you have climbed, but how you make a positive difference to the world. -Roy T. Bennett", + "A journey of a thousand miles begins with a single step. -Lao Tzu", + "The only way to do great work is to love what you do. -Steve Jobs", + "You have brains in your head. You have feet in your shoes. You can steer yourself any direction you choose. -Dr. Seuss", + "Believe you can and you're halfway there. -Theodore Roosevelt", + "You never know how strong you are until being strong is your only choice. -Bob Marley", + "Do not wait for opportunities, create them. -Roy T. Bennett", + "Believe in yourself, take on your challenges, dig deep within yourself to conquer fears. \n\t " + + "Never let anyone bring you down. You got this. -Chantal Sutherland", + "The greatest glory in living lies not in never falling, but in rising every time we fall. -Nelson Mandela", + "Believe in your infinite potential. Your only limitations are those you set upon yourself. -Roy T. Bennett", + "What you get by achieving your goals is not as important as what you become by achieving your goals. -Zig Ziglar", + "Don't watch the clock; do what it does. Keep going. -Sam Levenson", + "Believe that you will succeed, and you will. -Dale Carnegie", + "Success is not the key to happiness. Happiness is the key to success.\n\t" + + " If you love what you are doing, you will be successful. -Albert Schweitzer" + }; + Random rand = new Random(); + int index = rand.nextInt(quotes.length); + System.out.println("\t Have some motivation! Quack!" + "\n"); + System.out.println("\t " + quotes[index]); + } + + /** + * Prints out all currently stored tasks in the list + * + * @param tasks The array list of tasks + */ + static void list(ArrayList tasks) { + int taskCount = Task.getTaskCount(); + borderLine(); + if(!tasks.isEmpty()) { + System.out.println("\t Here are the tasks in your list:"); + for (int i = 0; i < taskCount; i++) { + System.out.println("\t " + (i + 1) + "." + tasks.get(i)); + if (!tasks.get(i).getAdditionalNotes().isEmpty()) { + printList(tasks, i); + } + } + } else { + System.out.println("\t There are no tasks in the list currently!"); + } + borderLine(); + } + + static void listClasses(PriorityQueue classes, ArrayList tasks) { + TaskList.refresh(tasks, classes); + Iterator iterator = classes.iterator(); + borderLine(); + System.out.println("\t Here is your class schedule:\n"); + while (iterator.hasNext()) { + System.out.println("\t" + iterator.next()); + } + borderLine(); + } + + /** + * prints out all classes, deadlines and events happening today + * + * @param tasks array list of all tasks + * @param classes pq of all classes + */ + static void listToday(ArrayList tasks, PriorityQueue classes) { + LocalDate today = LocalDate.now(); + DayOfWeek dayToday = today.getDayOfWeek(); + borderLine(); + System.out.println("\t Here is your class schedule for today"); + for (SchoolClass c : classes) { + if (c.getDay() == dayToday) { + System.out.println(c); + } + } + System.out.println(); + DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + String dateToday = dateFormat.format(today); + System.out.println("\t Here are your tasks today"); + for (Task task : tasks) { + if (task instanceof Deadline) { + listTodayDeadline(dayToday, dateToday, task); + } else if (task instanceof Event) { + listTodayEvent(dayToday, dateToday, task); + } + } + borderLine(); + } + + /** + * checks if an event happens today and prints the task if yes + * + * @param dayToday the DayOfWeek today + * @param dateToday the date today + * @param task the task (event specifically) to be checked + */ + private static void listTodayEvent(DayOfWeek dayToday, String dateToday, Task task) { + if (task instanceof RecurringEvent) { + if (((RecurringEvent) task).getDay() == dayToday) { + System.out.println(task); + } + } else { + if (((Event) task).getStart().startsWith(dateToday)) { + System.out.println(task); + } + } + } + + /** + * checks if a deadline happens today and prints the task if yes + * + * @param dayToday the DayOfWeek today + * @param dateToday the date today + * @param task the task (deadline specifically) to be checked + */ + private static void listTodayDeadline(DayOfWeek dayToday, String dateToday, Task task) { + if (task instanceof RecurringDeadline) { + if (((RecurringDeadline) task).getDay() == dayToday) { + System.out.println(task); + } + } else { + if (((Deadline) task).getDeadline().startsWith(dateToday)) { + System.out.println(task); + } + } + } + + /** + * Prints out all currently stored tasks in the list arranged by their priority from high, medium to low + * + * @param tasks the list of tasks + */ + static void printPriorityList(ArrayList tasks) { + borderLine(); + System.out.println("\t Here are the tasks in your list arranged by priority:"); + borderLine(); + printHighPriority(tasks); + printMediumPriority(tasks); + printLowPriority(tasks); + } + + /** + * Goes through the task list and prints out the tasks that are high in priority + * + * @param tasks the list of tasks + */ + static void printHighPriority(ArrayList tasks) { + ArrayList indexOfHighPriority = new ArrayList<>(); + int taskCount = Task.getTaskCount(); + for (int i = 0; i < taskCount; i++) { + if (tasks.get(i).returnPriority() == 3) { + indexOfHighPriority.add(i); + } + } + if (!indexOfHighPriority.isEmpty()) { + System.out.println("\t QUACK QUACK QUACK!!!"); + System.out.println("\t You have " + indexOfHighPriority.size() + " tasks that are high in priority!"); + for (int i = 0; i < indexOfHighPriority.size(); i++) { + System.out.println("\t" + (i + 1) + "." + tasks.get(indexOfHighPriority.get(i))); + if (!tasks.get(i).getAdditionalNotes().isEmpty()) { + ArrayList toBePrinted = tasks.get(i).getAdditionalNotes(); + for (int j = 0; j < toBePrinted.size(); j++) { + System.out.println("\t" + "\t - " + (j + 1) + ". " + toBePrinted.get(j)); + } + } + } + } else { + System.out.println("\t There are no tasks that are high in priority!"); + } + borderLine(); + } + + /** + * Goes through the task list and prints out the tasks that are medium in priority + * + * @param tasks the list of tasks + */ + static void printMediumPriority(ArrayList tasks) { + ArrayList indexOfMediumPriority = new ArrayList<>(); + int taskCount = Task.getTaskCount(); + for (int i = 0; i < taskCount; i++) { + if (tasks.get(i).returnPriority() == 2) { + indexOfMediumPriority.add(i); + } + } + if (!indexOfMediumPriority.isEmpty()) { + System.out.println("\t QUACK QUACK!!"); + System.out.println("\t You have " + indexOfMediumPriority.size() + " tasks that are medium in priority!"); + for (int i = 0; i < indexOfMediumPriority.size(); i++) { + System.out.println("\t" + (i + 1) + "." + tasks.get(indexOfMediumPriority.get(i))); + if (!tasks.get(i).getAdditionalNotes().isEmpty()) { + ArrayList toBePrinted = tasks.get(i).getAdditionalNotes(); + for (int j = 0; j < toBePrinted.size(); j++) { + System.out.println("\t" + "\t - " + (j + 1) + ". " + toBePrinted.get(j)); + } + } + } + } else { + System.out.println("\t There are no tasks that are medium in priority!"); + } + borderLine(); + } + + /** + * Goes through the task list and prints out the tasks that are low in priority + * + * @param tasks the list of tasks + */ + static void printLowPriority(ArrayList tasks) { + ArrayList indexOfLowPriority = new ArrayList<>(); + int taskCount = Task.getTaskCount(); + for (int i = 0; i < taskCount; i++) { + if (tasks.get(i).returnPriority() == 1) { + indexOfLowPriority.add(i); + } + } + if (!indexOfLowPriority.isEmpty()) { + System.out.println("\t Quack!"); + System.out.println("\t You have " + indexOfLowPriority.size() + " tasks that are low in priority!"); + for (int i = 0; i < indexOfLowPriority.size(); i++) { + System.out.println("\t" + (i + 1) + "." + tasks.get(indexOfLowPriority.get(i))); + if (!tasks.get(i).getAdditionalNotes().isEmpty()) { + ArrayList toBePrinted = tasks.get(i).getAdditionalNotes(); + for (int j = 0; j < toBePrinted.size(); j++) { + System.out.println("\t" + "\t - " + (j + 1) + ". " + toBePrinted.get(j)); + } + } + } + } else { + System.out.println("\t There are no tasks that are low in priority!"); + } + borderLine(); + } + + /** + * Finds tasks in the list that contain keywords input by the user + * + * @param tasks The array list of tasks + * @param words The array of words generated from the user input + */ + static void find(ArrayList tasks, String[] words) { + ArrayList matchingResults = new ArrayList<>(); + ArrayList matchingResultsIndex = new ArrayList<>(); + int matchCount = 0; + String keyword = Parser.processKeywords(words, 1); + + for (int i = 0; i < tasks.size(); i++) { + if (tasks.get(i).getDescription().contains(keyword)) { + matchingResults.add(tasks.get(i)); + matchingResultsIndex.add(i + 1); + matchCount++; + } + } + printFindResults(matchingResults, matchCount, matchingResultsIndex); + } + + + /** + * Prints the results of the find command + * + * @param matchingResults The array list of tasks that contain the keywords + * @param matchCount The number of tasks in the list that contain the keywords + */ + private static void printFindResults(ArrayList matchingResults, int matchCount, + ArrayList matchingResultsIndex) { + if (matchingResults.isEmpty()) { + noMatchMessage(); + } else { + printMatchingList(matchingResults, matchCount, matchingResultsIndex); + } + } + + /** + * Prints the list of tasks that contain the keywords + * + * @param matchingResults The array list of tasks that contain the keywords + * @param matchCount The number of tasks in the list that contain the keywords + * @param matchingResultsIndex The index of the task in the main list + */ + static void printMatchingList(ArrayList matchingResults, int matchCount, + ArrayList matchingResultsIndex) { + borderLine(); + System.out.println("\t Here are the matching tasks in your list:"); + for (int i = 0; i < matchCount; i++) { + System.out.println("\t " + (i + 1) + "." + matchingResults.get(i) + + " || The index of this item is " + matchingResultsIndex.get(i)); + } + borderLine(); + } + + /** + * Display upcoming deadline + * + * @param tasks tasks store in the file + */ + static void displayUpcomingDeadline(ArrayList tasks) { + System.out.println("\t Here are the upcoming deadlines: "); + int count = 0; + for (Task t : tasks) { + if (t instanceof Deadline && !(t instanceof RecurringDeadline)) { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HHmm"); + String deadline = ((Deadline) t).getDeadline(); + Date d; + Date n = new Date(); + try { + d = format.parse(deadline); + long diff = d.getTime() - n.getTime(); + String di = getTimeDiff(diff); + String description = t.getDescription().replace("Deadlines", ""); + System.out.println("\t " + (count + 1) + "." + description + " (" + di + "before the deadline)"); + count++; + } catch (ParseException e) { + e.printStackTrace(); + } + } + } + borderLine(); + } + + /** + * Display upcoming event + * + * @param tasks tasks store in the file + */ + static void displayUpcomingEvent(ArrayList tasks) { + System.out.println("\t Here are the upcoming events: "); + int count = 0; + for (Task t : tasks) { + if (t instanceof Event && !(t instanceof RecurringEvent)) { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HHmm"); + String deadline = ((Event) t).getStart(); + Date d; + Date n = new Date(); + try { + d = format.parse(deadline); + long diff = d.getTime() - n.getTime(); + String di = getTimeDiff(diff); + String description = t.getDescription().replace("Events", ""); + System.out.println("\t " + (count + 1) + "." + description + " (" + di + "before the event start)"); + count++; + } catch (ParseException e) { + e.printStackTrace(); + } + } + } + borderLine(); + } + + /** + * Prints the list of tasks in x days in the future + * + * @param tasks the array list of all the tasks + * @param days the required the number of days x from now onwards + */ + static void printUpcomingTasks(ArrayList tasks, String days) { + borderLine(); + System.out.println("\t Here are your tasks in " + days + " days:"); + int count = 0; + Date d; + Date n = new Date(); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HHmm"); + int requiredDays = Integer.parseInt(days); + for (Task t : tasks) { + String timeUntilTask = null; + if (t instanceof Deadline && !(t instanceof RecurringDeadline)) { + timeUntilTask = ((Deadline) t).getDeadline(); + } else if (t instanceof Event && !(t instanceof RecurringEvent)) { + timeUntilTask = ((Event) t).getStart(); + } + if (timeUntilTask != null) { + try { + d = format.parse(timeUntilTask); + long diff = d.getTime() - n.getTime(); + int di = getDayDiff(diff); + if (di <= requiredDays) { + count++; + System.out.println("\t " + count + "." + t); + } + } catch (ParseException e) { + e.printStackTrace(); + } + } + } + borderLine(); + } + + /** + * Prints the list of events in x days in the future + * + * @param tasks the array list of all the tasks + * @param days the required the number of days x from now onwards + */ + static void printUpcomingEvents(ArrayList tasks, String days) { + borderLine(); + System.out.println("\t Here are your events in " + days + " days:"); + int count = 0; + Date d; + Date n = new Date(); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HHmm"); + boolean noEvent = true; + for (Task t : tasks) { + String timeUntilTask; + if (t instanceof Event && !(t instanceof RecurringEvent)) { + noEvent = false; + timeUntilTask = ((Event) t).getStart(); + try { + d = format.parse(timeUntilTask); + long diff = d.getTime() - n.getTime(); + String di = getTimeDiff(diff); + String[] diffSplit = di.split(" "); + if (diffSplit.length >= 2 && ((diffSplit[1].contains("day") && Integer.parseInt(diffSplit[0]) + <= Integer.parseInt(days)) || diffSplit[1].contains("hour") + || diffSplit[1].contains("minute"))) { + count++; + System.out.println("\t " + count + "." + t); + } + } catch (ParseException e) { + e.printStackTrace(); + } + } + } + if (noEvent) { + System.out.println("\t No Upcoming Events!"); + } + borderLine(); + } + + /** + * Prints the list of deadlines in x days in the future + * + * @param tasks the array list of all the tasks + * @param days the required the number of days x from now onwards + */ + static void printUpcomingDeadline(ArrayList tasks, String days) { + borderLine(); + System.out.println("\t Here are your deadlines in " + days + " days:"); + int count = 0; + Date d; + Date n = new Date(); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HHmm"); + boolean noDeadline = true; + for (Task t : tasks) { + String timeUntilTask; + if (t instanceof Deadline && !(t instanceof RecurringDeadline)) { + noDeadline = false; + timeUntilTask = ((Deadline) t).getDeadline(); + try { + d = format.parse(timeUntilTask); + long diff = d.getTime() - n.getTime(); + String di = getTimeDiff(diff); + String[] diffSplit = di.split(" "); + if (diffSplit.length >= 2 && ((diffSplit[1].contains("day") && Integer.parseInt(diffSplit[0]) + <= Integer.parseInt(days)) || diffSplit[1].contains("hour") + || diffSplit[1].contains("minute"))) { + count++; + System.out.println("\t " + count + "." + t); + } + } catch (ParseException e) { + e.printStackTrace(); + } + } + } + if (noDeadline) { + System.out.println("\t No Upcoming Deadline!"); + } + borderLine(); + } + + /** + * gets the day difference + * @param timeDifferenceMilliseconds time difference between now and the input time + * @return day difference + */ + static int getDayDiff(long timeDifferenceMilliseconds) { + return (int) (timeDifferenceMilliseconds / (secondsPerMinute * minutesPerHour * 1000 * hoursPerDay)); + } + + static final int secondsPerMinute = 60; + static final int minutesPerHour = 60; + static final int hoursPerDay = 24; + static final double daysPerMonth = 30.41666666; + static final int monthsPerYear = 12; + + /** + * Function help for calculating time difference + * + * @param timeDifferenceMilliseconds time difference between now and deadline + * @return time difference in structured format + */ + static String getTimeDiff(long timeDifferenceMilliseconds) { + long diffMinutes = timeDifferenceMilliseconds / (secondsPerMinute * 1000) % minutesPerHour; + long diffHours = timeDifferenceMilliseconds / (secondsPerMinute * minutesPerHour * 1000) % hoursPerDay; + long diffDays = (long) (timeDifferenceMilliseconds / (secondsPerMinute * minutesPerHour * 1000 * hoursPerDay) % daysPerMonth); + long diffMonths = (long) (timeDifferenceMilliseconds / (secondsPerMinute * minutesPerHour * 1000 * hoursPerDay * daysPerMonth)) % monthsPerYear; + long diffYears = (long) (timeDifferenceMilliseconds / ( secondsPerMinute * minutesPerHour * 1000 * hoursPerDay * daysPerMonth * monthsPerYear)); + String result = ""; + if (diffYears != 0) { + result += diffYears; + result += " year"; + if (diffYears != 1) { + result += "s"; + } + result += " "; + } + if (diffMonths != 0) { + result += diffMonths; + result += " month"; + if (diffMonths != 1) { + result += "s"; + } + result += " "; + } + if (diffDays != 0) { + result += diffDays; + result += " day"; + if (diffDays != 1) { + result += "s"; + } + result += " "; + } + if (diffHours != 0) { + result += diffHours; + result += " hour"; + if (diffHours != 1) { + result += "s"; + } + result += " "; + } + if (diffMinutes != 0) { + result += diffMinutes; + result += " minute"; + if (diffMinutes != 1) { + result += "s"; + } + result += " "; + } + return result; + } + + /** + * Display Next Upcoming Class + * + * @param classes the priority queue of all the classes + */ + static void displayNextUpcomingClass(PriorityQueue classes) { + borderLine(); + ArrayList result = new ArrayList<>(classes); + System.out.println("\t Here are your next upcoming class: "); + if (result.isEmpty()) { + System.out.println("\t No upcoming class!"); + } else { + for (SchoolClass c : result) { + if (c.getStatusIcon() != "X") { + System.out.println("\t" + c); + } + } + } + borderLine(); + } + + /** + * Display Next Upcoming Event + * + * @param tasks the array list of all the tasks + */ + static void displayNextUpcomingEvent(ArrayList tasks) { + borderLine(); + System.out.println("\t Here are your next upcoming event: "); + for (int i = 0; i < tasks.size(); i++) { + if (tasks.get(i) instanceof Event && tasks.get(i).getStatusIcon() != "X") { + System.out.println("\t " + tasks.get(i)); + break; + } else if (i == tasks.size() - 1) { + System.out.println("\t No upcoming event!"); + } + } + borderLine(); + } + + /** + * Display Next Upcoming Deadline + * + * @param tasks the array list of all the tasks + */ + static void displayNextUpcomingDeadline(ArrayList tasks) { + borderLine(); + System.out.println("\t Here are your next upcoming event: "); + for (int i = 0; i < tasks.size(); i++) { + if (tasks.get(i) instanceof Deadline && tasks.get(i).getStatusIcon() != "X") { + System.out.println("\t" + tasks.get(i)); + break; + } else if (i == tasks.size() - 1) { + System.out.println("\t No upcoming deadline!"); + } + } + borderLine(); + } + + /** + * Display Next Upcoming Task + * + * @param tasks the array list of all the tasks + */ + static void displayNextUpcomingTask(ArrayList tasks) { + borderLine(); + System.out.println("\t Here are your next upcoming event: "); + if (tasks.isEmpty()) { + System.out.println("\t no upcoming task"); + } else { + System.out.println("\t" + tasks.get(0)); + } + borderLine(); + } + + /** + * Prints the border for opening or closing messages + */ + static void borderLine() { + System.out.println("\t____________________________________________________________"); + } + + private static void noMatchMessage() { + borderLine(); + System.out.println("\t There are no matching tasks in your list."); + borderLine(); + } + + static void emptyCommandMessage() { + borderLine(); + System.out.println("\t Please enter a non-empty command."); + borderLine(); + } + + static boolean doubleCheck() { + System.out.println("\t THIS IS AN IRREVERSIBLE PROCESS. ARE YOU SURE? Y/N"); + Scanner in = new Scanner(System.in); + String line; + line = in.nextLine(); + line = line.toUpperCase(); + return Objects.equals(line, "Y"); + } + + static void editTodoMessage() { + borderLine(); + System.out.println("\t Please enter a new Todo description:"); + } + + static void editDeadlineMessage() { + borderLine(); + System.out.println("\t Please edit one of the following:"); + System.out.println("\t For non-recurring deadlines: /description or /deadline"); + System.out.println("\t For recurring deadlines: /description or /deadline or /day"); + System.out.println("\t Please follow the format: "); + System.out.println("\t /description or /deadline or /day "); + System.out.println("\t e.g. /deadline 2023-06-30 1200 or /deadline 1200 (for recurring deadlines)"); + } + + static void editEventMessage() { + borderLine(); + System.out.println("\t Please edit one of the following:"); + System.out.println("\t For non-recurring events: /description or /from or /to"); + System.out.println("\t For recurring deadlines: /description or /from or /to or /day"); + System.out.println("\t Please follow the format: "); + System.out.println("\t /description or /from or /day "); + System.out.println("\t e.g. /from 2023-06-30 1200 or /from 1200 (for recurring events)"); + } + + static void printEditedTask(Task task) { + borderLine(); + System.out.println("\t Quack!"); + System.out.println("\t I have changed your task to:"); + System.out.println("\t " + task); + borderLine(); + } + + static String askForEditMessage() { + Scanner in = new Scanner(System.in); + String line = in.nextLine(); + return line; + } + + static void help() { + borderLine(); + System.out.println("\t Quack! Here are the commands you can give me:"); + System.out.println("\t - list: I'll list out all the tasks you have recorded."); + System.out.println("\t - list : I'll list out all the tasks in that number of days."); + System.out.println("\t - list_classes: I'll list out the classes you have on your schedule."); + System.out.println("\t - list_today: I'll list out all the classes, deadlines and events you have today."); + System.out.println("\t - priority_list: " + + "I'll list out all the tasks you have recorded arranged by their priority."); + System.out.println("\t - upcoming_class: I'll list out the next upcoming class."); + System.out.println("\t - priority <1/2/3>: I'll set the priority of a given task as"); + System.out.println("\t 1:Low, 2:Medium and 3:High."); + System.out.println("\t Default: Low priority."); + System.out.println("\t - low_priority: I'll list out all the tasks you have that are low in priority."); + System.out.println("\t - medium_priority: I'll list out all the tasks you have that are medium in priority."); + System.out.println("\t - high_priority: I'll list out all the tasks you have that are high in priority."); + System.out.println("\t - clear: The list will be cleared. This is an IRREVERSIBLE process."); + System.out.println("\t - mark : I'll mark that task as done."); + System.out.println("\t - unmark : I'll mark that task as undone."); + System.out.println("\t - delete : I'll delete that task from your list."); + System.out.println("\t - remove /class /description " + + "/day /from /to "); + System.out.println("\t (/description can be followed by whitespace if the class has no description."); + System.out.println("\t : I'll remove this class from your class schedule."); + System.out.println("\t - add_notes : I'll add an additional note to that task!"); + System.out.println("\t - delete_notes : I'll delete the note to that task!"); + System.out.println("\t - edit_notes : I'll edit the note for that task!"); + System.out.println("\t - view_notes : I'll print the additional notes for that task!"); + System.out.println("\t - purge: I'll delete all expired tasks from your list after a confirmation."); + System.out.println("\t - find : I'll find the tasks in your list that contain the keyword."); + System.out.println("\t - The index of the item will also be displayed."); + System.out.println("\t - motivation: I'll print a random motivational quack for you!"); + System.out.println("\t - bye: I will shut down my program.\n"); + System.out.println("\t Here are the following ways to input tasks/classes:"); + System.out.println("\t Deadlines: /by "); + System.out.println("\t (eg. Eat bread /by 2023-03-15 2015)"); + System.out.println("\t Recurring deadlines: /re /by /day "); + System.out.println("\t (eg. /re Eat bread /by 2015 /day MONDAY)"); + System.out.println("\t Events : /from /to "); + System.out.println("\t (eg. Meeting /from 2023-03-15 2015 /to 2023-03-15 2215)"); + System.out.println("\t Recurring events: /re /from /to /day "); + System.out.println("\t (eg. /re Meeting /from 2015 /to 2215 /day MONDAY)"); + System.out.println("\t Todo : /todo "); + System.out.println("\t (eg. /todo Water the plants)"); + System.out.println("\t Classes : /class /day " + + "/from /to "); + System.out.println("\t (eg. Bring laptop /class CS2113 /day TUESDAY /from 1100 /to 1200) \n"); + System.out.println("\t How else may I assist you today, human?"); + borderLine(); + } + + static void addedTaskMessage(Task currentTask) { + borderLine(); + System.out.println("\t Alright, I have added this task: \n\t" + currentTask); + System.out.println("\t You now have " + (Task.getTaskCount() + 1) + " tasks in your list."); + borderLine(); + } + + static void addedSchoolClassMessage(SchoolClass currentClass, PriorityQueue classes) { + borderLine(); + System.out.println("\t Alright, I have added this class: \n\t" + currentClass); + System.out.println("\t You now have " + (classes.size()) + " classes in your schedule."); + borderLine(); + } + + static void deleteTaskMessage(Task taskToDelete) { + borderLine(); + System.out.println("\t Understood. I have removed this task:"); + System.out.println("\t" + taskToDelete); + System.out.println("\t You now have " + Task.getTaskCount() + " tasks in your list."); + borderLine(); + } + + static void deleteClassMessage() { + borderLine(); + System.out.println("\t Class has been deleted successfully."); + borderLine(); + } + + static void unsuccessfulDeleteClassMessage() { + borderLine(); + System.out.println("\t Unsuccessful. No class has been deleted."); + borderLine(); + } + + static void refreshedMessage() { + borderLine(); + System.out.println("\t Your task list and class schedule have been refreshed!"); + borderLine(); + } + + static void exceedTaskNumberMessage(int taskNumber) { + borderLine(); + System.out.println("\t Task " + taskNumber + " does not exist."); + borderLine(); + } + + static void exceedNoteNumberMessage(int noteNumber) { + borderLine(); + System.out.println("\t Note " + noteNumber + " does not exist for this task"); + borderLine(); + } + + static void todoErrorMessage() { + borderLine(); + System.out.println("\t Error. Please enter a valid description."); + borderLine(); + } + + static void unknownCommandMessage() { + borderLine(); + System.out.println("\t Error. Please check that the command has been entered correctly."); + borderLine(); + } + + static void expiredErrorMessage() { + borderLine(); + System.out.println("\t Quack! I know humans wish to undo their past mistakes, " + + "but the start date has already passed!"); + System.out.println("\t Please try again!"); + borderLine(); + } + + static void emptyDescriptionErrorMessage() { + borderLine(); + System.out.println("\t Error. Description cannot be empty"); + borderLine(); + } + + static void startAfterEndErrorMessage() { + borderLine(); + System.out.println("\t Quack! Somehow this human has time travelled, " + + "and the start date seems to be after the end date! "); + System.out.println("\t Please try again!"); + borderLine(); + } + + static void eventErrorMessage() { + borderLine(); + System.out.println("\t Error. Please enter a valid description, start time and end time"); + borderLine(); + } + + static void deadlineErrorMessage() { + borderLine(); + System.out.println("\t Error. Please enter a valid description and deadline."); + borderLine(); + } + + static void schoolClassErrorMessage() { + borderLine(); + System.out.println("\t Error. Please enter a valid class name, description, start time and end time"); + borderLine(); + } + + static void invalidDayMessage() { + borderLine(); + System.out.println("\t Error. Please enter a valid day of week in all capital letters (Eg. MONDAY)."); + borderLine(); + } + + static void emptyDayErrorMessage() { + borderLine(); + System.out.println("\t Error. Please enter a day of week."); + borderLine(); + } + + static void priorityErrorMessage() { + Ui.borderLine(); + System.out.println("\t Please enter a priority from 1 to 3!"); + Ui.borderLine(); + } + + static void invalidRemoveClassMessage() { + borderLine(); + System.out.println("\t Error. Please follow the correct format to remove classes."); + borderLine(); + } + + static void invalidDateTimeMessage() { + borderLine(); + System.out.println("\t Please check the inputted format human!\n" + + "\t Try typing 'help' if you are not sure what the correct format is!\n"); + System.out.println("\t Please try again!"); + borderLine(); + } + + static void loadingErrorMessage() { + borderLine(); + System.out.println("\t Error loading save file."); + borderLine(); + } + + /** + * Prints the startup message, includes instructions on available commands + */ + static void greetingMessage() { + printDuck(); + borderLine(); + printMotivationalQuote(); + borderLine(); + System.out.println("\t Quack! Nice to meet you human. As you can see, I'm a Duck."); + System.out.println("\t As a Duck, I can only understand simple commands. Quack. " + + "Human speech is so confusing!"); + System.out.println("\t That being said, I am a smart Duck. " + + "If you wish to know what I understand, just enter 'help'."); + System.out.println("\t How may I assist you today, human?"); + } + + /** + * Prints out the notes of the task whose index was taken in + * + * @param tasks The arraylist of tasks + * @param words The index of the task + */ + static void printNotes(ArrayList tasks, String[] words) { + int index = Integer.parseInt(words[1]); + if (index > tasks.size() || index <= 0) { + Ui.exceedTaskNumberMessage(index); + } else { + ArrayList toBePrinted = tasks.get(index - 1).getAdditionalNotes(); + borderLine(); + if (!toBePrinted.isEmpty()) { + System.out.println("\t Here are the notes for that task quack!"); + System.out.println(tasks.get(index - 1).toString()); + for (int i = 0; i < toBePrinted.size(); i++) { + System.out.println("\t \t" + (i + 1) + ". " + toBePrinted.get(i)); + } + } else { + System.out.println("\t There are no notes for this task!"); + } + borderLine(); + } + } + + /** + * Prints the lists of notes under the specified tasks + * + * @param tasks The arraylist of tasks + * @param index The index of the task whose notes are to be printed + */ + static void printList(ArrayList tasks, int index) { + ArrayList toBePrinted = tasks.get(index).getAdditionalNotes(); + for (int j = 0; j < toBePrinted.size(); j++) { + System.out.println("\t" + "\t - " + (j + 1) + ". " + toBePrinted.get(j)); + } + } + + /** + * Prints the exiting message when closing the program + */ + static void exitMessage() { + printDuck(); + borderLine(); + System.out.println("\t Bye. Hope to see you again soon!"); + borderLine(); + } + + +} diff --git a/src/main/java/seedu/duck/exception/EmptyDescriptionException.java b/src/main/java/seedu/duck/exception/EmptyDescriptionException.java new file mode 100644 index 0000000000..fd61332512 --- /dev/null +++ b/src/main/java/seedu/duck/exception/EmptyDescriptionException.java @@ -0,0 +1,4 @@ +package seedu.duck.exception; + +public class EmptyDescriptionException extends Exception{ +} diff --git a/src/main/java/seedu/duck/exception/IllegalDeadlineException.java b/src/main/java/seedu/duck/exception/IllegalDeadlineException.java new file mode 100644 index 0000000000..394193a618 --- /dev/null +++ b/src/main/java/seedu/duck/exception/IllegalDeadlineException.java @@ -0,0 +1,5 @@ +package seedu.duck.exception; + +public class IllegalDeadlineException extends Exception { + +} diff --git a/src/main/java/seedu/duck/exception/IllegalEventException.java b/src/main/java/seedu/duck/exception/IllegalEventException.java new file mode 100644 index 0000000000..e3fa7261fa --- /dev/null +++ b/src/main/java/seedu/duck/exception/IllegalEventException.java @@ -0,0 +1,5 @@ +package seedu.duck.exception; + +public class IllegalEventException extends Exception { + +} diff --git a/src/main/java/seedu/duck/exception/IllegalSchoolClassException.java b/src/main/java/seedu/duck/exception/IllegalSchoolClassException.java new file mode 100644 index 0000000000..b5268f20ac --- /dev/null +++ b/src/main/java/seedu/duck/exception/IllegalSchoolClassException.java @@ -0,0 +1,4 @@ +package seedu.duck.exception; + +public class IllegalSchoolClassException extends Exception{ +} diff --git a/src/main/java/seedu/duck/exception/IllegalTodoException.java b/src/main/java/seedu/duck/exception/IllegalTodoException.java new file mode 100644 index 0000000000..8ac39560e7 --- /dev/null +++ b/src/main/java/seedu/duck/exception/IllegalTodoException.java @@ -0,0 +1,5 @@ +package seedu.duck.exception; + +public class IllegalTodoException extends Exception { + +} diff --git a/src/main/java/seedu/duck/exception/expiredDateException.java b/src/main/java/seedu/duck/exception/expiredDateException.java new file mode 100644 index 0000000000..f34db55da8 --- /dev/null +++ b/src/main/java/seedu/duck/exception/expiredDateException.java @@ -0,0 +1,5 @@ +package seedu.duck.exception; + +public class expiredDateException extends Exception { + +} diff --git a/src/main/java/seedu/duck/exception/startAfterEndException.java b/src/main/java/seedu/duck/exception/startAfterEndException.java new file mode 100644 index 0000000000..a81a5f10d7 --- /dev/null +++ b/src/main/java/seedu/duck/exception/startAfterEndException.java @@ -0,0 +1,5 @@ +package seedu.duck.exception; + +public class startAfterEndException extends Exception { + +} diff --git a/src/main/java/seedu/duck/task/Deadline.java b/src/main/java/seedu/duck/task/Deadline.java new file mode 100644 index 0000000000..08afc3027b --- /dev/null +++ b/src/main/java/seedu/duck/task/Deadline.java @@ -0,0 +1,28 @@ +package seedu.duck.task; + +public class Deadline extends Task { + private String by; + + public Deadline(String description, String deadline) { + super(description); + by = deadline; + } + + public void setDeadline(String deadline) { + by = deadline; + } + + public String getDeadline() { + return by; + } + + @Override + public String toSaveString() { + return super.toSaveString() + " /by " + getDeadline() + "

" + getPriorityIndex() + " " + getSavedNotes() + System.lineSeparator(); + } + + @Override + public String toString() { + return "\t [D]" + super.toString() + " (by: " + by + ")" + " (" + getPriority() + ")"; + } +} diff --git a/src/main/java/seedu/duck/task/Event.java b/src/main/java/seedu/duck/task/Event.java new file mode 100644 index 0000000000..1ece8fd64c --- /dev/null +++ b/src/main/java/seedu/duck/task/Event.java @@ -0,0 +1,40 @@ +package seedu.duck.task; + +public class Event extends Task { + private String start; // Start date/time + private String end; // End date/time + + public Event(String description, String start, String end) { + super(description); + this.start = start; + this.end = end; + } + + public String getStart() { + return start; + } + + public void setStart(String start) { + this.start = start; + } + + public String getEnd() { + return end; + } + + public void setEnd(String end) { + this.end = end; + } + + @Override + public String toSaveString() { + return super.toSaveString() + " /from " + getStart() + " /to " + getEnd() + + "

" + getPriorityIndex() + " " + getSavedNotes() + System.lineSeparator(); + } + + @Override + public String toString() { + return "\t [E]" + super.toString() + " (from: " + start + + " to: " + end + ")" + " (" + getPriority() + ")"; + } +} diff --git a/src/main/java/seedu/duck/task/RecurringDeadline.java b/src/main/java/seedu/duck/task/RecurringDeadline.java new file mode 100644 index 0000000000..c19a1a3d71 --- /dev/null +++ b/src/main/java/seedu/duck/task/RecurringDeadline.java @@ -0,0 +1,31 @@ +package seedu.duck.task; + +import java.time.DayOfWeek; +public class RecurringDeadline extends Deadline{ + private DayOfWeek day; + + public RecurringDeadline(String description, String by, DayOfWeek day) { + super(description, by); + this.day = day; + } + + public DayOfWeek getDay() { + return this.day; + } + + public void setDay(DayOfWeek day) { + this.day = day; + } + + @Override + public String toSaveString() { + return getDoneConditionString() + " " + getDescription() + " /by " + getDeadline() + "

" + + getPriorityIndex() + " /day " + day + System.lineSeparator(); + } + + @Override + public String toString() { + return "\t [D]" + "[" + getStatusIcon() + "] " + getDescription() + " (by: " + super.getDeadline() + ")" + + " (every " + day + ") (" + getPriority() + ")"; + } +} diff --git a/src/main/java/seedu/duck/task/RecurringEvent.java b/src/main/java/seedu/duck/task/RecurringEvent.java new file mode 100644 index 0000000000..ea6900b735 --- /dev/null +++ b/src/main/java/seedu/duck/task/RecurringEvent.java @@ -0,0 +1,32 @@ +package seedu.duck.task; + +import java.time.DayOfWeek; + +public class RecurringEvent extends Event { + private DayOfWeek day; + + public RecurringEvent(String description, String start, String end, DayOfWeek day) { + super(description, start, end); + this.day = day; + } + + public DayOfWeek getDay() { + return this.day; + } + + public void setDay(DayOfWeek day) { + this.day = day; + } + + @Override + public String toSaveString() { + return getDoneConditionString() + " " + getDescription() + " /from " + getStart() + " /to " + getEnd() + + "

" + getPriorityIndex() + " /day " + day + System.lineSeparator(); + } + + @Override + public String toString() { + return "\t [E]" + "[" + getStatusIcon() + "] " + getDescription() + " (from: " + super.getStart() + + " to: " + super.getEnd() + ")" + " (every " + day + ") (" + getPriority() + ")"; + } +} diff --git a/src/main/java/seedu/duck/task/SchoolClass.java b/src/main/java/seedu/duck/task/SchoolClass.java new file mode 100644 index 0000000000..c78d241d1a --- /dev/null +++ b/src/main/java/seedu/duck/task/SchoolClass.java @@ -0,0 +1,84 @@ +package seedu.duck.task; + +import java.time.DayOfWeek; + +public class SchoolClass extends Task implements Comparable { + private String className; // Name of class + private DayOfWeek day; // Day of the week + private String start; // Start date/time + private String end; // End date/time + + public SchoolClass(String className, String description, DayOfWeek day, String start, String end) { + super(description); + this.className = className; + this.day = day; + this.start = start; + this.end = end; + } + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + public DayOfWeek getDay() { + return this.day; + } + + public String getStart() { + return start; + } + + public void setStart(String start) { + this.start = start; + } + + public String getEnd() { + return end; + } + + public void setEnd(String end) { + this.end = end; + } + + @Override + public int compareTo(SchoolClass lesson) { + if (lesson.day.compareTo(this.day) == 0) { + return this.start.compareTo(lesson.start); + } else { + return this.day.compareTo(lesson.day); + } + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || obj.getClass() != this.getClass()) { + return false; + } + SchoolClass lesson = (SchoolClass) obj; + return className.equals(lesson.getClassName()) && this.getDescription().equals(lesson.getDescription()) + && day.equals(lesson.getDay()) && start.equals(lesson.getStart()) && end.equals(lesson.getEnd()); + } + + @Override + public String toSaveString() { + return super.toSaveString() + " /class " + getClassName() + " /day " + getDay() + " /from " + + getStart() + " /to "+ getEnd() + System.lineSeparator(); + } + + @Override + public String toString() { + if (getDescription().isBlank()) { + return "\t [" + day + "]" + "[" + getStatusIcon() + "] " + className + " (from: " + + start + " to: " + end + ")"; + } + return "\t [" + day + "]" + "[" + getStatusIcon() + "] " + className + ": " + + getDescription() + " (from: " + start + " to: " + end + ")"; + } +} diff --git a/src/main/java/seedu/duck/task/Task.java b/src/main/java/seedu/duck/task/Task.java new file mode 100644 index 0000000000..76f344e262 --- /dev/null +++ b/src/main/java/seedu/duck/task/Task.java @@ -0,0 +1,137 @@ +package seedu.duck.task; +import java.util.ArrayList; + +public class Task { + private static int taskCount; + private String description; + private int priority; + private boolean isDone; + private final ArrayList additionalNotes = new ArrayList(); + + public Task(String description) { + this.description = description; + this.isDone = false; + this.priority = 1; //automatically set to low priority + } + + public void setDescription(String description) { + this.description = description; + } + public void setPriority(String priority) { + this.priority = Integer.parseInt(priority); + } + + public String getDescription() { + return this.description; + } + + public static void clearCount() { + taskCount = 0; + } + + public void markAsDone() { + this.isDone = true; + } + + public void markAsNotDone() { + this.isDone = false; + } + + public String getStatusIcon() { + if(isDone) { + // Mark done task with X + return "X"; + } + return " "; + } + public String hasNotes(){ + if (additionalNotes.isEmpty()){ + return "0"; + } else { + return "1"; + } + } + public String getSavedNotes(){ + String save = ""; + save += hasNotes(); + for (int i = 0; i < additionalNotes.size(); i++){ + save += additionalNotes.get(i); + if (!(i==additionalNotes.size()-1)) { + save += "@"; + } + } + return save; + } + public void addNotes(String noteToAdd){ + this.additionalNotes.add(noteToAdd); + } + + public ArrayList getAdditionalNotes(){ + return this.additionalNotes; + } + public String getNote(int index){ + return this.additionalNotes.get(index); + } + + public void deleteNote(int indexToBeDeleted){ + this.additionalNotes.remove(indexToBeDeleted-1); + } + + public void editNote(int indexToBeEdited, String editedNote){ + this.additionalNotes.set(indexToBeEdited,editedNote); + } + + public int numberOfNotes(){ + return this.additionalNotes.size(); + } + + public String getPriority() { + if (priority == 1) { + // Mark done task with X + return "Low priority."; + } else if(priority == 2) { + // Mark done task with X + return "Medium priority."; + } else if(priority == 3) { + // Mark done task with X + return "High priority."; + } + return "No priority established."; + } + + public int returnPriority(){ + return this.priority; + } + + public int getPriorityIndex() { + return this.priority; + } + + public static void incrementCount() { + taskCount++; + } + + public static void decrementCount() { + taskCount--; + } + + public static int getTaskCount() { + return taskCount; + } + + public String getDoneConditionString() { + if (isDone) { + return "1"; + } + return "0"; + } + + public String toSaveString() { + return getDoneConditionString() + " " + getDescription(); + } + + @Override + public String toString() { + return "[" + getStatusIcon() + "] " + getDescription(); + } +} diff --git a/src/main/java/seedu/duck/task/Todo.java b/src/main/java/seedu/duck/task/Todo.java new file mode 100644 index 0000000000..cb94286ee2 --- /dev/null +++ b/src/main/java/seedu/duck/task/Todo.java @@ -0,0 +1,18 @@ +package seedu.duck.task; + +public class Todo extends Task { + + public Todo(String description) { + super(description); + } + + @Override + public String toSaveString() { + return super.toSaveString() + "

" + getPriorityIndex() + " " + getSavedNotes() + System.lineSeparator(); + } + + @Override + public String toString() { + return "\t [T]" + super.toString() + " (" + getPriority() + ")"; + } +} diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java deleted file mode 100644 index 5c74e68d59..0000000000 --- a/src/main/java/seedu/duke/Duke.java +++ /dev/null @@ -1,21 +0,0 @@ -package seedu.duke; - -import java.util.Scanner; - -public class Duke { - /** - * Main entry-point for the java.duke.Duke application. - */ - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - System.out.println("What is your name?"); - - Scanner in = new Scanner(System.in); - System.out.println("Hello " + in.nextLine()); - } -} diff --git a/src/test/java/seedu/duck/ParserTest.java b/src/test/java/seedu/duck/ParserTest.java new file mode 100644 index 0000000000..5520fce1ab --- /dev/null +++ b/src/test/java/seedu/duck/ParserTest.java @@ -0,0 +1,24 @@ +package seedu.duck; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import org.junit.jupiter.api.Test; + +public class ParserTest { + + String[] words = {"remove", "keyword"}; + + @Test + public void testIsNumeric() { + assertTrue(Parser.isNumeric("2")); + assertTrue(Parser.isNumeric("5")); + assertFalse(Parser.isNumeric("letter")); + } + + @Test + public void testProcessKeywords() { + assertEquals("keyword", Parser.processKeywords(words,1)); + } +} diff --git a/src/test/java/seedu/duck/TaskListTest.java b/src/test/java/seedu/duck/TaskListTest.java new file mode 100644 index 0000000000..736a3bb4ea --- /dev/null +++ b/src/test/java/seedu/duck/TaskListTest.java @@ -0,0 +1,92 @@ +package seedu.duck; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import seedu.duck.exception.IllegalDeadlineException; +import seedu.duck.exception.IllegalEventException; +import seedu.duck.exception.IllegalTodoException; +import seedu.duck.exception.expiredDateException; +import seedu.duck.exception.startAfterEndException; +import seedu.duck.task.Deadline; +import seedu.duck.task.Event; +import seedu.duck.task.RecurringDeadline; +import seedu.duck.task.RecurringEvent; +import seedu.duck.task.Task; +import seedu.duck.task.Todo; + +import java.util.ArrayList; + +public class TaskListTest { + @Test + public void addTodo_correctCommand_correctTypeAdded() throws IllegalTodoException { + ArrayList testTasks = new ArrayList<>(); + String line = "/todo todo"; + TaskList.addTodo(line, testTasks); + assertTrue(testTasks.get(0) instanceof Todo); + } + + @Test + public void addTodo_correctCommand_correctDescriptionAdded() throws IllegalTodoException { + ArrayList testTasks = new ArrayList<>(); + String line = "/todo todo"; + TaskList.addTodo(line, testTasks); + assertEquals("todo", testTasks.get(0).getDescription()); + } + @Test + public void addDeadline_correctCommand() throws expiredDateException, IllegalDeadlineException { + ArrayList testTasks = new ArrayList<>(); + String line = "deadline /by 2023-04-30 1200"; + TaskList.addDeadline(line, testTasks); + assertTrue(testTasks.get(0) instanceof Deadline && !(testTasks.get(0) instanceof RecurringDeadline)); + assertEquals("deadline", testTasks.get(0).getDescription()); + } + + @Test + public void addRecurringDeadline_correctCommand() throws IllegalDeadlineException { + ArrayList testTasks = new ArrayList<>(); + String line = "/re deadline /by 1200 /day MONDAY"; + TaskList.addRecurringDeadline(line, testTasks); + assertTrue(testTasks.get(0) instanceof RecurringDeadline); + assertEquals("deadline", testTasks.get(0).getDescription()); + } + + @Test + public void addEvent_correctCommand() throws expiredDateException, startAfterEndException, + IllegalEventException { + ArrayList testTasks = new ArrayList<>(); + String line = "event /from 2023-04-30 1200 /to 2023-04-30 1900"; + TaskList.addEvent(line, testTasks); + assertTrue(testTasks.get(0) instanceof Event && !(testTasks.get(0) instanceof RecurringEvent)); + assertEquals("event", testTasks.get(0).getDescription()); + } + + @Test + public void addRecurringEvent_correctCommand() throws IllegalEventException { + ArrayList testTasks = new ArrayList<>(); + String line = "/re event /from 1200 /to 1900 /day MONDAY"; + TaskList.addRecurringEvent(line, testTasks); + assertTrue(testTasks.get(0) instanceof RecurringEvent); + assertEquals("event", testTasks.get(0).getDescription()); + } + + @Test + public void setPriority_correctCommand() { + ArrayList testTasks = new ArrayList<>(); + Todo todo = new Todo("todo"); + testTasks.add(todo); + Task.incrementCount(); + int firstPri = 2; + int secondPri = 3; + String firstTest = "priority 1 2"; + String secondTest = "priority 1 3"; + String[] wordsOne = firstTest.split(" "); + String[] wordsTwo = secondTest.split(" "); + TaskList.setPriority(testTasks, wordsOne); + assertEquals(firstPri, testTasks.get(0).getPriorityIndex()); + TaskList.setPriority(testTasks, wordsTwo); + assertEquals(secondPri, testTasks.get(0).getPriorityIndex()); + } +} diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/duke/DukeTest.java deleted file mode 100644 index 2dda5fd651..0000000000 --- a/src/test/java/seedu/duke/DukeTest.java +++ /dev/null @@ -1,12 +0,0 @@ -package seedu.duke; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - -class DukeTest { - @Test - public void sampleTest() { - assertTrue(true); - } -} diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT deleted file mode 100644 index 892cb6cae7..0000000000 --- a/text-ui-test/EXPECTED.TXT +++ /dev/null @@ -1,9 +0,0 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| - -What is your name? -Hello James Gosling diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt deleted file mode 100644 index f6ec2e9f95..0000000000 --- a/text-ui-test/input.txt +++ /dev/null @@ -1 +0,0 @@ -James Gosling \ No newline at end of file diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat deleted file mode 100644 index 25ac7a2989..0000000000 --- a/text-ui-test/runtest.bat +++ /dev/null @@ -1,19 +0,0 @@ -@echo off -setlocal enableextensions -pushd %~dp0 - -cd .. -call gradlew clean shadowJar - -cd build\libs -for /f "tokens=*" %%a in ( - 'dir /b *.jar' -) do ( - set jarloc=%%a -) - -java -jar %jarloc% < ..\..\text-ui-test\input.txt > ..\..\text-ui-test\ACTUAL.TXT - -cd ..\..\text-ui-test - -FC ACTUAL.TXT EXPECTED.TXT >NUL && ECHO Test passed! || Echo Test failed! diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh deleted file mode 100755 index 1dcbd12021..0000000000 --- a/text-ui-test/runtest.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash - -# change to script directory -cd "${0%/*}" - -cd .. -./gradlew clean shadowJar - -cd text-ui-test - -java -jar $(find ../build/libs/ -mindepth 1 -print -quit) < input.txt > ACTUAL.TXT - -cp EXPECTED.TXT EXPECTED-UNIX.TXT -dos2unix EXPECTED-UNIX.TXT ACTUAL.TXT -diff EXPECTED-UNIX.TXT ACTUAL.TXT -if [ $? -eq 0 ] -then - echo "Test passed!" - exit 0 -else - echo "Test failed!" - exit 1 -fi