diff --git a/.gitignore b/.gitignore
index f69985ef1f..57c7491010 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,5 +13,12 @@ src/main/resources/docs/
*.iml
bin/
+src/main/java/META-INF
+/data/
+
/text-ui-test/ACTUAL.txt
text-ui-test/EXPECTED-UNIX.TXT
+
+logging.txt
+mainTest/personal.txt
+goalStoreTest/
\ No newline at end of file
diff --git a/_config.yml b/_config.yml
new file mode 100644
index 0000000000..c4192631f2
--- /dev/null
+++ b/_config.yml
@@ -0,0 +1 @@
+theme: jekyll-theme-cayman
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index b0c5528fb5..286deaa5de 100644
--- a/build.gradle
+++ b/build.gradle
@@ -12,6 +12,9 @@ repositories {
dependencies {
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0'
testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0'
+ // https://mvnrepository.com/artifact/org.apache.commons/commons-text
+ compile group: 'org.apache.commons', name: 'commons-text', version: '1.9'
+
}
test {
@@ -43,4 +46,5 @@ checkstyle {
run{
standardInput = System.in
+ enableAssertions = true
}
diff --git a/data/goal.txt b/data/goal.txt
new file mode 100644
index 0000000000..c7c7da3c64
--- /dev/null
+++ b/data/goal.txt
@@ -0,0 +1 @@
+hello there
diff --git a/datalogging.txt b/datalogging.txt
new file mode 100644
index 0000000000..08f3c50153
--- /dev/null
+++ b/datalogging.txt
@@ -0,0 +1,112 @@
+
+
+
+
+ 2020-11-05T07:22:31.002547300Z
+ 1604560951002
+ 547300
+ 0
+ storageLog
+ FINE
+ seedu.duke.storage.Storage
+ <init>
+ 1
+ Logger created
+
+
+ 2020-11-05T07:22:36.505251800Z
+ 1604560956505
+ 251800
+ 1
+ storageLog
+ FINE
+ seedu.duke.storage.Storage
+ saveAll
+ 1
+ personal file saved
+
+
+ 2020-11-05T07:22:36.509934200Z
+ 1604560956509
+ 934200
+ 2
+ storageLog
+ FINE
+ seedu.duke.storage.Storage
+ saveAll
+ 1
+ timetable file saved
+
+
+ 2020-11-05T07:22:36.514072700Z
+ 1604560956514
+ 72700
+ 3
+ storageLog
+ FINE
+ seedu.duke.storage.Storage
+ saveAll
+ 1
+ zoom file saved
+
+
+ 2020-11-05T07:22:36.516595800Z
+ 1604560956516
+ 595800
+ 4
+ storageLog
+ FINE
+ seedu.duke.storage.Storage
+ saveAll
+ 1
+ goal file saved
+
+
+ 2020-11-05T07:22:38.502631800Z
+ 1604560958502
+ 631800
+ 5
+ storageLog
+ FINE
+ seedu.duke.storage.Storage
+ saveAll
+ 1
+ personal file saved
+
+
+ 2020-11-05T07:22:38.505605600Z
+ 1604560958505
+ 605600
+ 6
+ storageLog
+ FINE
+ seedu.duke.storage.Storage
+ saveAll
+ 1
+ timetable file saved
+
+
+ 2020-11-05T07:22:38.509933600Z
+ 1604560958509
+ 933600
+ 7
+ storageLog
+ FINE
+ seedu.duke.storage.Storage
+ saveAll
+ 1
+ zoom file saved
+
+
+ 2020-11-05T07:22:38.513581Z
+ 1604560958513
+ 581000
+ 8
+ storageLog
+ FINE
+ seedu.duke.storage.Storage
+ saveAll
+ 1
+ goal file saved
+
+
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 0f072953ea..ce0358d31d 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -2,8 +2,8 @@
Display | Name | Github Profile | Portfolio
--------|:----:|:--------------:|:---------:
- | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
+ | Colin Ng | [Github](https://github.com/Colin386) | [Portfolio](team/colin386.md)
+ | Marcus Tan | [Github](https://github.com/marcursor) | [Portfolio](team/marcursor.md)
+ | Matthew Gani | [Github](https://github.com/matthewgani) | [Portfolio](team/matthewgani.md)
+ | Low Qing Ning | [Github](https://github.com/ninggggx99) | [Portfolio](team/ninggggx99.md)
+ | Marcus Ng | [Github](https://github.com/Reinbowl) | [Portfolio](team/reinbowl.md)
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 0ec3db103d..6c26268cf8 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,34 +1,1182 @@
# Developer Guide
-## Design & implementation
+## Table of Contents
+- [Introduction](#introduction)
+- [Setting up, getting started](#setting-up-getting-started)
+- [Design](#design)
+ * [Architecture](#architecture)
+ * [UI Component](#ui-component)
+ * [Command Component](#command-component)
+ * [Parser Component](#parser-component)
+ * [UserData Component](#userdata-component)
+ * [Storage component](#storage-component)
+- [Implementation](#implementation)
+ * [Help](#help-feature)
+ * [Add](#add-feature)
+ * [List](#list-feature)
+ * [Calendar](#calendar-feature)
+ * [Repeat](#repeat-feature)
+ * [Deadline](#deadline-feature)
+ * [Check](#check-feature)
+ * [Goal](#goal-feature)
+ * [Done](#done-feature)
+ * [Undone](#undone-feature)
+ * [Delete](#delete-feature)
+ * [Note](#note-feature)
+ * [View](#view-feature)
+ * [Reminder](#reminder-feature)
+ * [Extract](#extract-feature)
+ * [Bye](#bye-feature)
+- [Appendix: Requirements](#appendix-requirements)
+ * [Product scope](#product-scope)
+ * [User Stories](#user-stories)
+ * [Use Cases](#use-cases)
+ * [Non-Functional Requirements](#non-functional-requirements)
+- [Glossary](#glossary)
+- [Instructions for manual testing](#instructions-for-manual-testing)
+ * [Launch and Shutdown](#launch-and-shutdown)
+ * [Loading and Saving Data](#loading--and-saving-data)
+ * [Adding new event](#adding-new-event)
+ * [Delete Event](#delete-event)
+ * [Repeat Event](#repeat-event)
+ * [Changing status of events](#changing-status-of-events)
+ * [Help Command](#help-command)
+ * [Printing calendar](#printing-calendar)
+ * [Checking schedule availability](#checking-schedule-availability)
+ * [Adding deadline to event](#adding-deadline-to-event)
+ * [Setting reminder](#setting-reminder)
+ * [Adding new note for event](#adding-note-for-an-event)
+ * [View note](#viewing-note-for-an-event)
-{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.}
+
+## Introduction
-## Product scope
-### Target user profile
+Scheduler-\-; (S-\-) is a desktop app for managing deadlines from different sources. The user will use a Command Line Interface to control it. It is specially designed for Computing students who are comfortable in using CLI and have Git project deadlines as well as consolidated Zoom session links due which will suit home based learning in this COVID period.
-{Describe the target user profile}
+## Setting up, getting started
-### Value proposition
+1. Ensure you have version 11 of Java. You can install it from this [link](https://docs.aws.amazon.com/corretto/latest/corretto-11-ug/downloads-list.html).
+2. Fork the Scheduler-\-; repo from this [link](https://github.com/AY2021S1-CS2113T-T12-4/tp).
+3. Clone the fork on to your computer.
+4. Open Intellij. If you are not in the welcome screen, click `File` -> 'Close project'
+to close the existing project dialog.
+5. Set up the correct JDK version for Gradle.
+ 1. Click `Configure` -> `Project Defaults` -> `Project Structure`
+ 2. Click `New...` and set it to the directory of the JDK.
+6. Click `Open or Import` in Intellij.
+7. Locate the `build.gradle` file, select it and click `OK`.
+8. If asked, choose to `Open as Project`.
+9. Click `OK` to accept the default settings.
+10. Wait for the importing process to finish.
+11. Locate run the 'Duke.java' file, right click and select `Run Duke.main()`.
+12. Navigate to the `test\java` folder and right click `seedu.duke` and select `Run Tests in 'seedu.duke'`.
+If the setup is successful, you should see this after step 10. All tests should also pass.
+````
+_________________________________
+Welcome to scheduler--;!
+What can I do for you?
+_________________________________
+File Created: Personal
+0/0 loaded successfully for Personal
+File Created: Zoom
+0/0 loaded successfully for Zoom
+File Created: Timetable
+0/0 loaded successfully for Timetable
+File Created: Goal
+The file has successfully been loaded!
+_________________________________
+You have no events today!
+_________________________________
+````
-{Describe the value proposition: what problem does it solve?}
+
-## User Stories
+## Design
+
+### Architecture
+
+
+
+The ***Architecture Diagram*** given above explains the high-level design of App. Given below is a quick overview of
+each component.
+
+`Duke` class contains the main method. It is responsible for,
+
+- At app launch: Initializes the components in the correct sequence, and connects them up with each other.
+- At shut down: Stores the user data from memory into the hard disk.
+
+
+
+The rest of the App consists of five components.
+
+- `Ui`: The Ui of the App.
+- `Parser`: Parses the input to determine which command to execute.
+- `Command`: The command executer.
+- `UserData`: Holds the data of the App in the memory.
+- `Storage`: Reads data from, and writes data to, the hard disk.
+
+#### Ui Component
+
+The 'Ui' component is in charge of handling input from users and system output.
+
+#### Command Component
+
+
+
+API: [Command.java](https://github.com/AY2021S1-CS2113T-T12-4/tp/blob/master/src/main/java/seedu/duke/command/Command.java)
+1. `Parser` class to parse the user command
+1. This results in a `Command` object executed by the `Parser`
+1. The command execution can affect the `UserData`
+
+It listens for commands made in the Duke Class and sends the input to the parser class.
+It is also responsible for printing messages from commands and exception messages.
+
+#### Parser Component
+
+
+
+The `Parser` object uses its `parse` method to parse inputs passed to it from `Ui`. This method returns a `Command` object to be executed by `Duke`.
+
+#### UserData Component
+
+
+
+The `UserData`
+- stores `EventList` objects that store events of different categories (i.e. Personal, Timetable, Zoom).
+- stores a `Goal` object that represents the current user goal.
+- does not depend on the other components.
+
+#### Storage Component
+
+
+
+API [Storage.java](https://github.com/AY2021S1-CS2113T-T12-4/tp/blob/master/src/main/java/seedu/duke/storage/Storage.java)
+
+The storage component,
+- is able to store all event and goal information onto the computer
+- is able to load all event and goal information from the computer into the program
+
+
+
+How does the storage component load files
+- the storage component will read the correct txt file.
+- It passes the text to the StorageParser.
+- The event strings are converted to actual events using their respective class constructors
+
+The following object diagram shows the state of the storage object upon completion of converting each txt file to their respective events.
+
+
+- The events are stored within the UserData object to be used by the program
+
+The following object diagram illustrates the state of the storage object upon completion of this step
+
+
+How does the storage component save files
+
+The following diagram is a sequence diagram showing the process of saving information
+ 
+
+- The storage component will first retrieve the correct EventList from the UserData.
+- It will next send this EventList into the StorageParser
+- The StorageParser uses its functions to convert the events into string representations
+- The StorageParser then returns these string representations to Storage
+- the Storage class then writes them to the correct Path and the corresponding text(.txt) files are modified.
+
+
+
+## Implementation
+
+In this section, the features implemented will be explained using various UML diagrams and code snippets.
+
+#### Help feature
+
+The help feature in the program allows the user to extract out help information for a specified command. Information for help is stored in a file titled `helpfile.txt` located in the `resources` folder of the project
+
+When the help command is executed, help looks through `helpfile.txt` for information about the command requested. The following section shows how the help information for `calendar` is stored:
+
+```
+begin calendar
+calendar - displays your events in chronological order
+Format: calendar
+
+Enter calendar into the terminal. Your events will be displayed in chronological order, day by day
+To advance to the next day, press the enter key
+To terminate the function early, press the 'q' key
+
+end calendar
+```
+
+The `begin calendar` and `end calendar` serves as brackets to tell the help function what information to print from the help file.
+
+#### Add feature
+
+The add feature in the program allows the user to create one of 3 different events in scheduler-\-;.
+These 3 are the Personal, Zoom and Timetable events. These events have varying numbers of arguments or fields that can
+be inserted upon creation.
+
+The following is a class diagram to show the structure of the Add feature:
+
+
+Generally, Personal events are meant for non school related events and can be any general task the user wants to do.
+Zoom events are meant for events that require zoom links and helps the user to store their zoom links easily.
+Timetable events can contain a location and are meant for school related events like classes.
+
+The personal event can contain the following fields:
+- Description
+- Description and date
+- Description, date and time
+
+Examples of user inputs for the respective fields of personal events are:
+- add personal; run
+- add personal; run; 16/7/2020;
+- add personal; run; 16/7/2020; 1600
+
+The zoom event can contain the following fields:
+- Description and link
+- Description, link, date and time
+
+Examples of user inputs for the respective fields of zoom events are:
+- add zoom; meeting; zoom.com.sg
+- add zoom; meeting; zoom.com.sg; 17/10/2000; 2100
+
+The Timetable event can contain the following fields:
+- Description, date and time
+- Description, location, date and time
+
+Examples of user inputs for the respective fields of timetable events are:
+- add timetable; math class; 10/10/2000; 4:00 pm
+- add timetable; math class; NUS engineering; 10/10/2000; 4:00 pm
+
+The optional fields to fill in like the link and location for the zoom and timetable classes can be inserted respectively in the position right after the description field.
+The fields for what each event can contain were chosen based on what we as a team thought were important fields for the respective event types.
+However, these methods can easily be edited to accept different numbers of fields if we change our minds in the future.
+
+Given below is a general explanation of how the add feature works and an example usage scenario:
+
+When a valid format of the add command is called, the constructor to `AddCommand` will be able to detect the event type based on the user's input. It then stores the event type in that instance of the addCommand.
+Next, when `AddCommand#execute()` is called from the main, this method will call the respective method to create one of the three events. These methods are `AddCommand#addPersonal()`, `AddCommand#addZoom()`, and `AddCommand#addTimetable()`.
+These methods are then used to create events based on the number of fields/parameters entered by the user. Each event has multiple constructors and can contain different combinations of fields which was stated above.
+
+Example usage scenario:
+
+Step 1. The user launches the application for the first time. There will be no events stored at the moment.
+
+Step 2. The user inputs `add zoom; cs2113t meeting; zoom.sg; 16/09/20; 2100`. The `addCommand` instance is created and detects that the event is of Zoom type.
+
+Step 3. `addCommand#execute()` is called. The class knows the current addCommand is of Zoom type so it calls `addCommand#addZoom()`.
+
+Step 4. `addCommand#addZoom()` detects there are 4 fields in the command, separated by semicolons, and uses this to create a new Zoom event.
+
+Step 5. The Zoom event is then added to the user's `UserData` for further use.
+
+The following sequence diagram shows how the add feature works:
+
+
+
+
+
+
+#### List feature
+
+The list feature allows the user to print a list of events added by type.
+The list of events is print according to the order it was added in.
+
+The following is the class diagram for list command:
+
+
+
+User calls the list command by executing `list [argument]`.
+
+Executing `list` without specifying any argument prints a list of event types available.
+Specifying an event type as the argument prints a list of events of that type.
+Specifying `all` as the argument prints a list of all events in the order: Personal, Timetable, Zoom.
+
+Given below is an example usage scenario of the list feature.
+
+Step 1. The user executes `list zoom` command to print a list of Zoom events.
+The `list` command is passed through a parser to return the ListCommand with arguments initialised.
+
+Step 2. `ListCommand#execute()` is called, retrieving the list of Zoom events in `UserData`.
+
+Step 3. `ListCommand#execute()` calls `Ui#printList()` and passes the list of Zoom events to print.
+
+Step 4. `Ui#printList()` loops through every event in the Zoom event arraylist and prints it.
+
+The following sequence diagram shows how the `list zoom` command is parsed:
+
+
+
+
+
+The following sequence diagram shows how `ListCommand#execute()` works:
+
+
+
+
+
+#### Calendar feature
+
+The calendar feature allows the user to list all events in chronological order.
+Only events with a date and time will be included in the calendar.
+The calendar is sorted by date using a treemap with each entry being an arraylist of events on the same date.
+It is then sorted by time in the arraylist of events.
+
+The following is the class diagram for calendar command:
+
+
+
+User calls the list command by executing `calendar`.
+
+Given below is an example usage scenario of the calendar feature.
+
+Step 1. The user executes `calendar` command to list the events in a calendar.
+The `calendar` command is passed through a parser to return the CalendarCommand with arguments initialised.
+
+Step 2. `CalendarCommand#execute()` is called, sorting the events with date and time in chronological order in a map.
+
+Step 3. `CalendarCommand#execute()` calls `Ui#printCalendarStart()` to print the number of dates to list and the number
+of events that will not be in the calendar.
+
+Step 4. `CalendarCommand#execute()` calls `Ui#printCalendar()` for every date in the map.
+
+Step 5. The user can choose to end the calendar with the input `q` or press **Enter** to list the next date.
+
+Step 6. `CalendarCommand#execute()` calls `Ui#printCalendarEnd()` to print that it is the end of the calendar when the
+last date is listed or the user ends it.
+
+The following sequence diagram shows how `CalendarCommand#execute()` works:
+
+
+
+
+
+#### Repeat feature
+
+The repeat feature on the program allows for the user to be able to make certain events repeat several times over a defined time period.
+For instance, the user can request for a personal event to be repeated monthly for four months. To run the program, the user will need to key in the command `repeat EVENT_TYPE; INDEX; TIME_UNIT; COUNT`
+
+|Argument| Description |
+|--------|----------|
+|EVENT_TYPE|What type of event is to be repeated? Accepted arguments are `personal`, `timetable` or `zoom`|
+|INDEX|Index number of the event to be repeated that is stored on the Event List|
+|TIME_UNIT|For what unit of time each event is to be repeated. Accepted arguments are `daily`, `weekly` and `monthly`|
+|COUNT|Integer indicating how many times the event is to be repeated|
+
+For example, to repeat a personal event located at index 2 for 3 days, the user will key in this: `repeat personal 2 daily 3`.
+
+The following sequence diagram shows the overall process of repeating an event:
+
+
+
+The steps to repeating the command will be described in the following sections.
+
+##### Step 1: Obtaining the event information
+
+The repeat command first locates the event that is to be repeated along with its starting date.
+Next, it creates an empty ArrayList called `repeatEventList` that will contain all repeated events.
+
+
+
+##### Step 2: Incrementing the Original Date
+
+The repeatCommand object will now call upon Event to increment the date by a fixed unit of time. In the following diagram, it shows how the program flow will work should a monthly repeat increment be set.
+The increment date will be assigned the name `repeatDate`
+
+
+
+##### Step 3: Set Repeat type
+
+The repeatCommand now will set the original Event to have a repeat type setting. In the diagram's example, it will be set to `monthly`
+
+
+
+##### Step 4: Clone the event
+
+The original event is now cloned as shown in the following sequence diagram.
+
+
+
+##### Step 5: Add the cloned activity
+
+The cloned activity will now have its date set to be `repeatDate`. This new activity is now added into the `repeatEventList` as shown in the following diagram.
+
+
+
+##### Step 6: Repeat steps 2 to 5
+
+Steps 2 to 5 are repeated up till the number specified by `count` as shown in the loop.
+
+
+
+##### Step 7: Assign the ArrayList as the RepeatEventList attribute of the original event.
+
+Finally, set the `repeatEventList` using the `setRepeatEventList` command as shown in the following section of the sequence diagram. The results of this process is printed and control returns back to the main program.
+
+
+
+
+
+#### Deadline feature
+
+The deadline feature allows user to add/update the deadline for their personal events.
+
+|Argument| Description |
+|--------|----------|
+|index|Index number of the event to change deadline that is stored on the `Personal` List|
+|date|The new deadline for the event [Optional field]|
+|time|The new specified time for deadline [Optional field]|
+
+The deadline feature is implemented using `DeadlineCommand` class. `DeadlineCommand` accesses the Personal `Events` to get the event specified by the user and change the date of the event. It implements the following operations:
+
+- `DeadlineCommand#parseUserCommand(command)` -- Parses the command argument to take out the respective index, date/time given by the user
+- `DeadlineCommand#parsingNumber(stringIndex)` -- Check whether if index is a number. If it is not, exception would be thrown. If it is, the index will be parse to Integer and returned.
+- `DeadlineCommand#validDateRange(date)` -- Check whether date specified by user is equal or after current date
+- `DeadlineCommand#validTimeRange(time)` -- Check whether time specified by user is equal or after current time
+
+These operations are not exposed, and are used as private methods within the `DeadlineCommand`.
+
+Given below is an example usage scenario and how the deadline feature functions.
+
+Step 1. The user executes `deadline 1; 7/10/20; 11:20 PM` command to set the deadline for the 1st event in Personal event list
+to be on the 7th October 2020 at 11:20 PM.
+
+Step 2. `DeadlineCommand#execute()` is called. The command string is then parsed to `DeadlineCommand#parsingNumber(stringIndex)`.
+
+Step 3. Event is obtained using `EventList#getEventByIndex(index)`
+
+Step 4. `DeadlineCommand#validDateRange(date)` and `DeadlineCommand#validTimeRange(time)` are used to check if the user specified date and time is valid.
+
+Step 5. After validating the date and time, `Event#setDate(date)` and `Event#setTime(time)` are used to add/update the deadline.
+
+The following sequence diagram shows how the deadline operation works:
+
+
+
+Given below is how the deadline command behave:
+
+
+
+
+
+
+
+#### Check feature
+
+The check feature allows the user to check for events happening during a defined time period. The format for the check command is `check [START_DATE]; [START_TIME]; [END_DATE]; [END_TIME]`.
+
+|Argument| Description |
+|--------|----------|
+|START_DATE|The start date of the defined time period. Accepted formats are: d/M/yyyy, M/yyyy, yyyy, d/M/yy, M/yy, yy. Slashes may also be replaced with dashes. If left blank, the current date is taken by default. Missing fields (e.g. 2020 missing d/M) will be filled in using the current date (i.e if today's date is 15/10/2021, input 2020 will be read as 15/10/2020). |
+|START_TIME|The start time of the defined time period. Accepted formats are: hh:mm a, hhmm a, hh a, HH:mm, HHmm, HH. If left blank, current time is taken by default. If only hour is given, the time at the start of the hour is taken. |
+|END_DATE|The end date of the defined time period. Accepted formats are: d/M/yyyy, M/yyyy, yyyy, d/M/yy, M/yy, yy. Slashes may also be replaced with dashes. If left blank, the current date is taken by default. Missing fields (e.g. 2020 missing d/M) will be filled in using the current date (i.e if today's date is 15/10/2021, input 2020 will be read as 15/10/2020). |
+|END_TIME|The start time of the defined time period. Accepted formats are: hh:mm a, hhmm a, hh a, HH:mm, HHmm, HH. If left blank, current time is taken by default. If only hour is given, the time at the start of the hour is taken. |
+
+The following is the class diagram for CheckCommand:
+
+
+
+The check feature is implemented using the `CheckCommand` class. `CheckCommand` accesses the `Event` stored within `EventList` in order to determine if events are occurring within a given time period. It implements the following operations:
+
+- `CheckCommand#getDate(stringDate)` -- Parses a given string to get a LocalDate variable (either the start or end date for the time period).
+- `CheckCommand#getTime(stringTime)` -- Parses a given string to get a LocalTime variable (either the start or end time for the time period).
+- `CheckCommand#verifyValidTimePeriod(LocalDate, LocalDate, LocalTime, LocalTime)` -- Verifies that the given start of the time period is before the given end of the time period.
+- `CheckCommand#checkEventsInTimeRange(eventsList, startDate, endDate, startTime, endTime)` -- Checks each event in the eventsList to see if they occur within the time period defined in the command, and saves all coinciding events in an ArrayList.
+
+These operations are not exposed, and are used as private methods within the `CheckCommand` interface.
+
+Given below is an example usage scenario and how the check feature functions.
+
+Step 1. The user inputs `check 11/5/2020; 5:15 PM; 15/5/2020; 9 PM` in order to check for events occurring between 11th May 2020, 5:15 pm and 15th May 2020, 9:00 pm. This input is received by the Ui, which processes it into a string. The string is parsed by the Parser, which returns a CheckCommand with the date and time parameters stored in it as a string.
+
+Step 2. `CheckCommand#execute()` is called. The command string containing the date and time parameters are split into a String array to separate the different parameters.
+
+Step 3. Within `CheckCommand#execute()`, `CheckCommand#getDate()` is called to parse the start and end dates, and `CheckCommand#getTime()` is called to parse the start and end times.
+
+This sequence diagram shows how the `getDate` method functions:
+
+
+
+
+
+
+
+
+
+`getTime` adds ":00" to the back if only hh or HH is given, then uses `DateTimeParser#timeParser` to parse the time. The parsed time is then returned.
+
+Step 4. Within `CheckCommand#execute()`, the start date, start time, end date and end time are passed to `CheckCommand#verifyValidTimePeriod()`. This method checks that the start date and time of the time period happen before the end date and time, and returns a boolean indicating the validity of the given time period.
+
+Step 5. Within `CheckCommand#execute()`, the start date time and end date time is passed to `CheckCommand#checkEventsInTimeRange()` along with an `EventList` (i.e. Zoom, Personal or Timetable). This method checks each `Event` in the `EventList` to determine if the event occurs within the time period. If the event date time coincides with the time period, the event is added to an ArrayList that stores all the coinciding events in the current `EventList`. This is done for each `EventList`.
+
+Step 6. The contents of the ArrayLists returned by `CheckCommand#checkEventsInTimeRange()` are combined into a single ArrayList, and a new `EventList` ("coinciding") is created using this combined list of events.
+
+Step 7. `Ui#printList()` is called to print the list of coinciding events.
+
+The following sequence diagram shows how the check operation works:
+
+
+
+
+
+#### Goal feature
+
+The goal feature is achieved by `GoalCommand`. It either prints the current goal stored internally in
+`UserData` if there is one, or it sets the input as the new goal by replacing the previous goal.
+The user can only have one goal at any time.
+
+The following is the class diagram for goal command:
+
+
+
+Given below is an example usage scenario of the goal feature.
+
+Step 1. The user launches the application for the first time. `Ui#printGoal` is called,
+a line informing them they have no goal is printed.
+
+Step 2. The user executes `goal save money` command to set the goal as "save money".
+The `goal` command is passed through a parser to return the GoalCommand with arguments initialised.
+
+Step 3. `GoalCommand#execute()` is called, setting the goal in `UserData` to "save money". The change is printed
+with `Ui#printChangeGoal()`.
+
+Step 4. The user executes `goal` command to print the current goal.
+The `goal` command is passed through a parser to return the GoalCommand with blank arguments.
+
+Step 5. `GoalCommand#execute()` is called, to retrieve the current goal in `UserData` and print it
+with `Ui#printGoal()`.
+
+The following sequence diagram shows how `GoalCommand#execute()` works:
+
+
+
+
+
+#### Done feature
+
+The done feature allows users to mark events in Scheduler-\- as done. The format for the done command is `done EVENT_TYPE; EVENT_INDEX; [EVENT_DATE]`.
+
+|Argument| Description |
+|--------|----------|
+|EVENT_TYPE|The type of event to be marked done. Accepted arguments are `personal`, `timetable` or `zoom`.|
+|EVENT_INDEX|Index number of the event to be marked done that is stored on the Event List.|
+|EVENT_DATE|The date on which the repeated event to be marked done should occur. Accepted formats are: d/M/yyyy, d/M/yy, yyyy/M/d, yy/M/d. Slashes may also be replaced by dashes.|
+
+The following is the class diagram for DoneCommand:
+
+
+
+The done feature is implemented using the `DoneCommand` class. `DoneCommand` accesses the `Event` stored within `EventList` and marks it as done. It implements the following operations:
+
+- `DoneCommand#parse(input)` -- Parses the processed input from `Parser` to create a `DoneCommand` with the parsed event list type and event identifier.
+- `DoneCommand#scanRepeatList(repeatEventList, doneEventDate, ui)` -- Scans the `repeatEventList` of a repeat event and marks the matching event as done.
+
+`DoneCommand#parse(input)` is exposed and is used in Parser to create the `DoneCommand`.
+`DoneCommand#scanRepeatList(repeatEventList, doneEventDate, ui)` is not exposed, and is used as a private method within the `DoneCommand` interface.
+
+Given below is an example usage scenario and how the done feature functions.
+
+Step 1. The user inputs `done personal; 4` in order to mark the 4th `Personal` event as done. This input is received by the Ui, which processes it into a string. The string is parsed by the Parser, which removes "done" from the string and parses the resulting string with `DoneCommand#parse()`. This returns a `DoneCommand`.
+
+
+
+Step 2. `DoneCommand#execute()` is called. The `command` string is split at semicolons to separate the event index from an event date (if event date is given). The event index for this DoneCommand is 4, and it has no event date.
+
+Step 3. Within `DoneCommand#execute()`, the 4th `Event` is called from the Personal `EventList` and is marked as done.
+
+
+
+If the called event is a repeat event and an event date not matching the called event's date is given, `DoneCommand#scanRepeatList()` is called to check for events matching the event date in the repeat event's `repeatEventList`. When a matching event is found, it is marked as done.
+
+Step 4. `Ui#printEventMarkedDoneMessage()` is called to print the event marked as done.
+
+Step 5. `Storage#saveFile()` is called to save the updated event list to the external file.
+
+#### Undone feature
+
+The undone feature allows users to mark events in Scheduler-\- as undone. The format for the undone command is `undone EVENT_TYPE; EVENT_INDEX; [EVENT_DATE]`.
+
+|Argument| Description |
+|--------|----------|
+|EVENT_TYPE|The type of event to be marked undone. Accepted arguments are `personal`, `timetable` or `zoom`.|
+|EVENT_INDEX|Index number of the event to be marked undone that is stored on the Event List.|
+|EVENT_DATE|The date on which the repeated event to be marked undone should occur. Accepted formats are: d/M/yyyy, d/M/yy, yyyy/M/d, yy/M/d. Slashes may also be replaced by dashes.|
+
+The following is the class diagram for UndoneCommand:
+
+
+
+The undone feature is implemented using the `UndoneCommand` class. `UndoneCommand` accesses the `Event` stored within `EventList` and marks it as undone. It implements the following operations:
+
+- `UndoneCommand#parse(input)` -- Parses the processed input from `Parser` to create an `UndoneCommand` with the parsed event list type and event identifier.
+- `UndoneCommand#scanRepeatList(repeatEventList, doneEventDate, ui)` -- Scans the `repeatEventList` of a repeat event and marks the matching event as undone.
+
+`UndoneCommand#parse(input)` is exposed and is used in Parser to create the `UndoneCommand`.
+`UndoneCommand#scanRepeatList(repeatEventList, undoneEventDate, ui)` is not exposed, and is used as a private method within the `UndoneCommand` interface.
+
+Given below is an example usage scenario and how the done feature functions.
+
+Step 1. The user inputs `undone personal; 4` in order to mark the 4th `Personal` event as undone. This input is received by the Ui, which processes it into a string. The string is parsed by the Parser, which removes "undone" from the string and parses the resulting string with `UndoneCommand#parse()`. This returns a `UndoneCommand`.
+
+
+
+Step 2. `UndoneCommand#execute()` is called. The `command` string is split at semicolons to separate the event index from an event date (if event date is given). The event index for this UndoneCommand is 4, and it has no event date.
+
+Step 3. Within `UndoneCommand#execute()`, the 4th `Event` is called from the Personal `EventList` and is marked as undone.
+
+
+
+If the called event is a repeat event and an event date not matching the called event's date is given, `UndoneCommand#scanRepeatList()` is called to check for events matching the event date in the repeat event's `repeatEventList`. When a matching event is found, it is marked as undone.
+
+Step 4. `Ui#printEventMarkedUndoneMessage()` is called to print the event marked as undone.
+
+Step 5. `Storage#saveFile()` is called to save the updated event list to the external file.
+
+#### Delete feature
+
+The delete feature allows users to delete events from Scheduler-\-. The format for the delete command is `delete EVENT_TYPE; EVENT_INDEX; [EVENT_DATE]`.
+
+|Argument| Description |
+|--------|----------|
+|EVENT_TYPE|The type of event to be deleted. Accepted arguments are `personal`, `timetable` or `zoom`.|
+|EVENT_INDEX|Index number of the event to be deleted that is stored on the Event List.|
+|EVENT_DATE|The date on which the repeated event to deleted should occur. Accepted formats are: d/M/yyyy, d/M/yy, yyyy/M/d, yy/M/d. Slashes may also be replaced by dashes.|
+
+The following is the class diagram for DeleteCommand:
+
+
+
+The delete feature is implemented using the `DeleteCommand` class. `DeleteCommand` accesses the `Event` stored within `EventList` and removes it from the `EventList` to delete it from Scheduler-\-;. It implements the following operations:
+
+- `DeleteCommand#parse(input)` -- Parses the processed input from `Parser` to create an `DeleteCommand` with the parsed event list type and event identifier.
+- `DeleteCommand#scanRepeatList(repeatEventList, deleteEventDate, ui, deleteEvent)` -- Scans the `repeatEventList` of a repeat event and deletes the matching event from the `repeatEventList`.
+
+`DeleteCommand#parse(input)` is exposed and is used in Parser to create the `DeleteCommand`.
+`DeleteCommand#scanRepeatList(repeatEventList, undoneEventDate, ui)` is not exposed, and is used as a private method within the `DeleteCommand` interface.
+
+Given below is an example usage scenario and how the done feature functions.
+
+Step 1. The user inputs `delete timetable; 1; 10/10/2020` in order to delete the 1st `Timetable` repeat event that occurs on 10 October 2020. This input is received by the Ui, which processes it into a string. The string is parsed by the Parser, which removes "delete" from the string and parses the resulting string with `DeleteCommand#parse()`. This returns a `DeleteCommand`.
+
+
+
+Step 2. `DeleteCommand#execute()` is called. The `command` string is split at semicolons to separate the event index from an event date (if event date is given). The event index for this DeleteCommand is 1, and the event date is 10/10/2020.
+
+Step 3. Within `DeleteCommand#execute()`, the 1st `Event` is called from the Timetable `EventList`. Since the called event is a repeat event and an event date not matching the called event date was provided, `DeleteCommand#scanRepeatList()` is called to check for events matching the date in the repeat list. When a matching event is found, it is deleted. Since there are no more events in the repeatEventList, the repeat status of the called event is set to null.
+
+
+
+Step 4. `Ui#printEventDeletedMessage()` is called to print the event that was deleted.
+
+Step 5. `Storage#saveFile()` is called to save the updated event list to the external file.
+
+#### Note feature
+
+The Note feature allow user to add notes to the event.
+
+|Argument| Description |
+|--------|----------|
+|event type|Type of event. Accepted arguments are `personal`, `timetable` or `zoom` |
+|index|Index number of the event to add note that belongs to the specified `event type` List |
+
+The note feature is implemented using `NoteCommand` class. `NoteCommand` accesses the `Events` to get the event specified by the user and add notes to the event. It implements the following operations:
+
+- `NoteCommand#parseUserCommand(command)` -- Parses the command argument to take out the respective index, event type given by the user
+- `NoteCommand#parsingNumber(stringIndex)` -- Check whether if index is a number. If it is not, exception would be thrown. If it is, the index will be parse to Integer and returned.
+- `NoteCommand#getNotesFromUser(Ui)` -- Calls `Ui#receiveCommand()` to get user's input for notes
+- `NoteCommand#updatingNotesWithTimestamp(existingNotes,newNotes)` -- Include a timestamp of the time that user create a note
+- `NoteCommand#indicateNewLineUsingDelimeter(notes)` -- ` is used to indicate a new line and form the array list into a string of note
+
+These operations are not exposed, and are used as private methods within the `NoteCommand`.
+
+Given below is an example usage scenario and how the deadline feature functions.
+
+Step 1. The user executes `note personal; 1` command to create the note for the 1st event in Personal event list.
+
+Step 2. `NoteCommand#execute()` is called. The command string is then parsed to `NoteCommand#parseUserCommand(command)`
+
+Step 3. After obtaining the event using `Event#getEventByIndex(index)`, using the user input we have obtained from `NoteCommand#getNotesFromUser(Ui)` create a note for the event.
+
+Step 4. Before storing the note, notes have to be tagged with timestamp using `NoteCommand#updatingNotesWithTimestamp(existingNotes,newNotes)` and `NoteCommand#indicateNewLineUsingDelimeter(notes)` reformat notes to use ` as an indicator for new line.
+
+The following sequence diagram shows how the note operation works:
+
+
+
+Given below is how the note command behave:
+
+
+
+
+
+
+#### View feature
+
+The view feature allows user to see the notes they have created for a particular event.
+
+|Argument| Description |
+|--------|----------|
+|event type|Type of event. Accepted arguments are `personal`, `timetable` or `zoom` |
+|index|Index number of the event to view note that belongs to the specified `event type` List |
+
+The following is the class diagram for reminder command:
+
+
+
+
+
+
+The view feature is implemented using `ViewCommand` class. `ViewCommand` accesses the `Events` to get the event specified by the user and show the notes created to users. It implements the following operations:
+
+- `ViewCommand#parseUserCommand(command)` -- Parses the command argument to take out the respective index, event type given by the user
+- `ViewCommand#parsingNumber(stringIndex)` -- Check whether if index is a number. If it is not, exception would be thrown. If it is, the index will be parse to Integer and returned.
+
+These operations are not exposed, and are used as private methods within the `ViewCommand`.
+
+Given below is an example usage scenario and how the reminder feature functions.
+
+Step 1. The user executes `view` command to show events happening today.
+
+Step 2. `ViewCommand#execute()` is called.
+
+Step 3. After obtaining the event using `EventList#getEventByIndex(index)`, the user notes can be obtained using `Event#getNotes()`
+
+The following sequence diagram shows how the view operation works:
+
+
+
+
+#### Reminder feature
+
+The reminder feature allows user list to the user the events that are happening today. Events are sorted according to time if applicable. The remind feature would called at every start of the program.
+
+The following is the class diagram for reminder command:
+
+
+
+
+
+The reminder feature is implemented using `ReminderCommand` class. `ReminderCommand` accesses `EventList` to get all event and filter out events happening today and sort them according to with/without time. It implements the following operations:
+
+- `ReminderCommand#filterTodayEvents(eventlist)` -- Filter out the events happening today and return an array list of events
+- `ReminderCommand#checkingRepeatedEvent(event)` -- Check for events that have been repeated and is happening today
+
+These operations are not exposed, and are used as private methods within the `ReminderCommand`.
+
+Given below is an example usage scenario and how the reminder feature functions.
+
+Step 1. The user executes `reminder` command to show events happening today.
+
+Step 2. `ReminderCommand#execute()` is called.
+
+Step 3. After obtaining all the event using `UserData#getAllEventLists()`, `ReminderCommand#filterTodayEvents(eventlist)` will be called to check event if they are happening today and store the events in an ArrayList if they are.
+
+Step 4. While checking if the events fall on today in `ReminderCommand#filterTodayEvents(eventlist)`, `ReminderCommand#checkingRepeatedEvent(event)` will also check if there are repeated events that fall on the same day.
+
+Step 5. After getting all the events happening today, `ReminderCommand#execute()` would then sort the events into 2 ArrayList, one with time and one without time.
+
+The following sequence diagram shows how the reminder operation works:
+
+
+
+
+#### Extract feature
+The extract feature allows users to copy and paste a body of text like emails and it will help users create either
+a Zoom or a Personal event. It utilizes Regular Expressions (Regex) patterns in order to match dates, times and zoom links
+in the text entered.
+
+The following is a class diagram to show the structure of the Extract feature:
+
+
+Given below is an example usage scenario to explain how the extract feature works.
+
+Step 1. To begin, the user enters `extract CS2113T Quiz;`.
+The constructor for `ExtractCommand` will then be called and the `TEXT_SUBJECT` which is `CS2113T Quiz` will be stored in that instance of ExtractCommand.
+
+Step 2. Next, `ExtractCommand#execute()` is called from the main. This method will call `ExtractCommand#receiveTextBody()` which will let the user enter any text and only ends once the user types `extractend` on a new line.
+The user may then input a text copied from email, for example `The quiz will be on October 8 2020 or 9th October at 4pm or 5pm. The zoom link is at https://nus-sg.zoom.com`. After going to the next line, the user has to type `extractend`.
+
+Step 3. The text body is then used in multiple methods. These include `ExtractCommand#detectZoomLink()`, `ExtractCommand#detectDate()` and `ExtractCommand#detectTime()` which will use Regex patterns to find and match dates, times and zoom links.
+
+Step 4. `ExtractCommand#detectZoomLink()` will check if the URL found is a valid zoom link. In this case, the link is valid as it contains ".zoom.".
+
+Step 5. `ExtractCommand#verifyDate()` and `ExtractCommand#verifyTime()` will be called which will return dates and times that are valid. In this case, it will verify the 2 dates and 2 timings detected.
+
+Step 6. `ExtractCommand#chooseZoomLink()`, `ExtractCommand#chooseDate()` and `ExtractCommand#chooseTime()` will be called and will print out a list of valid zoom links/dates/times and allow the user to input the number of the link/date/time they want to select it. The user can only choose 1 of each detected.
+
+Step 7. `ExtractCommand#createEvent()` will be called. If the event has a zoom link, like the example, a `Zoom` event will be created using the link, date, time and `TEXT_SUBJECT` as its description. Otherwise, a `Personal` event will be created with the date, time and description fields. The event will be added to the user's `UserData`.
+
+The following sequence diagram shows how the Extract feature works in general like I have explained above:
+
+
+
+Design considerations:
+
+For the detection of date, the Regex pattern used detects dates in the DD/Month Name/YYYY format or Month name/DD/YYYY format. The current year can also be added automatically if no year was detected.
+It can also detect if the day portion of the date has any suffixes. This does not detect dates written in other formats like DD/MM/YYYY or DD/MM/YY because upon
+looking at many of the emails sent to us, we found most were of the Regex pattern we chose. However, this could be implemented in the future.
+
+For the detection of time, the Regex pattern used detects time in the 12 or 24 hour format. It can detect time with AM/PM behind it too. However, it cannot detect 24 hour timings
+with no "." or ":" in it unlike some commands above. This is because it may result in detecting a lot of false timings like if a 4 digit number like 2020 for a date was detected as a time and a year which may confuse the user.
+
+For the detection of zoom link, the Regex pattern used first detects any URL starting with https:// or http://. It then checks whether the URL contains ".zoom." which we found the be common in most zoom links.
+
+#### Bye feature
+
+The Bye feature allows user to exit smoothly after saving all the files.
+
+Step 1. The user executes `bye` command to exit Scheduler-\-.
+
+Step 2. `ByeCommand#execute()` is called and `ByeCommand` will set `isExit` to true.
+
+Step 3. Returning to `Duke`, `Duke` will call `Storage#saveAll()` to save all the files.
+
+Step 4. Before exiting, `Ui#printByeMessage()` will be called to print and exit the program smoothly.
+
+The following sequence diagram shows how the Bye feature works:
+
+
+
+
+
+
+
+## Appendix: Requirements
+
+### Product scope
+
+##### Target user profile
+
+
+ has a need to organise events using scheduler
+ prefer desktop application
+ can type fast
+ prefer typing to mouse interactions
+ is reasonably comfortable using CLI apps
+
+
+##### Value proposition
+
+Manage one's events quickly compared to GUI scheduling applications.
+Users can check when they are free using a simple command
+and also extract deadlines from any body of text.
+
+
+
+### 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|office worker (working from home)|organise large amount of online Zoom meetings to manage|keep track of his schedule for the day|
+|v1.0|university student|consolidate my schedule for both my online zoom lectures and my ordinary timetable|better manage my time|
+|v1.0|university student having projects|be informed of the goals of my project|keep track of the project details|
+|v1.0|university student|customise repeated task|set events like tutorials to weekly|
+|v1.0|teacher|organise all my zoom links for my classes|it can be easily accessible.|
+|v1.0|user|see the events that I have created|refer to them when I forget|
+|v1.0|user|check when I am available on that day|avoid manually searching through my entire calendar to find out if I have time|
+|v2.0|user|be able to keep track the status of completion of the events I created|focus more on the tasks that I need to complete|
+|v2.0|user|view which events are upcoming in a convenient readable format|locate the events easily by date|
+|v2.0|user|the application to alert me when my deadlines are coming up|be given enough time to work on them and not rush last minute|
+|v2.0|user|create deadlines from the email text body|avoid looking through the email to create one by one|
+|v2.0|user|create notes for the event|avoid creating new notes in word documents for every event|
+|v2.0|user|view notes written for the event|avoid looking through to find notes|
+|v2.0|new user (new to text-based application)|detailed directions on commands I can use|easily navigate through the application|
+|v2.0|new user (expert in using text-based application)|have some useful shortcut keys|speed up common tasks|
+
+
+
+### Use Cases
+(For all use cases below, the System is Scheduler-\-; and the Actor is the user, unless specified otherwise)
+
+**Use case: Extracting deadlines from an email**
+
+MSS:
+1. User enters extract command for a body of text/email
+2. Scheduler-\-; shows a list of dates detected
+3. User chooses one of the dates
+4. Scheduler-\-; shows a list of the time slots detected
+5. User chooses one of the time slots
+6. Scheduler-\-; creates a personal event based on the user's choices
+
+Use case ends.
+
+Extensions:
+
+2a. There are no dates detected.
+
+Scheduler-\-; creates a Personal event with no date or time.
+
+ 3a. User selects an invalid index from the list of dates.
+- 3a1. Scheduler-\-; requests for the user to pick a valid number.
+- 3a2. User enters new index.
+
+Steps 3a1-3a2 are repeated until the index entered is valid.
+Use case resumes from step 4.
+
+ 5a. User selects an invalid index from the list of time slots.
+- 5a1. Scheduler-\-; requests for the user to pick a valid number.
+- 5a2. User enters new index.
+
+Steps 5a1-5a2 are repeated until the index entered is valid.
+Use case resumes from step 6.
+
+**Use case: Repeating an event and checking its status**
+
+MSS:
+
+1. User uses list command for one of the three types of events.
+2. Scheduler-\-; prints a list for the event type chosen.
+3. User enters repeat command for one of the events.
+4. Scheduler-\-; prints a confirmation message.
+5. User uses repeat command to check status of the repeated event.
+6. Scheduler-\-; prints out the status of the repeated event.
+
+Use case ends.
+
+Extensions:
+
+3a. User selects a personal event that does not have a deadline
-## Non-Functional Requirements
+Scheduler-\-; prints an error message and use case ends.
-{Give non-functional requirements}
+
+
+### Non-Functional Requirements
+
+
+ Work on any mainstream OS that has Java 11 or above installed
+ A user who has faster typing speed than average should be able to use this program with ease
+
+
+
## Glossary
-* *glossary item* - Definition
+- Mainstream OS: Windows, Linux, Unix, OS-X
## 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}
+Below are some instructions provided for use in manual testing
+
+### Launch and Shutdown
+1. Initial Launch
+ 1. Copy the Java Archive file into an empty directory
+ 1. On windows machines, open up the terminal and navigate to the directory
+ 1. Type in `java -jar scheduler.jar` and press enter. Expected: You should now see the welcome message printed on the screen
+1. Shutdown
+ 1. In the program, type the word `bye` and press enter
+ 1. You should now see the goodbye message printed on the screen. Expected: The program should return you back to the main terminal window
+
+### Adding new event
+1. Add a personal event
+ 1. Load the program
+ 1. Type `add personal; dental appointment`
+ 1. Type `list personal`. Expected: your new personal event should be displayed on the screen
+1. Add a zoom event
+ 1. Load the program
+ 1. Type `add zoom; math lesson; www.zoom.com; 20/10/2020; 15:00`
+ 1. Type `list zoom`. Expected: your new zoom event should be displayed on the screen
+1. Add a timetable event
+ 1. Load the program
+ 1. Type `add timetable; science class; S17; 23/10/2020; 11:00`
+ 1. Type `list timetable`. Expected: your new timetable event should be displayed on the screen
+
+### Loading and Saving Data
+1. Sample Save Data
+ 1. Follow the instructions for "Initial Launch"
+ 1. Type `add personal; dental appointment`
+ 1. Type `list personal`, your new personal event should be displayed on the screen
+ 1. Type `save` into the terminal
+ 1. Type `bye` to exit the program
+ 1. At your current file directory, find the file `personal.txt` located in the `data` directory of where your program is running. Expected:You should see that the file is no longer empty and should see one line with the words `dental appointment` written.
+1. Sample Load data
+ 1. Follow the instructions in "Sample Save Data"
+ 1. Launch the program
+ 1. Type `list personal`. Expected: your previous personal event should be displayed on the screen
+
+### Delete Event
+1. Deleting a personal event
+ 1. Launch the program
+ 1. Type `add personal; dental appointment`
+ 1. Type `list personal`, your new personal event should be displayed on the screen
+ 1. Test Case: `delete personal; 1`
+ Expected: When you type `list personal`, you should notice that the list is blank
+ 1. Test Case: `delete personal; 3`
+ Expected: An error message should be displayed as none of the events are labelled with event index 3.
+
+### Repeat Event
+1. Repeating a personal event
+ 1. Load the program
+ 1. Type `add personal; dental appointment; 18/09/2020`
+ 1. Type `add personal; birthday`
+ 1. Type `list personal`, your new personal events should be displayed on the screen
+ 1. Test Case: `repeat personal; 2; monthly; 3`
+ Expected: An error message should be displayed indicating that you cannot repeat an event with no deadline
+ 1. Test Case: `repeat personal; 1; monthly; 3`
+ Expected:When you type `repeat personal; 1`, you should see a message indicating to you that the event is repeated monthly for three more times.
+
+### Changing status of events
+1. Changing the status of a personal event
+ 1. Load the program
+ 1. Type `add personal; dental appointment; 18/09/2020`
+ 1. Type `add personal; birthday`
+ 1. Type `list personal`, your new personal events should be displayed on the screen
+ 1. Test Case: `done pesonal; 1`
+ Expected: When you type `list personal`, you should see that the symbol of the first event has changed from a cross to a tick
+ 1. Test Case: `undone personal; 1`
+ Expected: When you type `list personal`, you should see that the symbol of the first event has changed from a tick to a cross
+
+### Help Command
+1. Getting generic help
+ 1. Load the program
+ 1. Type `help` and press enter
+ Expected: You should see a summary of all the commands available to the user printed on the screen
+
+### Printing calendar
+1. Printing calendar
+ 1. Launch the program
+ 1. Type `add personal; dental appointment; 18/09/2020; 1400`
+ 1. Type `add personal; birthday; 19/10/2020; 1200`
+ 1. Type `list all` Expected: You should now see the events listed on the terminal
+ 1. Type `calendar`
+ Expected: As you press enter, you should see all your events and timings displayed in chronological order. Once all the events have been printed, the `End of calendar` message should appear.
+ Your calendar should appear as shown:
+
+ ```
+ calendar
+ _________________________________
+ Calendar has 2 dates to display
+ ---------------------------------------------------------------------------------------
+ 18 Sep 2020
+ ---------------------------------------------------------------------------------------
+ P | 2:00 PM | X | dental appointment
+ ---------------------------------------------------------------------------------------
+ Enter 'q' to exit or enter to continue...
+
+ ---------------------------------------------------------------------------------------
+ 19 Oct 2020
+ ---------------------------------------------------------------------------------------
+ P | 12:00 PM | X | birthday
+ ---------------------------------------------------------------------------------------
+ End of calendar
+ _________________________________
+
+ ```
+
+
+
+### Checking schedule availability
+1. Check Schedule
+ 1. Launch the program
+ 1. Type `add personal; dental appointment; 18/09/2020; 1400`
+ 1. Type `add personal; birthday; 19/10/2020; 1200`
+ 1. Type `list all` Expected: You should now see the events listed on the terminal
+ 1. Test Case: `check 01/01/2010; 1100; 01/01/2010; 2359;`
+ Expected: The message `You have no coinciding events!` should be printed.
+ 1. Test Case: `check 01/01/2020; 1100; 01/01/2021; 2359;`
+ Expected: You should see the check command prints out all events that fall between the timing of 01 Jan 2010 1100hrs to 2359hrs as shown below
+
+ ```
+ _________________________________
+ Here is a list of your coinciding events:
+ 1. [P][X] dental appointment on 2020-09-18, 14:00
+ 2. [P][X] birthday on 2020-10-19, 12:00
+ _________________________________
+
+ ```
+
+### Adding deadline to event
+1. Repeating a personal event
+ 1. Load the program
+ 1. Type `add personal; dental appointment; 18/09/2020`
+ 1. Type `add personal; birthday`
+ 1. Type `list all` Expected: You should now see the events listed on the terminal
+ 1. Test Case: `deadline 2; ; ` where `date` and `time` are any date and time that is past the current date and time this test is being run.
+ Expected: A success message should be printed, indicating that the new deadline has been set
+ When `list personal` is typed, you should see that the second birthday event now has a deadline attached to it.
+
+### Setting reminder
+1. Repeating a personal event
+ 1. Load the program
+ 1. Type `add personal; dental appointment; 18/09/2020`
+ 1. Type `add personal; birthday; <>`
+ 1. Test Case: `reminder`
+ Expected: The program should show that the personal birthday event is the event that you have for today.
+
+ ```
+ reminder
+ _________________________________
+ You have the following events today:
+
+ _________Events Without Time_____
+ [P][X] birthday on 2020-11-09
+ _________________________________
+
+ ```
+
+
+### Adding note for an event
+1. Add a new note for an event
+ 1. Load the program
+ 1. Type `add personal; dental appointment; 18/09/2020`
+ 1. Test Case: `note personal; 1`
+ 1. Upon Scheduler prompt , type the following
+
+ ```
+ Teeth polished
+ Cost: 100
+ noteend
+ ```
+
+ Expected: The program should show that the note has been successfully created with the content of the note printed.
+
+
+### Viewing note for an event
+1. View note for an event
+ 1. Load the program
+ 1. Type `add personal; dental appointment; 18/09/2020`
+ 1. Type `note personal; 1`
+ 1. Upon Scheduler prompt , type the following
+
+ ```
+ Teeth polished
+ Cost: 100
+ noteend
+ ```
+
+ 1. Test Case: `view personal; 1`
+ Expected: The program should show that the notes for the dental appointment that you previously created
+
+ ```
+ _________________________________
+ These are the notes that you have taken:
+ ---------2020-11-09T22:57:09.242317700---------
+ Teeth polished
+ Cost: 100
+ _________________________________
+
+ ```
+
+### Extract information for an event
+1. Extracting information
+ 1. Load the program
+ 1. Type `extract quiz; `
+ 1. Upon Scheduler prompt, type the following
+
+ ```
+ 7 October 2020 at 4pm there is a quiz
+ extractend
+ ```
+
+ Expected: The terminal should indicate that you have added a personal event as shown below
+
+ ```
+ 7 October 2020 at 4pm there is a quiz
+ extractend
+ One date detected and chosen: 2020-10-07
+ One timing detected and chosen: 16:00
+ You have successfully added this event to your list!
+ [P][X] quiz on 2020-10-07, 16:00
+ _________________________________
+
+ ```
\ No newline at end of file
diff --git a/docs/README.md b/docs/README.md
index bbcc99c1e7..bb7777bc55 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,6 +1,6 @@
-# Duke
+# Scheduler-\-;
-{Give product intro here}
+Scheduler–\-; (S–\-) is a desktop app for managing deadlines from different sources. The user will use a Command Line Interface to control it. It is specially designed for Computing students who are comfortable in using CLI and have Git project deadlines as well as consolidated Zoom session links due which will suit home based learning in this COVID period.
Useful links:
* [User Guide](UserGuide.md)
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index abd9fbe891..12003f3ee2 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -1,42 +1,1436 @@
# User Guide
-## Introduction
+CS2113T – T12 – 04 Scheduler-\-;
+
+Scheduler-\-; does the job.
-{Give a product intro}
+## Table of Contents
-## Quick Start
+- [1. About Scheduler-\-;](#1-about-scheduler--)
+ - [1.1 What can it do?](#11-what-can-it-do)
+ - [1.2 About this guide](#12-about-this-guide)
+ - [1.3 How to use this document?](#13-how-to-use-this-document)
+ - [1.4 Conventions used in this document](#14-conventions-used-in-this-document)
-{Give steps to get started quickly}
+- [2. Quick Start](#2-quick-start)
+ - [2.1 System Requirements](#21-system-requirements)
+ - [2.2 Setup Instructions](#22-setup-instructions)
+ - [2.2.1 Windows](#221-windows)
+ - [2.2.2 Mac OS X](#222-mac-os-x)
+ - [2.2.3 Linux](#223-linux)
+ - [2.3 Test run](#23-test-run)
+ - [2.4 For Experienced Users](#24-for-experienced-users)
-1. Ensure that you have Java 11 or above installed.
-1. Down the latest version of `Duke` from [here](http://link.to/duke).
+- [3. Features](#3-features)
+ - [3.1 Viewing help: help](#31-viewing-help-help-colin-ng)
+ - [3.2 Adding an event: add](#32-adding-an-event-add-matthew-gani)
+ - [3.3 List events: list](#33-list-events-list-marcus-ng)
+ - [3.4 Calendar format list: calendar](#34-calendar-format-list-calendar-marcus-ng)
+ - [3.5 Deadlines: deadline](#35-deadlines-deadline-qing-ning)
+ - [3.6 Repeat on daily/weekly/monthly basis: repeat](#36-repeat-on-dailyweeklymonthly-basis-repeat-colin-ng)
+ - [3.7 Check availability on a specific date and time: check](#37-check-availability-on-a-specific-date-and-time-check-marcus-tan)
+ - [3.8 Goal setting and viewing: goal](#38-goal-setting-and-viewing-goal-marcus-ng)
+ - [3.9 Mark events as done: done](#39-mark-events-as-done-done-marcus-tan)
+ - [3.10 Mark events as not done: undone](#310-mark-events-as-not-done-undone-marcus-tan)
+ - [3.11 Deleting an event: delete](#311-deleting-an-event-delete-marcus-tan)
+ - [3.12 Note taking: note](#312-note-taking-note-qing-ning)
+ - [3.13 View note: view](#313-view-note-view-qing-ning)
+ - [3.14 Reminder: reminder](#314-reminder-reminder-qing-ning)
+ - [3.15 Extracting events from texts: extract](#315-extracting-events-from-texts-extract-matthew-gani)
+ - [3.16 Save events: save](#316-save-events-save-colin)
+ - [3.17 Exiting the program: bye](#317-exiting-the-program-bye)
-## Features
+- [4. FAQ](#4-faq)
-{Give detailed description of each feature}
+- [5. Command Summary](#5-command-summary)
-### Adding a todo: `todo`
-Adds a new item to the list of todo items.
+
-Format: `todo n/TODO_NAME d/DEADLINE`
+## 1. About Scheduler-\-;
-* The `DEADLINE` can be in a natural language format.
-* The `TODO_NAME` cannot contain punctuation.
+Hello! Welcome to Scheduler-\-;!
-Example of usage:
+Scheduler-\-; (S-\-) is a desktop app for managing deadlines from different sources. The user will use a Command Line Interface to control it. It is specially designed for Computing students who are comfortable in using CLI and have Git project deadlines as well as consolidated Zoom session links due which will suit home based learning in this COVID period.
-`todo n/Write the rest of the User Guide d/next week`
+### 1.1 What can it do?
-`todo n/Refactor the User Guide to remove passive voice d/13/04/2020`
+Scheduler-\-; comes with the following list of features to help you keep organised of your schedule.
+- Check if your schedule is free between a certain date and time period.
+- Categorise your events into personal events, zoom meetings or school timetable events.
+- Be reminded on the important tasks and expected deadlines.
+- Set certain events to occur repeatedly throughout your schedule.
+- Set a goal for yourself.
+- And many more...
-## FAQ
+### 1.2 About this guide
-**Q**: How do I transfer my data to another computer?
+This user guide is for your reference on how to set up the Scheduler-\-; program on your computer. It provides you with information on how to get things done with Scheduler-\-; and how to use the commands in the program.
-**A**: {your answer here}
+### 1.3 How to use this document?
-## Command Summary
+To get started, take a look at the section labelled “Quick Start” to set up the application to run on your computer.
+
+Once you have set up the application, you may use the table of contents present in this document to help find the correct command, learn how to use it and what it does.
+
+
+
+### 1.4 Conventions used in this document
+
+Words that are highlighted in grey, for instance, `list`, indicate that the words are commands to be typed into the command line interface.
+
+- Words that are highlighted in grey and are in lower case indicates commands that are to be typed exactly. For instance, if the instructions says to type the words `list all` to run a command, type on the program the exact words “list all”
+
+- Words that are highlighted in grey and are in upper case indicates command arguments or parameters that are to be provided by the user. For instance, if the usage instruction informs you to type `delete EVENT_INDEX` and you know that the EVENT_INDEX is 2, type into the computer program “delete 2”.
+
+- Words that are highlighted in grey, in upper case and are surrounded by square brackets indicates command arguments or parameters that the user can choose to provide or not to provide. For instance, if the command instruction informs you to type `goal [NEW_GOAL]`, you can choose to omit the argument for NEW_GOAL and instead just simply type “goal”.
+
+>**Note!**
+>
+> * This is a note section. Additional information about the command will be written in sections such as this one.
+
+>**Warning!**
+>
+> * This is a warning section. Any user interaction with the program that may result in issues or unintended results will be written in sections such as this one.
+
+
+
+## 2. Quick Start
+
+### 2.1 System Requirements
+
+To run Scheduler-\-;, your computer must meet these minimum system requirements:
+
+- Operating System: Windows 7 and above, Mac OS X or Linux.
+- Java 11 installed on your computer.
+
+You can install version 11 of Java from this [link](https://docs.aws.amazon.com/corretto/latest/corretto-11-ug/downloads-list.html).
+
+### 2.2 Setup Instructions
+
+#### 2.2.1 Windows
+
+1. Install Java 11 or above in your computer if you have not done so.
+1. Download the latest scheduler.jar from [here](https://github.com/AY2021S1-CS2113T-T12-4/tp/releases).
+1. Copy the file to the folder you want to use as the folder for Scheduler-\-;.
+1. Open the command prompt by entering `cmd` into the search bar and press enter.
+1. Navigate to the folder where you saved the jar file using the command prompt.
+1. Type `java -jar scheduler.jar` into the command prompt and press enter.
+1. The welcome message for the program should appear.
+
+#### 2.2.2 Mac OS X
+
+1. Install Java 11 or above in your computer if you have not done so.
+1. Download the latest scheduler.jar from [here](https://github.com/AY2021S1-CS2113T-T12-4/tp/releases).
+1. Copy the file to the folder you want to use as the folder for Scheduler-\-;.
+1. Open the terminal by clicking on Launchpad->Utilities->Terminal.
+1. Navigate to the folder you have stored the jar file in the command prompt.
+1. Type `java -jar scheduler.jar` into the terminal and press enter.
+1. The welcome message for the program should appear.
+
+#### 2.2.3 Linux
+
+1. Install Java 11 or above in your computer if you have not done so.
+1. Download the latest scheduler.jar from [here](https://github.com/AY2021S1-CS2113T-T12-4/tp/releases).
+1. Copy the file to the folder you want to use as the folder for Scheduler-\-;.
+1. Open the terminal in your linux distribution.
+1. Navigate to the folder you have stored the jar file in the command prompt.
+1. Type `java -jar scheduler.jar` into the terminal and press enter.
+1. The welcome message for the program should appear.
+
+### 2.3 Test run
+
+Type the command in the command box and press Enter to execute.
+Refer to the [Features](#3-features) section for details of each command.
+
+Some examples you can try:
+- `add personal; run; 12/10/20; 4pm`: Adds a personal event to Scheduler-\-;.
+- `list personal`: Lists all personal events.
+- `bye`: Exits the program.
+
+Congratulations! You have just finished setting up Scheduler-\-; Feel free to explore the program, or if you would like some assistance, take a look at the subsequent sections for instructions on how to use the available features of this program. Happy scheduling!
+
+### 2.4 For Experienced users
+
+If you are familiar with the commands used in the program, Scheduler-\-; now supports the processing of multiple commands.
+To input more than one command into Scheduler-\-;, simply separate each command with a pipe character '|'.
+
+Some examples you can try:
+- `add personal; dental appointment; 2020-05-05; | repeat personal; 1; monthly; 3;` will cause the program to add a new personal event named "Dental Appointment" and subsequently set the event to repeat monthly for three more months as shown below:
+
+```
+add personal; dental appointment; 2020-05-05; | repeat personal; 1; monthly; 3;
+_________________________________
+You have successfully added this event to your list!
+[P][X] dental appointment on 2020-05-05
+_________________________________
+[P][X] dental appointment on 2020-05-05
+is now repeating monthly for 3 times.
+_________________________________
+
+```
+
+> **Warning!**
+>
+> * When writing multiple commands at once, keep in mind that the program processes each command entry from left to right. If you make a mistake for one command, the subsequent commands towards the right of the mistake will not be processed by the program.
+>
+
+
+
+## 3. Features
+
+This section introduces and explains the features of Scheduler-\-;!
+
+>**Note!**
+>
+> * Do note that some of the expected output for longer code snippets may not be exactly the same. This is because this User Guide was edited due to the code snippets being too long and not translating well when converted to PDF.
+
+### 3.1 Viewing help: help (Colin Ng)
+
+If you are ever unsure on how to use a command, or you would like to learn more about the uses of a command, the help command is Scheduler-\-;'s onboard helpline for getting information. The help command allows you to get more information when you are unsure on how to use the program. It can also assist you with getting more information on how to use a specific command in the program.
+
+Format: `help [COMMAND]`
+
+#### Example usage
+
+The following few scenarios present 3 examples of how the help command is used.
+
+##### Scenario 1: Getting the summary of commands
+
+Let us assume that you want to see all the commands that is available.
+
+You can do this by typing `help`. The program will print out a summary of all the available commands.
+
+The output from the program should be:
+```
+help
+_________________________________
+Here is a summary of all the commands that the program can run
+help - Provides more information on how to use the commands available in the program
+add - Records a personal, timetable or zoom event into the program
+deadline - Sets or change a deadline for a personal event
+repeat - repeat an event for a specified amount of times every day, week or month
+goal - Sets up a goal for the user
+done - mark the event as completed
+delete - remove a personal, timetable or zoom event permamently from the program
+note - Add in additional notes associate to the personal, timetable or zoom event
+reminder - prints out events that will be occuring on the present day
+extract - extracts out important event information from text to be recorded as events
+ in the program
+save - save current changes made to the program
+bye - exits the program
+
+To find out more about each of these functions, type in "help [COMMAND]" into the
+ terminal where COMMAND is the name of the command you would like more information about
+_________________________________
+```
+
+
+
+##### Scenario 2: Getting help for `add` command
+
+Let us assume that you want to add an event but you are unsure of how to use the command.
+
+You can do this by typing `help add`.
+
+The output from the program should be:
+
+```
+help add
+_________________________________
+add - Records a personal, timetable or zoom event into the program
+Format: add EVENT_TYPE; EVENT_DESCRIPTION; [LINK/LOCATION]; EVENT_DATE; EVENT_TIME
+
+
+EVENT_TYPE specify the type of event you want to add. These include “zoom”,
+ “timetable” and “personal”.
+EVENT_DESCRIPTION is the description or name of the event.
+LINK/LOCATION is the website link or the location of the event. This is applicable and
+ optional for zoom and timetable events respectively. This field should always be after
+ the event description if used.
+- `EVENT_DATE` is the date associated with the event in the format DD/MM/YY or DD/MM/YYYY.
+- `EVENT_TIME` is the time of the event. It can be written in 12 hour format (HH:mm am/pm or HH am/pm)
+ or 24 hour format (HHmm or HH:mm).
+
+Timetable events can contain:
+- Description, date and time
+- Description, location, date and time
+
+Timetable Format: add Timetable; EVENT_DESCRIPTION; [LOCATION]; EVENT_DATE; EVENT_TIME
+
+Zoom events can contain:
+- Description and link
+- Description, link, date and time
+
+Zoom Format: add Zoom; EVENT_DESCRIPTION; LINK; [EVENT_DATE]; [EVENT_TIME]
+
+Personal events can contain:
+- Description
+- Description and date
+- Description, date and time
+
+Personal Format: add Personal; EVENT_DESCRIPTION; [EVENT_DATE]; [EVENT_TIME]
+
+_________________________________
+```
+
+##### Scenario 3: Getting help to understand `help` command
+
+Let us assume that you are unsure of how to use the help command and you will like to get help on how to use the help command.
+
+You can do this by typing `help help`.
+
+The output from the program should be:
+
+```
+help help
+_________________________________
+help - Provides more information on how to use the commands available in the program
+Format: help [COMMAND]
+
+[COMMAND] - command which you would like to find out more information about.
+
+text in lowercase indicates that you should type it exactly like this when you type
+ the command in the terminal
+text in uppercase indicates compulsory arguments that you will need to provide and
+ type into the terminal to operate the command
+text in uppercase surrounded by square brackets [] indicates optional arguments that
+ you can choose to provide and type into the terminal to operate the command
+_________________________________
+```
+
+
+
+### 3.2 Adding an event: `add` (Matthew Gani)
+New to creating your events using Scheduler-\-;?
+Utilize our simple to use yet effective add feature which comprises of 3 different event types.
+You can add dates and times to these events and even website links and locations to some of them!
+
+The types of events available to be added include:
+- Zoom Meetings: Zoom
+- NUS timetable/lessons: Timetable
+- Personal Events: Personal
+Each event can have different combinations of fields.
+
+General Format: `add EVENT_TYPE; EVENT_DESCRIPTION; [LINK/LOCATION]; EVENT_DATE; EVENT_TIME`
+
+- `EVENT_TYPE` specify the type of event you want to add. These include "zoom", "timetable" and "personal".
+- `EVENT_DESCRIPTION` is the description or name of the event.
+- `LINK/LOCATION` is the website link or the location of the event. This is applicable and optional for zoom and timetable events respectively. This field should always be after the event description if used.
+- `EVENT_DATE` is the date associated with the event in the format DD/MM/YY or DD/MM/YYYY.
+- `EVENT_TIME` is the time of the event. It can be written in 12 hour format (HH:mm am/pm or HH am/pm) or 24 hour format (HHmm or HH:mm).
+
+If you would like to make a Timetable event, the format for the command is as follows:
+`add Timetable; EVENT_DESCRIPTION; [LOCATION]; EVENT_DATE; EVENT_TIME`
+
+Timetable events can contain:
+- Description, date and time
+- Description, location, date and time
+
+If you would like to make a Zoom event, the format for the command is as follows:
+`add Zoom; EVENT_DESCRIPTION; LINK; [EVENT_DATE]; [EVENT_TIME]`
+
+Zoom events can contain:
+- Description and link
+- Description, link, date and time
+
+If you would like to make a Personal event, the format for the command is as follows:
+`add Personal; EVENT_DESCRIPTION; [EVENT_DATE]; [EVENT_TIME]`
+
+Personal events can contain:
+- Description
+- Description and date
+- Description, date and time
+
+> **Note!**
+>
+> * When giving the event type, take note that it is case-insensitive:
+> `add Zoom` is the same as `add zoom`.
+> * Only the full word will be recognized as the event type:
+> `add z` will NOT add a zoom event.
+
+> **Warning!**
+>
+> * Typing in the wrong format for date or time will cause the event to not be made.
+> * Events cannot contain an empty description.
+
+#### Example usage
+
+The following scenarios present 3 examples of how the add command is used.
+
+##### Scenario 1: Adding a Timetable event
+
+If you want to add a timetable event for a CS2101 Lecture at NUS Computing on 18/09/20 at 3.30pm,
+you should input: `add Timetable; CS2101 Lecture; NUS Computing; 18/09/20; 3:30 pm`.
+
+The output from the program should be:
+```
+_________________________________
+You have successfully added this event to your list!
+[T][X] CS2101 Lecture, Location: NUS Computing on 2020-09-18, 15:30
+_________________________________
+```
+
+##### Scenario 2: Adding a Zoom event
+
+If you want to add a zoom event for a CS2113T meeting on 16/09/20 at 9pm,
+you should input: `add Zoom; CS2113T meeting; zoom.com.sg; 16/09/20; 2100`.
+
+The output from the program should be:
+```
+_________________________________
+You have successfully added this event to your list!
+[Z][X] CS2113T meeting, Link: zoom.com.sg on 2020-09-16, 21:00
+_________________________________
+```
+
+##### Scenario 3: Adding a Personal event
+
+If you want to add a personal event for a family meeting on 18/09/20,
+you should input: `add personal; family meeting; 18/09/20`.
+
+The output from the program should be:
+```
+_________________________________
+You have successfully added this event to your list!
+[P][X] family meeting on 2020-09-18
+_________________________________
+```
+
+
+
+### 3.3 List events: `list` (Marcus Ng)
+
+After you have added your events, it is essential that you can view the events you have added.
+You can do so using the list command to print out a list of your events in the order it was added in.
+You can also see events that you set to repeat in the list.
+
+Format: ```list [EVENT_TYPE]```
+
+- ```EVENT_TYPE``` specify what kind of event you would like to list.
+The accepted arguments for this are “personal”, “timetable” and “zoom”.
+
+When the optional argument `[EVENT_TYPE]` is omitted,
+list will print a list of available event types.
+
+#### Example usage
+
+The following few scenarios present examples of how the list command can be used.
+
+##### Scenario 1: Listing Zoom events
+
+If you want to list all your Zoom events, you should input: ```list Zoom```.
+
+The output from the program should be:
+
+```
+list zoom
+_________________________________
+Here is a list of your Zoom events:
+1. [Z][O] CS2113T tutorial, Link: zoom.com.sg on 2020-10-03, 13:30 is also on:
+ 1. 2020-10-10 13:30 [X]
+ 2. 2020-10-17 13:30 [X]
+ 3. 2020-10-24 13:30 [X]
+_________________________________
+```
+
+
+> **Note!**
+>
+> * You do not need to type the exact event type, it is case-insensitive. In other words,
+> `list Zoom` is the same as `list zoom`
+
+##### Scenario 2: Listing all events
+
+If you want to list all your events, you should input: ```list all```.
+
+The output from the program should be:
+
+```
+list all
+_________________________________
+Here is a list of your Personal events:
+1. [P][X] sleep
+ Type "view Personal 1" to see notes
+_________________________________
+Here is a list of your Timetable events:
+1. [T][X] Science class, Location: S17 on 2020-05-04, 15:00
+_________________________________
+Here is a list of your Zoom events:
+1. [Z][O] CS2113T tutorial, Link: zoom.com.sg on 2020-10-03, 13:30 is also on:
+ 1. 2020-10-10 13:30 [X]
+ 2. 2020-10-17 13:30 [X]
+ 3. 2020-10-24 13:30 [X]
+_________________________________
+```
+
+> **Note!**
+>
+> * Events will always be listed in the order: Personal, Timetable, Zoom.
+> * The index of events in the list should be referenced for `EVENT_INDEX` of other commands unless stated otherwise.
+
+
+
+### 3.4 Calendar format list: `calendar` (Marcus Ng)
+
+If you feel that the list command does not print the events in a format that you like,
+you can use the calendar command to sort events with date and time in chronological order.
+The calendar will also include events that are set to repeat.
+
+Format: ```calendar```
+
+This brings you into calendar printing mode.
+- To print the next date, simply press enter.
+- To exit the calendar printing mode, simply type in ```q```.
+- This mode is exited once you see ```End of calendar``` on pressing enter.
+
+#### Example usage
+
+The following few scenarios present examples of how the calendar command can be used.
+
+##### Scenario 1: Listing events in calendar
+
+If you want to list your events in a calendar format, you should input: ```calendar```.
+
+The output from the program should be:
+
+```
+calendar
+_________________________________
+Calendar has 1 dates to display
+1 event not on the calendar because it has no date and time
+---------------------------------------------------------------------------------------
+04 May 2020
+---------------------------------------------------------------------------------------
+T | 3:00 PM | X | Science class | S17
+---------------------------------------------------------------------------------------
+End of calendar
+_________________________________
+```
+
+##### Scenario 2: Listing events in calendar but not all of them
+
+If you want to end the calendar before all the dates are displayed, you should input: `q` after inputting `calendar`.
+
+The output from the program should be:
+
+```
+calendar
+_________________________________
+Calendar has 5 dates to display
+1 event not on the calendar because it has no date and time
+---------------------------------------------------------------------------------------
+04 May 2020
+---------------------------------------------------------------------------------------
+T | 3:00 PM | X | Science class | S17
+---------------------------------------------------------------------------------------
+Enter 'q' to exit or enter to continue...
+
+---------------------------------------------------------------------------------------
+03 Oct 2020
+---------------------------------------------------------------------------------------
+Z | 1:30 PM | O | CS2113T tutorial | zoom.com.sg
+---------------------------------------------------------------------------------------
+Enter 'q' to exit or enter to continue...
+q
+---------------------------------------------------------------------------------------
+End of calendar
+_________________________________
+```
+
+> **Note!**
+>
+> * Only events with date and time will be in the calendar.
+> * In calendar printing mode, all input except ```q``` is ignored. In other words, commands cannot be executed until you exit this mode.
+
+
+
+### 3.5 Deadlines: `deadline` (Qing Ning)
+Want to set a deadline after you have created your personal event? Afraid that you forget your deadlines? Deadline is here to help! You can set the date and time of the task to be completed and on the day itself, we will remind you.
+
+Format: `deadline EVENT_INDEX; EVENT_DATE; [EVENT_TIME]`
+
+- `EVENT_INDEX` is a number. It contains the index of the personal event that will have its deadline set or changed.
+
+* `EVENT_DATE` contains a date string. This is the date of the deadline for the personal event.
+
+* `EVENT_TIME` is an optional argument containing the time of the deadline for the personal event. It can be written in either 12-hour or 24-hour format.
+
+Acceptable date formats: DD/MM/YYYY or DD/MM/YY
+
+Acceptable time formats:
+- 24 Hour format – HHmm or HH:mm (e.g. 14:00)
+- 12 Hour format – hh:mm am/pm or hhmm am/pm (e.g. 2:00 pm)
+
+> **Note!**
+>
+> * Deadline can only be used to set/update for Personal events
+> * Date and time must be after the current date and time
+> * The time can be omitted entirely. If you leave time field as blank, the command would not set any time for the event.
+> * You may also omit the minutes in a time. If the minutes(MM) field of any time is empty, the time is read as when the hour begins (e.g. 4 pm would be taken as 4:00 pm)
+> * AM/PM is required for 12 hour format
+
+
+> **Warning!**
+>
+> * The event index keyed in have to be valid so that deadline can be created for the specified event index.
+
+#### Example Usage
+
+The following few scenarios present examples of how the deadline command can be used.
+
+##### Scenario 1: Setting a deadline for a personal event
+
+Let us assume that you have a personal event titled `dental appointment` that you would like to set the deadline for.
+
+Firstly, find out what index number the `dental appointment` personal event has been assigned to. You can do this easily by typing in `list personal`.
+In my case, the following things will be printed out on the terminal.
+
+You can do this by typing the command list personal into your application. The result for the following command is shown in the code snippet below:
+
+```
+_________________________________
+list personal
+_________________________________
+Here is a list of your Personal events:
+1. [P][X] party on 2000-10-09, 13:00
+2. [P][X] surprise on 2020-09-14, 08:00
+3. [P][X] sleep on 2002-02-02, 23:00
+4. [P][X] dental appointment
+_________________________________
+```
+In this case, the index number of our dental appointment is 4.
+
+Next, type in the command `deadline 4; 29/08/20; 11:20 PM`. You should now see that the deadline command has successfully set your event `dental appointment` to be due on the 29/08/20 at 11:20 PM.
+
+```
+deadline 4; 29/08/20; 11:20 PM`
+You have successfully updated the deadline for this event!
+[P][X] sleep on 2020-08-29, 23:20
+```
+
+
+
+
+### 3.6 Repeat on daily/weekly/monthly basis: `repeat` (Colin Ng)
+Do you have any events that repeat frequently? For example, a dental appointment every month, or perhaps a math lesson every week? The repeat command in the program allows you to easily schedule such events into your event planner. Just select the event that you want to repeat, type in how often this event repeats and Scheduler-\-; will record down the event for you.
+
+General Format: `repeat EVENT_TYPE; EVENT_INDEX; [UNIT]; [COUNT]`
+
+- `EVENT_TYPE` specify what kind of event you would like to repeat. The accepted arguments for this are “personal”, “timetable” and “zoom”
+- `EVENT_INDEX` is a number. It contains the index of the event that is to be repeated
+- `[UNIT]` can either take the word “Weekly” or “Monthly”. It tells the program to repeat the event either every week or every month.
+- `[COUNT]` takes an integer which indicates how many units of time will this event repeat itself. For instance, if the unit input is Weekly, placing a number 5 will repeat the event for 5 weeks.
+
+If you would like to make an event a repeated event, the format for the command is as follows.
+
+Format: `repeat EVENT_TYPE; EVENT_INDEX; [UNIT]; [COUNT]`
+
+If you would like to view the status of a repeated event, the format for the command is as follows.
+
+Format: `repeat EVENT_TYPE; EVENT_INDEX`
+
+> **Note!**
+>
+> * When you repeat an event monthly, the date does not always increment up by 30 days. Incrementing monthly mainly changes the month without changing the day of the date being incremented.
+> * In the case of incrementing monthly from 31 Jan, the code will increment to 28 or 29 Feb followed by 31 Mar and 30 April and so on.
+> * In the case of incrementing monthly from 30 Jan, the code will increment to 28 or 29 Feb followed by 30 Mar and 30 April and so on.
+>
+> * Setting the `EVENT_INDEX` argument to zero will cause the event to revert to a standard non-repeating event.
+
+
+
+#### Example Usage
+
+The following few scenarios present examples of how the repeat command can be used.
+
+##### Scenario 1: Setting a personal event to repeat monthly for four months
+
+Let us assume that you have a personal event titled `dental appointment` that you would like to repeat monthly for four additional months.
+
+Firstly, find out what index number the `dental appointment` personal event has been assigned to. You can do this easily by typing in `list personal`.
+In my case, the following things will be printed out on the terminal.
+
+You can do this by typing the command list personal into your application. The result for the following command is shown in the following code snippet.
+
+```
+list personal
+_________________________________
+Here is a list of your Personal events:
+1. [P][X] dental appointment on 2020-09-18
+2. [P][X] birthday on 2020-11-09
+3. [P][X] quiz on 2020-10-07, 16:00
+_________________________________
+
+```
+In this case, the index number of the event `dental appointment` is 1.
+
+Next, type in the command `repeat personal; 1; monthly; 4;`. You should now see that the repeat command has successfully set your event `dental appointment` to be repeated monthly for the next four months.
+
+```
+repeat personal; 1; monthly; 4
+_________________________________
+[P][X] dental appointment on 2020-09-18
+is now repeating monthly for 4 times.
+_________________________________
+
+```
+
+##### Scenario 2: Erasing the repeat status of a personal event
+
+Let us look at the previous scenario where we have set the personal event `dental appointment` to repeat for four months.
+Let us assume that you would like to remove the repeated event and set it to occur only once.
+
+Firstly, find out what index number the `dental appointment` personal event has been assigned to. You can do this easily by typing in `list personal`.
+The result for the following command is shown in the following code snippet.
+
+```
+list personal
+_________________________________
+Here is a list of your Personal events:
+1. [P][X] dental appointment on 2020-09-18 is also on:
+ 1. 2020-10-18 [X]
+ 2. 2020-11-18 [X]
+ 3. 2020-12-18 [X]
+ 4. 2021-01-18 [X]
+2. [P][X] birthday on 2020-11-09
+3. [P][X] quiz on 2020-10-07, 16:00
+_________________________________
+
+
+```
+In this case, the index number of the event `dental appointment` is 1.
+
+Next, type in the command `repeat personal; 1; monthly; 0;`. You should now see that the repeat command has successfully reverted your `dental appointment` personal event to be an ordinary, non-repeating event.
+
+```
+repeat personal; 1; monthly; 0
+_________________________________
+[P][X] dental appointment on 2020-09-18
+is no longer a repeated event
+_________________________________
+
+```
+
+##### Scenario 3: Setting a zoom event to repeat daily for three days
+
+Let us assume that you have a zoom event titled `math class` that you would like to repeat daily for three additional days.
+
+Firstly, find out what index number the `math class` zoom event has been assigned to. You can do this easily by typing in `list zoom`.
+In my case, the following things will be printed out on the terminal.
+
+
+```
+list zoom
+_________________________________
+Here is a list of your Zoom events:
+1. [Z][X] math class, Link: www.zoom.com/mathclass on 2020-12-03, 10:00
+_________________________________
+
+```
+In this case, the index number of the event `math class` is 1.
+
+Next, type in the command `repeat zoom; 1; daily; 3;`. You should now see that the repeat command has successfully set your event `math class` to be repeated daily for the next three days.
+
+```
+repeat zoom; 1; daily; 3
+_________________________________
+[Z][X] math class, Link: www.zoom.com/mathclass on 2020-12-03, 10:00
+is now repeating daily for 3 times.
+_________________________________
+
+```
+
+##### Scenario 4: Viewing the repeat status of events
+
+Let us assume that you have a zoom event titled `math class` that you would like to view the repeat status.
+
+Firstly, find out what index number the `math class` zoom event has been assigned to. You can do this easily by typing in `list zoom`.
+In my case, the following things will be printed out on the terminal.
+
+```
+list zoom
+_________________________________
+Here is a list of your Zoom events:
+1. [Z][X] math class, Link: www.zoom.com/mathclass on 2020-12-03, 10:00
+_________________________________
+
+```
+In this case, the index number of the event `math class` is 1.
+
+Next, type in the command `repeat zoom; 1;`. You should now see that the repeat command has successfully displayed the repeat status of the `math class` zoom event.
+
+```
+repeat zoom; 1;
+_________________________________
+[Z][X] math class, Link: www.zoom.com/mathclass on 2020-12-03, 10:00 is also on:
+ 1. 2020-12-04 10:00 [X]
+ 2. 2020-12-05 10:00 [X]
+ 3. 2020-12-06 10:00 [X]
+_________________________________
+
+```
+From this, we know that the event `math class` is a repeated event that repeats itself daily for the next three days.
+
+
+
+### 3.7 Check availability on a specific date and time: `check` (Marcus Tan)
+
+Would you like to check if you happen to be free at a certain time? The check command allows you to scan through your events to check for any events you might have within a given time period.
+
+Format: `check [START_DATE]; [START_TIME]; [END_DATE]; [END_TIME]`
+
+Acceptable date formats: DD/MM/YYYY or DD/MM/YY
+
+Acceptable time formats:
+- 24 Hour format – HHmm, HH:mm or HH (e.g. 2359, 14:00, 14)
+- 12 Hour format – HH:mm am/pm or HH am/pm (e.g. 2:00 pm, 10 am)
+
+> **Note!**
+>
+> * You may omit the DD or DD/MM in a date. If you do not fill in these fields for the date, the command takes the current date for that field by default (e.g. input 2021 on 11 Oct 2020 would be taken as 11/10/2021)
+> * The date can also be omitted entirely. If you leave a date field as blank, the command takes the current date for that field by default.
+> * You may also omit the minutes in a time. If the minutes(mm) field of any time is empty, the time is read as when the hour begins (e.g. 4 pm would be taken as 4:00 pm)
+> * The time can also be omitted entirely. If you leave a time field as blank, the command takes the current time by default.
+
+
+> **Warning!**
+>
+> * Even when you leave a time field (e.g. `[START_DATE]`) as blank, a semicolon (;) should still be used to denote the blank field (e.g. `check ; 2:00 pm; 25/12/2020; 2359`)
+> * The start date and time should occur before the end date and time (i.e. start date and time cannot be 11/11/2020; 1 pm if end date and time is 11/11/2020; 12 pm)
+
+#### Example Usage
+
+The following few scenarios present examples of how the check command can be used
+
+##### Scenario 1: Checking availability between 9 May 2021 and 1 August 2021
+
+Let's say you want to check your availability between 9 May 2021 and 1 August 2021 in order to make plans for a holiday.
+
+To do so, you can type in the command `check 9/5/2021; 12 am; 1/8/2021; 11:59 am`. This will check for events from the start of 9 May 2021 (12 am) till the end of 1 August 2021 (11:59 pm). If for instance you have a driving test set as a Personal event on 15 June 2021, it will show up in a list as shown in the following output:
+
+```
+check 9/5/2021; 12 am; 1/8/2021; 11:59 pm
+_________________________________
+Here is a list of your coinciding events:
+1. [P][X] driving test on 2021-06-15, 13:00
+_________________________________
+```
+
+##### Scenario 2: Checking availability from now till 25 December 2020
+
+Let's say you are planning to do some Christmas shopping, and would like to check if you have any other events going on from now till then.
+
+To do so, you can type in the command `check ; ; 25/12/2020; 2359`. Leaving the start date and start time fields blank sets them to the current date and time. This will check for events from now till 25 December 2020 11:59 pm. Assuming you have no events within that time period, you can expect the following output:
+
+```
+check ; ; 25/12/2020; 2359
+_________________________________
+You have no coinciding events!
+_________________________________
+```
+
+> **Note!**
+>
+> * This assumes that the current date and time is before 25 December at 2359, since the start date and time has to be before the end date and time.
+
+
+
+
+### 3.8 Goal setting and viewing: `goal` (Marcus Ng)
+
+Have a long term or short-term goal? The goal command helps you to keep track of it.
+You can use the goal command to print, set, remove or change your goal.
+
+Format: ```goal [GOAL]```
+
+- `[GOAL]` specify the new goal you want to set.
+
+When the optional argument `[GOAL]` is omitted, the current goal will be displayed.
+
+When `[GOAL]` is specified as `delete`, the current goal will be removed.
+
+#### Example usage
+
+The following few scenarios present examples of how the goal command can be used.
+
+##### Scenario 1: Setting goal
+
+If you want to set `get cap 5.0` as your goal, you should input: `goal get cap 5.0`.
+
+The output from the program should be:
+
+```
+goal get cap 5.0
+_________________________________
+Goal changed to: get cap 5.0
+_________________________________
+```
+
+##### Scenario 2: Delete goal
+
+If you want to delete your goal, you should input: `goal delete`.
+
+The output from the program should:
+
+```
+goal delete
+_________________________________
+No more goal!
+_________________________________
+```
+
+> **Note!**
+>
+> * You can only have one goal at any time.
+> * You can use `na` or `nil` instead of `delete` to remove goal.
+
+
+
+### 3.9 Mark events as done: `done` (Marcus Tan)
+If you would like to mark an event as done, you can use the done command to do so.
+
+Format: `done EVENT_TYPE; EVENT_INDEX; [EVENT_DATE]`
+
+Acceptable date formats: DD/MM/YYYY or DD/MM/YY
+
+If you wish to mark a specific event in a repeated task as done, you may enter the date of that repeated event.
+
+> **Note!**
+>
+> * If you want to mark an original repeat event as done rather than one of the subsequent repeated events, you can omit the date in the command.
+
+#### Example Usage
+
+The following few scenarios present several examples of how the done command can be used.
+
+##### Scenario 1: Marking a non-repeat event as done
+
+Let's say you wish to mark a Personal event `buy groceries` as done. In order to find the index of the event, you may type in the command `list personal` as shown below:
+
+```
+list personal
+_________________________________
+Here is a list of your Personal events:
+1. [P][X] buy groceries on 2020-10-15, 13:00
+2. [P][X] clean room on 2020-11-20, 12:00
+_________________________________
+```
+
+You may also type in `list all` to list all Personal, Timetable and Zoom events.
+
+As seen from the list, the index of the Personal event `buy groceries` is 1. To mark it as done, type in the command `done personal; 1`. This will set the event to done, as shown by the status icon changing from 'X' to 'O' in the following output:
+
+```
+done personal; 1
+_________________________________
+You have successfully marked this event as done!
+[P][O] buy groceries on 2020-10-15, 13:00
+_________________________________
+```
+
+##### Scenario 2: Setting a particular repetition of a repeat event as done
+
+Let's say you now wish to mark a particular occurrence of the Timetable event `CG2271 lab` on 23 October 2020 as done. In order to find the event index, you may type in `list timetable` as shown below:
+
+```
+list timetable
+_________________________________
+Here is a list of your Timetable events:
+1. [T][O] CG2271 lab, Location: E4A-04-08 on 2020-10-09, 08:00 is also on:
+ 1. 2020-10-16 08:00 [O]
+ 2. 2020-10-23 08:00 [X]
+ 3. 2020-10-30 08:00 [X]
+ 4. 2020-11-06 08:00 [X]
+ 5. 2020-11-13 08:00 [X]
+2. [T][X] GER exam on 2020-11-16, 12:00
+_________________________________
+```
+
+As seen from the list, the index of the Timetable event `CG2271 lab` is 1. To mark the repetition on 23 October 2020 as done, type in the command `done timetable; 1; 23/10/2020`. This sets the intended repetition of the event to done, as shown in the following output:
+
+```
+done timetable; 1; 23/10/2020
+_________________________________
+You have successfully marked this event as done!
+[T][O] CG2271 lab, Location: E4A-04-08 on 2020-10-23, 08:00
+_________________________________
+```
+
+> **Note!**
+>
+> * In this scenario, marking the first repetition of `CG2271 lab` occurring on 9 October 2020 can be done by typing the command `done timetable; 1`, without needing to enter the date.
+
+
+
+### 3.10 Mark events as not done: `undone` (Marcus Tan)
+Did you accidentally mark an event as done? Not to worry, just use the undone command to set the event status back to undone.
+
+Format: `undone EVENT_TYPE; EVENT_INDEX; [EVENT_DATE]`
+
+Acceptable date formats: DD/MM/YYYY or DD/MM/YY
+
+If you wish to mark a specific event in a repeated task as undone, you may enter the date of that repeated event.
+
+> **Note!**
+>
+> * If you want to mark an original repeat event as undone rather than one of the subsequent repeated events, you can omit the date in the command.
+
+#### Example Usage
+
+The following few scenarios present several examples of how the undone command can be used.
+
+##### Scenario 1: Marking a non-repeat event as undone
+
+Let's say you marked a Personal event `buy groceries` as done by accident, and want to mark it as undone again. In order to find the index of the event, you may type in the command `list personal` as shown below:
+
+```
+list personal
+_________________________________
+Here is a list of your Personal events:
+1. [P][O] buy groceries on 2020-10-15, 13:00
+2. [P][X] clean room on 2020-11-20, 12:00
+_________________________________
+```
+
+You may also type in `list all` to list all Personal, Timetable and Zoom events.
+
+As seen from the list, the index of the Personal event `buy groceries` is 1. To mark it as undone, type in the command `undone personal; 1`. This will set the event back to undone, as shown by the status icon changing from 'O' to 'X' in the following output:
+
+```
+undone personal; 1
+_________________________________
+You have successfully marked this event as undone!
+[P][X] buy groceries on 2020-10-15, 13:00
+_________________________________
+```
+
+##### Scenario 2: Setting a particular repetition of a repeat event as undone
+
+Let's say you also marked another particular occurrence of the Timetable event `CG2271 lab` on 30 October 2020 as done by accident. In order to find the event index, you may type in `list timetable` as shown below:
+
+```
+list timetable
+_________________________________
+Here is a list of your Timetable events:
+1. [T][O] CG2271 lab, Location: E4A-04-08 on 2020-10-09, 08:00 is also on:
+ 1. 2020-10-16 08:00 [O]
+ 2. 2020-10-23 08:00 [O]
+ 3. 2020-10-30 08:00 [O]
+ 4. 2020-11-06 08:00 [X]
+ 5. 2020-11-13 08:00 [X]
+2. [T][X] GER exam on 2020-11-16, 12:00
+_________________________________
+```
+
+As seen from the list, the index of the Timetable event `CG2271 lab` is 1. To mark the repetition on 30 October 2020 as undone, type in the command `undone timetable; 1; 30/10/2020`. This sets the intended repetition of the event back to undone, as shown in the following output:
+
+```
+undone timetable; 1; 30/10/2020
+_________________________________
+You have successfully marked this event as undone!
+[T][X] CG2271 lab, Location: E4A-04-08 on 2020-10-30, 08:00
+_________________________________
+```
+
+
+
+
+### 3.11 Deleting an event: `delete` (Marcus Tan)
+
+Want to remove an event from your schedule? Use the delete command to get rid of unnecessary events.
+
+Format: `delete EVENT_TYPE; EVENT_INDEX; [EVENT_DATE]`
+
+> **Note!**
+>
+> * If you want to delete an original repeat event rather than one of the subsequent repeated events, you can omit the date in the command.
+
+
+> **Warning!**
+>
+> * If you delete the original repeat event, all the subsequent repeated events will be deleted along with it.
+> * Deleting an event on the original event's date will delete the main event, along with all the subsequent repeated events.
+
+#### Example Usage
+
+The following few scenarios present several examples of how the delete command can be used.
+
+##### Scenario 1: Deleting a non-repeat event
+
+Let's say you had a doctor's appointment (stored as a Personal event `doctor's appointment`) that you cancelled, and now wish to remove the event from Scheduler-\-;. In order to find the index of the event, you may type in the command `list personal` as shown below:
+
+```
+list personal
+_________________________________
+Here is a list of your Personal events:
+1. [P][X] buy groceries on 2020-10-15, 13:00
+2. [P][X] clean room on 2020-11-20, 12:00
+3. [P][X] doctor's appointment on 2020-11-30, 15:00
+_________________________________
+```
+
+You may also type in `list all` to list all personal, timetable and zoom events.
+
+As seen from the list, the index of the Personal event `doctor's appointment` is 3. To delete it, type in the command `delete personal; 3`. This will delete the event from Scheduler-\
+-;, as shown in the following output:
+
+```
+delete personal; 3
+_________________________________
+You have successfully deleted this event!
+[P][X] doctor's appointment on 2020-11-30, 15:00
+_________________________________
+```
+
+##### Scenario 2: Deleting a particular repetition of a repeat event
+
+Let's say you repeated the Timetable event `CG2271 lab` one too many times. In order to find the event index, you may type in `list timetable` as shown below:
+
+```
+list timetable
+_________________________________
+Here is a list of your Timetable events:
+1. [T][O] CG2271 lab, Location: E4A-04-08 on 2020-10-09, 08:00 is also on:
+ 1. 2020-10-16 08:00 [O]
+ 2. 2020-10-23 08:00 [O]
+ 3. 2020-10-30 08:00 [X]
+ 4. 2020-11-06 08:00 [X]
+ 5. 2020-11-13 08:00 [X]
+2. [T][X] GER exam on 2020-11-16, 12:00
+_________________________________
+```
+
+As seen from the list, the index of the Timetable event `CG2271 lab` is 1. To delete the last repetition on 13 November 2020, type in the command `delete timetable; 1; 13/11/2020`. This deletes the intended repetition of the event, as shown in the following output:
+
+```
+delete timetable; 1; 13/11/2020
+_________________________________
+You have successfully deleted this event!
+[T][X] CG2271 lab, Location: E4A-04-08 on 2020-11-13, 08:00
+_________________________________
+```
+
+> **Warning!**
+>
+> * Deleting the first repetition of a repeat event (such as the one occurring on 9 October 2020 for `CG2271 lab` here) will delete all subsequent repetitions along with it.
+> * In this scenario, typing `delete timetable; 1` or `delete timetable; 1; 9/10/2020` will delete all repetitions of `CG2271 lab`.
+
+
+
+### 3.12 Note Taking: `note` (Qing Ning)
+Need to take last minutes meeting notes? Need to write some notes but don’t know where to keep them? Fear not! We have got you covered. Use this command to take notes and attach them to the event you are attending. You can also choose to open an empty note and type the notes.
+
+Format: `note EVENT_TYPE; EVENT_INDEX`
+
+* The `EVENT_TYPE` have to be either personal, zoom or timetable.
+* Scheduler-\-; will prompt you to type your notes.
+
+> **Warning!**
+>
+> * The event index keyed in have to be valid so that note can be created for the specified event index.
+> * The grave accent symbol \` is an illegal character. If you use this symbol, the notes program will interpret it as a new line.
+
+#### Example Usage
+
+The following scenario presents an example of how the note command can be used.
+
+##### Scenario 1: Creating a note for a personal event
+
+Let us assume that you have a personal event titled `dental appointment` that you would like to create a note for.
+
+Firstly, find out what index number the `dental appointment` personal event has been assigned to. You can do this easily by typing in `list personal`.
+In my case, the following things will be printed out on the terminal.
+
+You can do this by typing the command list personal into your application. The result for the following command is shown in the following code snippet.
+
+```
+list personal
+_________________________________
+Here is a list of your Personal events:
+1. [P][X] dental appointment on 2020-09-18
+2. [P][X] birthday on 2020-11-09
+3. [P][X] quiz on 2020-10-07, 16:00
+_________________________________
+
+```
+In this case, the index number of the event `dental appointment` is 1.
+
+Next, type in the command `note personal; 1`. Scheduler-\-; will prompt you to key in your notes as shown in the code snippet below.
+
+```
+Please type in your notes. To stop note taking, ensure that you are in a new line and type 'noteend' and press enter
+
+```
+
+Type in your notes and indicate end of note by `enter` to a new line and type `noteend`. The following code snippet should be what you type in.
+
+```
+Bring appointment card
+Bring reports for dental appointment
+noteend
+```
+
+You should now see that the note command has successfully created your note for `dental appointment`.
+
+```
+You have successfully written the note for this event!
+[P][X] dental appointment on 2020-09-18
+---------2020-10-30T00:53:01.907824900---------
+Bring appointment card
+Bring reports for dental appointment
+_________________________________
+```
+
+
+
+
+### 3.13 View note: `view` (Qing Ning)
+Typed your notes but want to view them? Here's how you view them!
+
+Format: `view EVENT_TYPE; EVENT_INDEX`
+
+* The `EVENT_TYPE` have to be either personal, zoom or timetable.
+* Scheduler-\-; will prompt you to type your notes.
+
+> **Warning!**
+>
+> * The event index keyed in have to be valid so that note can be viewed for the specified event index.
+
+#### Example Usage
+
+The following scenario presents an example of how the view command can be used.
+
+##### Scenario 1: Viewing a note for a personal event
+
+Let us assume that you have a personal event titled `dental appointment` that you would like to view the notes from.
+
+Firstly, find out what index number the `dental appointment` personal event has been assigned to. You can do this easily by typing in `list personal`.
+In my case, the following things will be printed out on the terminal.
+
+You can do this by typing the command list personal into your application. The result for the following command is shown in the following code snippet.
+
+```
+list personal
+_________________________________
+Here is a list of your Personal events:
+1. [P][X] dental appointment on 2020-09-18
+2. [P][X] birthday on 2020-11-09
+3. [P][X] quiz on 2020-10-07, 16:00
+_________________________________
+
+```
+In this case, the index number of the event `dental appointment` is 1.
+
+Next, type in the command `view personal; 1`.
+
+You should now see that the view command has successfully shown your note for `dental appointment`.
+
+```
+These are the notes that you have taken:
+---------2020-10-30T00:53:01.907824900---------
+Bring appointment card
+Bring reports for dental appointment
+_________________________________
+```
+
+
+
+
+### 3.14 Reminder: `reminder` (Qing Ning)
+Fear of forgetting what you have today? Scheduler-\-; is here to show you your events and task to be completed for the day. Cheers to no more missed deadlines and meetings!
+
+Format: `reminder`
+
+
+#### Example Usage
+
+The following scenario will be used to explain how the reminder command works.
+
+##### Scenario 1: Remind the event happening today
+
+You want to be reminded of what is happening today.
+
+To do so, you should type in:
+
+```
+reminder
+```
+
+You should now see that the reminder command has successfully shown the events today:
+
+```
+You have the following events today:
+[T][X] math, Location: S17 on 2020-10-26, 12:00
+[Z][X] math, Link: www.zoom.com/blah on 2010-10-26, 12:00
+[P][X] sleep on 2020-10-26, 23:00
+```
+
+
+
+### 3.15 Extracting events from texts: `extract` (Matthew Gani)
+Ever feel tired of reading long emails everyday? Copy and paste your email into this feature!
+You can use our extract command which will help you read any text and extract out possible dates and times.
+You’ll be able to choose the dates and times detected and create a Personal or Zoom event.
+
+The extract feature detects dates in the "DD MONTH_NAME YYYY" format or the "MONTH_NAME DD YYYY" format which is used the most in emails.
+It will detect time in 12 hour format (HH:mm am/pm, HH.mm am/pm or HH am/pm) and 24 hour (HH:mm or HH.mm) format.
+If a valid zoom link is detected (links containing '.zoom.' and start with 'https://' or 'http://'), it will try to create a zoom event for you.
+Otherwise, a personal event will be made.
+
+Format: `extract EVENT_DESCRIPTION;`
+- `EVENT_DESCRIPTION` could be the subject of the email or the name you want your event to be.
+
+> **Note!**
+>
+> * The extract feature can also detect when there are suffixes like st/nd/rd/th for the day portion of the date.
+> * The extract feature has the ability to attach the current year to the date if it is not specified in the text.
+> * The text body can include multiple paragraphs that are copy and pasted from emails.
+> * The month name detected can only be detected if spelled fully or is in its 3 letter short form. For example, `20 sep 2020` will be detected while `20 sept 2020` will not be detected.
+> * Unlike some other commands, 24 hour time without "." or ":" will not be detected.
+> * The full date has to be in the same line to be detected properly. If the date is separated by a paragraph/by a new line it may not be detected properly. This goes for the time and zoom links too.
+
+> **Warning!**
+>
+> * It is not advised to edit the copy and pasted text in the command line. It may result in this feature not working as expected.
+> * It is not advised to copy and paste text containing emojis into the command line because the command line may crash. It is recommended to remove these emojis by editing the text in a text editor first like the Notepad app.
+
+#### Example Usage
+
+The following scenario presents an example of how the extract command can be used.
+
+##### Scenario 1: Extracting a zoom event from an email
+
+You receive an email for CG2271 Quiz 2 where the email body is "Hi all, we will be having the quiz on either
+ 4th October 2020 or October 15 2020 at either 3pm or 3.30pm.
+ The link is at https://nus-sg.zoom.us/j/2226375MG." and you want to use the extract command to quickly sieve out dates, times and zoom links for you.
+
+To do so, you should type in:
+ ```
+extract CG2271 Quiz 2;
+ ```
+Scheduler-\-; will prompt you to copy and paste or enter the text you want to extract from.
+````
+_________________________________
+Copy and paste or enter the body of the text you want to extract from!
+At the end of your text, press enter to go to the next line, enter 'extractend' with
+ no quotation marks and press enter once more.
+_________________________________
+````
+Next, copy and paste or type in the text you want to extract from.
+````
+Hi all, we will be having the quiz on either 4th October 2020 or October 15 2020 at
+ either 3pm or 3.30pm.
+The link is at https://nus-sg.zoom.us/j/2226375MG.
+````
+Then, tell Scheduler-\-; you are done by typing `extractend` on a new line.
+````
+extractend
+````
+The output should look like this:
+````
+One zoom link detected and chosen: https://nus-sg.zoom.us/j/2226375MG
+We have detected 2 dates in this text body!
+Please select the date you want for this event from the list below!
+_________________________________
+1. 2020-10-04
+2. 2020-10-15
+_________________________________
+````
+If you want choose the second option for your event's date to be 2020-10-15, input `2` and you should see something like this:
+````
+2
+We have detected 2 timings in this text body!
+Please select the time you want for this event from the list below!
+_________________________________
+1. 15:00
+2. 15:30
+_________________________________
+````
+Lastly, if you want the second option for your event's time to be 15:30, input `2` and your zoom event will be created.
+````
+2
+You have successfully added this event to your list!
+[Z][X] CG2271 Quiz 2, Link: https://nus-sg.zoom.us/j/2226375MG on 2020-10-15, 15:30
+_________________________________
+````
+
+
+### 3.16 Save events: `save` (Colin)
+Unsure if your files have been saved successfully? Use this function to save it!
+
+Format: `save`
+
+#### Example Usage
+
+The following scenario presents an example of how the save command can be used.
+
+##### Scenario 1: Saving your work after making some changes
+
+Let us assume that you would like to ensure that your latest changes to the program have been saved
+
+Simply type `save`
+
+The program will now save your current changes and a save confirmation message should be printed. The output should look like this:
+
+```
+save
+_________________________________
+The file has successfully been saved!
+_________________________________
+```
+
+
+
+
+### 3.17 Exiting the program: `bye`
+Time to have a rest? See you later! Do not worry, we will keep your events in Scheduler-\-.
+
+Format: `bye`
+
+#### Example Usage
+
+The following scenario presents an example of how the bye command can be used.
+
+##### Scenario 1: Exiting the program
+
+Let us assume that you would like to exit the program.
+
+Simply type `bye`
+
+The program will now save your current changes and exit the program. The output should look like this:
+
+```
+bye
+_________________________________
+Thank you for using scheduler--;!
+We hope to see you soon!
+_________________________________
+
+```
+
+Once the exit message have been printed, you may leave the terminal.
+
+## 4. FAQ
+In this section, you can find some frequently asked questions (FAQ).
+
+**Q**: How do I transfer my data to another computer?
+**A**: Transfer your data by copying the `data` file and `scheduler.jar`.
+
+**Q**: What if I run into bugs while using Scheduler-\-;?
+**A**: If you run into bugs that cause the program to crash, please contact us at scheduler@gmail.com with the `logging.txt`.
+
+**Q**: What if I have any suggestions for Scheduler-\-;?
+**A**: Feel free to drop us an email at scheduler@gmail.com.
+
+**Q**: Can I create 2 events on the same date and time?
+**A**: Yes you can. We are allowing this function so that you can add an event that does not require your presence.
+
+
+
+## 5. Command Summary
+
+In this section is a summary of all the commands with the accepted format and short examples for ease of usage.
+
+
-{Give a 'cheat sheet' of commands here}
-* Add todo `todo n/TODO_NAME d/DEADLINE`
diff --git a/docs/diagrams/AddCommandClassDiagram.jpg b/docs/diagrams/AddCommandClassDiagram.jpg
new file mode 100644
index 0000000000..a1f3e23c74
Binary files /dev/null and b/docs/diagrams/AddCommandClassDiagram.jpg differ
diff --git a/docs/diagrams/Architecture.jpg b/docs/diagrams/Architecture.jpg
new file mode 100644
index 0000000000..43a107dbac
Binary files /dev/null and b/docs/diagrams/Architecture.jpg differ
diff --git a/docs/diagrams/ByeCommandSequenceDiagram.png b/docs/diagrams/ByeCommandSequenceDiagram.png
new file mode 100644
index 0000000000..337ad5a249
Binary files /dev/null and b/docs/diagrams/ByeCommandSequenceDiagram.png differ
diff --git a/docs/diagrams/CalendarClassDiagram.png b/docs/diagrams/CalendarClassDiagram.png
new file mode 100644
index 0000000000..fa4fd57792
Binary files /dev/null and b/docs/diagrams/CalendarClassDiagram.png differ
diff --git a/docs/diagrams/CalendarExecuteSequenceDiagram.jpg b/docs/diagrams/CalendarExecuteSequenceDiagram.jpg
new file mode 100644
index 0000000000..5a13bc8f9d
Binary files /dev/null and b/docs/diagrams/CalendarExecuteSequenceDiagram.jpg differ
diff --git a/docs/diagrams/CheckCommandClassDiagram.jpg b/docs/diagrams/CheckCommandClassDiagram.jpg
new file mode 100644
index 0000000000..77314a3b4b
Binary files /dev/null and b/docs/diagrams/CheckCommandClassDiagram.jpg differ
diff --git a/docs/diagrams/CheckCommand_seq_diagram.jpg b/docs/diagrams/CheckCommand_seq_diagram.jpg
new file mode 100644
index 0000000000..f4da5d6faf
Binary files /dev/null and b/docs/diagrams/CheckCommand_seq_diagram.jpg differ
diff --git a/docs/diagrams/Command.png b/docs/diagrams/Command.png
new file mode 100644
index 0000000000..be6d665cbc
Binary files /dev/null and b/docs/diagrams/Command.png differ
diff --git a/docs/diagrams/CreatedDeleteCommand.jpg b/docs/diagrams/CreatedDeleteCommand.jpg
new file mode 100644
index 0000000000..2265623b76
Binary files /dev/null and b/docs/diagrams/CreatedDeleteCommand.jpg differ
diff --git a/docs/diagrams/CreatedDoneCommand.jpg b/docs/diagrams/CreatedDoneCommand.jpg
new file mode 100644
index 0000000000..c9bf01cf35
Binary files /dev/null and b/docs/diagrams/CreatedDoneCommand.jpg differ
diff --git a/docs/diagrams/CreatedUndoneCommand.jpg b/docs/diagrams/CreatedUndoneCommand.jpg
new file mode 100644
index 0000000000..c9bf01cf35
Binary files /dev/null and b/docs/diagrams/CreatedUndoneCommand.jpg differ
diff --git a/docs/diagrams/DeadlineScenario.png b/docs/diagrams/DeadlineScenario.png
new file mode 100644
index 0000000000..cb32f7c17d
Binary files /dev/null and b/docs/diagrams/DeadlineScenario.png differ
diff --git a/docs/diagrams/DeadlineSequenceDiagram.png b/docs/diagrams/DeadlineSequenceDiagram.png
new file mode 100644
index 0000000000..f4bcc8f44a
Binary files /dev/null and b/docs/diagrams/DeadlineSequenceDiagram.png differ
diff --git a/docs/diagrams/DeleteCommandClassDiagram.jpg b/docs/diagrams/DeleteCommandClassDiagram.jpg
new file mode 100644
index 0000000000..639551af30
Binary files /dev/null and b/docs/diagrams/DeleteCommandClassDiagram.jpg differ
diff --git a/docs/diagrams/DeleteCommandStates.jpg b/docs/diagrams/DeleteCommandStates.jpg
new file mode 100644
index 0000000000..a1938e1ce8
Binary files /dev/null and b/docs/diagrams/DeleteCommandStates.jpg differ
diff --git a/docs/diagrams/DoneCommandAfterState.jpg b/docs/diagrams/DoneCommandAfterState.jpg
new file mode 100644
index 0000000000..3b522c8d44
Binary files /dev/null and b/docs/diagrams/DoneCommandAfterState.jpg differ
diff --git a/docs/diagrams/DoneCommandClassDiagram.jpg b/docs/diagrams/DoneCommandClassDiagram.jpg
new file mode 100644
index 0000000000..55f15e9c25
Binary files /dev/null and b/docs/diagrams/DoneCommandClassDiagram.jpg differ
diff --git a/docs/diagrams/DoneCommandInitialState.jpg b/docs/diagrams/DoneCommandInitialState.jpg
new file mode 100644
index 0000000000..e26d3b6b65
Binary files /dev/null and b/docs/diagrams/DoneCommandInitialState.jpg differ
diff --git a/docs/diagrams/DoneCommandStates.jpg b/docs/diagrams/DoneCommandStates.jpg
new file mode 100644
index 0000000000..2d4960377e
Binary files /dev/null and b/docs/diagrams/DoneCommandStates.jpg differ
diff --git a/docs/diagrams/ExtractCommandClassDiagram.jpg b/docs/diagrams/ExtractCommandClassDiagram.jpg
new file mode 100644
index 0000000000..79c3c3446a
Binary files /dev/null and b/docs/diagrams/ExtractCommandClassDiagram.jpg differ
diff --git a/docs/diagrams/GoalClassDiagram.png b/docs/diagrams/GoalClassDiagram.png
new file mode 100644
index 0000000000..15e09e8ccd
Binary files /dev/null and b/docs/diagrams/GoalClassDiagram.png differ
diff --git a/docs/diagrams/GoalExecuteSequenceDiagram.jpg b/docs/diagrams/GoalExecuteSequenceDiagram.jpg
new file mode 100644
index 0000000000..142bc73c74
Binary files /dev/null and b/docs/diagrams/GoalExecuteSequenceDiagram.jpg differ
diff --git a/docs/diagrams/ListClassDiagram.png b/docs/diagrams/ListClassDiagram.png
new file mode 100644
index 0000000000..58d6bff58a
Binary files /dev/null and b/docs/diagrams/ListClassDiagram.png differ
diff --git a/docs/diagrams/ListExecuteSequenceDiagram.jpg b/docs/diagrams/ListExecuteSequenceDiagram.jpg
new file mode 100644
index 0000000000..cb39173275
Binary files /dev/null and b/docs/diagrams/ListExecuteSequenceDiagram.jpg differ
diff --git a/docs/diagrams/ListParseSequenceDiagram.jpg b/docs/diagrams/ListParseSequenceDiagram.jpg
new file mode 100644
index 0000000000..c43a5b8a91
Binary files /dev/null and b/docs/diagrams/ListParseSequenceDiagram.jpg differ
diff --git a/docs/diagrams/NoteCommandScenario.png b/docs/diagrams/NoteCommandScenario.png
new file mode 100644
index 0000000000..a98518316c
Binary files /dev/null and b/docs/diagrams/NoteCommandScenario.png differ
diff --git a/docs/diagrams/NoteCommandSequenceDiagram.png b/docs/diagrams/NoteCommandSequenceDiagram.png
new file mode 100644
index 0000000000..533483dad7
Binary files /dev/null and b/docs/diagrams/NoteCommandSequenceDiagram.png differ
diff --git a/docs/diagrams/Parser_diagram.jpg b/docs/diagrams/Parser_diagram.jpg
new file mode 100644
index 0000000000..a4bc85f886
Binary files /dev/null and b/docs/diagrams/Parser_diagram.jpg differ
diff --git a/docs/diagrams/ReminderCommandClass.png b/docs/diagrams/ReminderCommandClass.png
new file mode 100644
index 0000000000..fc807e881d
Binary files /dev/null and b/docs/diagrams/ReminderCommandClass.png differ
diff --git a/docs/diagrams/ReminderCommandSequenceDiagram.jpg b/docs/diagrams/ReminderCommandSequenceDiagram.jpg
new file mode 100644
index 0000000000..5a99e27435
Binary files /dev/null and b/docs/diagrams/ReminderCommandSequenceDiagram.jpg differ
diff --git a/docs/diagrams/RepeatScenario.jpg b/docs/diagrams/RepeatScenario.jpg
new file mode 100644
index 0000000000..b1dc8c9121
Binary files /dev/null and b/docs/diagrams/RepeatScenario.jpg differ
diff --git a/docs/diagrams/UndoneCommandClassDiagram.jpg b/docs/diagrams/UndoneCommandClassDiagram.jpg
new file mode 100644
index 0000000000..829e4b953e
Binary files /dev/null and b/docs/diagrams/UndoneCommandClassDiagram.jpg differ
diff --git a/docs/diagrams/UndoneCommandStates.jpg b/docs/diagrams/UndoneCommandStates.jpg
new file mode 100644
index 0000000000..dcac40a376
Binary files /dev/null and b/docs/diagrams/UndoneCommandStates.jpg differ
diff --git a/docs/diagrams/UserData_diagram.jpg b/docs/diagrams/UserData_diagram.jpg
new file mode 100644
index 0000000000..9b7295c07f
Binary files /dev/null and b/docs/diagrams/UserData_diagram.jpg differ
diff --git a/docs/diagrams/ViewCommandClass.png b/docs/diagrams/ViewCommandClass.png
new file mode 100644
index 0000000000..cf2ec3f274
Binary files /dev/null and b/docs/diagrams/ViewCommandClass.png differ
diff --git a/docs/diagrams/ViewCommandSequenceDiagram.png b/docs/diagrams/ViewCommandSequenceDiagram.png
new file mode 100644
index 0000000000..0bd0334b51
Binary files /dev/null and b/docs/diagrams/ViewCommandSequenceDiagram.png differ
diff --git a/docs/diagrams/addCommand.jpg b/docs/diagrams/addCommand.jpg
new file mode 100644
index 0000000000..8fe96ca899
Binary files /dev/null and b/docs/diagrams/addCommand.jpg differ
diff --git a/docs/diagrams/classDiagramStorage.jpg b/docs/diagrams/classDiagramStorage.jpg
new file mode 100644
index 0000000000..093a30d069
Binary files /dev/null and b/docs/diagrams/classDiagramStorage.jpg differ
diff --git a/docs/diagrams/command-summary.jpeg b/docs/diagrams/command-summary.jpeg
new file mode 100644
index 0000000000..34436b7cf1
Binary files /dev/null and b/docs/diagrams/command-summary.jpeg differ
diff --git a/docs/diagrams/extractCommand.jpg b/docs/diagrams/extractCommand.jpg
new file mode 100644
index 0000000000..4a98349aff
Binary files /dev/null and b/docs/diagrams/extractCommand.jpg differ
diff --git a/docs/diagrams/getDate-day-month-year.jpg b/docs/diagrams/getDate-day-month-year.jpg
new file mode 100644
index 0000000000..0706b14b3f
Binary files /dev/null and b/docs/diagrams/getDate-day-month-year.jpg differ
diff --git a/docs/diagrams/getDate-only-month-year.jpg b/docs/diagrams/getDate-only-month-year.jpg
new file mode 100644
index 0000000000..a57cb11b8d
Binary files /dev/null and b/docs/diagrams/getDate-only-month-year.jpg differ
diff --git a/docs/diagrams/getDate-only-year-given.jpg b/docs/diagrams/getDate-only-year-given.jpg
new file mode 100644
index 0000000000..0a5667e498
Binary files /dev/null and b/docs/diagrams/getDate-only-year-given.jpg differ
diff --git a/docs/diagrams/getDate_seq_diagram.jpg b/docs/diagrams/getDate_seq_diagram.jpg
new file mode 100644
index 0000000000..46399a9b8c
Binary files /dev/null and b/docs/diagrams/getDate_seq_diagram.jpg differ
diff --git a/docs/diagrams/object_diagram_storage_after.png b/docs/diagrams/object_diagram_storage_after.png
new file mode 100644
index 0000000000..0ce932339c
Binary files /dev/null and b/docs/diagrams/object_diagram_storage_after.png differ
diff --git a/docs/diagrams/object_diagram_storage_before.png b/docs/diagrams/object_diagram_storage_before.png
new file mode 100644
index 0000000000..1efc2ef11f
Binary files /dev/null and b/docs/diagrams/object_diagram_storage_before.png differ
diff --git a/docs/diagrams/repeatstep1.jpg b/docs/diagrams/repeatstep1.jpg
new file mode 100644
index 0000000000..6c5a825dae
Binary files /dev/null and b/docs/diagrams/repeatstep1.jpg differ
diff --git a/docs/diagrams/repeatstep2.jpg b/docs/diagrams/repeatstep2.jpg
new file mode 100644
index 0000000000..56c716813c
Binary files /dev/null and b/docs/diagrams/repeatstep2.jpg differ
diff --git a/docs/diagrams/repeatstep3.jpg b/docs/diagrams/repeatstep3.jpg
new file mode 100644
index 0000000000..92c6be3321
Binary files /dev/null and b/docs/diagrams/repeatstep3.jpg differ
diff --git a/docs/diagrams/repeatstep4.jpg b/docs/diagrams/repeatstep4.jpg
new file mode 100644
index 0000000000..45f9a0e038
Binary files /dev/null and b/docs/diagrams/repeatstep4.jpg differ
diff --git a/docs/diagrams/repeatstep5.jpg b/docs/diagrams/repeatstep5.jpg
new file mode 100644
index 0000000000..eea1e5d85c
Binary files /dev/null and b/docs/diagrams/repeatstep5.jpg differ
diff --git a/docs/diagrams/repeatstep6.jpg b/docs/diagrams/repeatstep6.jpg
new file mode 100644
index 0000000000..fa81a788cd
Binary files /dev/null and b/docs/diagrams/repeatstep6.jpg differ
diff --git a/docs/diagrams/repeatstep7.jpg b/docs/diagrams/repeatstep7.jpg
new file mode 100644
index 0000000000..9d29dec813
Binary files /dev/null and b/docs/diagrams/repeatstep7.jpg differ
diff --git a/docs/diagrams/savefull.png b/docs/diagrams/savefull.png
new file mode 100644
index 0000000000..bc875f8378
Binary files /dev/null and b/docs/diagrams/savefull.png differ
diff --git a/docs/diagrams/storageOverall.jpg b/docs/diagrams/storageOverall.jpg
new file mode 100644
index 0000000000..cb77eb2dfc
Binary files /dev/null and b/docs/diagrams/storageOverall.jpg differ
diff --git a/docs/diagrams/storageclass.jpg b/docs/diagrams/storageclass.jpg
new file mode 100644
index 0000000000..043794ba59
Binary files /dev/null and b/docs/diagrams/storageclass.jpg differ
diff --git a/docs/team/colin386.md b/docs/team/colin386.md
new file mode 100644
index 0000000000..7ae52168b4
--- /dev/null
+++ b/docs/team/colin386.md
@@ -0,0 +1,43 @@
+# Colin Ng - Project Portfolio Page
+
+## Overview
+Scheduler--; (S--) is a desktop app for managing deadlines from different sources. The user will use a Command Line Interface to control it. It is specially designed for Computing students who are comfortable in using CLI and have Git project deadlines as well as consolidated Zoom session links due which will suit home based learning in this COVID period.
+
+Given below are my contributions to the project
+
+## Summary of Contributions
+
+- **New Feature:** Added the ability to call for help in the application
+ - What it does: allows the users to seek help and assistance on how to use the commands in the application
+ - Justification: This features allows for users who are getting familiar with how to use the application a quick way to get help about a feature without having to dig through the thicker user manual.
+ - Highlights: This command required the implementation of a bracket system to inform the program which section of the help file to print out for each different variation of the help command called by the user.
+- **New Feature:** Assisted with the development of a repeat function
+ - What it does: allows the user to repeat an event for a specified amount of iteration, each iteration is a specified amount of unit time away from the original date and time.
+ - Justification: This features was implemented as the group felt that having to manually key in events that repeat periodically into the program would be tedious, hence the repeat command is provided to help speed up typing event information.
+ - Highlights: It was difficult implementing the repeat command. We fist assumed that we could just simply store the states of the repeated events along the main events, but later realised that this method proved difficult to manage the repeated events for future events. One of our team members decided to implement events as a cloneable event, and it helped to make the implementation of future functions easier.
+- **New Feature:** Added the ability for the program to load user data from the computer and save user data on the computer.
+
+- **Code Contributed:** [RepoSense Link](https://nus-cs2113-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=colin386&sort=groupTitle&sortWithin=title&since=2020-09-27&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+
+- **Project Management:**
+ - Help set up the team organisation repo
+ - Necessary general code enhancements
+ - Help set up logging code for the project.
+ - Managed releases `v1.0` - `v2.1` (3 releases) on Github.
+
+- **Enhancements to Existing Features:**
+ - Updated the parser to accept multiple commands in succession for users who are familiar with the product and would like to execute more than one command at once
+ - Assisted with the note taking application in terms of interpreting various special characters.
+
+- **Documentation:**
+ - User guide
+ - Add documentation for the features `repeat`, `help` and `save`.
+ - Add documentation for setting up the program.
+ - Developer Guide:
+ - Added implementation details for the saving functions.
+ - Added sequence diagram of the `repeat` command implementation.
+ - Wrote manual testing instructions.
+
+- **Community:**
+ - Assisted with peer review bug reporting [1](https://github.com/nus-cs2113-AY2021S1/tp/pull/51/files)
+
diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md
deleted file mode 100644
index ab75b391b8..0000000000
--- a/docs/team/johndoe.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# John Doe - Project Portfolio Page
-
-## Overview
-
-
-### Summary of Contributions
diff --git a/docs/team/marcursor.md b/docs/team/marcursor.md
new file mode 100644
index 0000000000..21349ae4a1
--- /dev/null
+++ b/docs/team/marcursor.md
@@ -0,0 +1,40 @@
+# Marcus Tan's Project Portfolio Page
+
+## Project: Scheduler--;
+
+Scheduler-\-; (S-\-) is a desktop daily planner application for managing day-to-day events. The user interacts with it using a Command Line Interface (CLI). It is written in Java.
+
+Given below are my contributions to this project.
+
+### Summary of Contributions
+
+- Feature: Implemented the ability to check for events occurring within a given time period.
+ - What it does: Allows the user to provide a start date, start time, end date and end time to specify a time period to check for events.
+ - Justification: This feature is useful because it enables users to easily check their availability during a given period.
+ - Highlights: This feature allows for various input formats for date and time, including partially filled out dates/times. This required analysis on how to process date and time inputs to return a valid date/time value.
+
+- Feature: Implemented the mark events as done/undone.
+ - What it does: Allows the user to set the status of events to done/undone.
+ - Justification: This feature is important as it allows users to keep track of the status of their events.
+ - Highlights: This feature needs to look for matching events in sub ArrayLists in the case of repeated events. This involves interpreting of the events during execution.
+
+- Feature: Implemented the ability to delete events from Scheduler--;.
+ - What it does: Allows the user to delete events from Scheduler--;.
+ - Justification: This feature is important as it allows users to have more control over their events schedule. The feature allows for removal of obsolete or erroneously input events.
+ - Highlights: This feature automatically sets repeat events to non-repeat if it deletes the last repeated event.
+
+- Code contributed: [RepoSense Link](https://nus-cs2113-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=marcursor&sort=groupTitle&sortWithin=title&since=2020-09-27&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other&tabOpen=true&tabType=authorship&tabAuthor=marcursor&tabRepo=AY2021S1-CS2113T-T12-4%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other)
+
+- Project Management:
+ - Managed releases `v1.0`, `v2.0` and `v2.1` (3 releases) on Github
+
+- Documentation:
+ - User Guide:
+ - Added documentation for the `check`, `done`, `undone` and `delete` features.
+ - Developer Guide:
+ - Added design details of the `Parser` and `UserData` components.
+ - Added implementation details of the `check`, `done`, `undone` and `delete` features.
+
+- Community:
+ - Tested and reviewed other groups project (with non-trivial review comments): [PR Review](https://github.com/marcursor/ped/issues)
+ - DG reviewed (with non-trivial review comments): [DG Review](https://github.com/nus-cs2113-AY2021S1/tp/pull/4#pullrequestreview-519320304)
diff --git a/docs/team/matthewgani.md b/docs/team/matthewgani.md
new file mode 100644
index 0000000000..aeb50cd8d1
--- /dev/null
+++ b/docs/team/matthewgani.md
@@ -0,0 +1,39 @@
+# Matthew Gani - Project Portfolio Page
+
+## Overview
+Scheduler--; (S--) is a desktop app for managing deadlines from different sources. The user will use a Command Line Interface to control it. It is specially designed for Computing students who are comfortable in using CLI and have Git project deadlines as well as consolidated Zoom session links due which will suit home based learning in this COVID period.
+
+Given below are my contributions to the project.
+
+### Summary of Contributions
+
+- **New Feature:** Added the ability to add events
+ - What it does: Allows the user to add events under 3 categories, Personal, Zoom and Timetable. These events all can take in multiple parameters to be created.
+ - Justification: This feature is one of the basic features a scheduling app should have, adding events to the user's list.
+ - Highlights: This feature required splitting up the user's command and checking individually if the fields like data and time were valid. There was also 2-3 combinations that takes in different numbers of parameters for each category of event that we needed to detect properly.
+
+- **New Feature:** Added the ability to extract dates, times and zoom links from any text
+ - What it does: Allows the user to copy and paste emails into the CLI and the feature will extract valid dates, times and zoom links. The user can then choose from them if there are multiple dates etc.
+ - Justification: This feature is a unique feature that was implemented so users didn't need to spend so long reading emails for important details and also serves as an alternative to adding an event quickly.
+ - Highlights: This feature required the use of Regex to match patterns for the date, time and zoom links. It required the ability to take in copy pasted texts that had multiple paragraphs (with newlines) in them. A lot of variations of the date were detected, for example with suffixes or short form like Sep instead of September. It also has the ability to add the event as Personal or Zoom depending on if a Zoom link was detected.
+
+- **Code Contributed:** [RepoSense Link](https://nus-cs2113-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=matthewgani&sort=groupTitle&sortWithin=title&since=2020-09-27&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+
+- **Project Management**
+ - Managed releases `v1.0` - `v2.1` (3 releases) on GitHub
+
+- **Documentation:**
+ - User guide:
+ - Added documentation for the `add` and `extract` features.
+ - Added and maintained the table of contents with hyperlinks, made the headings with numberings
+
+ - Developer Guide:
+ - Added implementation details for the `add` feature, with a sequence diagram and class diagram.
+ - Added implementation details for the `extract` feature, with a sequence diagram and class diagram.
+ - Added and maintained the table of contents with hyperlinks.
+ - Added the Setting up, getting started section.
+ - Added Use cases.
+
+- **Community:**
+ - Assisted with peer review for other group's PR [1](https://github.com/nus-cs2113-AY2021S1/tp/pull/1/files)
+ - Assisted in testing other group's tp based on their User Guide and finding bugs [1](https://github.com/matthewgani/ped/issues)
diff --git a/docs/team/ninggggx99.md b/docs/team/ninggggx99.md
new file mode 100644
index 0000000000..14666f4983
--- /dev/null
+++ b/docs/team/ninggggx99.md
@@ -0,0 +1,55 @@
+# Low Qing Ning - Project Portfolio Page
+
+## Overview
+Scheduler--; is a desktop app for managing deadlines from different sources. The user will use a Command Line Interface to control it. It is specially designed for Computing students who are comfortable in using CLI and have Git project deadlines as well as consolidated Zoom session links due which will suit home based learning in this COVID period.
+
+Given below are my contributions to the project.
+
+### Summary of Contributions
+
+- **New Feature:** Added the ability to set/update deadline for personal events
+ * What it does: Allows the user to set or change the deadline for personal events.
+ * Justification: This feature is one of the basic feature that allows user to add in date and time for their personal events.
+ * Highlights: This feature required splitting up the user's command and checking individually if the fields like index, data and time were valid.
+
+- **New Feature:** Added the ability to remind users of the events today
+ * What it does: Prompt the user at the start with the events happening today also allows the user to check the events that they are having today.
+ * Justification: This feature is one of the secondary feature that allows user to be reminded of the events happening on that day.
+ * Highlights: This feature compares the current date with event's date and list them in the order that it is happening. Events with date but no time will also be included at the top of the list.
+
+- **New Feature:** Added the ability to allow users to key notes for an event
+ * What it does: Allow user to key notes for event they specified. Timestamp is used to differentiate the newer notes from the older ones and newer notes will be appended to the older one.
+ * Justification: This feature is one of the secondary feature that allows user to key in notes and attaching it to the events.
+ * Highlights: This feature takes in user input after Scheduler has prompted, user have to avoid using ` as this will be the indicator for a new line. Users are required to follow the user guide and type ```noteend``` on a new line to indicate the end of notes.
+
+- **New Feature:** Added the ability to allow users to see notes for an event
+ * What it does: Allow user to see notes for event they specified. Timestamp is used to differentiate the newer notes from the older ones and newer notes will be appended to the older one.
+ * Justification: This feature is one of the secondary feature that allows user to see notes they have attached to event.
+ * Highlights: This feature takes in user input that specify the type of event and index of event.
+
+- **New Feature:** Added the ability to exit the program
+ * What it does: Allow user to exit the program smoothly.
+ * Justification: This feature is one of the basic feature that allows user to exit the program smoothly.
+ * Highlights: This feature does not take in user input.
+
+- **Code Contributed:** [RepoSense Link](https://nus-cs2113-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=ninggggx99&sort=groupTitle&sortWithin=title&since=2020-09-27&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other&tabOpen=true&tabType=authorship&tabAuthor=ninggggx99&tabRepo=AY2021S1-CS2113T-T12-4%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other)
+
+- **Project Management**
+ * Managed releases `v1.0` - `v2.1` (3 releases) on GitHub
+- **Documentation:**
+ * User Guide:
+ * Added documentation for the `deadline`, `notes`, `bye` , `view` and `reminder` features.
+ * Added and maintained command summary with the commands and examples of usage
+
+ * Developer Guide:
+ * Added implementation details for Command Component
+ * Added implementation details for the `deadline` feature, with a sequence diagram and diagram showing how the program behave at each state.
+ * Added implementation details for the `notes` feature, with a sequence diagram and diagram showing how the program behave at each state.
+ * Added implementation details for the `view` feature, with a sequence diagram and class diagram.
+ * Added implementation details for the `reminder` feature, with a sequence diagram and class diagram.
+ * Added implementation details for the `bye` feature, with a sequence diagram.
+ * Added user stories
+- **Community:**
+ * Reported bugs and suggestions for other teams [1](https://github.com/ninggggx99/ped/issues)
+
+
\ No newline at end of file
diff --git a/docs/team/reinbowl.md b/docs/team/reinbowl.md
new file mode 100644
index 0000000000..f4b36f8818
--- /dev/null
+++ b/docs/team/reinbowl.md
@@ -0,0 +1,68 @@
+# Marcus Ng - Project Portfolio Page
+
+## Overview
+
+Scheduler--; (S--) is a desktop app for managing deadlines from different sources.
+The user will use a Command Line Interface to control it.
+It is specially designed for Computing students who are comfortable in using CLI and have Git project deadlines as well
+as consolidated Zoom session links due which will suit home based learning in this COVID period.
+
+Given below are my contributions to the project.
+
+## Summary of Contributions
+
+* **New Feature**: Added the structure to store user's data.
+ * What it does: Allows the user's data to be stored and accessed of easily during the program runtime.
+ * Justification: This feature is essential to keep track of the user's data in a manner that is not complicated and
+ can then be retrieved with simple code.
+ * Highlights: Because of the different data to keep track of, having a single list of event was not an ideal
+ solution. Multiple lists and each list with a name to keep track of the data had to be implemented for easy tracking
+ of the different data.
+
+* **New Feature**: Added the ability to list events.
+ * What it does: Allows the user to list events of a specific type or all events according to the order the events
+ are add in.
+ * Justification: This feature is essential to retrieving data and displaying the data back to the user.
+ * Highlights: This feature required the capability to check if user had specified a type of event to list.
+
+* **New Feature**: Added the ability to set goal.
+ * What it does: Allows the user to set a goal which is different from an event in that goal is a motivation.
+ * Justification: This feature allows user to remind themselves of their current goal in the midst of their busy
+ schedule.
+
+
+
+* **New Feature**: Added the ability to format events into a calendar.
+ * What it does: Allows the user to list events in the chronological order and in a format that is easy to read.
+ * Justification: This features allows user to sort their events according to date and time which every scheduler
+ should have.
+ * Highlights: This feature required a design that could take in multiple list of events and sort all first according
+ date then within each date according to time. This implementation was challenging as it had to factor in that
+ certain events are repeating with the repeat command.
+
+* **Code Contributed**:
+[RepoSense Link](https://nus-cs2113-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=reinbowl&sort=groupTitle&sortWithin=title&since=2020-09-27&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+
+* **Project Management**:
+ * Review pull requests to check if code can be refactored or improved.
+ * Ensure that issues are assigned and were done in time for each milestone.
+
+* **Enhancements to existing features**:
+ * Add DateTimeParser to accept different formats build upon Java DateTime parse. (Pull request [\#30](https://github.com/AY2021S1-CS2113T-T12-4/tp/pull/30))
+ * Updated repeat command and its data structure. (Pull request [\#83](https://github.com/AY2021S1-CS2113T-T12-4/tp/pull/83))
+
+* **Documentation**:
+ * User Guide:
+ * Added documentation for the features `list`, `goal` and `calendar`. (Pull request [\#161](https://github.com/AY2021S1-CS2113T-T12-4/tp/pull/161))
+
+ * Developer Guide:
+ * Added the architecture details. (Pull request [\#126](https://github.com/AY2021S1-CS2113T-T12-4/tp/pull/126))
+ * Added implementation details of the `list` feature. (Pull request [\#126](https://github.com/AY2021S1-CS2113T-T12-4/tp/pull/126))
+ * Added implementation details of the `goal` feature. (Pull request [\#96](https://github.com/AY2021S1-CS2113T-T12-4/tp/pull/96))
+ * Added implementation details of the `calendar` feature. (Pull request [\#255](https://github.com/AY2021S1-CS2113T-T12-4/tp/pull/255))
+
+* **Community**:
+ * Assisted with peer review for other group's PR
+ [1](https://github.com/nus-cs2113-AY2021S1/tp/pull/4/files)
+ * Assisted in testing other group's tp based on their User Guide and for bugs
+ [2](https://github.com/AY2021S1-CS2113T-F14-3/tp/releases)
diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java
index 5c74e68d59..bfb3711b2f 100644
--- a/src/main/java/seedu/duke/Duke.java
+++ b/src/main/java/seedu/duke/Duke.java
@@ -1,13 +1,96 @@
package seedu.duke;
+import seedu.duke.command.Command;
+import seedu.duke.command.ReminderCommand;
+import seedu.duke.data.UserData;
+import seedu.duke.event.EventList;
+import seedu.duke.exception.DukeException;
+import seedu.duke.parser.Parser;
+import seedu.duke.storage.Storage;
+import seedu.duke.storage.StorageParser;
+import seedu.duke.ui.Ui;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.Scanner;
public class Duke {
+
+ private Storage storage;
+ private UserData data;
+ private Ui ui;
+ private Command com;
+ private Parser currentParse;
+
+ //Location refers to the "data" directory stored in the folder of this application
+ private static final String FILELOCATION = "data";
+
+
+ /**
+ * Constructor for the Schedule components.
+ *
+ * @param filePath is a string containing the location of where the data text files are to be stored
+ */
+ public Duke(String filePath) {
+ ui = new Ui();
+
+ storage = new Storage(filePath, ui);
+
+ data = new UserData();
+
+ currentParse = new Parser();
+
+ EventLogger.clearLog();
+ EventLogger.initEventLogger(ui);
+
+
+ }
+
/**
- * Main entry-point for the java.duke.Duke application.
+ * Main run function. Loops until the bye command is entered.
*/
- public static void main(String[] args) {
- String logo = " ____ _ \n"
+ public void run() {
+
+ ui.printWelcomeMessage();
+ storage.loadAll(data);
+ ui.printDividerLine();
+ try {
+ Command reminder = currentParse.parse("reminder");
+ reminder.execute(data, ui, storage);
+ } catch (DukeException e) {
+ ui.printErrorMessage(e.getMessage());
+ }
+
+ boolean isExit = false;
+ while (!isExit) {
+ try {
+
+ ui.printDividerLine();
+ String userInput = ui.receiveCommand();
+ ArrayList allCommandInputs = currentParse.multiParse(userInput);
+ for (String commInputs : allCommandInputs) {
+ ui.printDividerLine();
+ Command c = currentParse.parse(commInputs);
+ isExit = c.isExit();
+ c.execute(data, ui, storage);
+ }
+
+ } catch (DukeException e) {
+ ui.printErrorMessage(e.getMessage());
+ }
+ }
+ storage.saveAll(data);
+ ui.printByeMessage();
+
+ }
+
+ /**
+ * Main function where the program starts.
+ *
+ * @param args No arguments entered so far
+ */
+ public static void main(String[] args) throws Exception {
+ /*String logo = " ____ _ \n"
+ "| _ \\ _ _| | _____ \n"
+ "| | | | | | | |/ / _ \\\n"
+ "| |_| | |_| | < __/\n"
@@ -16,6 +99,10 @@ public static void main(String[] args) {
System.out.println("What is your name?");
Scanner in = new Scanner(System.in);
- System.out.println("Hello " + in.nextLine());
+ System.out.println("Hello " + in.nextLine());*/
+ Duke duke = new Duke(FILELOCATION);
+ duke.run();
}
+
+
}
diff --git a/src/main/java/seedu/duke/EventLogger.java b/src/main/java/seedu/duke/EventLogger.java
new file mode 100644
index 0000000000..ad825165fd
--- /dev/null
+++ b/src/main/java/seedu/duke/EventLogger.java
@@ -0,0 +1,77 @@
+package seedu.duke;
+
+import seedu.duke.ui.Ui;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.logging.FileHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class EventLogger {
+
+ private static Logger logger = Logger.getLogger("allEventLog");
+ private static FileHandler fh;
+ private static Ui userInterface;
+
+ /**
+ * Function initialise the logger and inform it to write all messages to a text file.
+ *
+ * @param ui UserInterface object for printing out messages should the logger file fail to load
+ */
+ public static void initEventLogger(Ui ui) {
+ String logging = "logging.txt";
+ userInterface = ui;
+ try {
+ logger.setUseParentHandlers(false);
+ fh = new FileHandler(logging);
+ logger.addHandler(fh);
+ logger.setLevel(Level.ALL);
+ logger.fine("Logger created");
+
+ } catch (IOException e) {
+ ui.printErrorMessage("log file was not created");
+ }
+ }
+
+
+ /**
+ * Getter function for returning the main logger to be used by other programs.
+ *
+ * @return Logger object that will log information to a logging.txt file
+ */
+ public static Logger getEventLogger() {
+ return logger;
+ }
+
+ /**
+ * Function resets the log file to a blank state.
+ */
+ public static void clearLog() {
+ String[] pathName = {"logging.txt"};
+ Path fileName = createPath(pathName);
+ ArrayList toBeWritten = new ArrayList<>();
+ try {
+ Files.write(fileName, toBeWritten);
+ } catch (IOException e) {
+ userInterface.printErrorMessage("log cannot be cleared");
+ }
+ }
+
+ /**
+ * Function accepts a string and creates a path object originating from the user directory.
+ *
+ * @param pathName is a string array which accepts in the path name words, each word represents a folder
+ * @return Path object indicating the location of the pathName keyed in initially.
+ */
+ private static Path createPath(String[] pathName) {
+
+ String origin = System.getProperty("user.dir");
+ Path newPath = Paths.get(origin, pathName);
+ return newPath;
+ }
+
+}
diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java
new file mode 100644
index 0000000000..409cbf0358
--- /dev/null
+++ b/src/main/java/seedu/duke/command/AddCommand.java
@@ -0,0 +1,224 @@
+package seedu.duke.command;
+
+import seedu.duke.EventLogger;
+import seedu.duke.data.UserData;
+import seedu.duke.event.Personal;
+import seedu.duke.event.Timetable;
+import seedu.duke.event.Zoom;
+import seedu.duke.exception.DukeException;
+import seedu.duke.exception.DateErrorException;
+import seedu.duke.exception.TimeErrorException;
+import seedu.duke.exception.WrongNumberOfArgumentsException;
+import seedu.duke.exception.EventAddErrorException;
+import seedu.duke.exception.MissingDescriptionException;
+import seedu.duke.exception.InvalidListException;
+import seedu.duke.parser.DateTimeParser;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.Arrays;
+import java.util.logging.Logger;
+
+
+/**
+ * Command to add events.
+ */
+public class AddCommand extends Command {
+ private String eventType;
+ private String argument;
+ private Boolean isInvalidEventType = false;
+ private static Logger logger = EventLogger.getEventLogger();
+
+ /**
+ * Constructor for adding events.
+ *
+ * @param command from user input
+ */
+ public AddCommand(String command) {
+ this.isExit = false;
+ if (command == null || command.equals("") || command.equals(" ")) {
+ isInvalidEventType = true;
+ } else {
+ String[] commandWords = command.split(";");
+ if (!(commandWords.length == 0)) {
+ String tempEventType = commandWords[0].toLowerCase().trim();
+ switch (tempEventType) {
+ case "zoom":
+ eventType = "Zoom";
+ break;
+ case "personal":
+ eventType = "Personal";
+ break;
+ case "timetable":
+ eventType = "Timetable";
+ break;
+ default:
+ isInvalidEventType = true;
+ break;
+ }
+ String[] argumentWords = Arrays.copyOfRange(commandWords, 1, commandWords.length);
+ argument = String.join(";", argumentWords);
+ }
+ logger.fine("Add Command constructed");
+ }
+ }
+
+ /**
+ * Adds an event to individual eventLists.
+ *
+ * @param data object of UserData class containing user's data.
+ * @param ui containing the responses to print.
+ * @param storage with the save file path to write to.
+ * @throws DukeException Various exceptions can be thrown which extend from DukeException.
+ */
+ @Override
+ public void execute(UserData data, Ui ui, Storage storage) throws DukeException {
+ if (argument == null) {
+ logger.warning("EventAddErrorException -- Wrong format for add command.");
+ throw new EventAddErrorException("Wrong format for the add command!");
+ }
+ if (isInvalidEventType) {
+ logger.warning("EventAddErrorException -- Invalid event type was entered.");
+ throw new EventAddErrorException("Invalid event type to be added! Valid event types are Personal,"
+ + " Timetable or Zoom.");
+ }
+
+ String[] argumentWords = argument.split(";");
+ if (argumentWords[0].trim().equals("")) {
+ logger.warning("MissingDescriptionException -- Event has empty description.");
+ throw new MissingDescriptionException("This event has an empty description!");
+ }
+ switch (eventType) {
+ case "Personal":
+ addPersonal(data, ui, argumentWords);
+ ui.printEventAddedMessage(data.getEventList(eventType).getNewestEvent());
+ storage.saveFile(storage.getFileLocation(eventType), data, eventType);
+ break;
+ case "Zoom":
+ addZoom(data, ui, argumentWords);
+ ui.printEventAddedMessage(data.getEventList(eventType).getNewestEvent());
+ storage.saveFile(storage.getFileLocation(eventType), data, eventType);
+ break;
+ case "Timetable":
+ addTimetable(data, ui, argumentWords);
+ ui.printEventAddedMessage(data.getEventList(eventType).getNewestEvent());
+ storage.saveFile(storage.getFileLocation(eventType), data, eventType);
+ break;
+ default:
+ logger.warning("EventAddErrorException -- Invalid event type has been detected.");
+ throw new EventAddErrorException("Invalid event type to be added! Valid event types are Personal,"
+ + " Timetable or Zoom");
+ }
+
+ logger.fine("Add Command executed successfully");
+ }
+
+ /**
+ * Adds a Timetable event.
+ *
+ * @param data object of UserData class containing user's data.
+ * @param ui containing the responses to print.
+ * @param argumentWords String array containing user input arguments
+ * @throws DateErrorException the date input is not valid.
+ * @throws TimeErrorException the time input is not valid.
+ * @throws WrongNumberOfArgumentsException the number of arguments cannot create a valid timetable event.
+ * @throws InvalidListException the eventlist that the event added to is not valid (should never occur).
+ */
+ private void addTimetable(UserData data, Ui ui, String[] argumentWords) throws DateErrorException,
+ TimeErrorException, WrongNumberOfArgumentsException, InvalidListException {
+ if (argumentWords.length == 3 || argumentWords.length == 4) {
+ // 2 cases: description & date & time , description & location & date & time
+ if (argumentWords.length == 3) {
+ LocalDate localDate = DateTimeParser.dateParser(argumentWords[1].trim());
+ assert localDate != null : "date is not detected after parsing";
+ LocalTime localTime = DateTimeParser.timeParser(argumentWords[2].trim());
+ assert localTime != null : "time is not detected after parsing";
+ data.addToEventList("Timetable", new Timetable(argumentWords[0].trim(), localDate, localTime));
+ } else {
+ LocalDate localDate = DateTimeParser.dateParser(argumentWords[2].trim());
+ assert localDate != null : "date is not detected after parsing";
+ LocalTime localTime = DateTimeParser.timeParser(argumentWords[3].trim());
+ assert localTime != null : "time is not detected after parsing";
+ data.addToEventList("Timetable", new Timetable(argumentWords[0].trim(),
+ argumentWords[1].trim(), localDate, localTime));
+
+ }
+ logger.fine("Timetable event successfully added.");
+ } else {
+ logger.warning("WrongNumberOfArgumentsException -- Incorrect number of parameters for Timetable event!");
+ throw new WrongNumberOfArgumentsException("Incorrect number of parameters for Timetable event! A Timetable "
+ + " event can contain description, date and time or description, location, date and time.");
+ }
+ }
+
+ /**
+ * Adds a Personal event.
+ *
+ * @param data object of UserData class containing user's data.
+ * @param ui containing the responses to print.
+ * @param argumentWords String array containing user input arguments
+ * @throws DateErrorException the date input is not valid.
+ * @throws TimeErrorException the time input is not valid.
+ * @throws WrongNumberOfArgumentsException the number of arguments cannot create a valid personal event.
+ * @throws InvalidListException the eventlist that the event added to is not valid (should never occur).
+ */
+ private void addPersonal(UserData data, Ui ui, String[] argumentWords) throws DateErrorException,
+ TimeErrorException, WrongNumberOfArgumentsException, InvalidListException {
+ if (argumentWords.length >= 1 && argumentWords.length <= 3) {
+ // 3 cases: only description, description and date, description and date and time
+ if (argumentWords.length == 1) {
+ data.addToEventList("Personal", new Personal(argumentWords[0].trim()));
+ } else if (argumentWords.length == 2) {
+ LocalDate localDate = DateTimeParser.dateParser(argumentWords[1].trim());
+ assert localDate != null : "date is not detected after parsing";
+ data.addToEventList("Personal", new Personal(argumentWords[0].trim(), localDate));
+ } else {
+ LocalDate localDate = DateTimeParser.dateParser(argumentWords[1].trim());
+ assert localDate != null : "date is not detected after parsing";
+ LocalTime localTime = DateTimeParser.timeParser(argumentWords[2].trim());
+ assert localTime != null : "time is not detected after parsing";
+ data.addToEventList("Personal", new Personal(argumentWords[0].trim(), localDate, localTime));
+ }
+ logger.fine("Personal event successfully added.");
+ } else {
+ logger.warning("WrongNumberOfArgumentsException -- Incorrect number of parameters for Personal event!");
+ throw new WrongNumberOfArgumentsException("Incorrect number of parameters for Personal event! A Personal"
+ + " event can contain description only, description and date or description, date and time.");
+ }
+ }
+
+ /**
+ * Adds a Zoom event.
+ *
+ * @param data object of UserData class containing user's data.
+ * @param ui containing the responses to print.
+ * @param argumentWords String array containing user input arguments
+ * @throws DateErrorException the date input is not valid.
+ * @throws TimeErrorException the time input is not valid.
+ * @throws WrongNumberOfArgumentsException the number of arguments cannot create a valid zoom event.
+ * @throws InvalidListException the eventlist that the event added to is not valid (should never occur).
+ */
+ private void addZoom(UserData data, Ui ui, String[] argumentWords) throws DateErrorException,
+ TimeErrorException, WrongNumberOfArgumentsException, InvalidListException {
+ if (argumentWords.length == 2 || argumentWords.length == 4) {
+ // 2 cases: only have description & zoomlink , have description,zoomlink, date,time
+ if (argumentWords.length == 2) {
+ data.addToEventList("Zoom", new Zoom(argumentWords[0].trim(), argumentWords[1].trim()));
+ } else {
+ LocalDate localDate = DateTimeParser.dateParser(argumentWords[2].trim());
+ assert localDate != null : "date is not detected after parsing";
+ LocalTime localTime = DateTimeParser.timeParser(argumentWords[3].trim());
+ assert localTime != null : "time is not detected after parsing";
+ data.addToEventList("Zoom", new Zoom(argumentWords[0].trim(),
+ argumentWords[1].trim(), localDate, localTime));
+ }
+ logger.fine("Zoom event successfully added.");
+ } else {
+ logger.warning("WrongNumberOfArgumentsException -- Incorrect number of parameters for Zoom event!");
+ throw new WrongNumberOfArgumentsException("Incorrect number of parameters for Zoom event! A Zoom event"
+ + " can contain description and link or description, link, date and time.");
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/command/ByeCommand.java b/src/main/java/seedu/duke/command/ByeCommand.java
new file mode 100644
index 0000000000..0d440bd027
--- /dev/null
+++ b/src/main/java/seedu/duke/command/ByeCommand.java
@@ -0,0 +1,29 @@
+package seedu.duke.command;
+
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+import seedu.duke.data.UserData;
+
+/**
+ * Command to end program seedu.duke.
+ */
+public class ByeCommand extends Command {
+ /**
+ * Constructor for exiting the program.
+ */
+ public ByeCommand() {
+ this.isExit = true;
+ }
+
+ /**
+ * Do nothing, exits the program.
+ *
+ * @param data object of UserData class containing user's data.
+ * @param ui containing the responses to print.
+ * @param storage with the save file path to write to.
+ */
+ @Override
+ public void execute(UserData data, Ui ui, Storage storage) {
+ }
+
+}
diff --git a/src/main/java/seedu/duke/command/CalendarCommand.java b/src/main/java/seedu/duke/command/CalendarCommand.java
new file mode 100644
index 0000000000..95743b41cf
--- /dev/null
+++ b/src/main/java/seedu/duke/command/CalendarCommand.java
@@ -0,0 +1,101 @@
+package seedu.duke.command;
+
+import seedu.duke.EventLogger;
+import seedu.duke.data.UserData;
+import seedu.duke.event.Event;
+import seedu.duke.event.EventList;
+import seedu.duke.exception.DukeException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.logging.Logger;
+
+/**
+ * Command to print events in a calendar format.
+ */
+public class CalendarCommand extends Command {
+ private Map> calendarMap = new TreeMap<>();
+ private int eventsWithoutDateCount = 0;
+ private static Logger logger = EventLogger.getEventLogger();
+
+ /**
+ * Constructor for calendar command.
+ *
+ * @param command arguments for command, as of now is ignored.
+ */
+ public CalendarCommand(String command) {
+ this.command = command;
+ }
+
+ @Override
+ public void execute(UserData data, Ui ui, Storage storage) throws DukeException {
+ ArrayList eventLists = data.getAllEventLists();
+ for (EventList list : eventLists) {
+ ArrayList events = list.getEvents();
+ addEventsToCalendar(events);
+ }
+ logger.fine("Calendar created successfully.");
+ int calendarCount = calendarMap.size();
+ ui.printCalendarStart(calendarCount, eventsWithoutDateCount);
+ for (Map.Entry> entry : calendarMap.entrySet()) {
+ ui.printCalendar(entry);
+ if (calendarCount > 1) {
+ ui.printContinueQuery();
+ if (ui.receiveCommand().toLowerCase().equals("q")) {
+ break;
+ }
+ }
+ calendarCount--;
+ }
+ ui.printCalendarEnd();
+ logger.fine("Exited calendar mode successfully.");
+ }
+
+ /**
+ * Adds events from event arraylist into the calendar treemap.
+ *
+ * @param events to add into the calendar.
+ */
+ private void addEventsToCalendar(ArrayList events) {
+ for (Event e : events) {
+ ArrayList eventRepeatList = e.getRepeatEventList();
+ if (eventRepeatList != null) {
+ addEventsToCalendar(eventRepeatList);
+ }
+ addEventToCalendar(e);
+ }
+ }
+
+ private void addEventToCalendar(Event e) {
+ LocalDate eventDate = e.getDate();
+ LocalTime eventTime = e.getTime();
+ if (eventDate != null && eventTime != null) {
+ ArrayList eventsOnDate;
+ if (calendarMap.containsKey(eventDate)) {
+ eventsOnDate = calendarMap.get(eventDate);
+ eventsOnDate.add(e);
+ } else {
+ eventsOnDate = new ArrayList<>();
+ eventsOnDate.add(e);
+ calendarMap.put(eventDate, eventsOnDate);
+ }
+ } else {
+ eventsWithoutDateCount++;
+ }
+ }
+
+ /**
+ * Static parser for calendar command creation.
+ *
+ * @param input user input, as of now ignored.
+ * @return CalendarCommand default to null.
+ */
+ public static Command parse(String input) {
+ return new CalendarCommand(null);
+ }
+}
diff --git a/src/main/java/seedu/duke/command/CheckCommand.java b/src/main/java/seedu/duke/command/CheckCommand.java
new file mode 100644
index 0000000000..04e512b4dd
--- /dev/null
+++ b/src/main/java/seedu/duke/command/CheckCommand.java
@@ -0,0 +1,299 @@
+package seedu.duke.command;
+
+import seedu.duke.EventLogger;
+import seedu.duke.data.UserData;
+import seedu.duke.event.Event;
+import seedu.duke.event.EventList;
+import seedu.duke.exception.DateErrorException;
+import seedu.duke.exception.InvalidListException;
+import seedu.duke.exception.InvalidTimePeriodException;
+import seedu.duke.exception.MissingSemicolonException;
+import seedu.duke.exception.TimeErrorException;
+import seedu.duke.exception.TryRegularParserException;
+import seedu.duke.exception.WrongNumberOfArgumentsException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.Year;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+import static seedu.duke.parser.DateTimeParser.timeParser;
+
+/**
+ * Command to check availability.
+ */
+public class CheckCommand extends Command {
+ private static Logger logger = EventLogger.getEventLogger();
+
+ /**
+ * Constructor for checking availability.
+ *
+ * @param command from user input
+ */
+ public CheckCommand(String command) {
+ this.isExit = false;
+ this.command = command;
+ }
+
+ /**
+ * Execute function for the command to state user's availability in a given period.
+ *
+ * @param data object of UserData class containing user's data.
+ * @param ui containing the responses to print.
+ * @param storage with the save file path to write to.
+ * @throws MissingSemicolonException if the input does not contain any semicolons to separate input fields
+ * @throws DateErrorException the date is not input in a valid format
+ * @throws TimeErrorException the time is not input in a valid format
+ * @throws InvalidTimePeriodException the start of the time period is after the end
+ * @throws InvalidListException the event list indicated is not valid (should not be thrown in normal operation)
+ * @throws WrongNumberOfArgumentsException if insufficient fields are given after the "check" keyword
+ */
+ @Override
+ public void execute(UserData data, Ui ui, Storage storage) throws MissingSemicolonException, DateErrorException,
+ TimeErrorException, InvalidTimePeriodException, InvalidListException, WrongNumberOfArgumentsException {
+ logger.fine("Start executing check command: \"" + command + "\"");
+ if (!command.contains(";")) {
+ logger.warning("MissingSemicolonException: User input fields was not separated with semicolon.");
+ throw new MissingSemicolonException("Remember to separate input fields with a ';'." + System.lineSeparator()
+ + "The format for check is: \"check []; []; []; []\".");
+ }
+
+ String[] datesAndTime = command.split(";");
+
+ try {
+ LocalDate startDate = getDate(datesAndTime[0].trim());
+ LocalDate endDate = getDate(datesAndTime[2].trim());
+ assert startDate != null : "null date read for startDate";
+ assert endDate != null : "null date read for endDate";
+
+ LocalTime startTime = getTime(datesAndTime[1].trim());
+ LocalTime endTime = getTime(datesAndTime[3].trim());
+ assert startTime != null : "null time read for startTime";
+ assert endTime != null : "null time read for endTime";
+
+ boolean isTimePeriodValid = verifyValidTimePeriod(startDate, endDate, startTime, endTime);
+
+ if (!isTimePeriodValid) {
+ logger.warning("InvalidTimePeriodException: Start of time period given was not before end of period.");
+ throw new InvalidTimePeriodException("The start of the time period should be earlier than the end.");
+ }
+
+ ArrayList eventsInTimeRange = new ArrayList<>();
+ String[] eventTypes = new String[]{"Personal", "Timetable", "Zoom"};
+ for (String type: eventTypes) {
+ ArrayList events = data.getEventList(type).getEvents();
+ eventsInTimeRange.addAll(checkEventsInTimeRange(events, startDate, endDate, startTime, endTime));
+ }
+ EventList coinciding = new EventList("coinciding", eventsInTimeRange);
+
+ ui.printList(coinciding);
+ logger.fine("Check command successfully executed.");
+ } catch (ArrayIndexOutOfBoundsException e) { // if datesAndTime[x] is unable to be accessed
+ logger.warning("WrongNumberOfArgumentsException: Not enough date/time fields were given to be processed.");
+ throw new WrongNumberOfArgumentsException("Insufficient fields provided to check events. "
+ + "Remember to put a semicolon even for blank fields." + System.lineSeparator()
+ + "The format for check is: \"check []; []; []; []\".");
+ }
+ }
+
+ /**
+ * Date parser that interprets a date from a given string.
+ *
+ * @param stringDate the string containing date information
+ * @return a LocalDate corresponding to the date in stringDate or the current date if no date is found in the string
+ * @throws DateErrorException if stringDate does not correspond to a valid date format
+ */
+ private LocalDate getDate(String stringDate) throws DateErrorException {
+ logger.fine("Begin parsing date: \"" + stringDate + "\"");
+ stringDate = stringDate.replace("-","/");
+ String[] dateFields = stringDate.split("/");
+
+ LocalDate date;
+ LocalDate currentDate = LocalDate.now();
+
+ if (stringDate.isBlank()) { // if date is blank, defaults to current date
+ logger.fine("Date field left blank, current date returned.");
+ return currentDate;
+ }
+
+ try {
+ switch (dateFields.length) {
+ case 1: // only year is given
+ DateTimeFormatter yearFormat = DateTimeFormatter.ofPattern("[yyyy][yy]");
+ Year givenYear = Year.parse(stringDate, yearFormat);
+ date = currentDate.with(givenYear);
+ logger.fine("Year provided, current d/m at provided year returned.");
+ return date;
+ case 2: // month and year is given
+ DateTimeFormatter yearMonthFormat = DateTimeFormatter.ofPattern("M/[yyyy][yy]");
+ YearMonth givenYearMonth = YearMonth.parse(stringDate, yearMonthFormat);
+ date = currentDate.with(givenYearMonth);
+ logger.fine("Month and year provided, current d at provided m/yyyy returned.");
+ return date;
+ case 3: // day, month and year given
+ DateTimeFormatter dayMonthYearFormat = DateTimeFormatter.ofPattern("d/M/[yyyy][yy]");
+ date = LocalDate.parse(stringDate, dayMonthYearFormat);
+ logger.fine("Full date provided, provided date returned.");
+ return date;
+ default:
+ logger.warning("DateErrorException: More fields than d/m/yyyy were given.");
+ throw new DateErrorException();
+ }
+ } catch (DateTimeParseException e) {
+ logger.warning("DateErrorException: Invalid date was given.");
+ throw new DateErrorException();
+ }
+ }
+
+ /**
+ * Time parser that interprets a time from a given string.
+ *
+ * @param stringTime the string containing time information
+ * @return a LocalTime corresponding to the time in stringTime or the current time if no time is found in the string
+ * @throws TimeErrorException if stringTime does not correspond to a valid time format
+ */
+ private LocalTime getTime(String stringTime) throws TimeErrorException {
+ logger.fine("Begin parsing time: \"" + stringTime + "\"");
+ LocalTime time;
+ if (stringTime.isBlank()) { // if blank time is provided, default to current time
+ DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("h:m a");
+ String currentTime = LocalTime.now().format(timeFormatter);
+ time = LocalTime.parse(currentTime, timeFormatter);
+ logger.fine("Time field left blank, current time returned.");
+ return time;
+ }
+
+ stringTime = stringTime.toUpperCase();
+
+ String[] stringTimeArray = stringTime.split(" ");
+
+ try {
+ if (stringTimeArray.length == 2) { // 12 hour format hh a
+ int givenTwelveHour = Integer.parseInt(stringTimeArray[0]);
+ String amPmIndicator = stringTimeArray[1];
+ if (givenTwelveHour >= 0 & givenTwelveHour <= 12) {
+ time = timeParser(givenTwelveHour + ":00 " + amPmIndicator); // default to minute 00
+ logger.fine("Hour given in 12 hour format, start of hour time returned.");
+ return time;
+ } else {
+ logger.warning("Integer >12 provided for hour in 12 hour format.");
+ logger.warning("TryRegularParserException: Try to parse in hhmm am/pm format.");
+ throw new TryRegularParserException("12 hour format (h) requires hours between 1-12.");
+ }
+ } else if (stringTimeArray.length == 1) { // 24 hour format HH
+ int givenTwentyFourHour = Integer.parseInt(stringTimeArray[0]);
+ if (givenTwentyFourHour >= 0 & givenTwentyFourHour <= 24) {
+ time = timeParser(givenTwentyFourHour + ":00"); // default to minute 00
+ logger.fine("Hour given in 24 hour format, start of hour time returned.");
+ return time;
+ } else {
+ logger.warning("Integer >24 provided for hour in 24 hour format.");
+ logger.warning("TryRegularParserException: Try to parse in HHmm format.");
+ throw new TryRegularParserException("HH format time requires hours between 0-24.");
+ }
+ } else {
+ logger.warning("TimeErrorException: Time has more than hh:mm am/pm fields.");
+ throw new TimeErrorException();
+ }
+ } catch (NumberFormatException | TryRegularParserException e) {
+ // if hh:mm, HH:mm or other invalid non integers is given
+ time = timeParser(stringTime); // exception will be thrown if invalid non-integer is given
+ logger.fine("Time successfully returned.");
+ return time;
+ }
+ }
+
+ /**
+ * Checks to make sure the start of the time period is not after the end.
+ *
+ * @param startDate the start date of the time period
+ * @param endDate the end date of the time period
+ * @param startTime the start time of the time period
+ * @param endTime the end time of the time period
+ * @return boolean showing if the time period is valid i.e. the start is before the end
+ */
+ private boolean verifyValidTimePeriod(LocalDate startDate, LocalDate endDate,
+ LocalTime startTime, LocalTime endTime) {
+ logger.fine("Start verifying time period validity.");
+ logger.info("Start date: \"" + startDate + "\", Start time: \"" + startTime + "\"");
+ logger.info("End date: \"" + endDate + "\", End time: \"" + endTime + "\"");
+ boolean isStartAndEndValid;
+ boolean isStartBeforeEnd;
+ boolean isStartDateBeforeEndDate = startDate.isBefore(endDate);
+
+ if (isStartDateBeforeEndDate) { // if start date is before end date start is before end
+ isStartBeforeEnd = true;
+ } else if (startDate.isEqual(endDate)) { // if start time before end time when start date equal end date
+ isStartBeforeEnd = startTime.isBefore(endTime);
+ } else {
+ isStartBeforeEnd = false;
+ }
+
+ isStartAndEndValid = isStartBeforeEnd;
+ logger.fine("Successfully checked time period validity.");
+ return isStartAndEndValid;
+ }
+
+ /**
+ * Checks for events within a given time period.
+ *
+ * @param events the eventsList containing events to be checked
+ * @param startDate the start date of the time period to be checked
+ * @param endDate the end date of the time period to be checked
+ * @param startTime the start time of the time period to be checked
+ * @param endTime the end time of the time period to be checked
+ * @return an ArrayList of events found occurring during the time period
+ */
+ private ArrayList checkEventsInTimeRange(ArrayList events, LocalDate startDate, LocalDate endDate,
+ LocalTime startTime, LocalTime endTime) {
+ logger.fine("Start checking events in time period.");
+
+ ArrayList eventsInTimeRange = new ArrayList<>();
+
+ for (Event event : events) {
+ if (event.getDate() == null) {
+ continue;
+ }
+
+ boolean eventIsBetweenDate = event.getDate().isAfter(startDate) && event.getDate().isBefore(endDate);
+ boolean eventIsWithinTimePeriod;
+
+ if (eventIsBetweenDate) { // if an event is after start date and before end date, it is in the time period
+ eventIsWithinTimePeriod = true;
+ } else if (event.getDate().isEqual(startDate)) { // if an event is on the start date check event time
+ if (event.getTime() == null) { // if the event has no time, by default count it as coinciding
+ eventIsWithinTimePeriod = true;
+ } else { // if the event is before the start time, it is not within the time period
+ eventIsWithinTimePeriod = !(event.getTime().isBefore(startTime));
+ }
+ } else if (event.getDate().isEqual(endDate)) { // if an event is on the end date check event time
+ if (event.getTime() == null) { // if the event has no time, by default count it as coinciding
+ eventIsWithinTimePeriod = true;
+ } else { // if the event is after the end time, it is not within the time period
+ eventIsWithinTimePeriod = !(event.getTime().isAfter(endTime));
+ }
+ } else {
+ eventIsWithinTimePeriod = false;
+ }
+
+ if (eventIsWithinTimePeriod) {
+ eventsInTimeRange.add(event);
+ logger.info("Coinciding event added: \"" + event + "\"");
+ }
+
+ if (event.getRepeatType() != null && event.getRepeatEventList() != null) {
+ eventsInTimeRange.addAll(checkEventsInTimeRange(event.getRepeatEventList(),
+ startDate, endDate, startTime, endTime));
+ }
+ }
+
+ logger.fine("Successfully checked events in time period.");
+ return eventsInTimeRange;
+ }
+}
diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java
new file mode 100644
index 0000000000..e5802b7b1b
--- /dev/null
+++ b/src/main/java/seedu/duke/command/Command.java
@@ -0,0 +1,29 @@
+package seedu.duke.command;
+
+import seedu.duke.data.UserData;
+import seedu.duke.exception.DukeException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+public abstract class Command {
+ protected boolean isExit;
+ protected String command;
+
+ /**
+ * abstract class for commands.
+ *
+ * @param data object of UserData class containing user's data.
+ * @param ui containing the responses to print.
+ * @param storage with the save file path to write to.
+ */
+ public abstract void execute(UserData data, Ui ui, Storage storage) throws DukeException;
+
+ /**
+ * Signal exit program.
+ *
+ * @return true if user give exit seedu.duke.command
+ */
+ public boolean isExit() {
+ return isExit;
+ }
+}
diff --git a/src/main/java/seedu/duke/command/DeadlineCommand.java b/src/main/java/seedu/duke/command/DeadlineCommand.java
new file mode 100644
index 0000000000..2c629557b4
--- /dev/null
+++ b/src/main/java/seedu/duke/command/DeadlineCommand.java
@@ -0,0 +1,163 @@
+package seedu.duke.command;
+
+import seedu.duke.EventLogger;
+import seedu.duke.data.UserData;
+import seedu.duke.event.Event;
+import seedu.duke.event.EventList;
+import seedu.duke.exception.DateErrorException;
+import seedu.duke.exception.DukeException;
+import seedu.duke.exception.InvalidTimePeriodException;
+import seedu.duke.exception.TimeErrorException;
+import seedu.duke.exception.WrongNumberFormatException;
+import seedu.duke.exception.WrongNumberOfArgumentsException;
+import seedu.duke.parser.DateTimeParser;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.logging.Logger;
+
+/**
+ * Command to set deadline for personal events.
+ */
+public class DeadlineCommand extends Command {
+ private int index;
+ private LocalDate date;
+ private LocalTime time;
+ private static Logger logger = EventLogger.getEventLogger();
+
+ /**
+ * Constructor for setting deadline seedu.duke.
+ *
+ * @param command from user input
+ */
+ public DeadlineCommand(String command) {
+ this.isExit = false;
+ this.command = command;
+ }
+
+ /**
+ * To change to deadline of personal event.
+ *
+ * @param data object of UserData class containing user's data.
+ * @param ui containing the responses to print.
+ * @param storage with the save file path to write to.
+ */
+ @Override
+ public void execute(UserData data, Ui ui, Storage storage) throws DukeException {
+ parseUserCommand(command);
+ EventList personalList = data.getEventList("Personal");
+ Event updatedEvent = personalList.getEventByIndex(index - 1);
+ if (updatedEvent != null) {
+ if (time == null) {
+ updatedEvent.setDate(date);
+ } else {
+ updatedEvent.setDate(date);
+ updatedEvent.setTime(time);
+ }
+ ui.printDeadlineChangedMessage(updatedEvent);
+ }
+ logger.fine("Deadline for event was created/updated successfully.");
+ storage.saveFile(storage.getFileLocation("Personal"), data, "Personal");
+ }
+
+ /**
+ * Parsing user command to put in the correct format for arguments and checking if its date or date and time.
+ *
+ * @param command user input arguments
+ */
+ private void parseUserCommand(String command) throws DukeException {
+ command = command.trim();
+ String[] commandSplit = command.split(";");
+ if (commandSplit.length == 2) {
+ try {
+ index = parsingNumber(commandSplit[0].trim());
+ date = DateTimeParser.dateParser(commandSplit[1].trim());
+ validDateRange(date);
+ assert date != null : "date is not detected after parsing";
+ } catch (DateErrorException e) {
+ logger.warning("DateErrorException encountered -- Deadline date is not in the correct format");
+ throw new DateErrorException();
+ } catch (NumberFormatException e) {
+ logger.warning("WrongNumberFormatException encountered -- Deadline index is not numerical");
+ throw new WrongNumberFormatException("Index must be numerical format!");
+ }
+
+ } else if (commandSplit.length == 3) {
+
+ try {
+ index = parsingNumber(commandSplit[0].trim());
+ date = DateTimeParser.dateParser(commandSplit[1].trim());
+ validDateRange(date);
+ assert date != null : "date is not detected after parsing";
+ String timeString = commandSplit[2].trim();
+ time = DateTimeParser.timeParser(timeString);
+ validTimeRange(time);
+ assert time != null : "time is not detected after parsing";
+ } catch (DateErrorException e) {
+ logger.warning("DateErrorException encountered -- Deadline date is not in the correct format");
+ throw new DateErrorException();
+ } catch (TimeErrorException e) {
+ logger.warning("TimeErrorException encountered -- Deadline time is not in the correct format");
+ throw new TimeErrorException();
+ } catch (NumberFormatException e) {
+ logger.warning("WrongNumberFormatException encountered -- Deadline index is not numerical");
+ throw new WrongNumberFormatException("Index must be numerical format!");
+ }
+ } else {
+ logger.warning("WrongNumberOfArgumentsException encountered -- "
+ + "Deadline have incorrect number of arguments");
+ throw new WrongNumberOfArgumentsException("Incorrect number of parameters for Deadline!"
+ + System.lineSeparator()
+ + "The format for Deadline is : \"deadline ; [EVENT_DATE]; [EVENT_TIME]\" ");
+ }
+
+ }
+
+ /**
+ * Deadline Date should not be before current date.
+ *
+ * @param date specified by user
+ * @throws InvalidTimePeriodException catch when date is before current
+ */
+ private void validDateRange(LocalDate date) throws InvalidTimePeriodException {
+ LocalDate curr = LocalDate.now();
+ if (date.isBefore(curr)) {
+ logger.warning("InvalidTimePeriodException encountered -- Deadline date is"
+ + " before today");
+ throw new InvalidTimePeriodException("Deadline date should not be before today!");
+ }
+ }
+
+ /**
+ * Deadline Time should not be before current time.
+ *
+ * @param time specified by user
+ * @throws InvalidTimePeriodException catch when date is before current
+ */
+ private void validTimeRange(LocalTime time) throws InvalidTimePeriodException {
+ LocalTime curr = LocalTime.now();
+ if (time.isBefore(curr)) {
+ logger.warning("InvalidTimePeriodException encountered -- Deadline time is"
+ + " before now");
+ throw new InvalidTimePeriodException("Deadline time should not be before now!");
+ }
+ }
+
+ /**
+ * Check if index is numerical format.
+ *
+ * @param number index in string format
+ * @return index of event
+ */
+ private int parsingNumber(String number) {
+ try {
+ int index = Integer.parseInt(number);
+ return index;
+ } catch (NumberFormatException e) {
+ throw new NumberFormatException("Index must be numerical format!");
+ }
+
+ }
+}
diff --git a/src/main/java/seedu/duke/command/DeleteCommand.java b/src/main/java/seedu/duke/command/DeleteCommand.java
new file mode 100644
index 0000000000..6a9ac7116d
--- /dev/null
+++ b/src/main/java/seedu/duke/command/DeleteCommand.java
@@ -0,0 +1,162 @@
+package seedu.duke.command;
+
+import seedu.duke.EventLogger;
+import seedu.duke.data.UserData;
+import seedu.duke.event.Event;
+import seedu.duke.event.EventList;
+import seedu.duke.exception.DukeException;
+import seedu.duke.exception.InvalidEventDateException;
+import seedu.duke.exception.MissingSemicolonException;
+import seedu.duke.exception.WrongNumberFormatException;
+import seedu.duke.exception.WrongNumberOfArgumentsException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+import static seedu.duke.parser.DateTimeParser.dateParser;
+
+public class DeleteCommand extends Command {
+ private final String listType;
+
+ private static Logger logger = EventLogger.getEventLogger();
+
+ public DeleteCommand(String listType, String command) {
+ this.isExit = false;
+ this.listType = listType;
+ this.command = command;
+ }
+
+ /**
+ * Parses the user input and returns the corresponding DeleteCommand.
+ *
+ * @param input the processed user input with the command keyword removed.
+ * @return the DeleteCommand corresponding to the list type and event indicated in the input.
+ * @throws MissingSemicolonException if the input does not contain any semicolons to separate input fields.
+ * @throws WrongNumberOfArgumentsException if the event type or event index is missing.
+ * @throws WrongNumberFormatException if the event index is not an integer.
+ */
+ public static Command parse(String input) throws MissingSemicolonException,
+ WrongNumberOfArgumentsException, WrongNumberFormatException {
+ logger.fine("Parsing DeleteCommand input: \"" + input + "\"");
+
+ if (!input.contains(";")) {
+ logger.warning("MissingSemicolonException: User input fields was not separated with semicolon.");
+ throw new MissingSemicolonException("Remember to separate input fields with a ';'." + System.lineSeparator()
+ + "The format for delete is: \"delete ; ; []\".");
+ }
+
+ String[] inputParameters = input.trim().split(";", 2);
+
+ if (inputParameters[0].isBlank() || inputParameters[1].isBlank()) {
+ logger.warning("WrongNumberOfArgumentsException: User did not provide event type or event index.");
+ throw new WrongNumberOfArgumentsException("Event type or index is missing." + System.lineSeparator()
+ + "The format for delete is: \"delete ; ; []\".");
+ }
+
+ String listType = capitaliseFirstLetter(inputParameters[0].trim());
+ String eventIdentifier = inputParameters[1].trim();
+
+ try {
+ String[] eventIdentifierArray = eventIdentifier.split(";",2);
+ Integer.parseInt(eventIdentifierArray[0]);
+ } catch (NumberFormatException e) {
+ logger.warning("WrongNumberFormatException: Event index given is not an integer.");
+ throw new WrongNumberFormatException("Event index given is not an integer." + System.lineSeparator()
+ + "The format for delete is: \"delete ; ; []\".");
+ }
+
+ logger.fine("Successfully parsed input and created DeleteCommand.");
+ return new DeleteCommand(listType, eventIdentifier);
+ }
+
+ /**
+ * Looks for the event indicated and deletes it.
+ *
+ * @param data object of UserData class containing user's data.
+ * @param ui containing the responses to print.
+ * @param storage with the save file path to write to.
+ * @throws DukeException if en error occurs during the execution of a method called in the command.
+ */
+ @Override
+ public void execute(UserData data, Ui ui, Storage storage) throws DukeException {
+ logger.info("Start executing delete command.");
+ logger.info("listType: \"" + listType + "\", command: \"" + command + "\"");
+ EventList eventList = data.getEventList(listType);
+ String[] eventIdentifierArray = command.split(";");
+
+ int eventIndex = Integer.parseInt(eventIdentifierArray[0]) - 1;
+ Event deleteEvent = eventList.getEventByIndex(eventIndex);
+
+ if (deleteEvent.getRepeatType() == null || eventIdentifierArray.length == 1) {
+ eventList.getEvents().remove(deleteEvent);
+ ui.printEventDeletedMessage(deleteEvent);
+ storage.saveFile(storage.getFileLocation(listType), data, listType);
+ logger.fine("Event deleted: \"" + deleteEvent + "\"");
+ } else { // event is a repeat task
+ LocalDate deleteEventDate = dateParser(eventIdentifierArray[1].trim());
+ boolean isDateFound;
+
+ if (deleteEventDate.isEqual(deleteEvent.getDate())) {
+ isDateFound = true;
+ eventList.getEvents().remove(deleteEvent);
+ ui.printEventDeletedMessage(deleteEvent);
+ logger.fine("Event deleted: \"" + deleteEvent + "\"");
+ } else {
+ ArrayList repeatEventList = deleteEvent.getRepeatEventList();
+ isDateFound = scanRepeatList(repeatEventList, deleteEventDate, ui, deleteEvent);
+ }
+
+ if (!isDateFound) {
+ throw new InvalidEventDateException();
+ }
+
+ storage.saveFile(storage.getFileLocation(listType), data, listType);
+ logger.fine("Changes saved to external file.");
+ }
+ }
+
+ /**
+ * Capitalises the first letter of an input string.
+ *
+ * @param input the string to be capitalised.
+ * @return the capitalised string.
+ */
+ private static String capitaliseFirstLetter(String input) {
+ input = input.toLowerCase();
+ return input.substring(0, 1).toUpperCase() + input.substring(1);
+ }
+
+ /**
+ * Scans the repeat event array list of a repeat event for an event matching the given date and deletes it.
+ *
+ * @param repeatEventList the array list containing all the repeated sub events under the main repeat event.
+ * @param deleteEventDate the date of the sub repeat event to be deleted.
+ * @param ui containing the responses to print.
+ * @param deleteEvent the main repeat event.
+ * @return boolean stating if an event matching the date given was found and deleted
+ */
+ private boolean scanRepeatList(ArrayList repeatEventList, LocalDate deleteEventDate,
+ Ui ui, Event deleteEvent) {
+ logger.fine("Checking events in repeat event list.");
+ boolean isDateFound = false;
+ for (Event e: repeatEventList) {
+ if (e.getDate().isEqual(deleteEventDate)) {
+ isDateFound = true;
+ repeatEventList.remove(e);
+ ui.printEventDeletedMessage(e);
+ logger.info("Repeat list event deleted: \"" + e + "\"");
+ break;
+ }
+ }
+ if (repeatEventList.size() == 0) {
+ deleteEvent.setRepeatType(null);
+ deleteEvent.setRepeatEventList(null);
+ logger.info("Last sub repeat event was deleted, repeat status and repeat list set to null.");
+ }
+ logger.fine("Finished checking repeat event list.");
+ return isDateFound;
+ }
+}
diff --git a/src/main/java/seedu/duke/command/DoneCommand.java b/src/main/java/seedu/duke/command/DoneCommand.java
new file mode 100644
index 0000000000..e75006a255
--- /dev/null
+++ b/src/main/java/seedu/duke/command/DoneCommand.java
@@ -0,0 +1,159 @@
+package seedu.duke.command;
+
+import seedu.duke.EventLogger;
+import seedu.duke.data.UserData;
+import seedu.duke.event.Event;
+import seedu.duke.event.EventList;
+import seedu.duke.exception.DukeException;
+import seedu.duke.exception.InvalidEventDateException;
+import seedu.duke.exception.MissingSemicolonException;
+import seedu.duke.exception.WrongNumberFormatException;
+import seedu.duke.exception.WrongNumberOfArgumentsException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+import static seedu.duke.parser.DateTimeParser.dateParser;
+
+public class DoneCommand extends Command {
+ private final String listType;
+
+ private static Logger logger = EventLogger.getEventLogger();
+
+ /**
+ * Constructor for setting event to done.
+ *
+ * @param command from user input
+ */
+ public DoneCommand(String listType, String command) {
+ this.isExit = false;
+ this.listType = listType;
+ this.command = command;
+ }
+
+ /**
+ * Parses the user input and returns the corresponding DoneCommand.
+ *
+ * @param input the processed user input with the command keyword removed.
+ * @return the DoneCommand corresponding to the list type and event indicated in the input.
+ * @throws MissingSemicolonException if the input does not contain any semicolons to separate input fields.
+ * @throws WrongNumberOfArgumentsException if the event type or event index is missing.
+ * @throws WrongNumberFormatException if the event index is not an integer.
+ */
+ public static Command parse(String input) throws MissingSemicolonException,
+ WrongNumberOfArgumentsException, WrongNumberFormatException {
+ logger.fine("Parsing DoneCommand input: \"" + input + "\"");
+
+ if (!input.contains(";")) {
+ logger.warning("MissingSemicolonException: User input fields was not separated with semicolon.");
+ throw new MissingSemicolonException("Remember to separate input fields with a ';'." + System.lineSeparator()
+ + "The format for done is: \"done ; ; []\".");
+ }
+
+ String[] inputParameters = input.trim().split(";", 2);
+
+ if (inputParameters[0].isBlank() || inputParameters[1].isBlank()) {
+ logger.warning("WrongNumberOfArgumentsException: User did not provide event type or event index.");
+ throw new WrongNumberOfArgumentsException("Event type or index is missing." + System.lineSeparator()
+ + "The format for done is: \"done ; ; []\".");
+ }
+
+ String listType = capitaliseFirstLetter(inputParameters[0].trim());
+ String eventIdentifier = inputParameters[1].trim();
+
+ try {
+ String[] eventIdentifierArray = eventIdentifier.split(";",2);
+ Integer.parseInt(eventIdentifierArray[0]);
+ } catch (NumberFormatException e) {
+ logger.warning("WrongNumberFormatException: Event index given is not an integer.");
+ throw new WrongNumberFormatException("Event index given is not an integer." + System.lineSeparator()
+ + "The format for done is: \"done ; ; []\".");
+ }
+
+ logger.fine("Successfully parsed input and created DoneCommand.");
+ return new DoneCommand(listType, eventIdentifier);
+ }
+
+ /**
+ * Looks for the event indicated and marks it as done.
+ *
+ * @param data object of UserData class containing user's data.
+ * @param ui containing the responses to print.
+ * @param storage with the save file path to write to.
+ * @throws DukeException if en error occurs during the execution of a method called in the command.
+ */
+ @Override
+ public void execute(UserData data, Ui ui, Storage storage) throws DukeException {
+ logger.fine("Start executing done command.");
+ logger.info("listType: \"" + listType + "\", command: \"" + command + "\"");
+ EventList eventList = data.getEventList(listType);
+ String[] eventIdentifierArray = command.split(";");
+
+ int eventIndex = Integer.parseInt(eventIdentifierArray[0]) - 1;
+ Event doneEvent = eventList.getEventByIndex(eventIndex);
+
+ if (doneEvent.getRepeatType() == null || eventIdentifierArray.length == 1) {
+ doneEvent.markAsDone();
+ ui.printEventMarkedDoneMessage(doneEvent);
+ storage.saveFile(storage.getFileLocation(listType), data, listType);
+ logger.fine("Event marked as done: \"" + doneEvent + "\"");
+ } else { // event is a repeat task
+ LocalDate doneEventDate = dateParser(eventIdentifierArray[1].trim());
+ boolean isDateFound;
+
+ if (doneEventDate.isEqual(doneEvent.getDate())) {
+ isDateFound = true;
+ doneEvent.markAsDone();
+ ui.printEventMarkedDoneMessage(doneEvent);
+ logger.fine("Event marked as done: \"" + doneEvent + "\"");
+ } else {
+ ArrayList repeatEventList = doneEvent.getRepeatEventList();
+ isDateFound = scanRepeatList(repeatEventList, doneEventDate, ui);
+ }
+
+ if (!isDateFound) {
+ throw new InvalidEventDateException();
+ }
+
+ storage.saveFile(storage.getFileLocation(listType), data, listType);
+ logger.fine("Changes saved to external file.");
+ }
+ }
+
+ /**
+ * Capitalises the first letter of an input string.
+ *
+ * @param input the string to be capitalised.
+ * @return the capitalised string.
+ */
+ private static String capitaliseFirstLetter(String input) {
+ input = input.toLowerCase();
+ return input.substring(0, 1).toUpperCase() + input.substring(1);
+ }
+
+ /**
+ * Scans the repeat event array list of a repeat event for an event matching the given date and marks it done.
+ *
+ * @param repeatEventList the array list containing all the repeated sub events under the main repeat event.
+ * @param doneEventDate the date of the sub repeat event to be marked done.
+ * @param ui containing the responses to print.
+ * @return boolean stating if an event matching the date given was found and marked done
+ */
+ private boolean scanRepeatList(ArrayList repeatEventList, LocalDate doneEventDate, Ui ui) {
+ logger.fine("Checking events in repeat event list.");
+ boolean isDateFound = false;
+ for (Event e: repeatEventList) {
+ if (e.getDate().isEqual(doneEventDate)) {
+ isDateFound = true;
+ e.markAsDone();
+ ui.printEventMarkedDoneMessage(e);
+ logger.info("Repeat list event marked as done: \"" + e + "\"");
+ }
+ }
+ logger.fine("Finished checking repeat event list.");
+ return isDateFound;
+ }
+}
diff --git a/src/main/java/seedu/duke/command/ExtractCommand.java b/src/main/java/seedu/duke/command/ExtractCommand.java
new file mode 100644
index 0000000000..e692c22aa8
--- /dev/null
+++ b/src/main/java/seedu/duke/command/ExtractCommand.java
@@ -0,0 +1,512 @@
+package seedu.duke.command;
+
+import seedu.duke.EventLogger;
+import seedu.duke.data.UserData;
+import seedu.duke.event.Personal;
+import seedu.duke.event.Zoom;
+import seedu.duke.exception.DateErrorException;
+import seedu.duke.exception.InvalidExtractCommandException;
+import seedu.duke.exception.TimeErrorException;
+import seedu.duke.exception.InvalidListException;
+import seedu.duke.parser.DateTimeParser;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ExtractCommand extends Command {
+ private int dateCount;
+ private int timeCount;
+ private int zoomLinkCount;
+ private String textSubject = null;
+ private String eventType;
+ private static Logger logger = EventLogger.getEventLogger();
+
+ /**
+ * Constructor for parsing email/texts seedu.duke.
+ *
+ * @param command from user input.
+ */
+ public ExtractCommand(String command) {
+ this.isExit = false;
+ int semicolonCount = command.length() - command.replace(";", "").length();
+ if (command.endsWith(";") && semicolonCount == 1) {
+ textSubject = command.split(";", 2)[0];
+ }
+ eventType = "Personal";
+ logger.fine("Extract command successfully constructed.");
+ }
+
+ /**
+ * Extracts dates,timings and zoom links from any block of text and creates Personal Events or Zoom events.
+ *
+ * @param data object of UserData class containing user's data.
+ * @param ui containing the responses to print.
+ * @param storage with the save file path to write to.
+ * @throws InvalidExtractCommandException The textBody or textSubject is null or empty.
+ * @throws InvalidListException the eventlist that the event added to is not valid (should never occur).
+ */
+ @Override
+ public void execute(UserData data, Ui ui, Storage storage) throws InvalidExtractCommandException,
+ InvalidListException {
+ if (textSubject == null) {
+ logger.warning("InvalidExtractCommandException -- Text subject not entered correctly.");
+ throw new InvalidExtractCommandException("Text subject was not entered correctly!");
+ }
+ if (textSubject.equals("")) {
+ logger.warning("InvalidExtractCommandException -- No text subject entered.");
+ throw new InvalidExtractCommandException("There is no text subject entered!");
+ }
+
+ ui.printExtractTextBodyRequestMessage();
+ ui.printDividerLine();
+ String textBody = receiveTextBody(ui);
+
+ if (textBody == null) {
+ logger.warning("InvalidExtractCommandException -- Text body not detected or is null.");
+ throw new InvalidExtractCommandException("Text body was not entered correctly!");
+ }
+ if (textBody.trim().equals("extractend") || textBody.equals(" ")) {
+ logger.warning("InvalidExtractCommandException -- No text body entered.");
+ throw new InvalidExtractCommandException("There is no text body entered!");
+ }
+
+ ArrayList zoomLinkList = detectZoomLink(textBody);
+ String zoomLink = null;
+ if (zoomLinkList.size() > 0) {
+ eventType = "Zoom";
+ zoomLink = chooseZoomLink(zoomLinkList, ui);
+ }
+
+ ArrayList dateList = detectDate(textBody);
+ LocalDate finalDate = chooseDate(dateList, ui);
+
+ ArrayList timeList = detectTime(textBody);
+ LocalTime finalTime = chooseTime(timeList, ui);
+
+ createEvent(data, ui, finalDate, finalTime, zoomLink);
+
+ ui.printEventAddedMessage(data.getEventList(eventType).getNewestEvent());
+ storage.saveFile(storage.getFileLocation(eventType), data, eventType);
+ logger.fine("Extract command successfully executed, a new " + eventType + " event was created.");
+ }
+
+ /**
+ * Creates a personal or zoom event based on if there is a zoom link and the detected fields.
+ *
+ * @param data object of UserData class containing user's data.
+ * @param ui containing the responses to print.
+ * @param finalDate final date chosen by user, may be null if no date detected.
+ * @param finalTime final time chosen by user, may be null if no time detected.
+ * @param zoomLink final zoom link chosen by user, may be null if no zoom link detected.
+ * @throws InvalidListException the eventlist that the event added to is not valid (should never occur).
+ */
+ private void createEvent(UserData data, Ui ui, LocalDate finalDate, LocalTime finalTime, String zoomLink)
+ throws InvalidListException {
+ if (finalDate == null) {
+ if (eventType.equals("Personal")) {
+ ui.printExtractNoDatePersonalEventMessage();
+ data.addToEventList("Personal", new Personal(textSubject));
+ } else if (eventType.equals("Zoom")) {
+ assert zoomLink != null : "Zoom link is not detected after choosing";
+ ui.printExtractNoDateZoomEventMessage();
+ data.addToEventList("Zoom", new Zoom(textSubject, zoomLink));
+ }
+ } else {
+ if (finalTime == null) {
+ if (eventType.equals("Personal")) {
+ ui.printExtractNoTimePersonalEventMessage();
+ data.addToEventList("Personal", new Personal(textSubject, finalDate));
+ } else if (eventType.equals("Zoom")) {
+ assert zoomLink != null : "Zoom link is not detected after choosing";
+ ui.printExtractNoTimeZoomEventMessage();
+ data.addToEventList("Zoom", new Zoom(textSubject, zoomLink));
+ }
+ } else {
+ if (eventType.equals("Personal")) {
+ data.addToEventList("Personal", new Personal(textSubject, finalDate, finalTime));
+ } else if (eventType.equals("Zoom")) {
+ assert zoomLink != null : "Zoom link is not detected after choosing";
+ data.addToEventList("Zoom", new Zoom(textSubject, zoomLink, finalDate, finalTime));
+ }
+ }
+ }
+ }
+
+ /**
+ * Takes in the textBody from the user.
+ *
+ * @param ui is used to receive Strings the user input.
+ * @return String containing the full text body entered.
+ */
+ private String receiveTextBody(Ui ui) {
+ String bodyLine = "";
+ String fullTextBody = "";
+ while (!bodyLine.equals("extractend")) {
+ bodyLine = ui.receiveCommand().trim();
+ fullTextBody = fullTextBody.concat(" " + bodyLine);
+ }
+ return fullTextBody;
+ }
+
+ /**
+ * Detects zoom links from the text the user inputs.
+ *
+ * @param textBody A string of the text body that is scanned through for zoom links.
+ * @return An ArrayList of String with valid zoom links.
+ */
+ private ArrayList detectZoomLink(String textBody) {
+ ArrayList zoomLinkList = new ArrayList<>();
+ Pattern urlPattern = Pattern.compile("https?://(www\\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\\."
+ + "[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)");
+ Matcher urlMatcher = urlPattern.matcher(textBody);
+
+ while (urlMatcher.find()) {
+ String url = urlMatcher.group(0);
+ if (url.contains(".zoom.")) {
+ zoomLinkList.add(url);
+ }
+ }
+ zoomLinkCount = zoomLinkList.size();
+ return zoomLinkList;
+ }
+
+ /**
+ * Allows the user to choose from Zoom Links detected.
+ *
+ * @param zoomLinkList An ArrayList of type String containing zoom links.
+ * @param ui contains responses to print.
+ * @return The zoom link in String chosen by the user.
+ */
+ private String chooseZoomLink(ArrayList zoomLinkList, Ui ui) {
+ String zoomLink = null;
+ if (zoomLinkCount > 1) {
+ ui.printExtractChooseZoomLinkMessage(zoomLinkCount, zoomLinkList);
+ boolean zoomLinkChosen = false;
+ while (!zoomLinkChosen) {
+ try {
+ int zoomLinkNumberChosen = Integer.parseInt(ui.receiveCommand());
+ if (zoomLinkNumberChosen > zoomLinkCount || zoomLinkNumberChosen <= 0) {
+ ui.printExtractInvalidFieldChosenMessage("zoom link");
+ } else {
+ zoomLink = zoomLinkList.get(zoomLinkNumberChosen - 1);
+ zoomLinkChosen = true;
+ assert zoomLink != null : "zoomLink is null when chosen in extract";
+ }
+ } catch (NumberFormatException e) {
+ logger.warning("NumberFormatException occured -- "
+ + "User chose an invalid zoom link number from list.");
+ ui.printErrorMessage("We couldn't detect a number! Please choose again!");
+ }
+ }
+ } else {
+ zoomLink = zoomLinkList.get(0);
+ assert zoomLink != null : "zoomLink is null when chosen in extract";
+ ui.printExtractSingleZoomLinkDetectedMessage(zoomLink);
+ }
+ return zoomLink;
+ }
+
+ /**
+ * Detects 12 and 24 hour time slots from a text.
+ *
+ * @param textBody A string of the text body that is scanned through.
+ * @return An ArrayList of LocalTime objects for the time slots detected.
+ */
+ private ArrayList detectTime(String textBody) {
+ ArrayList timeListInString = new ArrayList<>();
+ String upperCaseTextBody = textBody.toUpperCase();
+ Pattern timePattern = Pattern.compile("\\b(1[0-9]|0?[0-9]|2[0-3])([:.][0-5][0-9])?[\\h]?([AP][M])?\\b");
+ Matcher timeMatcher = timePattern.matcher(upperCaseTextBody);
+
+ while (timeMatcher.find()) {
+ String time = timeMatcher.group(0);
+ if (time.contains(".")) {
+ time = time.replaceAll("\\.", ":");
+ }
+ time = time.toLowerCase();
+ timeListInString.add(time);
+ }
+
+ ArrayList timeList = verifyTime(timeListInString);
+ timeCount = timeList.size();
+
+ return timeList;
+ }
+
+ /**
+ * Verifies whether the time slots detected are legitimate and converts to LocalTime.
+ *
+ * @param timeListInString An ArrayList of strings of time slots detected.
+ * @return An ArrayList of LocalTime containing time slots that have been successfully converted.
+ */
+ private ArrayList verifyTime(ArrayList timeListInString) {
+ ArrayList timeList = new ArrayList<>();
+
+ for (String timeInString : timeListInString) {
+ try {
+ LocalTime localTime = DateTimeParser.timeParser(timeInString.trim());
+ timeList.add(localTime);
+ } catch (TimeErrorException e) {
+ logger.fine(timeInString + " was detected but could not be parsed");
+ }
+ }
+
+ return timeList;
+ }
+
+ /**
+ * Allows user to choose the time they want from timeList.
+ *
+ * @param timeList An ArrayList of LocalTime containing all time slots detected.
+ * @param ui contains responses to print.
+ * @return the LocalTime object chosen by user.
+ */
+ private LocalTime chooseTime(ArrayList timeList, Ui ui) {
+ LocalTime finalTime = null;
+ if (timeCount > 1) {
+ ui.printExtractChooseTimeMessage(timeCount, timeList);
+ boolean timeChosen = false;
+ while (!timeChosen) {
+ try {
+ int timeNumberChosen = Integer.parseInt(ui.receiveCommand());
+ if (timeNumberChosen > timeCount || timeNumberChosen <= 0) {
+ ui.printExtractInvalidFieldChosenMessage("timing");
+ } else {
+ finalTime = timeList.get(timeNumberChosen - 1);
+ timeChosen = true;
+ assert finalTime != null : "time is null when chosen in extract";
+ }
+ } catch (NumberFormatException e) {
+ logger.warning("NumberFormatException occured -- "
+ + "User chose an invalid time number from list.");
+ ui.printErrorMessage("We couldn't detect a number! Please choose again!");
+ }
+ }
+ } else if (timeCount == 0) {
+ ui.printExtractNoFieldMessage("timing");
+ } else {
+ finalTime = timeList.get(0);
+ assert finalTime != null : "time is null when chosen in extract";
+ ui.printExtractSingleTimeDetectedMessage(finalTime);
+ }
+
+ return finalTime;
+
+ }
+
+ /**
+ * Detects all the dates from a block of text.
+ *
+ * @param textBody A string containing the text to be scanned through.
+ * @return An ArrayList of LocalDate objects for the dates detected.
+ */
+ private ArrayList detectDate(String textBody) {
+ ArrayList dateListInString = new ArrayList<>();
+ String upperCaseTextBody = textBody.toUpperCase();
+ Pattern dayMonthYearPattern = Pattern.compile("\\b(([0]?[0-9])|([0-2][0-9])|([3][0-1]))(ST|ND|RD|TH)?[\\h-]"
+ + "(JAN|JANUARY|FEB|FEBRUARY|MAR|MARCH|APR|APRIL|MAY|JUN|JUNE|JUL|JULY|AUG"
+ + "|AUGUST|SEP|SEPTEMBER|OCT|OCTOBER|NOV|NOVEMBER|DEC|DECEMBER),?([\\h-]\\d{4})?\\b");
+ Pattern monthDayYearPattern = Pattern.compile("\\b(JAN|JANUARY|FEB|FEBRUARY|MAR|MARCH|APR|APRIL|MAY|JUN|JUNE|"
+ + "JUL|JULY|AUG|AUGUST|SEP|SEPTEMBER|OCT|OCTOBER|NOV|NOVEMBER|DEC|DECEMBER)[\\h-]"
+ + "(([0]?[0-9])|([0-2][0-9])|([3][0-1]))(ST|ND|RD|TH)?,?([\\h-]\\d{4})?\\b");
+ Matcher dayMonthYearMatcher = dayMonthYearPattern.matcher(upperCaseTextBody);
+ Matcher monthDayYearMatcher = monthDayYearPattern.matcher(upperCaseTextBody);
+
+ while (dayMonthYearMatcher.find()) {
+ String date = dayMonthYearMatcher.group(0);
+ String day = detectDay(date);
+ String month = detectMonth(date);
+ String year = detectYear(date);
+ String combinedDate = day + "/" + month + "/" + year;
+ dateListInString.add(combinedDate);
+ }
+
+ while (monthDayYearMatcher.find()) {
+ String date = monthDayYearMatcher.group(0);
+ String day = detectDay(date);
+ String month = detectMonth(date);
+ String year = detectYear(date);
+ String combinedDate = day + "/" + month + "/" + year;
+ dateListInString.add(combinedDate);
+ }
+
+ ArrayList dateList = verifyDate(dateListInString);
+ dateCount = dateList.size();
+ return dateList;
+ }
+
+ /**
+ * Verifies dates detected and converts them from string to LocalDate.
+ *
+ * @param dateListInString An ArrayList of strings containing dates.
+ * @return An ArrayList of LocalDate that contains verified dates.
+ */
+ private ArrayList verifyDate(ArrayList dateListInString) {
+ ArrayList dateList = new ArrayList<>();
+ for (String dateInString : dateListInString) {
+ try {
+ LocalDate localDate = DateTimeParser.dateParser(dateInString);
+ dateList.add(localDate);
+ } catch (DateErrorException e) {
+ logger.fine(dateInString + " was detected but could not be parsed");
+ }
+ }
+
+ return dateList;
+ }
+
+ /**
+ * Allows user to choose the date they want from the dateList.
+ *
+ * @param dateList An ArrayList containing LocalDate objects to choose from.
+ * @param ui contains responses to print.
+ * @return A localDate object chosen for the event.
+ */
+ private LocalDate chooseDate(ArrayList dateList, Ui ui) {
+ LocalDate finalDate = null;
+ if (dateCount > 1) {
+ ui.printExtractChooseDateMessage(dateCount, dateList);
+ boolean dateChosen = false;
+ while (!dateChosen) {
+ try {
+ int dateNumberChosen = Integer.parseInt(ui.receiveCommand());
+ if (dateNumberChosen > dateCount || dateNumberChosen <= 0) {
+ ui.printExtractInvalidFieldChosenMessage("date");
+ } else {
+ finalDate = dateList.get(dateNumberChosen - 1);
+ dateChosen = true;
+ assert finalDate != null : "date is null when chosen in extract";
+ }
+ } catch (NumberFormatException e) {
+ logger.warning("NumberFormatException occurred -- User chose an invalid date number from list.");
+ ui.printErrorMessage("We couldn't detect a number! Please choose again!");
+ }
+ }
+ } else if (dateCount == 0) {
+ ui.printExtractNoFieldMessage("date");
+ } else {
+ finalDate = dateList.get(0);
+ assert finalDate != null : "date is null when chosen in extract";
+ ui.printExtractSingleDateDetectedMessage(finalDate);
+ }
+
+ return finalDate;
+ }
+
+ /**
+ * Detects the day portion of the date.
+ *
+ * @param date String containing the full date detected.
+ * @return String containing the day.
+ */
+ private String detectDay(String date) {
+ String day = null;
+ Pattern dayPattern = Pattern.compile("\\b(([0]?[0-9])|([0-2][0-9])|([3][0-1])){1,2}(ST|ND|RD|TH)?\\b");
+ Matcher dayMatcher = dayPattern.matcher(date);
+ if (dayMatcher.find()) {
+ String dayMatch = dayMatcher.group(0);
+ if (dayMatch.contains("ST") || dayMatch.contains("ND")
+ || dayMatch.contains("RD") || dayMatch.contains("TH")) {
+ dayMatch = dayMatch.substring(0, dayMatch.length() - 2);
+ }
+ if (dayMatch.startsWith("0")) {
+ day = dayMatch.substring(1);
+ } else {
+ day = dayMatch;
+ }
+ }
+ return day;
+ }
+
+ /**
+ * Detects the month from the date.
+ *
+ * @param date A string containing the full date detected.
+ * @return A string containing the month detected.
+ */
+ private String detectMonth(String date) {
+ String month = null;
+ Pattern monthPattern = Pattern.compile("(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)");
+ Matcher monthMatcher = monthPattern.matcher(date);
+ if (monthMatcher.find()) {
+ switch (monthMatcher.group(0)) {
+ case "JAN":
+ month = "1";
+ break;
+ case "FEB":
+ month = "2";
+ break;
+ case "MAR":
+ month = "3";
+ break;
+ case "APR":
+ month = "4";
+ break;
+ case "MAY":
+ month = "5";
+ break;
+ case "JUN":
+ month = "6";
+ break;
+ case "JUL":
+ month = "7";
+ break;
+ case "AUG":
+ month = "8";
+ break;
+ case "SEP":
+ month = "9";
+ break;
+ case "OCT":
+ month = "10";
+ break;
+ case "NOV":
+ month = "11";
+ break;
+ case "DEC":
+ month = "12";
+ break;
+ default:
+ break;
+ }
+ }
+
+ return month;
+ }
+
+ /**
+ * Detects the year from the date.
+ *
+ * @param date A string containing the full date.
+ * @return A string containing the year portion of the date.
+ */
+ private String detectYear(String date) {
+ String year = date.substring(date.length() - 4);
+ try {
+ // Test if year is a valid integer
+ Integer.parseInt(year);
+ } catch (NumberFormatException e) {
+ year = getCurrentYear();
+ }
+ return year;
+ }
+
+ /**
+ * Retrieves the current year.
+ *
+ * @return A string containing the current year.
+ */
+ private String getCurrentYear() {
+ Calendar currentDay = Calendar.getInstance();
+ int yearInInt = currentDay.get(Calendar.YEAR);
+ return String.valueOf(yearInInt);
+ }
+
+}
diff --git a/src/main/java/seedu/duke/command/GoalCommand.java b/src/main/java/seedu/duke/command/GoalCommand.java
new file mode 100644
index 0000000000..da208ecff7
--- /dev/null
+++ b/src/main/java/seedu/duke/command/GoalCommand.java
@@ -0,0 +1,48 @@
+package seedu.duke.command;
+
+import seedu.duke.EventLogger;
+import seedu.duke.data.UserData;
+import seedu.duke.event.Goal;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.util.logging.Logger;
+
+/**
+ * Command to set goals.
+ */
+public class GoalCommand extends Command {
+ private static Logger logger = EventLogger.getEventLogger();
+
+ /**
+ * Constructor for goal command.
+ *
+ * @param command user input.
+ */
+ public GoalCommand(String command) {
+ assert command != null : "String for goal command should not be null";
+ this.isExit = false;
+ this.command = command;
+ }
+
+ @Override
+ public void execute(UserData data, Ui ui, Storage storage) {
+ if (command.isBlank()) {
+ Goal goal = data.getGoal();
+ ui.printGoalMessage(goal);
+ } else {
+ Goal goal = null;
+ switch (command.toLowerCase()) {
+ case "na": case "nil": case "delete":
+ data.setGoal(null);
+ break;
+ default:
+ goal = new Goal(command);
+ data.setGoal(goal);
+ }
+ ui.printChangeGoalMessage(goal);
+ storage.saveFile(storage.getFileLocation("Goal"), data, "Goal");
+ }
+ logger.fine("Goal command finished running successfully.");
+ }
+}
diff --git a/src/main/java/seedu/duke/command/HelpCommand.java b/src/main/java/seedu/duke/command/HelpCommand.java
new file mode 100644
index 0000000000..59f82a5175
--- /dev/null
+++ b/src/main/java/seedu/duke/command/HelpCommand.java
@@ -0,0 +1,81 @@
+package seedu.duke.command;
+
+import org.apache.commons.text.WordUtils;
+import seedu.duke.data.UserData;
+import seedu.duke.exception.InvalidHelpTopicException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.util.ArrayList;
+
+
+
+public class HelpCommand extends Command {
+
+ ArrayList helpStuff;
+ String helpTopic;
+ WordUtils wrapper = new WordUtils();
+
+ /**
+ * Creates a help command object to be executed.
+ *
+ * @param argument is a String containing the user input
+ */
+ public HelpCommand(String argument) {
+ this.isExit = false;
+ this.command = null;
+ argument = argument.trim();
+ if (argument.equals("")) { //blank argument set to be summary
+ argument = "summary";
+ }
+ this.helpTopic = argument;
+ helpStuff = new ArrayList<>();
+
+ }
+
+ /**
+ * Function will execute the command. Prints out the help information requested by user
+ *
+ * @param data object of UserData class containing user's data.
+ * @param ui object of UI class that contains the user interface needed to print the help file
+ * @param storage Storage oject with the save file path to write to to save changes.
+ * @throws InvalidHelpTopicException if there is no such help topic available
+ */
+ @Override
+ public void execute(UserData data, Ui ui, Storage storage) throws InvalidHelpTopicException {
+ storage.loadSystemResources("/helpfile.txt", helpStuff);
+ int beginIndex = searchIndex(helpStuff, true);
+ int endIndex = searchIndex(helpStuff, false);
+
+ if (beginIndex < 0 || endIndex < 0) {
+ throw new InvalidHelpTopicException(this.helpTopic);
+ }
+
+ for (int i = beginIndex + 1; i < endIndex; i++) {
+ String line = helpStuff.get(i);
+ line = wrapper.wrap(line, 80, "\n", false);
+ ui.printMessage(line);
+ }
+
+ }
+
+ /**
+ * Finds the line number of the start and end of the help topic located in the txt file.
+ * @param helpData ArrayList of Strings containing all help file info loaded from the helpfile.txt
+ * @param isBegin boolean Set to True to find the beginning of the help topic, set to false to find the end.
+ * @return integer of line number of the help topic beginning or end
+ */
+ private int searchIndex(ArrayList helpData, boolean isBegin) {
+
+ String toSearchFor;
+ if (isBegin) {
+ toSearchFor = "begin " + helpTopic;
+ } else {
+ toSearchFor = "end " + helpTopic;
+ }
+
+ int index;
+ index = helpData.indexOf(toSearchFor);
+ return index;
+ }
+}
diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java
new file mode 100644
index 0000000000..254ff507f1
--- /dev/null
+++ b/src/main/java/seedu/duke/command/ListCommand.java
@@ -0,0 +1,67 @@
+package seedu.duke.command;
+
+import seedu.duke.EventLogger;
+import seedu.duke.event.EventList;
+import seedu.duke.exception.DukeException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+import seedu.duke.data.UserData;
+
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+/**
+ * Command to list events.
+ */
+public class ListCommand extends Command {
+ private static Logger logger = EventLogger.getEventLogger();
+
+ /**
+ * Constructor for listing events seedu.duke
+ *
+ * @param command from user input
+ */
+ public ListCommand(String command) {
+ this.command = command;
+ }
+
+ @Override
+ public void execute(UserData data, Ui ui, Storage storage) throws DukeException {
+ ArrayList eventLists = data.getAllEventLists();
+ if (command == null) {
+ ui.printAvailableList(eventLists);
+ } else if (command.equals("All")) {
+ listAll(ui, eventLists);
+ } else {
+ ui.printList(data.getEventList(command));
+ }
+ logger.fine("List command finished running successfully.");
+ }
+
+ private void listAll(Ui ui, ArrayList eventLists) {
+ int listNum = eventLists.size();
+ for (EventList list : eventLists) {
+ ui.printList(list);
+ if (listNum > 1) {
+ ui.printDividerLine();
+ }
+ listNum--;
+ }
+ }
+
+ /**
+ * Static parser for list command creation.
+ *
+ * @param input user input.
+ * @return ListCommand with input formatted.
+ */
+ public static Command parse(String input) {
+ if (input.isBlank()) {
+ return new ListCommand(null);
+ } else {
+ input = input.toLowerCase();
+ input = input.substring(0, 1).toUpperCase() + input.substring(1);
+ return new ListCommand(input);
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/command/NoteCommand.java b/src/main/java/seedu/duke/command/NoteCommand.java
new file mode 100644
index 0000000000..6ffe709617
--- /dev/null
+++ b/src/main/java/seedu/duke/command/NoteCommand.java
@@ -0,0 +1,152 @@
+package seedu.duke.command;
+
+
+import seedu.duke.EventLogger;
+import seedu.duke.data.UserData;
+import seedu.duke.event.Event;
+import seedu.duke.event.EventList;
+import seedu.duke.exception.DukeException;
+import seedu.duke.exception.WrongNumberFormatException;
+import seedu.duke.exception.WrongNumberOfArgumentsException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * Command to make notes.
+ */
+public class NoteCommand extends Command {
+ private int index;
+ private String event;
+ private static Logger logger = EventLogger.getEventLogger();
+
+ /**
+ * Constructor for note.
+ *
+ * @param command user's command.
+ */
+ public NoteCommand(String command) {
+ this.isExit = false;
+ this.command = command;
+ }
+
+ /**
+ * Create and save notes.
+ *
+ * @param data object of UserData class containing user's data.
+ * @param ui containing the responses to print.
+ * @param storage with the save file path to write to.
+ * @throws DukeException catch any exception under Duke.
+ */
+ @Override
+ public void execute(UserData data, Ui ui, Storage storage) throws DukeException {
+ parseUserCommand(command);
+ EventList list = data.getEventList(event);
+ Event eventRequested = list.getEventByIndex(index - 1);
+ if (eventRequested != null) {
+ ui.printMessage("Please type in your notes."
+ + " To stop note taking, ensure that you are in a new line"
+ + " and type 'noteend', and press enter");
+ ArrayList existingNotes = eventRequested.getNotes();
+ ArrayList additionalNotes = getNotesFromUser(ui);
+ ArrayList updatedNotes = updatingNotesWithTimestamp(existingNotes, additionalNotes);
+ eventRequested.setNotes(updatedNotes);
+ ui.printNoteMessage(eventRequested, updatedNotes);
+ }
+
+ storage.saveFile(storage.getFileLocation(list.getName()), data, list.getName());
+ logger.fine("Note for event was created/updated successfully.");
+ }
+
+ /**
+ * Parse user command.
+ *
+ * @param command user's command.
+ * @throws DukeException catch any exception under Duke.
+ */
+ private void parseUserCommand(String command) throws DukeException {
+ command = command.trim();
+ String[] commandSplit = command.split(";");
+ if (commandSplit.length == 2) {
+ try {
+ index = parsingNumber(commandSplit[1].trim());
+ event = commandSplit[0].trim();
+ } catch (NumberFormatException e) {
+ logger.warning("WrongNumberFormatException encountered -- Note index is not a number");
+ throw new WrongNumberFormatException("Index must be numerical format!");
+ }
+ } else {
+ logger.warning("WrongNumberOfArgumentsException encountered "
+ + "-- Note have incorrect number of arguments");
+ throw new WrongNumberOfArgumentsException("Incorrect number of parameters for Note!"
+ + System.lineSeparator()
+ + "The format for Note is : \"note ; ;\" ");
+ }
+ }
+
+ /**
+ * Require user's input for notes.
+ *
+ * @return a list of notes.
+ */
+ private ArrayList getNotesFromUser(Ui ui) {
+
+ ArrayList notesList = new ArrayList();
+ String temp = ui.receiveCommand().trim();
+ while (!temp.equals("noteend")) {
+ notesList.add(temp);
+ temp = ui.receiveCommand().trim();
+ }
+ return notesList;
+ }
+
+ /**
+ * Concatenate existing and new notes with timestamp.
+ *
+ * @param existingNotes notes stored in storage.
+ * @param additionalNotes notes that are just inputted.
+ * @return a combined list of notes.
+ */
+ private ArrayList updatingNotesWithTimestamp(ArrayList existingNotes,
+ ArrayList additionalNotes) {
+ LocalDateTime now = LocalDateTime.now();
+ String timestamp = "---------" + now + "---------";
+ existingNotes.add(timestamp);
+ additionalNotes = indicateNewLineUsingDelimeter(additionalNotes);
+ existingNotes.addAll(additionalNotes);
+ return existingNotes;
+ }
+
+ private ArrayList indicateNewLineUsingDelimeter(ArrayList notes) {
+ ArrayList convertedList = new ArrayList<>();
+
+ for (String note : notes) { //after splitting up the lines based on `, add them
+ String[] lines = note.split("`", -1);
+ List toBeAdded = Arrays.asList(lines);
+ convertedList.addAll(toBeAdded);
+ }
+ return convertedList;
+ }
+
+ /**
+ * Check if index is numerical format.
+ *
+ * @param number index in string format
+ * @return index of event
+ */
+ private int parsingNumber(String number) {
+ try {
+ int index = Integer.parseInt(number);
+ return index;
+ } catch (NumberFormatException e) {
+ throw new NumberFormatException("Index must be numerical format!");
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/duke/command/ReminderCommand.java b/src/main/java/seedu/duke/command/ReminderCommand.java
new file mode 100644
index 0000000000..6359b31545
--- /dev/null
+++ b/src/main/java/seedu/duke/command/ReminderCommand.java
@@ -0,0 +1,109 @@
+package seedu.duke.command;
+
+import seedu.duke.EventLogger;
+import seedu.duke.data.UserData;
+import seedu.duke.event.Event;
+import seedu.duke.event.EventList;
+import seedu.duke.exception.DukeException;
+import seedu.duke.exception.InvalidIndexException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+/**
+ * Command to print reminder for user.
+ */
+public class ReminderCommand extends Command {
+ private static Logger logger = EventLogger.getEventLogger();
+
+ /**
+ * Constructor for reminder seedu.duke.
+ */
+ public ReminderCommand() {
+ this.isExit = false;
+ }
+
+ /**
+ * To find the events for today.
+ *
+ * @param data object of UserData class containing user's data.
+ * @param ui containing the responses to print.
+ * @param storage with the save file path to write to.
+ * @throws DukeException error caught by duke
+ */
+ @Override
+ public void execute(UserData data, Ui ui, Storage storage) throws DukeException {
+ ArrayList allEventList = data.getAllEventLists();
+ ArrayList reminderEvents = new ArrayList<>();
+ for (int i = 0; i < allEventList.size(); i++) {
+ ArrayList eventList = filterTodayEvents(allEventList.get(i));
+ reminderEvents.addAll(eventList);
+ }
+ ArrayList reminderEventsWithoutTime = new ArrayList<>();
+ ArrayList reminderEventsWithTime = new ArrayList<>();
+ for (int i = 0; i < reminderEvents.size(); i++) {
+ if (reminderEvents.get(i).getTime() == null) {
+ reminderEventsWithoutTime.add(reminderEvents.get(i));
+ } else {
+ reminderEventsWithTime.add(reminderEvents.get(i));
+ }
+ }
+ logger.fine("Reminder Command executed");
+ ui.printReminder(reminderEventsWithTime, reminderEventsWithoutTime);
+
+ }
+
+ /**
+ * Filter the events.
+ *
+ * @param eventList list of events in the category
+ * @return todayEvents events happening today
+ * @throws InvalidIndexException index is invalid
+ */
+ private ArrayList filterTodayEvents(EventList eventList) throws InvalidIndexException {
+
+ ArrayList todayEvents = new ArrayList<>();
+ LocalDate dateNow = LocalDate.now();
+ for (int i = 0; i < eventList.getSize(); i++) {
+
+ LocalDate eventDate = eventList.getEventByIndex(i).getDate();
+ ArrayList repeatedList = eventList.getEventByIndex(i).getRepeatEventList();
+ if (repeatedList != null) {
+ if (checkingRepeatedEvent(repeatedList) != null) {
+ todayEvents.addAll(checkingRepeatedEvent(repeatedList));
+ }
+ }
+ if (eventDate != null) {
+ if (dateNow.compareTo(eventDate) == 0) {
+ todayEvents.add(eventList.getEventByIndex(i));
+ }
+ }
+
+ }
+ return todayEvents;
+ }
+
+ /**
+ * Filter through repeated events.
+ *
+ * @param event list of repeated eventss
+ * @return todayRepeatedEvent repeated events that happen today
+ */
+ private ArrayList checkingRepeatedEvent(ArrayList event) {
+ LocalDate dateNow = LocalDate.now();
+ ArrayList todayRepeatedEvent = new ArrayList<>();
+ for (int i = 0; i < event.size(); i++) {
+ LocalDate eventDate = event.get(i).getDate();
+ if (eventDate != null) {
+ if (dateNow.compareTo(eventDate) == 0) {
+ todayRepeatedEvent.add(event.get(i));
+ }
+ }
+ }
+ return todayRepeatedEvent;
+ }
+
+}
diff --git a/src/main/java/seedu/duke/command/RepeatCommand.java b/src/main/java/seedu/duke/command/RepeatCommand.java
new file mode 100644
index 0000000000..d61f3d4563
--- /dev/null
+++ b/src/main/java/seedu/duke/command/RepeatCommand.java
@@ -0,0 +1,252 @@
+package seedu.duke.command;
+
+import seedu.duke.EventLogger;
+import seedu.duke.data.UserData;
+import seedu.duke.event.Event;
+import seedu.duke.event.EventList;
+import seedu.duke.exception.DukeException;
+import seedu.duke.exception.InvalidTimeUnitException;
+import seedu.duke.exception.MissingDeadlineRepeatException;
+import seedu.duke.exception.MissingRepeatListException;
+import seedu.duke.exception.NumberOverflowException;
+import seedu.duke.exception.WrongNumberFormatException;
+import seedu.duke.exception.WrongNumberOfArgumentsException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+
+public class RepeatCommand extends Command {
+ public static final String DAILY = "DAILY";
+ public static final String WEEKLY = "WEEKLY";
+ public static final String MONTHLY = "MONTHLY";
+ private static final String COMMANDTYPE_LIST = "list";
+ private static final String COMMANDTYPE_ADD = "add";
+ private static final String COMMANDTYPE_ERROR = "error";
+ private String commandType;
+ private static Logger logger = EventLogger.getEventLogger();
+ private static final int MAXIMUM_SIZE = 1000;
+
+
+ /**
+ * Constructor for the repeat command.
+ *
+ * @param command String containing the user arguments to repeat an event
+ * @param commandType Can be add, list or null. Tells repeat what to execute with the arguments
+ */
+ public RepeatCommand(String command, String commandType) {
+ this.isExit = false;
+ this.command = command;
+ this.commandType = commandType;
+ }
+
+ /**
+ * Executes the repeat command according to the user input stored in the command object.
+ *
+ * @param data object of UserData class containing user's data.
+ * @param ui object of UI class which helps to print out instructions or responses on the terminal
+ * @param storage object of Storage class that helps save information to a location on the computer
+ * @throws DukeException if any errors occurs with repeating an event occurs
+ */
+ @Override
+ public void execute(UserData data, Ui ui, Storage storage) throws DukeException {
+ switch (commandType) {
+ case COMMANDTYPE_ADD:
+ executeAdd(data, ui, storage);
+ break;
+ case COMMANDTYPE_LIST:
+ executeList(data, ui);
+ break;
+ case COMMANDTYPE_ERROR:
+ executeNull(data, ui, storage);
+ break;
+ default:
+ break;
+ }
+
+
+ }
+
+ /**
+ * Static parser for repeat command creation. Distinguish between adding repeated dates or listing current repeats.
+ *
+ * @param input String containing user inputs
+ * @return RepeatCommand set to either add additional dates or set to list out current dates in event
+ */
+ public static Command parse(String input) throws DukeException {
+ String[] words = input.split(";");
+ for (int i = 0; i < words.length; i++) {
+ words[i] = words[i].trim();
+ }
+ logger.fine("Arguments for Repeat processed");
+ switch (words.length) {
+ case 2:
+ logger.fine("List Repeat command initiated");
+ words[0] = formatListName(words[0]);
+ isValidNumber(words[1]);
+ input = String.join(" ", words);
+ return new RepeatCommand(input, COMMANDTYPE_LIST);
+ case 4:
+ logger.fine("Add Repeat command initiated");
+ words[0] = formatListName(words[0]);
+ isValidNumber(words[1]);
+ words[2] = words[2].toUpperCase();
+ isValidNumber(words[3]);
+ input = String.join(" ", words);
+ return new RepeatCommand(input, COMMANDTYPE_ADD);
+ default:
+
+
+ String errorMessage = "Wrong number of arguments provided, "
+ + "The format for repeat is: \"repeat EVENT_TYPE; EVENT_INDEX; [UNIT]; [COUNT]\"";
+ throw new WrongNumberOfArgumentsException(errorMessage);
+ }
+
+ }
+
+ /**
+ * Change the name of the list to ensure the first character is capitalised.
+ *
+ * @param name String containing name of list for the first character to be capitalised
+ * @return String with first character capitalised
+ */
+ private static String formatListName(String name) {
+ name = name.toLowerCase();
+ return name.substring(0, 1).toUpperCase() + name.substring(1);
+ }
+
+ /**
+ * Checks if the string can be converted to an integer.
+ *
+ * @param number String containing the String form of an integer
+ * @throws DukeException if the number given by the users is not written in numeric format
+ */
+ private static void isValidNumber(String number) throws DukeException {
+ try {
+ Integer.parseInt(number);
+ } catch (NumberFormatException e) {
+ logger.warning("Number provided by user was not in numerical format");
+ throw new WrongNumberFormatException("Numbers must be in numerical format");
+ }
+ }
+
+ /**
+ * List command. Used to show dates of repeated events.
+ *
+ * @param data location where all user event information is stored
+ * @param ui User Interface class for printing on screens
+ * @throws DukeException if there are no list of repeated event to show or if there are errors in user inputs
+ */
+ private void executeList(UserData data, Ui ui) throws DukeException {
+ logger.fine("Begin executeList in Repeat");
+ String[] words = command.split(" ");
+ EventList eventList = data.getEventList(words[0]);
+ int index = Integer.parseInt(words[1]) - 1;
+ Event repeatEvent = eventList.getEventByIndex(index);
+ if (repeatEvent.getRepeatEventList() == null) {
+ logger.warning("Event has no repeated stuff to list out");
+ throw new MissingRepeatListException();
+ }
+ ui.printRepeatList(repeatEvent);
+ }
+
+ /**
+ * Add command. Used to add repeated dates to an event.
+ *
+ * @param data location where all user event information is stored
+ * @param ui User Interface class for printing on screens
+ * @param storage File storage location on computer
+ * @throws DukeException if there is an error processing the user input for adding repeated events
+ */
+ private void executeAdd(UserData data, Ui ui, Storage storage) throws DukeException {
+ logger.fine("Begin executeAdd in Repeat");
+ String[] words = command.split(" ");
+ EventList eventList = data.getEventList(words[0]);
+ int index = Integer.parseInt(words[1]) - 1;
+ Event eventToRepeat = eventList.getEventByIndex(index);
+ LocalDate startDate = eventToRepeat.getDate();
+ if (startDate == null) {
+ logger.warning("User provided event had no deadline");
+ throw new MissingDeadlineRepeatException();
+ }
+ String repeatType = words[2];
+ int count = Integer.parseInt(words[3]);
+ repeat(eventToRepeat, startDate, repeatType, count);
+ ui.printRepeatAdd(eventToRepeat);
+ storage.saveFile(storage.getFileLocation(eventList.getName()), data, eventList.getName());
+ }
+
+ /**
+ * Private function that helps to record repeated events in a list to be stored under the original event.
+ *
+ * @param eventToRepeat Event object that is to be repeated over a period of time
+ * @param startDate Date of the original eventToRepeat object
+ * @param repeatType String containing the time unit to advance. Can be daily, weekly or monthly
+ * @param count Integer containing how many times to advance by repeatType
+ * @throws DukeException if there are errors occur while repeating the event such as incorrect arguments given
+ */
+ private void repeat(Event eventToRepeat, LocalDate startDate, String repeatType, int count) throws DukeException {
+ ArrayList repeatEventList = new ArrayList<>();
+ if (count > MAXIMUM_SIZE) {
+ throw new NumberOverflowException("Repeat amount is too large, please limit to only "
+ + Integer.toString(MAXIMUM_SIZE) + " repetitions.");
+ }
+ if (count <= 0) { //repetition is negative or zero, not possible to repeat
+ eventToRepeat.setRepeatEventList(null);
+ eventToRepeat.setRepeatType(null);
+ return;
+ }
+ for (int i = 1; i <= count; i++) {
+ LocalDate repeatDate;
+ logger.fine("Repeat repetition number" + Integer.toString(i));
+ switch (repeatType) {
+ case MONTHLY:
+ logger.fine("Event date incremented monthly");
+ repeatDate = startDate.plusMonths(i);
+ break;
+ case WEEKLY:
+ logger.fine("Event date incremented weekly");
+ repeatDate = startDate.plusWeeks(i);
+ break;
+ case DAILY:
+ logger.fine("Event date incremented daily");
+ repeatDate = startDate.plusDays(i);
+ break;
+ default:
+ throw new InvalidTimeUnitException(repeatType);
+ }
+ eventToRepeat.setRepeatType(repeatType);
+ eventToRepeat.setRepeatEventList(null);
+ Event repeatEvent;
+ try {
+ repeatEvent = eventToRepeat.clone();
+ repeatEvent.markAsUndone();
+ logger.fine("Event for " + Integer.toString(i) + " has been cloned");
+ } catch (CloneNotSupportedException e) {
+ logger.warning("Event cannot be cloned");
+ throw new DukeException("Cant clone");
+ }
+ repeatEvent.setDate(repeatDate);
+ logger.fine("Cloned event incremented date set");
+ repeatEventList.add(repeatEvent);
+ logger.fine("Cloned event added to repeat event list");
+ }
+ eventToRepeat.setRepeatEventList(repeatEventList);
+ logger.fine("All cloned event list stored in original event");
+ }
+
+ /**
+ * Function does nothing. Usually occurs if there are errors in the user input detected by parser.
+ *
+ * @param data object of UserData class containing user's data.
+ * @param ui object of UI class which helps to print out instructions or responses on the terminal
+ * @param storage object of Storage class that helps save information to a location on the computer
+ */
+ private void executeNull(UserData data, Ui ui, Storage storage) {
+ //do nothing
+ logger.warning("There was an error in the user input. Command does nothing");
+ }
+}
diff --git a/src/main/java/seedu/duke/command/SaveCommand.java b/src/main/java/seedu/duke/command/SaveCommand.java
new file mode 100644
index 0000000000..91725b0e76
--- /dev/null
+++ b/src/main/java/seedu/duke/command/SaveCommand.java
@@ -0,0 +1,20 @@
+package seedu.duke.command;
+
+import seedu.duke.data.UserData;
+import seedu.duke.exception.DukeException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+public class SaveCommand extends Command {
+
+ public SaveCommand() {
+ isExit = false;
+
+ }
+
+ @Override
+ public void execute(UserData data, Ui ui, Storage storage) throws DukeException {
+ storage.saveAll(data);
+ ui.printStorageSavedMessage();
+ }
+}
diff --git a/src/main/java/seedu/duke/command/UndoneCommand.java b/src/main/java/seedu/duke/command/UndoneCommand.java
new file mode 100644
index 0000000000..57aa609a16
--- /dev/null
+++ b/src/main/java/seedu/duke/command/UndoneCommand.java
@@ -0,0 +1,159 @@
+package seedu.duke.command;
+
+import seedu.duke.EventLogger;
+import seedu.duke.data.UserData;
+import seedu.duke.event.Event;
+import seedu.duke.event.EventList;
+import seedu.duke.exception.DukeException;
+import seedu.duke.exception.InvalidEventDateException;
+import seedu.duke.exception.MissingSemicolonException;
+import seedu.duke.exception.WrongNumberFormatException;
+import seedu.duke.exception.WrongNumberOfArgumentsException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+import static seedu.duke.parser.DateTimeParser.dateParser;
+
+public class UndoneCommand extends Command {
+ private final String listType;
+
+ private static Logger logger = EventLogger.getEventLogger();
+
+ /**
+ * Constructor for setting event to undone.
+ *
+ * @param command from user input
+ */
+ public UndoneCommand(String listType, String command) {
+ this.isExit = false;
+ this.listType = listType;
+ this.command = command;
+ }
+
+ /**
+ * Parses the user input and returns the corresponding UndoneCommand.
+ *
+ * @param input the processed user input with the command keyword removed.
+ * @return the UndoneCommand corresponding to the list type and event indicated in the input.
+ * @throws MissingSemicolonException if the input does not contain any semicolons to separate input fields.
+ * @throws WrongNumberOfArgumentsException if the event type or event index is missing.
+ * @throws WrongNumberFormatException if the event index is not an integer.
+ */
+ public static Command parse(String input) throws MissingSemicolonException,
+ WrongNumberOfArgumentsException, WrongNumberFormatException {
+ logger.fine("Parsing UndoneCommand input: \"" + input + "\"");
+
+ if (!input.contains(";")) {
+ logger.warning("MissingSemicolonException: User input fields was not separated with semicolon.");
+ throw new MissingSemicolonException("Remember to separate input fields with a ';'." + System.lineSeparator()
+ + "The format for undone is: \"undone ; ; []\".");
+ }
+
+ String[] inputParameters = input.trim().split(";", 2);
+
+ if (inputParameters[0].isBlank() || inputParameters[1].isBlank()) {
+ logger.warning("WrongNumberOfArgumentsException: User did not provide event type or event index.");
+ throw new WrongNumberOfArgumentsException("Event type or index is missing." + System.lineSeparator()
+ + "The format for undone is: \"undone ; ; []\".");
+ }
+
+ String listType = capitaliseFirstLetter(inputParameters[0].trim());
+ String eventIdentifier = inputParameters[1].trim();
+
+ try {
+ String[] eventIdentifierArray = eventIdentifier.split(";",2);
+ Integer.parseInt(eventIdentifierArray[0]);
+ } catch (NumberFormatException e) {
+ logger.warning("WrongNumberFormatException: Event index given is not an integer.");
+ throw new WrongNumberFormatException("Event index given is not an integer." + System.lineSeparator()
+ + "The format for undone is: \"undone ; ; []\".");
+ }
+
+ logger.fine("Successfully parsed input and created UndoneCommand.");
+ return new UndoneCommand(listType, eventIdentifier);
+ }
+
+ /**
+ * Looks for the event indicated and marks it as undone.
+ *
+ * @param data object of UserData class containing user's data.
+ * @param ui containing the responses to print.
+ * @param storage with the save file path to write to.
+ * @throws DukeException if en error occurs during the execution of a method called in the command.
+ */
+ @Override
+ public void execute(UserData data, Ui ui, Storage storage) throws DukeException {
+ logger.fine("Start executing undone command.");
+ logger.info("listType: \"" + listType + "\", command: " + command + "\"");
+ EventList eventList = data.getEventList(listType);
+ String[] eventIdentifierArray = command.split(";");
+
+ int eventIndex = Integer.parseInt(eventIdentifierArray[0]) - 1;
+ Event undoneEvent = eventList.getEventByIndex(eventIndex);
+
+ if (undoneEvent.getRepeatType() == null || eventIdentifierArray.length == 1) {
+ undoneEvent.markAsUndone();
+ ui.printEventMarkedUndoneMessage(undoneEvent);
+ storage.saveFile(storage.getFileLocation(listType), data, listType);
+ logger.fine("Event marked as undone: \"" + undoneEvent + "\"");
+ } else { // event is a repeat task
+ LocalDate undoneEventDate = dateParser(eventIdentifierArray[1].trim());
+ boolean isDateFound;
+
+ if (undoneEventDate.isEqual(undoneEvent.getDate())) {
+ isDateFound = true;
+ undoneEvent.markAsUndone();
+ ui.printEventMarkedUndoneMessage(undoneEvent);
+ logger.fine("Event marked as undone: \"" + undoneEvent + "\"");
+ } else {
+ ArrayList repeatEventList = undoneEvent.getRepeatEventList();
+ isDateFound = scanRepeatList(repeatEventList, undoneEventDate, ui);
+ }
+
+ if (!isDateFound) {
+ throw new InvalidEventDateException();
+ }
+
+ storage.saveFile(storage.getFileLocation(listType), data, listType);
+ logger.fine("Changes saved to external file.");
+ }
+ }
+
+ /**
+ * Capitalises the first letter of an input string.
+ *
+ * @param input the string to be capitalised.
+ * @return the capitalised string.
+ */
+ private static String capitaliseFirstLetter(String input) {
+ input = input.toLowerCase();
+ return input.substring(0, 1).toUpperCase() + input.substring(1);
+ }
+
+ /**
+ * Scans the repeat event array list of a repeat event for an event matching the given date and marks it undone.
+ *
+ * @param repeatEventList the array list containing all the repeated sub events under the main repeat event.
+ * @param undoneEventDate the date of the sub repeat event to be marked done.
+ * @param ui containing the responses to print.
+ * @return boolean stating if an event matching the date given was found and marked undone
+ */
+ private boolean scanRepeatList(ArrayList repeatEventList, LocalDate undoneEventDate, Ui ui) {
+ logger.fine("Checking events in repeat event list.");
+ boolean isDateFound = false;
+ for (Event e: repeatEventList) {
+ if (e.getDate().isEqual(undoneEventDate)) {
+ isDateFound = true;
+ e.markAsUndone();
+ ui.printEventMarkedUndoneMessage(e);
+ logger.info("Repeat list event marked as undone: \"" + e + "\"");
+ }
+ }
+ logger.fine("Finished checking repeat event list.");
+ return isDateFound;
+ }
+}
diff --git a/src/main/java/seedu/duke/command/ViewCommand.java b/src/main/java/seedu/duke/command/ViewCommand.java
new file mode 100644
index 0000000000..70c3d67366
--- /dev/null
+++ b/src/main/java/seedu/duke/command/ViewCommand.java
@@ -0,0 +1,96 @@
+package seedu.duke.command;
+
+import seedu.duke.EventLogger;
+import seedu.duke.data.UserData;
+import seedu.duke.event.Event;
+import seedu.duke.event.EventList;
+import seedu.duke.exception.DukeException;
+import seedu.duke.exception.InvalidIndexException;
+import seedu.duke.exception.InvalidListException;
+import seedu.duke.exception.WrongNumberFormatException;
+import seedu.duke.exception.WrongNumberOfArgumentsException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+/**
+ * Command to view notes.
+ */
+public class ViewCommand extends Command {
+ private int index;
+ private String event;
+ private static Logger logger = EventLogger.getEventLogger();
+
+ /**
+ * Constructor for viewing.
+ *
+ * @param command user's command.
+ */
+ public ViewCommand(String command) {
+ this.isExit = false;
+ this.command = command;
+ }
+
+ /**
+ * View notes.
+ *
+ * @param data object of UserData class containing user's data.
+ * @param ui containing the responses to print.
+ * @param storage with the save file path to write to.
+ * @throws DukeException catch any exception under Duke.
+ */
+ @Override
+ public void execute(UserData data, Ui ui, Storage storage) throws DukeException {
+ parseUserCommand(command);
+ EventList list = data.getEventList(event);
+ Event eventRequested = list.getEventByIndex(index - 1);
+ if (eventRequested != null) {
+ ArrayList existingNotes = eventRequested.getNotes();
+ ui.printViewNote(existingNotes);
+ logger.fine("View Command executed");
+ }
+
+ }
+
+ /**
+ * Parsing user command.
+ * @param command user input
+ * @throws DukeException catch any exception under Duke.
+ */
+ private void parseUserCommand(String command) throws DukeException {
+ command = command.trim();
+ String[] commandSplit = command.split(";");
+ if (commandSplit.length == 2) {
+ try {
+ index = parsingNumber(commandSplit[1].trim());
+ event = commandSplit[0].trim();
+ } catch (NumberFormatException e) {
+ logger.warning("WrongNumberFormatException encountered -- View index is not a number");
+ throw new WrongNumberFormatException("Index must be numerical format!");
+ }
+ } else {
+ logger.warning("WrongNumberOfArgumentsException encountered "
+ + "-- View have incorrect number of arguments");
+ throw new WrongNumberOfArgumentsException("Incorrect number of parameters for View!"
+ + System.lineSeparator()
+ + "The format for View is : \"view ; ;\" ");
+ }
+ }
+
+ /**
+ * Check if index is numerical format.
+ *
+ * @param number index in string format
+ * @return index of event
+ */
+ private int parsingNumber(String number) {
+ try {
+ int index = Integer.parseInt(number);
+ return index;
+ } catch (NumberFormatException e) {
+ throw new NumberFormatException("Index must be numerical format!");
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/data/UserData.java b/src/main/java/seedu/duke/data/UserData.java
new file mode 100644
index 0000000000..5685e7955c
--- /dev/null
+++ b/src/main/java/seedu/duke/data/UserData.java
@@ -0,0 +1,96 @@
+package seedu.duke.data;
+
+import seedu.duke.EventLogger;
+import seedu.duke.event.Event;
+import seedu.duke.event.EventList;
+import seedu.duke.event.Goal;
+import seedu.duke.exception.InvalidListException;
+
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+/**
+ * Stores the data of the user.
+ */
+public class UserData {
+ private ArrayList eventLists = new ArrayList<>();
+ private Goal goal;
+ private static Logger logger = EventLogger.getEventLogger();
+
+ /**
+ * Constructor for UserData. Initialises with empty data.
+ */
+ public UserData() {
+ eventLists.add(new EventList("Personal"));
+ eventLists.add(new EventList("Timetable"));
+ eventLists.add(new EventList("Zoom"));
+ setGoal(null);
+ }
+
+ /**
+ * Constructor for UserData. Initialises the data with the given eventLists.
+ *
+ * @param eventLists to initialise UserData with.
+ */
+ public UserData(ArrayList eventLists) {
+ this.eventLists = new ArrayList<>(eventLists);
+ }
+
+ /**
+ * Adds the given Event into the EventList with specified name.
+ *
+ * @param name of EventList to add to.
+ * @param e Event to add to EventList.
+ * @throws InvalidListException if a list with the given name does not exist.
+ */
+ public void addToEventList(String name, Event e) throws InvalidListException {
+ assert e != null : "event cannot be null";
+ getEventList(name).add(e);
+ }
+
+ /**
+ * Returns the EventList with the given name.
+ *
+ * @param name of EventList to retrieve.
+ * @return EventList with the given name.
+ * @throws InvalidListException if a list with the given name does not exist.
+ */
+ public EventList getEventList(String name) throws InvalidListException {
+ assert name != null : "name should not be null";
+ for (EventList e : eventLists) {
+ if (e.getName().equalsIgnoreCase(name)) {
+ return e;
+ }
+ }
+ logger.warning("InvalidListException encountered -- " + name + " list does not exist");
+ throw new InvalidListException(name + " list does not exist.");
+ }
+
+ /**
+ * Returns the ArrayList of EventList that contains data of all events.
+ *
+ * @return ArrayList of EventList.
+ */
+ public ArrayList getAllEventLists() {
+ return eventLists;
+ }
+
+ /**
+ * Sets the user's goal.
+ *
+ * @param goal to set to.
+ */
+ public void setGoal(Goal goal) {
+ this.goal = goal;
+ }
+
+ /**
+ * Returns the user's goal.
+ *
+ * @return user's goal.
+ */
+ public Goal getGoal() {
+ return goal;
+ }
+
+}
diff --git a/src/main/java/seedu/duke/event/Event.java b/src/main/java/seedu/duke/event/Event.java
new file mode 100644
index 0000000000..e5c3010397
--- /dev/null
+++ b/src/main/java/seedu/duke/event/Event.java
@@ -0,0 +1,199 @@
+package seedu.duke.event;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+
+public abstract class Event implements Cloneable {
+ protected String description;
+ protected LocalDate date;
+ protected LocalTime time;
+ protected boolean isDone;
+ protected ArrayList repeatEventList;
+ protected String repeatType;
+ protected ArrayList notes;
+
+ /**
+ * Creates a new event with the given description and default its done status to false.
+ *
+ * @param description of event.
+ */
+ public Event(String description) {
+ setDescription(description);
+ isDone = false;
+ repeatType = null;
+ repeatEventList = null;
+ notes = new ArrayList();
+ assert notes.size() == 0 : "Notes should be empty";
+
+ }
+
+ /**
+ * Sets event's description.
+ *
+ * @param description of event.
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ /**
+ * Sets event's date.
+ *
+ * @param date of event.
+ */
+ public void setDate(LocalDate date) {
+ this.date = date;
+ }
+
+ /**
+ * Sets event's time.
+ *
+ * @param time of event.
+ */
+ public void setTime(LocalTime time) {
+ this.time = time;
+ }
+
+ /**
+ * Sets event's repeatType.
+ *
+ * @param repeatType of event.
+ */
+ public void setRepeatType(String repeatType) {
+ this.repeatType = repeatType;
+ }
+
+ /**
+ * Sets event's repeatEventList.
+ *
+ * @param repeatEventList to set to.
+ */
+ public void setRepeatEventList(ArrayList repeatEventList) {
+ this.repeatEventList = repeatEventList;
+ }
+
+ /**
+ * Sets the event's done status to true.
+ */
+ public void markAsDone() {
+ this.isDone = true;
+ }
+
+ /**
+ * Sets the event's done status to false.
+ */
+ public void markAsUndone() {
+ this.isDone = false;
+ }
+
+ /**
+ * Set notes for event.
+ *
+ * @param notes for event.
+ */
+ public void setNotes(ArrayList notes) {
+ this.notes = notes;
+ }
+
+ /**
+ * Returns a string representation of event's done status.
+ *
+ * @return string representation of event's done status.
+ */
+ public String getStatus() {
+ return (isDone) ? "O" : "X";
+ }
+
+ /**
+ * Returns the event's description.
+ *
+ * @return event's description.
+ */
+ public String getDescription() {
+ return this.description;
+ }
+
+ /**
+ * Gets date of the event.
+ *
+ * @return LocalDate object containing the date of the event
+ */
+ public LocalDate getDate() {
+ return this.date;
+ }
+
+ /**
+ * Gets time of the event.
+ *
+ * @return LocalTime object containing the time of the event
+ */
+ public LocalTime getTime() {
+ return this.time;
+ }
+
+ /**
+ * Returns an ArrayList of Event containing the Events that are repeated from this Event.
+ *
+ * @return RepeatEventList as an ArrayList of Event.
+ */
+ public ArrayList getRepeatEventList() {
+ return repeatEventList;
+ }
+
+ /**
+ * Returns the repeatType of the event.
+ *
+ * @return repeatType of event.
+ */
+ public String getRepeatType() {
+ if (repeatType == null) {
+ return null;
+ } else {
+ return repeatType.toLowerCase();
+ }
+ }
+
+ /**
+ * Returns the repeatCount of the event.
+ *
+ * @return repeatCount of event.
+ */
+ public int getRepeatCount() {
+ if (repeatEventList == null) {
+ return 0;
+ }
+ return repeatEventList.size();
+ }
+
+ /**
+ * Get notes for event.
+ *
+ * @return list of notes.
+ */
+ public ArrayList getNotes() {
+ return notes;
+ }
+
+ @Override
+ public Event clone() throws CloneNotSupportedException {
+ return (Event) super.clone();
+ }
+
+ @Override
+ public String toString() {
+ return "[" + getStatus() + "] " + getDescription();
+ }
+
+ /**
+ * Returns a String representation of the event details formatted for calendar output.
+ *
+ * @return String representation of event in calendar format.
+ */
+ public String toCalendarString() {
+ return String.format("%s | ", time.format(DateTimeFormatter.ofPattern("h:mm a")))
+ + String.format("%s | ", getStatus())
+ + String.format("%s ", getDescription());
+ }
+}
diff --git a/src/main/java/seedu/duke/event/EventList.java b/src/main/java/seedu/duke/event/EventList.java
new file mode 100644
index 0000000000..e794c3ae57
--- /dev/null
+++ b/src/main/java/seedu/duke/event/EventList.java
@@ -0,0 +1,99 @@
+package seedu.duke.event;
+
+import seedu.duke.EventLogger;
+import seedu.duke.exception.InvalidIndexException;
+
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+public class EventList {
+ private String name;
+ private ArrayList events;
+ private static Logger logger = EventLogger.getEventLogger();
+
+ /**
+ * Constructor for EventList. Creates an empty EventList with specified name.
+ *
+ * @param name EventList.
+ */
+ public EventList(String name) {
+ this.name = name;
+ events = new ArrayList();
+ }
+
+ /**
+ * Constructor for EventList. Creates an EventList with the given ArrayList of Event with specified name.
+ *
+ * @param name of EventList.
+ */
+ public EventList(String name, ArrayList events) {
+ this.name = name;
+ this.events = new ArrayList(events);
+ }
+
+ /**
+ * Adds given Event to the EventList.
+ *
+ * @param event to add.
+ */
+ public void add(Event event) {
+ events.add(event);
+ }
+
+ /**
+ * Returns the name of the EventList.
+ *
+ * @return name of EventList.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the event in the EventList at the specified index.
+ *
+ * @param index of event to return.
+ * @return event at specified index.
+ * @throws InvalidIndexException if index is not valid.
+ */
+ public Event getEventByIndex(int index) throws InvalidIndexException {
+
+ try {
+ return events.get(index);
+ } catch (IndexOutOfBoundsException e) {
+ logger.warning("InvalidIndexException encountered");
+ throw new InvalidIndexException("Error, no such index is available!");
+ }
+
+
+ }
+
+ /**
+ * Returns an ArrayList of Event containing all the events in the EventList.
+ *
+ * @return all events in EventList.
+ */
+ public ArrayList getEvents() {
+ return events;
+ }
+
+ /**
+ * Returns the last event added in the EventList.
+ *
+ * @return last event added.
+ */
+ public Event getNewestEvent() {
+ return events.get(events.size() - 1);
+
+ }
+
+ /**
+ * Returns the number of events in the EventList.
+ *
+ * @return number of events.
+ */
+ public int getSize() {
+ return events.size();
+ }
+
+}
diff --git a/src/main/java/seedu/duke/event/Goal.java b/src/main/java/seedu/duke/event/Goal.java
new file mode 100644
index 0000000000..a77e06210f
--- /dev/null
+++ b/src/main/java/seedu/duke/event/Goal.java
@@ -0,0 +1,23 @@
+package seedu.duke.event;
+
+public class Goal {
+ String description;
+
+ public Goal(String description) {
+ setDescription(description);
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ /**
+ * Returns a string representation of goal.
+ *
+ * @return string representation of goal.
+ */
+ @Override
+ public String toString() {
+ return description;
+ }
+}
diff --git a/src/main/java/seedu/duke/event/Personal.java b/src/main/java/seedu/duke/event/Personal.java
new file mode 100644
index 0000000000..f8fed54381
--- /dev/null
+++ b/src/main/java/seedu/duke/event/Personal.java
@@ -0,0 +1,72 @@
+package seedu.duke.event;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+
+public class Personal extends Event {
+
+ /**
+ * Constructor for Personal Event with all details given.
+ *
+ * @param description of Personal Event.
+ * @param date of Personal Event.
+ * @param time of Personal Event.
+ * @param repeatCount of Personal Event.
+ */
+ public Personal(String description, LocalDate date, LocalTime time, int repeatCount) {
+ super(description);
+ setDate(date);
+ setTime(time);
+ }
+
+ /**
+ * Constructor for Personal Event without repeatCount.
+ *
+ * @param description of Personal Event.
+ * @param date of Personal Event.
+ * @param time of Personal Event.
+ */
+ public Personal(String description, LocalDate date, LocalTime time) {
+ this(description, date, time, 0);
+ }
+
+ /**
+ * Constructor for Personal Event with date.
+ *
+ * @param description of Personal Event.
+ * @param date of Personal Event.
+ */
+ public Personal(String description, LocalDate date) {
+ this(description, date, null, 0);
+ }
+
+ /**
+ * Constructor for Personal Event with description only.
+ *
+ * @param description of Personal Event.
+ */
+ public Personal(String description) {
+ this(description, null, null);
+ }
+
+ @Override
+ public Personal clone() throws CloneNotSupportedException {
+ return (Personal) super.clone();
+ }
+
+ @Override
+ public String toString() {
+ if (time != null && date != null) {
+ return "[P]" + super.toString() + " on " + date + ", " + time;
+ } else if (time == null && date != null) {
+ return "[P]" + super.toString() + " on " + date;
+ } else {
+ return "[P]" + super.toString();
+ }
+ }
+
+ @Override
+ public String toCalendarString() {
+ return "P | " + super.toCalendarString();
+ }
+}
diff --git a/src/main/java/seedu/duke/event/Timetable.java b/src/main/java/seedu/duke/event/Timetable.java
new file mode 100644
index 0000000000..2b675ee9ff
--- /dev/null
+++ b/src/main/java/seedu/duke/event/Timetable.java
@@ -0,0 +1,71 @@
+package seedu.duke.event;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+
+public class Timetable extends Event {
+ protected String location;
+
+ /**
+ * Constructor for Timetable Event with all details given.
+ *
+ * @param description of Timetable Event.
+ * @param location of Timetable Event.
+ * @param date of Timetable Event.
+ * @param time of Timetable Event.
+ */
+ public Timetable(String description, String location, LocalDate date, LocalTime time) {
+ super(description);
+ setLocation(location);
+ setDate(date);
+ setTime(time);
+ }
+
+ /**
+ * Constructor for Timetable Event without location.
+ *
+ * @param description of Timetable Event.
+ * @param date of Timetable Event.
+ * @param time of Timetable Event.
+ */
+ public Timetable(String description, LocalDate date, LocalTime time) {
+ this(description, null, date, time);
+ }
+
+ /**
+ * Sets the location of the Timetable Event.
+ *
+ * @param location of Timetable Event.
+ */
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ /**
+ * Returns the location of the Timetable Event.
+ *
+ * @return location of Timetable Event.
+ */
+ public String getLocation() {
+ return this.location;
+ }
+
+ @Override
+ public Timetable clone() throws CloneNotSupportedException {
+ return (Timetable) super.clone();
+ }
+
+ @Override
+ public String toString() {
+ if (location != null) {
+ return "[T]" + super.toString() + ", Location: " + location + " on " + date + ", " + time;
+ } else {
+ return "[T]" + super.toString() + " on " + date + ", " + time;
+ }
+ }
+
+ @Override
+ public String toCalendarString() {
+ return "T | " + super.toCalendarString() + String.format("| %s", getLocation());
+ }
+}
diff --git a/src/main/java/seedu/duke/event/Zoom.java b/src/main/java/seedu/duke/event/Zoom.java
new file mode 100644
index 0000000000..60724b5859
--- /dev/null
+++ b/src/main/java/seedu/duke/event/Zoom.java
@@ -0,0 +1,70 @@
+package seedu.duke.event;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+
+public class Zoom extends Event {
+ protected String zoomLink;
+
+ /**
+ * Constructor for Zoom Event with all details given.
+ *
+ * @param description of Zoom Event.
+ * @param zoomLink of Zoom Event.
+ * @param date of Zoom Event.
+ * @param time of Zoom Event.
+ */
+ public Zoom(String description, String zoomLink, LocalDate date, LocalTime time) {
+ super(description);
+ setZoomLink(zoomLink);
+ setDate(date);
+ setTime(time);
+ }
+
+ /**
+ * Constructor for Zoom Event without date and time.
+ *
+ * @param description of Zoom Event.
+ * @param zoomLink of Zoom Event.
+ */
+ public Zoom(String description, String zoomLink) {
+ this(description, zoomLink, null, null);
+ }
+
+ /**
+ * Sets the link of the Zoom Event.
+ *
+ * @param zoomLink of Zoom Event.
+ */
+ public void setZoomLink(String zoomLink) {
+ this.zoomLink = zoomLink;
+ }
+
+ /**
+ * Returns the link of the Zoom Event.
+ *
+ * @return link of Zoom Event.
+ */
+ public String getZoomLink() {
+ return this.zoomLink;
+ }
+
+ @Override
+ public Zoom clone() throws CloneNotSupportedException {
+ return (Zoom) super.clone();
+ }
+
+ @Override
+ public String toString() {
+ if (time != null && date != null) {
+ return "[Z]" + super.toString() + ", Link: " + zoomLink + " on " + date + ", " + time;
+ } else {
+ return "[Z]" + super.toString() + ", Link: " + zoomLink;
+ }
+ }
+
+ @Override
+ public String toCalendarString() {
+ return "Z | " + super.toCalendarString() + String.format("| %s", getZoomLink());
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/DateErrorException.java b/src/main/java/seedu/duke/exception/DateErrorException.java
new file mode 100644
index 0000000000..3e7c2bae9f
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/DateErrorException.java
@@ -0,0 +1,10 @@
+package seedu.duke.exception;
+
+public class DateErrorException extends DukeException {
+
+ public DateErrorException() {
+ super("Something is wrong with the date!" + System.lineSeparator()
+ + "The accepted formats are: d/M/yyyy, M/yyyy or yyyy. yyyy can be shortened to yy."
+ + System.lineSeparator() + "Dashes may be used in place of slashes.");
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/DukeException.java b/src/main/java/seedu/duke/exception/DukeException.java
new file mode 100644
index 0000000000..6dfcbcd57d
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/DukeException.java
@@ -0,0 +1,18 @@
+package seedu.duke.exception;
+
+public class DukeException extends Exception {
+ public String message;
+
+ public DukeException(String message) {
+ this.message = message;
+ }
+
+ public void printErrorMessage() {
+ System.out.println(message);
+ }
+
+ @Override
+ public String getMessage() {
+ return message;
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/EventAddErrorException.java b/src/main/java/seedu/duke/exception/EventAddErrorException.java
new file mode 100644
index 0000000000..ffd95145fd
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/EventAddErrorException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exception;
+
+public class EventAddErrorException extends DukeException {
+ public EventAddErrorException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/InvalidEventDateException.java b/src/main/java/seedu/duke/exception/InvalidEventDateException.java
new file mode 100644
index 0000000000..939817e54d
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/InvalidEventDateException.java
@@ -0,0 +1,8 @@
+package seedu.duke.exception;
+
+public class InvalidEventDateException extends DukeException {
+
+ public InvalidEventDateException() {
+ super("This event does not occur on this date.");
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/InvalidExtractCommandException.java b/src/main/java/seedu/duke/exception/InvalidExtractCommandException.java
new file mode 100644
index 0000000000..890cfff985
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/InvalidExtractCommandException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exception;
+
+public class InvalidExtractCommandException extends DukeException {
+ public InvalidExtractCommandException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/InvalidHelpTopicException.java b/src/main/java/seedu/duke/exception/InvalidHelpTopicException.java
new file mode 100644
index 0000000000..fc45725a7d
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/InvalidHelpTopicException.java
@@ -0,0 +1,8 @@
+package seedu.duke.exception;
+
+public class InvalidHelpTopicException extends DukeException {
+
+ public InvalidHelpTopicException(String topic) {
+ super(topic + " is not a valid help command. Type help to see a list of all available help topics");
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/InvalidIndexException.java b/src/main/java/seedu/duke/exception/InvalidIndexException.java
new file mode 100644
index 0000000000..7b07dc64e6
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/InvalidIndexException.java
@@ -0,0 +1,8 @@
+package seedu.duke.exception;
+
+public class InvalidIndexException extends DukeException {
+
+ public InvalidIndexException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/InvalidListException.java b/src/main/java/seedu/duke/exception/InvalidListException.java
new file mode 100644
index 0000000000..150c88e57f
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/InvalidListException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exception;
+
+public class InvalidListException extends DukeException {
+ public InvalidListException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/InvalidStatusException.java b/src/main/java/seedu/duke/exception/InvalidStatusException.java
new file mode 100644
index 0000000000..c083879621
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/InvalidStatusException.java
@@ -0,0 +1,8 @@
+package seedu.duke.exception;
+
+public class InvalidStatusException extends DukeException {
+
+ public InvalidStatusException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/InvalidTimePeriodException.java b/src/main/java/seedu/duke/exception/InvalidTimePeriodException.java
new file mode 100644
index 0000000000..256d9ace6d
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/InvalidTimePeriodException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exception;
+
+public class InvalidTimePeriodException extends DukeException {
+ public InvalidTimePeriodException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/InvalidTimeUnitException.java b/src/main/java/seedu/duke/exception/InvalidTimeUnitException.java
new file mode 100644
index 0000000000..8032f435f7
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/InvalidTimeUnitException.java
@@ -0,0 +1,9 @@
+package seedu.duke.exception;
+
+
+public class InvalidTimeUnitException extends DukeException {
+
+ public InvalidTimeUnitException(String wrongTimeUnit) {
+ super(wrongTimeUnit + " is not a valid time unit. Valid types are: daily, weekly, monthly");
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/MissingDeadlineRepeatException.java b/src/main/java/seedu/duke/exception/MissingDeadlineRepeatException.java
new file mode 100644
index 0000000000..bc9f3b38d6
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/MissingDeadlineRepeatException.java
@@ -0,0 +1,10 @@
+package seedu.duke.exception;
+
+public class MissingDeadlineRepeatException extends DukeException {
+
+
+ public MissingDeadlineRepeatException() {
+ super("Error! You cannot repeat an event that has no deadline!");
+
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/MissingDescriptionException.java b/src/main/java/seedu/duke/exception/MissingDescriptionException.java
new file mode 100644
index 0000000000..abc81c5450
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/MissingDescriptionException.java
@@ -0,0 +1,8 @@
+package seedu.duke.exception;
+
+public class MissingDescriptionException extends DukeException {
+
+ public MissingDescriptionException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/MissingRepeatListException.java b/src/main/java/seedu/duke/exception/MissingRepeatListException.java
new file mode 100644
index 0000000000..fb387489f4
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/MissingRepeatListException.java
@@ -0,0 +1,9 @@
+package seedu.duke.exception;
+
+public class MissingRepeatListException extends DukeException {
+
+ public MissingRepeatListException() {
+ super("This event has not been repeated currently");
+ }
+
+}
diff --git a/src/main/java/seedu/duke/exception/MissingSemicolonException.java b/src/main/java/seedu/duke/exception/MissingSemicolonException.java
new file mode 100644
index 0000000000..a3614cf699
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/MissingSemicolonException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exception;
+
+public class MissingSemicolonException extends DukeException {
+ public MissingSemicolonException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/NumberOverflowException.java b/src/main/java/seedu/duke/exception/NumberOverflowException.java
new file mode 100644
index 0000000000..41a251baca
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/NumberOverflowException.java
@@ -0,0 +1,8 @@
+package seedu.duke.exception;
+
+public class NumberOverflowException extends DukeException {
+
+ public NumberOverflowException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/TimeErrorException.java b/src/main/java/seedu/duke/exception/TimeErrorException.java
new file mode 100644
index 0000000000..1aa6fe70cd
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/TimeErrorException.java
@@ -0,0 +1,10 @@
+package seedu.duke.exception;
+
+public class TimeErrorException extends DukeException {
+ public TimeErrorException() {
+ super("Something is wrong with the time!" + System.lineSeparator()
+ + "The accepted formats are:" + System.lineSeparator()
+ + "(12 hour) hh:mm am/pm, hhmm am/pm, hh am/pm or " + System.lineSeparator()
+ + "(24 hour) HH:mm, HHmm, HH.");
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/TryRegularParserException.java b/src/main/java/seedu/duke/exception/TryRegularParserException.java
new file mode 100644
index 0000000000..331f62bdd0
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/TryRegularParserException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exception;
+
+public class TryRegularParserException extends DukeException {
+ public TryRegularParserException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/WrongNumberFormatException.java b/src/main/java/seedu/duke/exception/WrongNumberFormatException.java
new file mode 100644
index 0000000000..3397e1c92e
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/WrongNumberFormatException.java
@@ -0,0 +1,8 @@
+package seedu.duke.exception;
+
+public class WrongNumberFormatException extends DukeException {
+
+ public WrongNumberFormatException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/WrongNumberOfArgumentsException.java b/src/main/java/seedu/duke/exception/WrongNumberOfArgumentsException.java
new file mode 100644
index 0000000000..ef29f4c2c1
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/WrongNumberOfArgumentsException.java
@@ -0,0 +1,13 @@
+package seedu.duke.exception;
+
+public class WrongNumberOfArgumentsException extends DukeException {
+
+ private String message;
+
+ public WrongNumberOfArgumentsException(String errorMessage) {
+
+ super(errorMessage);
+ }
+
+
+}
diff --git a/src/main/java/seedu/duke/parser/DateTimeParser.java b/src/main/java/seedu/duke/parser/DateTimeParser.java
new file mode 100644
index 0000000000..fa1c56c41c
--- /dev/null
+++ b/src/main/java/seedu/duke/parser/DateTimeParser.java
@@ -0,0 +1,69 @@
+package seedu.duke.parser;
+
+import seedu.duke.exception.DateErrorException;
+import seedu.duke.exception.TimeErrorException;
+
+import java.time.DateTimeException;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+
+public class DateTimeParser {
+ private static final String[] dateFormatStrings = {"yyyy/M/d", "yyyy-M-d", "d/M/yy",
+ "d/M/yyyy", "d-M-yy", "d-M-yyyy"};
+ private static final String[] timeFormatStrings = {"h:m a", "hhmm a", "H:m", "HHmm"};
+
+ /**
+ * Parses the given date through the acceptable formats.
+ * Acceptable date format is dd/mm/yy, yyyy/mm/dd, slashes can be replace with dashes.
+ *
+ * @param date given to parse.
+ * @return LocalDate object containing given date.
+ * @throws DateErrorException if date format is not accepted.
+ */
+ public static LocalDate dateParser(String date) throws DateErrorException {
+ assert date != null : "date cannot be null";
+ int i = 0;
+ while (i < dateFormatStrings.length) {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormatStrings[i]);
+ try {
+ return LocalDate.parse(date, formatter);
+ } catch (DateTimeException e) {
+ i++;
+ }
+ }
+
+ throw new DateErrorException();
+ }
+
+ /**
+ * Parses the given time through the acceptable formats.
+ * Acceptable time format is both 12 hour and 24 hour, omitting colon requires the format hhmm.
+ *
+ * @param time given to parse.
+ * @return LocalTime object containing the given time.
+ * @throws TimeErrorException if time format is not accepted.
+ */
+ public static LocalTime timeParser(String time) throws TimeErrorException {
+ assert time != null : "time cannot be null";
+ time = time.toUpperCase();
+ if (time.contains("PM") || time.contains("AM")) {
+ if (!time.contains(" ")) {
+ time = time.substring(0, time.length() - 2) + " " + time.substring(time.length() - 2);
+ }
+ if (!time.contains(":")) {
+ time = time.substring(0, time.length() - 3) + ":00 " + time.substring(time.length() - 2);
+ }
+ }
+ int i = 0;
+ while (i < timeFormatStrings.length) {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern(timeFormatStrings[i]);
+ try {
+ return LocalTime.parse(time, formatter);
+ } catch (DateTimeException e) {
+ i++;
+ }
+ }
+ throw new TimeErrorException();
+ }
+}
diff --git a/src/main/java/seedu/duke/parser/Parser.java b/src/main/java/seedu/duke/parser/Parser.java
new file mode 100644
index 0000000000..5589c66cc0
--- /dev/null
+++ b/src/main/java/seedu/duke/parser/Parser.java
@@ -0,0 +1,134 @@
+package seedu.duke.parser;
+
+
+import seedu.duke.command.AddCommand;
+import seedu.duke.command.ByeCommand;
+import seedu.duke.command.CalendarCommand;
+import seedu.duke.command.CheckCommand;
+import seedu.duke.command.Command;
+import seedu.duke.command.DeadlineCommand;
+import seedu.duke.command.DeleteCommand;
+import seedu.duke.command.DoneCommand;
+import seedu.duke.command.ExtractCommand;
+import seedu.duke.command.GoalCommand;
+import seedu.duke.command.HelpCommand;
+import seedu.duke.command.ListCommand;
+import seedu.duke.command.NoteCommand;
+import seedu.duke.command.ReminderCommand;
+import seedu.duke.command.RepeatCommand;
+import seedu.duke.command.SaveCommand;
+import seedu.duke.command.UndoneCommand;
+import seedu.duke.command.ViewCommand;
+import seedu.duke.exception.DukeException;
+
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class Parser {
+
+ /**
+ * Default Constructor for parser.
+ */
+ public Parser() {
+
+ }
+
+ /**
+ * Function takes in user input and then generates the correct command requested from the user input.
+ *
+ * @param userInput String containing what is typed by the user on the computer
+ * @return Command from what is written by user. Return null if incorrect command
+ */
+ public Command parse(String userInput) throws DukeException {
+
+ String[] words = userInput.split(" ");
+
+ if (words.length == 0) {
+
+ System.out.println("Error, no command entered");
+ return null;
+ }
+
+ String commandWord = words[0];
+ String[] argumentWords = Arrays.copyOfRange(words, 1, words.length);
+ String argument = String.join(" ", argumentWords);
+
+ switch (commandWord) {
+ case "add":
+ return new AddCommand(argument);
+
+ case "list":
+ return ListCommand.parse(argument);
+
+ case "bye":
+ return new ByeCommand();
+
+ case "check":
+ return new CheckCommand(argument + " ");
+
+ case "repeat":
+ return RepeatCommand.parse(argument);
+
+ case "goal":
+ return new GoalCommand(argument);
+
+ case "deadline":
+ return new DeadlineCommand(argument);
+
+ case "reminder":
+ return new ReminderCommand();
+
+ case "done":
+ return DoneCommand.parse(argument);
+
+ case "undone":
+ return UndoneCommand.parse(argument);
+
+ case "delete":
+ return DeleteCommand.parse(argument);
+
+ case "calendar":
+ return CalendarCommand.parse(argument);
+
+ case "help":
+ return new HelpCommand(argument);
+
+ case "extract":
+ return new ExtractCommand(argument);
+
+ case "note":
+ return new NoteCommand(argument);
+
+ case "save":
+ return new SaveCommand();
+
+ case "view":
+ return new ViewCommand(argument);
+
+ default:
+ throw new DukeException("Invalid Command.");
+ }
+ }
+
+ /**
+ * Function helps to split user inputs into separate command inputs.
+ * Used to allow users to key in more than one command at a time.
+ *
+ * @param userInput String separated by pipe characters to indicate separate commands
+ * @return String ArrayList containing the command inputs as if they were typed separately
+ */
+ public ArrayList multiParse(String userInput) {
+
+ String[] commandInputs;
+ commandInputs = userInput.split("\\|", -1);
+ ArrayList inputList = new ArrayList<>();
+
+ for (String commandInput : commandInputs) {
+ commandInput = commandInput.trim();
+ inputList.add(commandInput);
+ }
+ return inputList;
+ }
+
+}
diff --git a/src/main/java/seedu/duke/storage/Storage.java b/src/main/java/seedu/duke/storage/Storage.java
new file mode 100644
index 0000000000..49ced4aeaa
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/Storage.java
@@ -0,0 +1,344 @@
+package seedu.duke.storage;
+
+import seedu.duke.EventLogger;
+import seedu.duke.data.UserData;
+import seedu.duke.event.Event;
+import seedu.duke.event.EventList;
+import seedu.duke.event.Goal;
+import seedu.duke.exception.InvalidListException;
+import seedu.duke.ui.Ui;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.FileHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Saves and loads the events list to and from an external txt file.
+ */
+public class Storage {
+
+ private Path fileDirectoryPath;
+ private Path filePersonalPath;
+ private Path fileZoomPath;
+ private Path fileTimeTablePath;
+ private Path fileGoalPath;
+
+ private Ui ui;
+ private static Logger logger = EventLogger.getEventLogger();
+
+ /**
+ * Returns the path to the file specified by the user.
+ *
+ * @param name Name of the event type stored in the file that will be returned
+ * @return path to the file storing the event type.
+ */
+ public Path getFileLocation(String name) {
+ switch (name) {
+ case "Personal":
+ return filePersonalPath;
+ case "Zoom":
+ return fileZoomPath;
+ case "Timetable":
+ return fileTimeTablePath;
+ case "Goal":
+ return fileGoalPath;
+ default:
+ ui.printErrorMessage("Error! No such file exists");
+ return null;
+
+ }
+ }
+
+ /**
+ * Creates a new storage manager that can load and save files to and from a given filepath.
+ *
+ * @param initPath is the name of the filepath which files are saved to and loaded from
+ */
+ public Storage(String initPath, Ui ui) {
+
+ //firstly, make string representation of storage files
+ //Directory words only contain info on making the folder
+ //File words contain the info on how to make the file itself
+
+
+ String[] pathDirectoryWords = initPath.split(",");
+ fileDirectoryPath = createPath(pathDirectoryWords);
+
+ String personal = initPath + ",personal.txt";
+ String[] personalWords = personal.split(",");
+ filePersonalPath = createPath(personalWords);
+
+ String zoom = initPath + ",zoom.txt";
+ String[] zoomWords = zoom.split(",");
+ fileZoomPath = createPath(zoomWords);
+
+ String goal = initPath + ",goal.txt";
+ String[] goalWords = goal.split(",");
+ fileGoalPath = createPath(goalWords);
+
+ String timeTable = initPath + ",timetable.txt";
+ String[] timeTableWords = timeTable.split(",");
+ fileTimeTablePath = createPath(timeTableWords);
+
+ initialiseFolder();
+ this.ui = ui;
+
+ }
+
+ /**
+ * Creates a folder for the events list if it does not exist yet.
+ */
+ protected void initialiseFolder() {
+ if (!Files.exists(fileDirectoryPath)) {
+ try {
+ Files.createDirectory(fileDirectoryPath);
+ logger.fine("Data folder was created on the computer successfully.");
+ } catch (IOException e) {
+ System.out.println("IO exception encountered when creating data directory.");
+ logger.severe("Data folder failed to be created on the computer.");
+ }
+ }
+ }
+
+ /**
+ * Creates a txt file for the event if it does not exists yet.
+ *
+ * @param fileText path object of the file to be created if non-existent
+ * @param fileType String of the type of event data this file will store
+ */
+ private void initialiseFile(Path fileText, String fileType) {
+
+ if (!Files.exists(fileText)) {
+ try {
+ Files.createFile(fileText);
+ System.out.println("File Created: " + fileType);
+ logger.fine("File " + fileType + " was created on the computer successfully.");
+ } catch (IOException e) {
+ ui.printErrorMessage("IO exception error! File cannot be created on system!");
+ logger.severe("File " + fileType + " encountered an error while being produced");
+ }
+ }
+ }
+
+ /**
+ * Saves All information onto the computer.
+ *
+ * @param data UserData object where all the current user event information is stored
+ */
+ public void saveAll(UserData data) {
+ saveFile(filePersonalPath, data, "Personal");
+ saveFile(fileTimeTablePath, data, "Timetable");
+ saveFile(fileZoomPath, data, "Zoom");
+ saveFile(fileGoalPath, data, "Goal");
+
+ }
+
+ /**
+ * Saves information to a specified file.
+ *
+ * @param fileName Path object of the file for information to be saved currently
+ * @param data UserData object where all the current user event information is stored
+ * @param fileType Name of the information type that will be saved.
+ */
+ public void saveFile(Path fileName, UserData data, String fileType) {
+
+ try {
+ //firstly, form a temporary List of strings to store the data
+ ArrayList toBeWritten = new ArrayList<>();
+
+ if (fileType.equals("Goal")) { //special case for goal
+ goalSave(fileName, data, toBeWritten);
+ } else { //special case for event
+ eventSave(fileName, data, fileType, toBeWritten);
+
+ }
+ logger.fine(fileType + " saved successfully");
+
+
+
+ } catch (InvalidListException e) {
+ System.out.println("Error! List invalid type. Should not happen");
+ } catch (IOException e) {
+ ui.printErrorMessage("Error! File cannot be written to");
+ logger.severe("File " + fileType + " could not be saved due to IO error!");
+ }
+
+ }
+
+ /**
+ * Helper function for saving event information.
+ *
+ * @param fileName location on the computer where the event data is to be saved at
+ * @param data UserData object containing all user information currently stored in the program
+ * @param fileType String indicating what type of event we are storing
+ * @param toBeWritten String ArrayList object which stores all test to be written to computer fileName
+ *
+ * @throws InvalidListException if there is no such event type stored in the program
+ * @throws IOException if there are problems encountered while writing to the file
+ */
+ private void eventSave(Path fileName, UserData data, String fileType, ArrayList toBeWritten)
+ throws InvalidListException, IOException {
+ //next, read out event by event and process it into a storable string
+ EventList listOfEvents = data.getEventList(fileType);
+ ArrayList events = listOfEvents.getEvents();
+
+ for (Event event:events) {
+ String entry = StorageParser.eventToString(event, fileType);
+ toBeWritten.add(entry);
+ logger.fine("\"" + entry + "\" added to stuff to be written");
+ }
+
+ Files.write(fileName, toBeWritten);
+ logger.fine("All changes recorded successfully to " + fileType + " save file");
+ }
+
+ /**
+ * Helper function for saving goal information.
+ *
+ * @param fileName path of the goal.txt file to save goal information
+ * @param data UserData object containing all user information currently stored in the program
+ * @param toBeWritten String ArrayList object which stores all test to be written to computer fileName
+ * @throws IOException if there are problems encountered while writing to the file
+ */
+ private void goalSave(Path fileName, UserData data, ArrayList toBeWritten) throws IOException {
+ Goal entry = data.getGoal();
+ if (entry != null) {
+ toBeWritten.add(entry.toString());
+ logger.fine("\"" + entry + "\" added to stuff to be written");
+ Files.write(fileName, toBeWritten);
+ } else { //nothing to modify in toBeWritten, so write a blank file
+ Files.write(fileName, toBeWritten);
+ }
+ logger.fine("All changes recorded successfully to goal save file");
+
+ }
+
+ /**
+ * Loads every single data file into the program.
+ *
+ * @param data UserData structure with all the user information stored
+ */
+ public void loadAll(UserData data) {
+ loadFile(filePersonalPath, data, "Personal");
+ loadFile(fileZoomPath, data, "Zoom");
+ loadFile(fileTimeTablePath, data, "Timetable");
+ loadFile(fileGoalPath, data, "Goal");
+
+ ui.printStorageLoadMessage();
+
+ }
+
+ /**
+ * Loads events from an external txt file.
+ *
+ * @param fileName is the file to load events from
+ * @throws FileNotFoundException if no file with the given fileName is found
+ */
+ public void loadFile(Path fileName, UserData data, String fileType) {
+
+ int totalEntries = 0;
+ int errorEntries = 0;
+ try {
+
+ //First, extract out all the file information
+ this.initialiseFile(fileName, fileType);
+ List fileLines = Files.readAllLines(fileName);
+
+ //Next, line by line reform the event
+
+ //Extraction of goal
+ if (fileType.equals("Goal")) {
+ if (fileLines.size() != 0) {
+ Goal prevGoal = new Goal(fileLines.get(0));
+ data.setGoal(prevGoal);
+ logger.fine("Goal file loaded");
+ }
+ return;
+ }
+
+ //extraction of all other events
+ for (int i = 0; i < fileLines.size(); i++) {
+ totalEntries++;
+ String line = fileLines.get(i);
+ Event activity = StorageParser.stringToEvent(line, fileType, ui);
+ if (activity == null) {
+ errorEntries++;
+ continue;
+ }
+ data.addToEventList(fileType, activity);
+ logger.fine(fileType + " information and events successfully loaded");
+ }
+
+ ui.printMessage(Integer.toString(totalEntries - errorEntries) + "/" + Integer.toString(totalEntries)
+ + " loaded successfully for " + fileType);
+
+ //finally, store the information in the correct list
+ } catch (IOException e) {
+ //do nothing
+ ui.printStorageLoadingErrorMessage();
+ logger.warning(fileType + " was not loaded completely successfully");
+ } catch (InvalidListException e) {
+ //do nothing for now
+ System.out.println("Error, invalid list");
+ logger.warning(fileType + " is not a event type that exists");
+ }
+ }
+
+
+ /**
+ * Function accepts a string and creates a path object originating from the user directory.
+ *
+ * @param pathName is a string array which accepts in the path name words, each word represents a folder
+ * @return Path object indicating the location of the pathName keyed in initially.
+ */
+ private Path createPath(String[] pathName) {
+
+ String origin = System.getProperty("user.dir");
+ Path newPath = Paths.get(origin, pathName);
+ return newPath;
+ }
+
+ /**
+ * Function gives a string containing the Directory location.
+ *
+ * @return String containing directory location
+ */
+ public String getDirectoryString() {
+ return fileDirectoryPath.toString();
+ }
+
+ /**
+ * Function loads files that are meant to be system resources such as images or text files.
+ *
+ * @param fileName location of the file in string form. Start from the root using the slash symbol
+ * @param resource String ArrayList to store the information
+ */
+ public void loadSystemResources(String fileName, ArrayList resource) {
+ try {
+ InputStream is = getClass().getResourceAsStream(fileName);
+ InputStreamReader streamReader = new InputStreamReader(is, StandardCharsets.UTF_8);
+ BufferedReader reader = new BufferedReader(streamReader);
+ String line;
+
+ while ((line = reader.readLine()) != null) {
+ resource.add(line);
+ }
+ logger.fine(fileName + " could be read and has been loaded properly");
+ } catch (IOException e) {
+ ui.printErrorMessage("Resource file could not be loaded!");
+ logger.severe("There was an error when attempting to load" + fileName);
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/duke/storage/StorageParser.java b/src/main/java/seedu/duke/storage/StorageParser.java
new file mode 100644
index 0000000000..c40ebc80c3
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/StorageParser.java
@@ -0,0 +1,497 @@
+package seedu.duke.storage;
+
+import seedu.duke.EventLogger;
+import seedu.duke.event.Event;
+import seedu.duke.event.Personal;
+import seedu.duke.event.Timetable;
+import seedu.duke.event.Zoom;
+import seedu.duke.exception.InvalidStatusException;
+import seedu.duke.exception.InvalidTimeUnitException;
+import seedu.duke.parser.DateTimeParser;
+import seedu.duke.ui.Ui;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.logging.Logger;
+
+public class StorageParser {
+
+ private static Logger logger = EventLogger.getEventLogger();
+
+ /**
+ * Function helps to convert any event to a recordable string format to be written to txt file.
+ *
+ * @param activity Event object to be convert to a string representation
+ * @param type What kind of event the activity is
+ * @return String representation to be recorded into the txt files
+ */
+ public static String eventToString(Event activity, String type) {
+
+ ArrayList words = new ArrayList<>();
+
+ switch (type) {
+
+ case "Personal":
+ personalToArguments((Personal) activity, words);
+ break;
+ case "Zoom":
+ zoomToArguments((Zoom) activity, words);
+ break;
+ case "Timetable":
+ timetableToArguments((Timetable) activity, words);
+ break;
+ default:
+ System.out.println("Error, wrong data type provided");
+ break;
+ }
+
+ return String.join(" | ", words);
+
+ }
+
+ /**
+ * Function used for recording down statuses of repeated events.
+ *
+ * @param statuses Location of where the list of statuses will be recorded
+ * @param activities The list of repeated activities
+ * @param repeatCount number of repetitions present
+ */
+ public static void handleRepeatSave(ArrayList statuses, ArrayList activities, int repeatCount) {
+
+ for (int i = 0; i < repeatCount; i++) {
+ Boolean repeatedActivityStatus = activities.get(i).getStatus().equals("O");
+ statuses.add(Boolean.toString(repeatedActivityStatus));
+ }
+ }
+
+ /**
+ * Converts a personal event properties into strings.
+ *
+ * @param activity Personal event to be converted
+ * @param words Array List of strings that the arguments will be recorded to
+ */
+ public static void personalToArguments(Personal activity, ArrayList words) {
+
+ ArrayList statuses = new ArrayList<>();
+
+
+ //obtain the dates and time of the event
+ String date = "0";
+ String time = "0";
+ if (activity.getDate() != null) {
+ date = activity.getDate().toString();
+ }
+ if (activity.getTime() != null) {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HHmm");
+ time = activity.getTime().format(formatter);
+ }
+
+ Boolean activityStatus = activity.getStatus().equals("O");
+ statuses.add(Boolean.toString(activityStatus));
+ String repeatUnit = "0";
+ String repeatNumber = "0";
+ if (activity.getRepeatCount() != 0) { //it is a repeated activity
+ repeatUnit = activity.getRepeatType().toUpperCase();
+ repeatNumber = Integer.toString(activity.getRepeatCount());
+ ArrayList activitiesRepeated = activity.getRepeatEventList();
+
+ handleRepeatSave(statuses, activitiesRepeated, activity.getRepeatCount());
+
+ }
+
+ //obtain the name of the event
+ String name = activity.getDescription();
+ words.add(name);
+ words.add(date);
+ words.add(time);
+ words.add(repeatUnit);
+ words.add(repeatNumber);
+ String noteString = notesListToString(activity.getNotes());
+ words.add(noteString);
+ for (int i = 0; i < statuses.size(); i++) {
+ boolean isDone = Boolean.parseBoolean(statuses.get(i));
+ if (isDone) {
+ words.add("T");
+ } else {
+ words.add("F");
+ }
+ }
+
+ }
+
+ /**
+ * Converts a Zoom event properties into strings.
+ *
+ * @param activity Personal event to be converted
+ * @param words Array List of strings that the arguments will be recorded to
+ */
+ public static void zoomToArguments(Zoom activity, ArrayList words) {
+
+ ArrayList statuses = new ArrayList<>();
+
+
+ //obtain the dates and time of the event
+ String date = "0";
+ String time = "0";
+ if (activity.getDate() != null) {
+ date = activity.getDate().toString();
+ }
+ if (activity.getTime() != null) {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HHmm");
+ time = activity.getTime().format(formatter);
+ }
+
+ Boolean activityStatus = activity.getStatus().equals("O");
+ statuses.add(Boolean.toString(activityStatus));
+ String repeatUnit = "0";
+ String repeatNumber = "0";
+ if (activity.getRepeatCount() != 0) { //it is a repeated activity
+ repeatUnit = activity.getRepeatType().toUpperCase();
+ repeatNumber = Integer.toString(activity.getRepeatCount());
+ ArrayList activitiesRepeated = activity.getRepeatEventList();
+
+ handleRepeatSave(statuses, activitiesRepeated, activity.getRepeatCount());
+
+ }
+
+ String url = activity.getZoomLink();
+ //obtain the name of the event
+ String name = activity.getDescription();
+ words.add(name);
+ words.add(date);
+ words.add(time);
+ words.add(repeatUnit);
+ words.add(repeatNumber);
+ words.add(url);
+ String noteString = notesListToString(activity.getNotes());
+ words.add(noteString);
+ for (int i = 0; i < statuses.size(); i++) {
+ boolean isDone = Boolean.parseBoolean(statuses.get(i));
+ if (isDone) {
+ words.add("T");
+ } else {
+ words.add("F");
+ }
+ }
+
+ }
+
+ /**
+ * Converts a timetable event properties into strings.
+ *
+ * @param activity timetable event to be converted
+ * @param words Array List of strings that the arguments will be recorded to
+ */
+ public static void timetableToArguments(Timetable activity, ArrayList words) {
+
+ ArrayList statuses = new ArrayList<>();
+
+
+ //obtain the dates and time of the event
+ String date = "0";
+ String time = "0";
+ if (activity.getDate() != null) {
+ date = activity.getDate().toString();
+ }
+ if (activity.getTime() != null) {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HHmm");
+ time = activity.getTime().format(formatter);
+ }
+
+ Boolean activityStatus = activity.getStatus().equals("O");
+ statuses.add(Boolean.toString(activityStatus));
+ String repeatUnit = "0";
+ String repeatNumber = "0";
+ if (activity.getRepeatCount() != 0) { //it is a repeated activity
+ repeatUnit = activity.getRepeatType().toUpperCase();
+ repeatNumber = Integer.toString(activity.getRepeatCount());
+ ArrayList activitiesRepeated = activity.getRepeatEventList();
+
+ handleRepeatSave(statuses, activitiesRepeated, activity.getRepeatCount());
+
+ }
+
+ String location = activity.getLocation();
+ if (location == null) {
+ location = "";
+ }
+ //obtain the name of the event
+ String name = activity.getDescription();
+ words.add(name);
+ words.add(date);
+ words.add(time);
+ words.add(repeatUnit);
+ words.add(repeatNumber);
+ words.add(location);
+ String noteString = notesListToString(activity.getNotes());
+ words.add(noteString);
+
+ for (int i = 0; i < statuses.size(); i++) {
+ boolean isDone = Boolean.parseBoolean(statuses.get(i));
+ if (isDone) {
+ words.add("T");
+ } else {
+ words.add("F");
+ }
+ }
+
+ }
+
+ /**
+ * Converts a string into the proper event.
+ *
+ * @param line text from the txt file
+ * @param type what kind of event we are reconstructing
+ * @param ui UserInterface for printing things such as error messages
+ * @return Event object constructed by the txt file
+ */
+ public static Event stringToEvent(String line, String type, Ui ui) {
+ String[] words = line.split("\\|");
+ String[] statuses;
+ String[] info;
+ String[] notes;
+
+ try {
+ for (int i = 0; i < words.length; i++) {
+ words[i] = words[i].trim();
+ }
+ switch (type) {
+ case "Personal":
+ info = Arrays.copyOfRange(words, 0, 5);
+ notes = Arrays.copyOfRange(words, 5, 6);
+ statuses = Arrays.copyOfRange(words, 6, words.length);
+ return makePersonal(info, statuses, notes);
+ case "Zoom":
+ info = Arrays.copyOfRange(words, 0, 6);
+ notes = Arrays.copyOfRange(words, 6, 7);
+ statuses = Arrays.copyOfRange(words, 7, words.length);
+ return makeZoom(info, statuses, notes);
+ case "Timetable":
+ info = Arrays.copyOfRange(words, 0, 6);
+ notes = Arrays.copyOfRange(words, 6, 7);
+ statuses = Arrays.copyOfRange(words, 7, words.length);
+ return makeTimetable(info, statuses, notes);
+ default:
+ return null;
+ }
+ } catch (Exception e) {
+ logger.warning("Event not loaded due to data corruption.");
+ }
+ return null;
+ }
+
+ /**
+ * Creates a personal event from a string of arguments.
+ *
+ * @param info Array of strings containing all the event information in text form
+ * @param statuses Array of strings containing statuses of repeated event if any
+ * @param notes Array of strings containing the notes associated with the event
+ * @return Personal event constructed from the arguments provided
+ * @throws Exception if there is data corruption in the files
+ */
+ private static Personal makePersonal(String[] info, String[] statuses, String[] notes) throws Exception {
+ //0 is name, 1 is date, 2 is time, 3 is repeat unit, 4 is repeat number
+ Personal p = new Personal(info[0]);
+ if (info[1].equals("0")) {
+ //no date, event can be returned as is
+ setDone(p, statuses[0]);
+ notesSetter(p, notes[0]);
+ return p;
+ } else if (info[2].equals("0")) {
+ //no time, but got date
+ LocalDate date = DateTimeParser.dateParser(info[1]);
+ LocalTime time = null;
+ p.setDate(date);
+ } else {
+ //has both date and time
+ LocalDate date = DateTimeParser.dateParser(info[1]);
+ LocalTime time = DateTimeParser.timeParser(info[2]);
+ p.setDate(date);
+ p.setTime(time);
+ }
+ setDone(p, statuses[0]);
+ repeatSetter(p, statuses, info[3], info[4]);
+ notesSetter(p, notes[0]);
+ return p;
+
+
+ }
+
+ /**
+ * Creates a zoom event from a string of arguments.
+ *
+ * @param info Array of strings containing all the event information in text form
+ * @param statuses Array of strings containing statuses of repeated event if any
+ * @param notes Array of strings containing the notes associated with the event
+ * @return zoom event constructed from the arguments provided
+ * @throws Exception if there is data corruption in the files
+ */
+ private static Zoom makeZoom(String[] info, String[] statuses, String[] notes) throws Exception {
+ //0 is name, 1 is date, 2 is time, 3 is repeat unit, 4 is repeat number, 5 is zoom link
+ Zoom z = new Zoom(info[0], info[5]);
+ if (info[1].equals("0")) {
+ //no date, event can be returned as is
+ setDone(z, statuses[0]);
+ notesSetter(z, notes[0]);
+ return z;
+ } else if (info[2].equals("0")) {
+ //no time, but got date
+ LocalDate date = DateTimeParser.dateParser(info[1]);
+ LocalTime time = null;
+ z.setDate(date);
+ } else {
+ //has both date and time
+ LocalDate date = DateTimeParser.dateParser(info[1]);
+ LocalTime time = DateTimeParser.timeParser(info[2]);
+ z.setDate(date);
+ z.setTime(time);
+ }
+ setDone(z, statuses[0]);
+ repeatSetter(z, statuses, info[3], info[4]);
+ notesSetter(z, notes[0]);
+ return z;
+ }
+
+ /**
+ * Creates a personal event from a string of arguments.
+ *
+ * @param info Array of strings containing all the event information in text form
+ * @param statuses Array of strings containing statuses of repeated event if any
+ * @param notes Array of strings containing the notes associated with the event
+ * @return Personal event constructed from the arguments provided
+ * @throws Exception if there is data corruption in the files
+ */
+ private static Timetable makeTimetable(String[] info, String[] statuses, String[] notes) throws Exception {
+ //0 is name, 1 is date, 2 is time, 3 is repeat unit, 4 is repeat number, 5 is location
+ LocalDate date = DateTimeParser.dateParser(info[1]);
+ LocalTime time = DateTimeParser.timeParser(info[2]);
+ Timetable t = new Timetable(info[0], date, time);
+ if (!info[5].equals("")) { //location provided
+ t.setLocation(info[5]);
+ }
+ setDone(t, statuses[0]);
+ repeatSetter(t, statuses, info[3], info[4]);
+ notesSetter(t, notes[0]);
+ return t;
+ }
+
+ /**
+ * Set the done status of the event from the string argument.
+ *
+ * @param e The event for the status to be set
+ * @param doneStatus String containing the status. "T" for true, "F" for false
+ * @throws InvalidStatusException if the status is not a "T" or "F"
+ */
+ private static void setDone(Event e, String doneStatus) throws InvalidStatusException {
+ boolean isDone = doneStatus.equals("T");
+ boolean isNotDone = doneStatus.equals("F");
+ if (isDone) {
+ e.markAsDone();
+ } else if (isNotDone) {
+ e.markAsUndone();
+ } else {
+ throw new InvalidStatusException("Status needs to be true or false");
+ }
+
+ }
+
+ /**
+ * Setup of of repeated events.
+ *
+ * @param activity the event object where the repeated event is to be set
+ * @param statuses status information of all repeated events to be recorded
+ * @param timeUnit The time increment used to make the repeated event
+ * @param repeatNumber How many times the event repeated
+ * @throws Exception if there are file corruption errors
+ */
+ private static void repeatSetter(Event activity, String[] statuses, String timeUnit, String repeatNumber)
+ throws Exception {
+
+ LocalDate startDate = activity.getDate();
+ LocalTime startTime = activity.getTime();
+ int count = Integer.parseInt(repeatNumber);
+ if (count == 0) {
+ return;
+ }
+ ArrayList repeatEventList = new ArrayList<>();
+ for (int i = 1; i <= count; i++) {
+ LocalDate repeatDate;
+ switch (timeUnit) {
+ case "MONTHLY":
+ repeatDate = startDate.plusMonths(i);
+ break;
+ case "WEEKLY":
+ repeatDate = startDate.plusWeeks(i);
+ break;
+ case "DAILY":
+ repeatDate = startDate.plusDays(i);
+ break;
+ default:
+ throw new InvalidTimeUnitException(timeUnit);
+ }
+ activity.setRepeatType(timeUnit);
+ Event repeatEvent;
+ repeatEvent = activity.clone();
+ repeatEvent.setDate(repeatDate);
+ if (statuses[i].equals("T")) {
+ repeatEvent.markAsDone();
+ } else {
+ repeatEvent.markAsUndone();
+ }
+ repeatEventList.add(repeatEvent);
+ }
+ activity.setRepeatEventList(repeatEventList);
+
+
+
+
+ }
+
+ /**
+ * Set the notes for event.
+ * @param e event.
+ * @param notes notes for event.
+ */
+ private static void notesSetter(Event e, String notes) {
+ String noteString = notes;
+ noteString = noteString.trim();
+ ArrayList noteList = new ArrayList<>();
+ if (noteString.equals("")) {
+ e.setNotes(noteList);
+ assert noteList.size() == 0 : "There were notes provided";
+ } else {
+ //do not discard trailing empty strings
+ String[] noteArr = noteString.split("`", -1);
+ Collections.addAll(noteList, noteArr);
+ }
+
+
+ e.setNotes(noteList);
+ }
+
+ /**
+ * Convert ArrayList to String for notes.
+ * @param notes list of notes.
+ * @return notes in string format.
+ */
+ private static String notesListToString(ArrayList notes) {
+ StringBuffer notesBuffer = new StringBuffer();
+ String noteString = "";
+ if (!notes.isEmpty() && !(notes.size() == 1 && notes.get(0).equals(""))) {
+ for (String s : notes) {
+ if (s != "") {
+ notesBuffer.append(s);
+ notesBuffer.append("`");
+ }
+
+ }
+ noteString = notesBuffer.toString();
+ }
+
+ return noteString;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/duke/ui/Ui.java b/src/main/java/seedu/duke/ui/Ui.java
new file mode 100644
index 0000000000..1b6a03e3a8
--- /dev/null
+++ b/src/main/java/seedu/duke/ui/Ui.java
@@ -0,0 +1,467 @@
+package seedu.duke.ui;
+
+import seedu.duke.event.Event;
+import seedu.duke.event.EventList;
+import seedu.duke.event.Goal;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Scanner;
+
+public class Ui {
+ Scanner in;
+
+ public Ui() {
+ this.in = new Scanner(System.in);
+ }
+
+ public void printDividerLine() {
+ System.out.println("_________________________________");
+ }
+
+ public void printWelcomeMessage() {
+ printDividerLine();
+ System.out.println("Welcome to scheduler--;!");
+ System.out.println("What can I do for you?");
+ printDividerLine();
+ }
+
+ public void printByeMessage() {
+ System.out.println("Thank you for using scheduler--;!");
+ System.out.println("We hope to see you soon!");
+ printDividerLine();
+ }
+
+ public String receiveCommand() {
+ return in.nextLine().trim();
+ }
+
+ public void printEventAddedMessage(Event event) {
+ System.out.println("You have successfully added this event to your list!");
+ System.out.println(event);
+ }
+
+ /**
+ * Informs users of repeat command repeating the event as per instructed.
+ *
+ * @param event Event object that is being repeated for the specified time by the user
+ */
+ public void printRepeatAdd(Event event) {
+
+ System.out.println(event);
+ if (event.getRepeatCount() == 0) {
+ System.out.println("is no longer a repeated event");
+ } else {
+ System.out.println("is now repeating " + event.getRepeatType() + " for "
+ + event.getRepeatCount() + " times.");
+ }
+
+ }
+
+ /**
+ * Informs users about the current repeat status of the event.
+ *
+ * @param event Event object that contains repeated event information that will be printed by the UI
+ */
+ public void printRepeatList(Event event) {
+ System.out.println(event + " is also on:");
+ ArrayList repeatEventList = event.getRepeatEventList();
+ int index = 1;
+ for (Event e : repeatEventList) {
+ System.out.print(" " + index + ". ");
+ System.out.printf("%s ", e.getDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+ if (e.getTime() != null) {
+ System.out.printf("%s ", e.getTime().format(DateTimeFormatter.ofPattern("HH:mm")));
+ }
+ System.out.printf("[%s]", e.getStatus());
+ System.out.println();
+ index++;
+ }
+ }
+
+ public void printGoalMessage(Goal goal) {
+ if (goal != null) {
+ System.out.println("Goal: " + goal);
+ } else {
+ System.out.println("You have no goal! Why not set one now?");
+ }
+ }
+
+ public void printChangeGoalMessage(Goal goal) {
+ if (goal != null) {
+ System.out.println("Goal changed to: " + goal);
+ } else {
+ System.out.println("No more goal!");
+ }
+ }
+
+ private void printCalendarDivider() {
+ System.out.println("---------------------------------------------------------------------------------------");
+ }
+
+ public void printCalendar(Map.Entry> entry) {
+ printCalendarDivider();
+ System.out.println(entry.getKey().format(DateTimeFormatter.ofPattern("dd MMM yyyy")));
+ printCalendarDivider();
+ ArrayList eventsOnDate;
+ eventsOnDate = entry.getValue();
+ eventsOnDate.sort(Comparator.comparing(Event::getTime));
+ for (Event e : eventsOnDate) {
+ System.out.println(e.toCalendarString());
+ }
+ }
+
+ /**
+ * Prints the number dates to list in the calendar and the number of events not in the calendar.
+ *
+ * @param size number of unique dates in the calendar.
+ * @param count number of events not in the calendar.
+ */
+ public void printCalendarStart(int size, int count) {
+ System.out.println("Calendar has " + size + " dates to display");
+ if (count > 1) {
+ System.out.println(count + " events not on the calendar because they have no date and time");
+ } else if (count > 0) {
+ System.out.println(count + " event not on the calendar because it has no date and time");
+ }
+ }
+
+ /**
+ * Prints the end of the calendar.
+ */
+ public void printCalendarEnd() {
+ printCalendarDivider();
+ System.out.println("End of calendar");
+ }
+
+ /**
+ * Prints a message to tell the user instructions to continue or to exit the calendar.
+ */
+ public void printContinueQuery() {
+ printCalendarDivider();
+ System.out.println("Enter 'q' to exit or enter to continue...");
+ }
+
+ public void printCheckMessage() {
+
+ }
+
+ /**
+ * Lists the name of all EventList in the given EventList ArrayList.
+ *
+ * @param eventLists EventList ArrayList to list.
+ */
+ public void printAvailableList(ArrayList eventLists) {
+ System.out.println("Following are all existing lists:");
+ for (EventList list : eventLists) {
+ System.out.println(list.getName());
+ }
+ System.out.println("'list All' will list all existing lists.");
+ }
+
+ /**
+ * Lists all the events in the given EventList.
+ *
+ * @param eventList EventList to list.
+ */
+ public void printList(EventList eventList) {
+ String eventListName = eventList.getName();
+ ArrayList events = eventList.getEvents();
+ if (events.size() == 0) {
+ System.out.println("You have no " + eventListName + " events!");
+ } else {
+ System.out.println("Here is a list of your " + eventListName + " events:");
+ int index = 1;
+ for (Event e : events) {
+ System.out.print(index + ". ");
+ if (e.getRepeatEventList() != null) {
+ printRepeatList(e);
+ } else {
+ System.out.println(e);
+ }
+ if (e.getNotes().size() > 0) {
+ String indexWord = Integer.toString(index);
+ System.out.println(" Type \"view " + eventListName + " " + indexWord + "\" to see notes");
+ }
+ index++;
+ }
+ }
+ }
+
+ public void printDeadlineChangedMessage(Event eventUpdated) {
+ System.out.println("You have successfully updated the deadline for this event!");
+ System.out.println(eventUpdated);
+ }
+
+ /**
+ * Printing reminder.
+ *
+ * @param eventsWithTime events that have time.
+ * @param eventsWithoutTime events without time.
+ */
+ public void printReminder(ArrayList eventsWithTime, ArrayList eventsWithoutTime) {
+ if (eventsWithoutTime.size() == 0 && eventsWithTime.size() == 0) {
+ System.out.println("You have no events today!");
+ } else {
+ printReminderEvents(eventsWithTime, eventsWithoutTime);
+ }
+ }
+
+ /**
+ * Printing events according to events that have time and without time.
+ *
+ * @param eventsWithTime events that have time.
+ * @param eventsWithoutTime events without time.
+ */
+ private void printReminderEvents(ArrayList eventsWithTime, ArrayList eventsWithoutTime) {
+ System.out.println("You have the following events today: ");
+ if (eventsWithTime.size() > 0) {
+ System.out.println("_________Events With Time________");
+ Collections.sort(eventsWithTime, new Comparator() {
+ @Override
+ public int compare(Event o1, Event o2) {
+ return o1.getTime().compareTo(o2.getTime());
+ }
+ });
+ for (int i = 0; i < eventsWithTime.size(); i++) {
+ System.out.println(eventsWithTime.get(i).toString());
+ }
+
+ }
+
+ if (eventsWithoutTime.size() > 0) {
+ System.out.println();
+ System.out.println("_________Events Without Time_____");
+ for (int i = 0; i < eventsWithoutTime.size(); i++) {
+ System.out.println(eventsWithoutTime.get(i).toString());
+ }
+ }
+
+ }
+
+ public void printEventMarkedDoneMessage(Event doneEvent) {
+ System.out.println("You have successfully marked this event as done!");
+ System.out.println(doneEvent);
+ }
+
+ public void printEventMarkedUndoneMessage(Event undoneEvent) {
+ System.out.println("You have successfully marked this event as undone!");
+ System.out.println(undoneEvent);
+ }
+
+ public void printEventDeletedMessage(Event deleteEvent) {
+ System.out.println("You have successfully deleted this event!");
+ System.out.println(deleteEvent);
+ }
+
+ /**
+ * Print note created along with existing notes.
+ *
+ * @param eventUpdated event attached to notes.
+ * @param notes list of notes.
+ */
+ public void printNoteMessage(Event eventUpdated, ArrayList notes) {
+ System.out.println("You have successfully written the note for this event!");
+ System.out.println(eventUpdated);
+ for (int i = 0; i < notes.size(); i++) {
+ System.out.println(notes.get(i));
+ }
+
+ }
+
+ public void printViewNote(ArrayList notes) {
+
+ if (notes.size() == 0) {
+ System.out.println("You have not written any notes for this event!");
+ } else {
+ System.out.println("These are the notes that you have taken: ");
+ for (int i = 0; i < notes.size(); i++) {
+ System.out.println(notes.get(i));
+ }
+ }
+
+
+ }
+
+ /**
+ * The program prints out this message to indicate the successful saving of the event information to a file.
+ */
+ public void printStorageSavedMessage() {
+ System.out.println("The file has successfully been saved!");
+ }
+
+ /**
+ * The program prints out this message to indicate the successful loading of event information for a file.
+ */
+ public void printStorageLoadMessage() {
+ System.out.println("The file has successfully been loaded!");
+ }
+
+ /**
+ * Command prints this message if there are signs the users have modified the txt files incorrectly.
+ */
+ public void printStorageLoadingErrorMessage() {
+ System.out.println("The file does not exist or has been corrupted!");
+ }
+
+ /**
+ * Prints the message when no date was detected for a personal event.
+ */
+ public void printExtractNoDatePersonalEventMessage() {
+ System.out.println("Since no date was detected in the text body, "
+ + "the personal event will only contain the description.");
+ }
+
+ /**
+ * Prints the message when no date was detected for a zoom event.
+ */
+ public void printExtractNoDateZoomEventMessage() {
+ System.out.println("Since no date was detected in the text body, "
+ + "the zoom event will only contain the description and zoom link.");
+ }
+
+ /**
+ * Prints the message when no time was detected in the zoom event,
+ * resulting in creation of zoom event with only link.
+ */
+ public void printExtractNoTimeZoomEventMessage() {
+ System.out.println("Even though date was detected, time was not, in Scheduler--; "
+ + "a zoom event cannot have the date without the time. So "
+ + "a zoom event with only the date and zoom link will be created.");
+ }
+
+ /**
+ * Prints the message when no time was detected for a personal event.
+ */
+ public void printExtractNoTimePersonalEventMessage() {
+ System.out.println("Since no time was detected in the text body, "
+ + "the personal event will only contain the description and the date.");
+ }
+
+ /**
+ * Prints the message to choose from timings detected.
+ *
+ * @param timeCount The number of timings detected.
+ * @param timeList An ArrayList of LocalTime containing all timings detected.
+ */
+ public void printExtractChooseTimeMessage(int timeCount, ArrayList timeList) {
+ System.out.println("We have detected " + timeCount + " timings in this text body!");
+ System.out.println("Please select the time you want for this event from the list below!");
+ int timeNumber = 0;
+ printDividerLine();
+ for (LocalTime time : timeList) {
+ System.out.println(timeNumber + 1 + ". " + time);
+ timeNumber++;
+ }
+ printDividerLine();
+ }
+
+ /**
+ * Prints message when only 1 time was detected.
+ *
+ * @param finalTime The only LocalTime object detected.
+ */
+ public void printExtractSingleTimeDetectedMessage(LocalTime finalTime) {
+ System.out.println("One timing detected and chosen: " + finalTime);
+ }
+
+ /**
+ * Prints the message to choose from date detected.
+ *
+ * @param dateCount The number of dates detected.
+ * @param dateList An ArrayList of LocalDate containing all dates detected.
+ */
+ public void printExtractChooseDateMessage(int dateCount, ArrayList dateList) {
+ System.out.println("We have detected " + dateCount + " dates in this text body!");
+ System.out.println("Please select the date you want for this event from the list below!");
+ int dateNumber = 0;
+ printDividerLine();
+ for (LocalDate date : dateList) {
+ System.out.println(dateNumber + 1 + ". " + date);
+ dateNumber++;
+ }
+ printDividerLine();
+ }
+
+ /**
+ * Prints the message when only 1 date was detected.
+ *
+ * @param finalDate The only LocalDate object detected.
+ */
+ public void printExtractSingleDateDetectedMessage(LocalDate finalDate) {
+ System.out.println("One date detected and chosen: " + finalDate);
+ }
+
+ /**
+ * Prints the instructions to tell users what to do for the extract feature.
+ */
+ public void printExtractTextBodyRequestMessage() {
+ System.out.println("Copy and paste or enter the body of the text you want to extract from!");
+ System.out.println("At the end of your text, press enter to go to the next line, enter 'extractend' "
+ + "with no quotation marks and press enter once more.");
+ }
+
+ /**
+ * Prints the message when an invalid number was chosen from the list.
+ *
+ * @param field A string that could contain the any field that needs this method.
+ */
+ public void printExtractInvalidFieldChosenMessage(String field) {
+ System.out.println("Invalid " + field + " number to choose! Please choose again!");
+ printDividerLine();
+ }
+
+ /**
+ * Prints the message when 0 of a field was detected from the text body.
+ *
+ * @param field A string that could contain the any field that needs this method.
+ */
+ public void printExtractNoFieldMessage(String field) {
+ System.out.println("No " + field + " detected for this text body!");
+ }
+
+ /**
+ * Prints a message to choose from all zoom links detected.
+ *
+ * @param zoomLinkCount The number of zoom links detected.
+ * @param zoomLinkList An ArrayList of String containing all the zoom links detected.
+ */
+ public void printExtractChooseZoomLinkMessage(int zoomLinkCount, ArrayList zoomLinkList) {
+ System.out.println("We have detected " + zoomLinkCount + " zoom links in this text body!");
+ System.out.println("Please select the zoom link you want for this event from the list below!");
+ int zoomLinkNumber = 0;
+ printDividerLine();
+ for (String zoomLink : zoomLinkList) {
+ System.out.println(zoomLinkNumber + 1 + ". " + zoomLink);
+ zoomLinkNumber++;
+ }
+ printDividerLine();
+ }
+
+ /**
+ * Prints a message when only 1 zoom link was detected.
+ *
+ * @param zoomLink The only String of zoom link detected.
+ */
+ public void printExtractSingleZoomLinkDetectedMessage(String zoomLink) {
+ System.out.println("One zoom link detected and chosen: " + zoomLink);
+ }
+
+ /**
+ * Prints exception message.
+ *
+ * @param exceptionMessage String of warning message from various exceptions.
+ */
+ public void printErrorMessage(String exceptionMessage) {
+ System.out.println(exceptionMessage);
+ }
+
+ public void printMessage(String message) {
+ System.out.println(message);
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/helpfile.txt b/src/main/resources/helpfile.txt
new file mode 100644
index 0000000000..08b57ef8ab
--- /dev/null
+++ b/src/main/resources/helpfile.txt
@@ -0,0 +1,228 @@
+summary | help | add | deadline | repeat | goal | done | undone | delete | note | reminder | extract | list | save | view | check | calendar | bye
+begin summary
+Here is a summary of all the commands that the program can run
+help - Provides more information on how to use the commands available in the program
+add - Records a personal, timetable or zoom event into the program
+deadline - Sets or change a deadline for a personal event
+repeat - repeat an event for a specified amount of times every day, week or month
+goal - Sets up a goal for the user
+done - mark the event as completed
+undone - mark the event as not completed
+delete - remove a personal, timetable or zoom event permanently from the program
+note - Add in additional notes associated to the personal, timetable or zoom event
+reminder - prints out events that will be occurring on the present day
+extract - extracts out important event information from text to be recorded as events in the program
+list - list down all events by category or list out events belonging to only one category
+save - save current changes made to the program
+view - view notes that have been written for the event
+check - checks between a start period and end period if your schedule is available or not
+calendar - displays your events in chronological order
+bye - exits the program
+
+To find out more about each of these functions, type in "help [COMMAND]" into the terminal where COMMAND is the name of the command you would like more information about
+Format information:
+Text in lowercase indicates that you should type it exactly like this when you type the command in the terminal
+Text in uppercase indicates compulsory arguments that you will need to provide and type into the terminal to operate the command
+Text in uppercase surrounded by square brackets [] indicates optional arguments that you can choose to provide and type into the terminal to operate the command
+end summary
+
+begin help
+help - Provides more information on how to use the commands available in the program
+Format: help [COMMAND]
+
+[COMMAND] - command which you would like to find out more information about.
+
+text in lowercase indicates that you should type it exactly like this when you type the command in the terminal
+text in uppercase indicates compulsory arguments that you will need to provide and type into the terminal to operate the command
+text in uppercase surrounded by square brackets [] indicates optional arguments that you can choose to provide and type into the terminal to operate the command
+end help
+
+begin add
+add - Records a personal, timetable or zoom event into the program
+Format: add EVENT_TYPE; EVENT_DESCRIPTION; [LINK/LOCATION]; DD/MM/YY; HH:MM AM/PM
+
+
+EVENT_TYPE specify the type of event you want to add. These include zoom, timetable and personal.
+EVENT_DESCRIPTION is the description or name of the event.
+LINK/LOCATION is the website link or the location of the event. This is applicable and optional for zoom and timetable events respectively. This field should always be after the event description if used.
+DD/MM/YY is the date associated with the event in the format DD/MM/YY.
+HH:MM AM/PM is the time of the event. It can be written in 12 or 24 hour format.
+
+Timetable events can contain:
+- Description, date and time
+- Description, location, date and time
+
+Timetable Format: add Timetable; EVENT_DESCRIPTION; [LOCATION]; DD/MM/YY; HH:MM AM/PM
+
+Zoom events can contain:
+- Description and link
+- Description, link, date and time
+
+Zoom Format: add Zoom; EVENT_DESCRIPTION; LINK; [DD/MM/YY]; [HH:MM AM/PM]
+
+Personal events can contain:
+- Description
+- Description and date
+- Description, date and time
+
+Personal Format: add Personal; EVENT_DESCRIPTION; [DD/MM/YY]; [HH:MM AM/PM]
+
+end add
+
+begin deadline
+deadline - Sets or change a deadline for a personal event
+
+Format: deadline EVENT_INDEX; DD/MM/YY; [HH:MM] AM/PM
+
+EVENT_INDEX is a number. It contains the index of the personal event that will have its deadline set or changed.
+DD/MM/YY contains a date string in the format [DD/MM/YY]. This is the date of the deadline for the personal event.
+[HH:MM] is an optional argument containing the time of the deadline for the personal event. It can be written in either 12-hour or 24-hour format.
+
+end deadline
+
+begin repeat
+repeat - repeat an event for a specified amount of times every day, week or month
+
+Format: repeat EVENT_TYPE; EVENT_INDEX; [UNIT]; [COUNT]
+
+EVENT_TYPE specify what kind of event you would like to repeat. The accepted arguments for this are "personal", "timetable" and "zoom"
+EVENT_INDEX is an number. It contains the index of the event that is to be repeated
+[UNIT] can either take the word "Daily", "Weekly" or "Monthly". It tells the program to repeat the event either every day, every week or every month.
+[COUNT] takes an integer which indicates how many units of time will this event repeat itself. For instance, if the unit input is Weekly, placing a number 5 will repeat the event for 5 weeks.
+
+When the optional arguments of [UNIT] and [COUNT] are omitted in the code, repeat will instead display the repeat status of the event.
+
+end repeat
+
+begin goal
+goal - Sets up a goal for the user
+
+Format: goal [NEW_GOAL]
+
+[NEW_GOAL] is the goal that you will want to set for yourself. For example, "goal get cap 5.0" will set your current goal as "get cap 5.0".
+
+end goal
+
+begin done
+done - mark the event as completed
+
+Format: done EVENT_TYPE; EVENT_INDEX; [EVENT_DATE]
+
+EVENT_TYPE specify what kind of event you would like to mark as done. The accepted arguments for this are "personal", "timetable" and "zoom"
+EVENT_INDEX is a number. It contains the index of the event that is to be marked as done
+[EVENT_DATE] is an optional argument. When a date is provided for marking a repeated command as done, the done command will mark the repetition with the date matching EVENT_DATE as done
+
+If you would like to mark a non repeating event as done, the format to use is : done EVENT_TYPE; EVENT_INDEX;
+If you would like to mark the repetitions of a repeated event as done, the format to use is : done EVENT_TYPE; EVENT_INDEX; [EVENT_DATE]
+
+end done
+
+begin undone
+undone - mark the event as not completed
+
+Format: undone EVENT_TYPE; EVENT_INDEX; [EVENT_DATE]
+
+EVENT_TYPE specify what kind of event you would like to mark as not done. The accepted arguments for this are "personal", "timetable" and "zoom"
+EVENT_INDEX is a number. It contains the index of the event that is to be marked as not done
+[EVENT_DATE] is an optional argument. When a date is provided for marking a repeated command as not done, the undone command will mark the repetition with the date matching EVENT_DATE as undone
+
+If you would like to mark a non repeating event as not done, the format to use is : undone EVENT_TYPE; EVENT_INDEX;
+If you would like to mark the repetitions of a repeated event as not done, the format to use is : undone EVENT_TYPE; EVENT_INDEX; [EVENT_DATE]
+
+end undone
+
+begin delete
+delete - remove a personal, timetable or zoom event permanently from the program
+
+Format: delete EVENT_TYPE; EVENT_INDEX; [EVENT_DATE]
+
+EVENT_TYPE specify what kind of event you would like to delete. The accepted arguments for this are "personal", "timetable" and "zoom"
+EVENT_INDEX is a number. It contains the index of the event that is to be deleted
+[EVENT_DATE] is an optional argument. When a date is provided for deleting a repeated command, the delete command will delete the repetition with the date matching EVENT_DATE
+
+end delete
+
+begin note
+note - Add in additional notes associated to the personal, timetable or zoom event
+
+Format: note EVENT_TYPE; EVENT_INDEX
+EVENT_TYPE specify what kind of event you would like to add notes to. The accepted arguments for this are "personal", "timetable" and "zoom"
+EVENT_INDEX is a number. It contains the index of the event that notes are to be written to
+
+Scheduler will prompt you to type your notes
+To stop typing notes, enter "noteend" and press enter
+
+end note
+
+begin reminder
+reminder - prints out events that will occur on the present day
+
+Format: reminder
+
+end reminder
+
+begin extract
+extract - extracts out important event information from text to be recorded as events in the program
+
+Format: extract TEXT_SUBJECT;
+
+TEXT_SUBJECT is the subject of the email or the name you want your event to be.
+
+Once you have pressed enter, the program now prompts you to input the text you would like scheduler to extract out the scheduling information from
+When you have transferred all your text to the terminal, press enter to go to a new line, enter "extractend" and press enter again.
+
+end extract
+
+begin list
+list - list down all events by category or list out events belonging to only one category
+
+Format: list EVENT_TYPE
+
+EVENT_TYPE specify what kind of event you would like to list out. The accepted arguments for this are "personal", "timetable" and "zoom"
+
+Typing "list all" will print out all events of all categories
+end list
+
+begin save
+save - save current changes made to the program
+
+Format: save
+
+end save
+
+begin view
+view - view notes that have been written for the event
+
+Format: view EVENT_TYPE; EVENT_INDEX
+EVENT_TYPE specify what kind of event you would like to view your notes from. The accepted arguments for this are "personal", "timetable" and "zoom"
+EVENT_INDEX is a number. It contains the index of the event that the notes are to be printed out for viewing
+
+end view
+
+begin check
+check - checks between a start period and end period if your schedule is available or not
+Format: check [START_DATE]; [START_TIME]; [END_DATE]; [END_TIME]
+
+[START_DATE] is a date which you would like the program to start checking from
+[START_TIME] is a time during the start date which you would like the program to start checking from
+[END_DATE] is a date which you would like the program to check up to
+[END_TIME] is a time during the start date which you would like the program to start checking from
+
+end check
+
+begin calendar
+calendar - displays your events in chronological order
+Format: calendar
+
+Enter calendar into the terminal. Your events will be displayed in chronological order, day by day
+To advance to the next day, press the enter key
+To terminate the function early, press the 'q' key
+
+end calendar
+
+begin bye
+bye - exits the program
+
+Format: bye
+
+end bye
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/duke/DukeTest.java
index 2dda5fd651..a1c2ad58c2 100644
--- a/src/test/java/seedu/duke/DukeTest.java
+++ b/src/test/java/seedu/duke/DukeTest.java
@@ -1,10 +1,192 @@
package seedu.duke;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import seedu.duke.command.Command;
+import seedu.duke.data.UserData;
+import seedu.duke.exception.DukeException;
+import seedu.duke.parser.Parser;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Scanner;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
class DukeTest {
+
+ private final PrintStream standardOut = System.out;
+ private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
+
+
+ private Ui ui;
+
+ private Storage storage;
+
+ private UserData data;
+
+ private Parser currentParse;
+ private InputStream stdin = System.in;
+ private ByteArrayInputStream inStream;
+
+
+
+ public void setupComponents(String inputString) {
+
+
+ inStream = new ByteArrayInputStream(inputString.getBytes());
+ System.setIn(inStream);
+ Scanner scan = new Scanner(System.in);
+
+ ui = new Ui();
+ storage = new Storage("mainTest", ui);
+ data = new UserData();
+ currentParse = new Parser();
+
+ }
+
+ @Test
+ public void multiParser_multipleValidCommands_allCommandsRun() {
+
+ System.setOut(new PrintStream(outputStreamCaptor));
+ String inputString = "add personal; birthday; 03/01/2001; 0000; | list personal | delete personal; 1\r\n";
+
+ try {
+ setupComponents(inputString);
+ String userInput = ui.receiveCommand();
+ ArrayList allCommandInputs = currentParse.multiParse(userInput);
+ for (String commInputs : allCommandInputs) {
+ ui.printDividerLine();
+ Command c = currentParse.parse(commInputs);
+ c.execute(data, ui, storage);
+ }
+
+ assertEquals("_________________________________" + System.lineSeparator()
+ + "You have successfully added this event to your list!" + System.lineSeparator()
+ + "[P][X] birthday on 2001-01-03, 00:00" + System.lineSeparator()
+ + "_________________________________" + System.lineSeparator()
+ + "Here is a list of your Personal events:" + System.lineSeparator()
+ + "1. [P][X] birthday on 2001-01-03, 00:00" + System.lineSeparator()
+ + "_________________________________" + System.lineSeparator()
+ + "You have successfully deleted this event!" + System.lineSeparator()
+ + "[P][X] birthday on 2001-01-03, 00:00" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+ } catch (DukeException e) {
+ fail("Should not have any errors in executing commands");
+ } finally {
+ System.setIn(stdin);
+ }
+
+ }
+
+ @Test
+ public void multiParser_singleValidCommand_singleCommandsRun() {
+
+ System.setOut(new PrintStream(outputStreamCaptor));
+ String inputString = "add personal; birthday; 03/01/2001; 0000;\r\n";
+
+ try {
+ setupComponents(inputString);
+ String userInput = ui.receiveCommand();
+ ArrayList allCommandInputs = currentParse.multiParse(userInput);
+ for (String commInputs : allCommandInputs) {
+ ui.printDividerLine();
+ Command c = currentParse.parse(commInputs);
+ c.execute(data, ui, storage);
+ }
+
+ assertEquals("_________________________________" + System.lineSeparator()
+ + "You have successfully added this event to your list!" + System.lineSeparator()
+ + "[P][X] birthday on 2001-01-03, 00:00" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+ } catch (DukeException e) {
+ fail("Should not have any errors in executing commands");
+ } finally {
+ System.setIn(stdin);
+ }
+
+ }
+
+ @Test
+ public void multiParser_singleErrorCommand_singleCommandsRunWithException() {
+
+ System.setOut(new PrintStream(outputStreamCaptor));
+ String inputString = "add personal; birthday; 03/01/2001; 0000; | delete 1\r\n";
+
+ try {
+ setupComponents(inputString);
+ String userInput = ui.receiveCommand();
+ ArrayList allCommandInputs = currentParse.multiParse(userInput);
+ for (String commInputs : allCommandInputs) {
+ ui.printDividerLine();
+ Command c = currentParse.parse(commInputs);
+ c.execute(data, ui, storage);
+ }
+
+
+ } catch (DukeException e) {
+ assertTrue(true);
+ } finally {
+ System.setIn(stdin);
+ assertEquals("_________________________________" + System.lineSeparator()
+ + "You have successfully added this event to your list!" + System.lineSeparator()
+ + "[P][X] birthday on 2001-01-03, 00:00" + System.lineSeparator()
+ + "_________________________________" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+ }
+
+ }
+
+ /*
+ @Test
+ public void multiParser_extractNoteCommand_commandRunOneAtATime() {
+
+ System.setOut(new PrintStream(outputStreamCaptor));
+ String inputExtract = "extract meeting; |";
+ String inputEventDetail = "Dont forget your meeting on 27 Jan 2021 at 9am\r\nextractend\r\n";
+ String inputNote = "note personal; 1;\r\n";
+ String inputNoteDetail = "Meeting is fun!\r\n;\r\n";
+ String inputString = inputExtract + inputNote + inputEventDetail + inputNoteDetail;
+
+ try {
+ setupComponents(inputString);
+ String userInput = ui.receiveCommand();
+ ArrayList allCommandInputs = currentParse.multiParse(userInput);
+ for (String commInputs : allCommandInputs) {
+ ui.printDividerLine();
+ Command c = currentParse.parse(commInputs);
+ c.execute(data, ui, storage);
+ }
+
+
+ } catch (DukeException e) {
+ fail("No exception should occur");
+ } finally {
+ System.setIn(stdin);
+ assertEquals("_________________________________" + System.lineSeparator()
+ + "Copy and paste or enter the body of the text you want to extract from!"
+ + System.lineSeparator()
+ + "At the end of your text, press enter to go to the next line,"
+ + enter \'extractend\' with no quotation marks and press enter once more."
+ + System.lineSeparator()
+ + "_________________________________" + System.lineSeparator()
+ + "One date detected and chosen: 2021-01-27" + System.lineSeparator()
+ + "One timing detected and chosen: 09:00" + System.lineSeparator()
+ + "You have successfully added this event to your list!" + System.lineSeparator()
+ + "[P][X] meeting on 2021-01-27, 09:00" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+ }
+
+ } */
+
@Test
public void sampleTest() {
assertTrue(true);
diff --git a/src/test/java/seedu/duke/command/AddCommandTest.java b/src/test/java/seedu/duke/command/AddCommandTest.java
new file mode 100644
index 0000000000..53a02b13a1
--- /dev/null
+++ b/src/test/java/seedu/duke/command/AddCommandTest.java
@@ -0,0 +1,121 @@
+package seedu.duke.command;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.data.UserData;
+import seedu.duke.exception.DateErrorException;
+import seedu.duke.exception.DukeException;
+import seedu.duke.exception.EventAddErrorException;
+import seedu.duke.exception.MissingDescriptionException;
+import seedu.duke.exception.TimeErrorException;
+import seedu.duke.exception.WrongNumberOfArgumentsException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class AddCommandTest {
+ private final PrintStream standardOut = System.out;
+ private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
+
+ @BeforeEach
+ public void setUp() {
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Test
+ void execute_emptyInputForAddCommand_EventAddErrorException() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+ String addInput = "";
+ assertThrows(EventAddErrorException.class, () -> {
+ AddCommand addCommand = new AddCommand(addInput);
+ addCommand.execute(data, ui, storage);
+ });
+ }
+
+ @Test
+ void execute_emptyDescriptionForZoomEvent_printEmptyDescriptionError() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+ String addInput = "zoom; ; 16/10/20; 4pm";
+
+ assertThrows(MissingDescriptionException.class, () -> {
+ Command addCommand = new AddCommand(addInput);
+ addCommand.execute(data, ui, storage);
+ });
+
+ }
+
+ @Test
+ void execute_timetableEvent_printEventAdded() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+
+ // Add timetable event to data
+ String addInput = "timetable; Math class; math building; 17/10/2000; 1300";
+ Command addCommand = new AddCommand(addInput);
+ addCommand.execute(data, ui, storage);
+
+ assertEquals("You have successfully added this event to your list!" + System.lineSeparator()
+ + "[T][X] Math class, Location: "
+ + "math building on 2000-10-17, 13:00" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+ }
+
+ @Test
+ void execute_invalidZoomEvent_printZoomEventError() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+
+ // Add zoom event with incorrect number of parameters to date
+ assertThrows(WrongNumberOfArgumentsException.class, () -> {
+ String addInput = "zoom; class meeting; zoom.sg; 17/10/2000";
+ Command addCommand = new AddCommand(addInput);
+ addCommand.execute(data, ui, storage);
+ });
+ }
+
+ @Test
+ void execute_invalidPersonalEventTiming_printTimeParseError() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+ assertThrows(TimeErrorException.class, () -> {
+ String addInput = "personal; meeting; 16/9/2020; 18:89 PM";
+ Command addCommand = new AddCommand(addInput);
+ addCommand.execute(data, ui, storage);
+ });
+ }
+
+ @Test
+ void execute_invalidPersonalEventDate_printDateParseError() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+
+ assertThrows(DateErrorException.class, () -> {
+ String addInput = "personal; meeting; 35/9/2020; 4 PM";
+ Command addCommand = new AddCommand(addInput);
+ addCommand.execute(data, ui, storage);
+ });
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(standardOut);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/command/CalendarCommandTest.java b/src/test/java/seedu/duke/command/CalendarCommandTest.java
new file mode 100644
index 0000000000..4d97982cd1
--- /dev/null
+++ b/src/test/java/seedu/duke/command/CalendarCommandTest.java
@@ -0,0 +1,57 @@
+package seedu.duke.command;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.data.UserData;
+import seedu.duke.exception.DukeException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CalendarCommandTest {
+ private final PrintStream standardOut = System.out;
+ private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
+
+ @BeforeEach
+ public void setUp() {
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Test
+ void execute_eventWithoutDate_doNotPrintCalendarForEvent() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+
+ StringWriter expectedStringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(expectedStringWriter);
+
+ // Add personal event without date
+ String personalInput = "personal; to finish project";
+ new AddCommand(personalInput).execute(data, ui, storage);
+ printWriter.println("You have successfully added this event to your list!");
+ printWriter.println("[P][X] to finish project");
+
+ // Execute calendar command
+ CalendarCommand.parse("").execute(data, ui, storage);
+ printWriter.println("Calendar has 0 dates to display");
+ printWriter.println("1 event not on the calendar because it has no date and time");
+ printWriter.println("---------------------------------------------------------------------------------------");
+ printWriter.println("End of calendar");
+ printWriter.close();
+ String expected = expectedStringWriter.toString();
+ assertEquals(expected, outputStreamCaptor.toString());
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(standardOut);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/command/CheckCommandTest.java b/src/test/java/seedu/duke/command/CheckCommandTest.java
new file mode 100644
index 0000000000..fb1d276081
--- /dev/null
+++ b/src/test/java/seedu/duke/command/CheckCommandTest.java
@@ -0,0 +1,236 @@
+package seedu.duke.command;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.data.UserData;
+import seedu.duke.exception.DateErrorException;
+import seedu.duke.exception.DukeException;
+import seedu.duke.exception.InvalidTimePeriodException;
+import seedu.duke.exception.MissingSemicolonException;
+import seedu.duke.exception.TimeErrorException;
+import seedu.duke.exception.WrongNumberOfArgumentsException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class CheckCommandTest {
+ private final PrintStream standardOut = System.out;
+ private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
+
+ private final UserData data = new UserData();
+ private final Ui ui = new Ui();
+ private final Storage storage = new Storage("data", ui);
+
+ @BeforeEach
+ void setupEventList() throws DukeException {
+ // Add Personal events to data
+ String personalInput = "personal; Go out for dinner; 05/05/20; 12:00";
+ Command addCommand = new AddCommand(personalInput);
+ addCommand.execute(data, ui, storage);
+
+ personalInput = "personal; Stay at home; 04/05/20";
+ addCommand = new AddCommand(personalInput);
+ addCommand.execute(data, ui, storage);
+
+ // Add Zoom event to data
+ String zoomInput = "zoom; CS2113T tutorial; zoom.com/blahblah; 03/10/2020; 1330";
+ addCommand = new AddCommand(zoomInput);
+ addCommand.execute(data, ui, storage);
+
+ // Repeat Zoom event
+ String repeatZoomInput = "zoom; 1; weekly; 1";
+ Command repeatCommand = RepeatCommand.parse(repeatZoomInput);
+ repeatCommand.execute(data, ui, storage);
+
+ //Add Timetable Event to Data
+ String timeTableInput = "timetable; Science class; S17; 4/5/2020; 3 pm";
+ addCommand = new AddCommand(timeTableInput);
+ addCommand.execute(data, ui, storage);
+
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(standardOut);
+ }
+
+ @Test
+ void execute_someEventsInTimeRange_printEventsInTimeRange() throws DukeException {
+ // Check 4 May 2020 1:15 pm to 5 May 2:30 pm
+ String inputString = "04/05/20; 13:15; 05/05/20; 14:30";
+
+ Command checkCommand = new CheckCommand(inputString);
+ checkCommand.execute(data, ui, storage);
+
+ String expectedString = "Here is a list of your coinciding events:" + System.lineSeparator()
+ + "1. [P][X] Go out for dinner on 2020-05-05, 12:00" + System.lineSeparator()
+ + "2. [P][X] Stay at home on 2020-05-04" + System.lineSeparator()
+ + "3. [T][X] Science class, Location: S17 on 2020-05-04, 15:00";
+ assertEquals(expectedString, outputStreamCaptor.toString().trim());
+ }
+
+ @Test
+ void execute_repeatedEventInsideTimeRange_printEventInTimeRange() throws DukeException {
+ // Check 10 Oct 2020 12 pm to 10 Oct 2020 5 pm
+ String inputString = "10/10/2020; 12 pm; 10/10/20; 17";
+
+ Command checkCommand = new CheckCommand(inputString);
+ checkCommand.execute(data, ui, storage);
+
+ String expectedString = "Here is a list of your coinciding events:" + System.lineSeparator()
+ + "1. [Z][X] CS2113T tutorial, Link: zoom.com/blahblah on 2020-10-10, 13:30";
+ assertEquals(expectedString, outputStreamCaptor.toString().trim());
+ }
+
+ @Test
+ void execute_eventsOutsideTimeRange_printEventsInTimeRange() throws DukeException {
+ // Check 20 Oct 2020 1 pm to current date and time
+ String inputStringOne = "20/10/20; 13:00; ; ";
+
+ Command checkCommand = new CheckCommand(inputStringOne);
+ checkCommand.execute(data, ui, storage);
+
+ String expectedString = "You have no coinciding events!";
+ assertEquals(expectedString, outputStreamCaptor.toString().trim());
+
+ // Check 15 Oct 2020 1 pm to current date in 2020 at current time
+ String inputStringTwo = "15/10/20; 13:00; 20; ";
+
+ checkCommand = new CheckCommand(inputStringTwo);
+ checkCommand.execute(data, ui, storage);
+
+ expectedString = expectedString + System.lineSeparator()
+ + "You have no coinciding events!";
+ assertEquals(expectedString, outputStreamCaptor.toString().trim());
+ }
+
+ @Test
+ void execute_semicolonsNotUsedToSeparateFields_missingSemicolonExceptionThrown() {
+ // Execute check command
+ String inputString = "9/10/2020 3 pm 10/10/20 5 pm";
+
+ Exception e = assertThrows(MissingSemicolonException.class, () -> {
+ Command checkCommand = new CheckCommand(inputString);
+ checkCommand.execute(data, ui, storage);
+ });
+
+ String expectedMessage = "Remember to separate input fields with a ';'." + System.lineSeparator()
+ + "The format for check is: \"check []; []; []; []\".";
+ String actualMessage = e.getMessage();
+ assertEquals(expectedMessage, actualMessage);
+ }
+
+ @Test
+ void execute_notEnoughFieldsFilled_WrongNumberOfArgumentsExceptionThrown() {
+ // Execute check command
+ String inputString = "9/10/2020; 3 pm";
+
+ Exception e = assertThrows(WrongNumberOfArgumentsException.class, () -> {
+ Command checkCommand = new CheckCommand(inputString);
+ checkCommand.execute(data, ui, storage);
+ });
+
+ String expectedMessage = "Insufficient fields provided to check events. "
+ + "Remember to put a semicolon even for blank fields." + System.lineSeparator()
+ + "The format for check is: \"check []; []; []; []\".";
+ String actualMessage = e.getMessage();
+ assertEquals(expectedMessage, actualMessage);
+ }
+
+ @Test
+ void execute_invalidDateFormatGiven_DateErrorExceptionThrown() {
+ // dots used instead of slashes or dashes
+ String inputStringOne = "9/10.2020; 3 pm; 10.10.2020; 5 pm";
+
+ assertThrows(DateErrorException.class, () -> {
+ Command checkCommand = new CheckCommand(inputStringOne);
+ checkCommand.execute(data, ui, storage);
+ });
+
+ // comma used instead of slashes or dashes
+ String inputStringTwo = "9/10/2020; 3 pm; 10,10,2020; 5 pm";
+
+ assertThrows(DateErrorException.class, () -> {
+ Command checkCommand = new CheckCommand(inputStringTwo);
+ checkCommand.execute(data, ui, storage);
+ });
+
+ // Excess fields for date
+ String inputStringThree = "5/9/10/2020; 3 pm; 10/10/2020; 5 pm";
+
+ assertThrows(DateErrorException.class, () -> {
+ Command checkCommand = new CheckCommand(inputStringThree);
+ checkCommand.execute(data, ui, storage);
+ });
+ }
+
+ @Test
+ void execute_invalidTimeFormatGiven_TimeErrorExceptionThrown() {
+ // dot used in time instead of colon
+ String inputStringOne = "9/10/2020; 3.00 pm; 10/10/2020; 5.00 pm";
+
+ Exception firstE = assertThrows(TimeErrorException.class, () -> {
+ Command checkCommand = new CheckCommand(inputStringOne);
+ checkCommand.execute(data, ui, storage);
+ });
+
+ String expectedMessage = "Something is wrong with the time!" + System.lineSeparator()
+ + "The accepted formats are:" + System.lineSeparator()
+ + "(12 hour) hh:mm am/pm, hhmm am/pm, hh am/pm or " + System.lineSeparator()
+ + "(24 hour) HH:mm, HHmm, HH.";
+ String actualMessage = firstE.getMessage();
+ assertEquals(expectedMessage, actualMessage);
+
+ // time has excess fields
+ String inputStringTwo = "9/10/2020; 3:00 pm; 10/10/2020; 5:00 PST pm";
+
+ Exception secondE = assertThrows(TimeErrorException.class, () -> {
+ Command checkCommand = new CheckCommand(inputStringTwo);
+ checkCommand.execute(data, ui, storage);
+ });
+
+ actualMessage = secondE.getMessage();
+ assertEquals(expectedMessage, actualMessage);
+
+ // >12 integer used in 12 hour format
+ String inputStringThree = "9/10/2020; 13 pm; 10/10/2020; 5:00 pm";
+
+ Exception thirdE = assertThrows(TimeErrorException.class, () -> {
+ Command checkCommand = new CheckCommand(inputStringThree);
+ checkCommand.execute(data, ui, storage);
+ });
+
+ actualMessage = thirdE.getMessage();
+ assertEquals(expectedMessage, actualMessage);
+
+ // >24 integer used in 24 hour format
+ String inputStringFour = "9/10/2020; 2500; 10/10/2020; 5:00 pm";
+
+ Exception fourthE = assertThrows(TimeErrorException.class, () -> {
+ Command checkCommand = new CheckCommand(inputStringFour);
+ checkCommand.execute(data, ui, storage);
+ });
+
+ actualMessage = fourthE.getMessage();
+ assertEquals(expectedMessage, actualMessage);
+
+ // start time input is after end time
+ String inputStringFive = "; ; 1/1/2019; 5:00 pm";
+
+ Exception fifthE = assertThrows(InvalidTimePeriodException.class, () -> {
+ Command checkCommand = new CheckCommand(inputStringFive);
+ checkCommand.execute(data, ui, storage);
+ });
+
+ expectedMessage = "The start of the time period should be earlier than the end.";
+ actualMessage = fifthE.getMessage();
+ assertEquals(expectedMessage, actualMessage);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/command/DeadlineCommandTest.java b/src/test/java/seedu/duke/command/DeadlineCommandTest.java
new file mode 100644
index 0000000000..3522c9895a
--- /dev/null
+++ b/src/test/java/seedu/duke/command/DeadlineCommandTest.java
@@ -0,0 +1,181 @@
+package seedu.duke.command;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.data.UserData;
+import seedu.duke.exception.DateErrorException;
+import seedu.duke.exception.DukeException;
+import seedu.duke.exception.InvalidIndexException;
+import seedu.duke.exception.InvalidTimePeriodException;
+import seedu.duke.exception.TimeErrorException;
+import seedu.duke.exception.WrongNumberFormatException;
+import seedu.duke.exception.WrongNumberOfArgumentsException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.time.LocalDate;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class DeadlineCommandTest {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+
+ private final PrintStream standardOut = System.out;
+ private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
+
+ @BeforeEach
+ public void setUp() {
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Test
+ public void execute_withDate_printDeadline() throws DukeException {
+ String input = "personal; sleep";
+ Command addPersonalEvent = new AddCommand(input);
+ addPersonalEvent.execute(data, ui, storage);
+
+ DeadlineCommand testDeadlineWithDateOnly = new DeadlineCommand("1; 1/12/21");
+ testDeadlineWithDateOnly.execute(data, ui, storage);
+ StringWriter expectedStringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(expectedStringWriter);
+ printWriter.println("You have successfully added this event to your list!");
+ printWriter.println("[P][X] sleep");
+ printWriter.println("You have successfully updated the deadline for this event!");
+ printWriter.println("[P][X] sleep on 2021-12-01");
+ printWriter.close();
+ String expected = expectedStringWriter.toString();
+ assertEquals(expected,
+ outputStreamCaptor.toString());
+ }
+
+ @Test
+ public void execute_withDateAndTime_printDeadline() throws DukeException {
+ String input = "personal; sleep";
+ Command addPersonalEvent = new AddCommand(input);
+ addPersonalEvent.execute(data, ui, storage);
+
+ DeadlineCommand testDeadlineWithDateOnly = new DeadlineCommand("1; 1/12/30; 11:20 PM");
+ testDeadlineWithDateOnly.execute(data, ui, storage);
+ StringWriter expectedStringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(expectedStringWriter);
+ printWriter.println("You have successfully added this event to your list!");
+ printWriter.println("[P][X] sleep");
+ printWriter.println("You have successfully updated the deadline for this event!");
+ printWriter.println("[P][X] sleep on 2030-12-01, 23:20");
+ printWriter.close();
+ String expected = expectedStringWriter.toString();
+ assertEquals(expected,
+ outputStreamCaptor.toString());
+ }
+
+ @Test
+ public void execute_withoutDateandTime_wrongNumberOfArgumentsException() throws DukeException {
+ String input = "personal; sleep";
+ Command addPersonalEvent = new AddCommand(input);
+ addPersonalEvent.execute(data, ui, storage);
+
+
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+ assertThrows(WrongNumberOfArgumentsException.class, () -> {
+ DeadlineCommand testDeadlineWithoutDateandTime = new DeadlineCommand("1");
+ testDeadlineWithoutDateandTime.execute(data, ui, storage);
+ });
+ }
+
+ @Test
+ public void execute_withInvalidIndex_invalidIndexException() throws DukeException {
+ String input = "personal; sleep";
+ Command addPersonalEvent = new AddCommand(input);
+ addPersonalEvent.execute(data, ui, storage);
+
+
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+ assertThrows(InvalidIndexException.class, () -> {
+ DeadlineCommand testDeadlineWithInvalidIndex = new DeadlineCommand("0; 1/12/21; 11:20 PM");
+ testDeadlineWithInvalidIndex.execute(data, ui, storage);
+ });
+ }
+
+ @Test
+ public void execute_withIndexIsNotaNumber_wrongNumberFormatException() throws DukeException {
+ String input = "personal; sleep";
+ Command addPersonalEvent = new AddCommand(input);
+ addPersonalEvent.execute(data, ui, storage);
+
+
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+ assertThrows(WrongNumberFormatException.class, () -> {
+ DeadlineCommand testDeadlineWithInvalidIndex = new DeadlineCommand("a; 7/10/20; 11:20 PM");
+ testDeadlineWithInvalidIndex.execute(data, ui, storage);
+ });
+ }
+
+ @Test
+ public void execute_withInvalidDateFormat_DateErrorException() throws DukeException {
+ String input = "personal; sleep";
+ Command addPersonalEvent = new AddCommand(input);
+ addPersonalEvent.execute(data, ui, storage);
+
+
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+ assertThrows(DateErrorException.class, () -> {
+ DeadlineCommand testDeadlineWithInvalidDateFormat = new DeadlineCommand("1; 40/30/10; 11:20 PM");
+ testDeadlineWithInvalidDateFormat.execute(data, ui, storage);
+ });
+ }
+
+ @Test
+ public void execute_withInvalidTimeFormat_DateErrorException() throws DukeException {
+ String input = "personal; sleep";
+ Command addPersonalEvent = new AddCommand(input);
+ addPersonalEvent.execute(data, ui, storage);
+
+
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+ assertThrows(TimeErrorException.class, () -> {
+ DeadlineCommand testDeadlineWithInvalidTimeFormat = new DeadlineCommand("1; 7/11/21; 23:20 PM");
+ testDeadlineWithInvalidTimeFormat.execute(data, ui, storage);
+ });
+ }
+
+ @Test
+ public void execute_withInvalidDateRange_InvalidTimePeriodException() throws DukeException {
+ String input = "personal; sleep";
+ Command addPersonalEvent = new AddCommand(input);
+ addPersonalEvent.execute(data, ui, storage);
+ LocalDate yesterday = LocalDate.now().minusDays(1);
+
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+ assertThrows(InvalidTimePeriodException.class, () -> {
+ DeadlineCommand testDeadlineWithInvalidTimePeriodException = new DeadlineCommand("1;"
+ + yesterday + "; 23:20 PM");
+ testDeadlineWithInvalidTimePeriodException.execute(data, ui, storage);
+ });
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(standardOut);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/command/DeleteCommandTest.java b/src/test/java/seedu/duke/command/DeleteCommandTest.java
new file mode 100644
index 0000000000..da7ddc4e56
--- /dev/null
+++ b/src/test/java/seedu/duke/command/DeleteCommandTest.java
@@ -0,0 +1,168 @@
+package seedu.duke.command;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.data.UserData;
+import seedu.duke.exception.DukeException;
+import seedu.duke.exception.InvalidEventDateException;
+import seedu.duke.exception.InvalidIndexException;
+import seedu.duke.exception.MissingSemicolonException;
+import seedu.duke.exception.WrongNumberFormatException;
+import seedu.duke.exception.WrongNumberOfArgumentsException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class DeleteCommandTest {
+ private final PrintStream standardOut = System.out;
+ private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
+
+ private final UserData data = new UserData();
+ private final Ui ui = new Ui();
+ private final Storage storage = new Storage("data", ui);
+
+ @BeforeEach
+ void setUp() throws DukeException {
+ // Add Personal events to data
+ String personalInput = "personal; Go out for dinner; 05/05/20; 12:00";
+ Command addCommand = new AddCommand(personalInput);
+ addCommand.execute(data, ui, storage);
+
+ personalInput = "personal; Stay at home; 04/05/20";
+ addCommand = new AddCommand(personalInput);
+ addCommand.execute(data, ui, storage);
+
+ // Add Zoom event to data
+ String zoomInput = "zoom; CS2113T tutorial; zoom.com/blahblah; 03/10/2020; 1330";
+ addCommand = new AddCommand(zoomInput);
+ addCommand.execute(data, ui, storage);
+
+ // Repeat Zoom event
+ String repeatZoomInput = "zoom; 1; weekly; 1";
+ Command repeatCommand = RepeatCommand.parse(repeatZoomInput);
+ repeatCommand.execute(data, ui, storage);
+
+ //Add Timetable Event to Data
+ String timeTableInput = "timetable; Science class; S17; 4/5/2020; 3 pm";
+ addCommand = new AddCommand(timeTableInput);
+ addCommand.execute(data, ui, storage);
+
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @AfterEach
+ void tearDown() {
+ System.setOut(standardOut);
+ }
+
+ @Test
+ void execute_deleteValidEvents_eventsAreDeleted() throws DukeException {
+ // First valid event
+ String inputStringOne = "personal; 1";
+ Command deleteCommand = DeleteCommand.parse(inputStringOne);
+ deleteCommand.execute(data, ui, storage);
+
+ String expectedString = "You have successfully deleted this event!" + System.lineSeparator()
+ + "[P][X] Go out for dinner on 2020-05-05, 12:00";
+ assertEquals(expectedString, outputStreamCaptor.toString().trim());
+
+ // Second valid event
+ String inputStringTwo = "zoom; 1; 10/10/2020";
+ deleteCommand = DeleteCommand.parse(inputStringTwo);
+ deleteCommand.execute(data, ui, storage);
+
+ expectedString = expectedString + System.lineSeparator()
+ + "You have successfully deleted this event!" + System.lineSeparator()
+ + "[Z][X] CS2113T tutorial, Link: zoom.com/blahblah on 2020-10-10, 13:30";
+ assertEquals(expectedString, outputStreamCaptor.toString().trim());
+
+ // Third valid event
+ String inputStringThree = "timetable; 1; extra accidental entries";
+ deleteCommand = DeleteCommand.parse(inputStringThree);
+ deleteCommand.execute(data, ui, storage);
+
+ expectedString = expectedString + System.lineSeparator()
+ + "You have successfully deleted this event!" + System.lineSeparator()
+ + "[T][X] Science class, Location: S17 on 2020-05-04, 15:00";
+ assertEquals(expectedString, outputStreamCaptor.toString().trim());
+
+ expectedString = expectedString + System.lineSeparator()
+ + "Here is a list of your Personal events:" + System.lineSeparator()
+ + "1. [P][X] Stay at home on 2020-05-04" + System.lineSeparator()
+ + "_________________________________" + System.lineSeparator()
+ + "You have no Timetable events!" + System.lineSeparator()
+ + "_________________________________" + System.lineSeparator()
+ + "Here is a list of your Zoom events:" + System.lineSeparator()
+ + "1. [Z][X] CS2113T tutorial, Link: zoom.com/blahblah on 2020-10-03, 13:30";
+ ListCommand.parse("all").execute(data, ui, storage);
+ assertEquals(expectedString, outputStreamCaptor.toString().trim());
+
+ }
+
+ @Test
+ void execute_deleteInvalidEvents_correspondingExceptionThrown() {
+ // Event index exceeds max event index
+ String inputStringOne = "personal; 3";
+ Exception firstE = assertThrows(InvalidIndexException.class, () -> {
+ Command deleteCommand = DeleteCommand.parse(inputStringOne);
+ deleteCommand.execute(data, ui, storage);
+ });
+
+ String expectedStringOne = "Error, no such index is available!";
+ String actualStringOne = firstE.getMessage();
+ assertEquals(expectedStringOne, actualStringOne);
+
+ // Event does not fall on the provided date
+ String inputStringTwo = "zoom; 1; 5/10/2020";
+ Exception secondE = assertThrows(InvalidEventDateException.class, () -> {
+ Command deleteCommand = DeleteCommand.parse(inputStringTwo);
+ deleteCommand.execute(data, ui, storage);
+ });
+
+ String expectedStringTwo = "This event does not occur on this date.";
+ String actualStringTwo = secondE.getMessage();
+ assertEquals(expectedStringTwo, actualStringTwo);
+
+ // Semicolons not used to separate fields
+ String inputStringThree = "personal 1";
+ Exception thirdE = assertThrows(MissingSemicolonException.class, () -> {
+ Command deleteCommand = DeleteCommand.parse(inputStringThree);
+ deleteCommand.execute(data, ui, storage);
+ });
+
+ String expectedStringThree = "Remember to separate input fields with a ';'." + System.lineSeparator()
+ + "The format for delete is: \"delete ; ; []\".";
+ String actualStringThree = thirdE.getMessage();
+ assertEquals(expectedStringThree, actualStringThree);
+
+ // Event index not given
+ String inputStringFour = "zoom;";
+ Exception fourthE = assertThrows(WrongNumberOfArgumentsException.class, () -> {
+ Command deleteCommand = DeleteCommand.parse(inputStringFour);
+ deleteCommand.execute(data, ui, storage);
+ });
+
+ String expectedStringFour = "Event type or index is missing." + System.lineSeparator()
+ + "The format for delete is: \"delete ; ; []\".";
+ String actualStringFour = fourthE.getMessage();
+ assertEquals(expectedStringFour, actualStringFour);
+
+ // Event index is not an integer
+ String inputStringFive = "zoom; a";
+ Exception fifthE = assertThrows(WrongNumberFormatException.class, () -> {
+ Command deleteCommand = DeleteCommand.parse(inputStringFive);
+ deleteCommand.execute(data, ui, storage);
+ });
+
+ String expectedStringFive = "Event index given is not an integer." + System.lineSeparator()
+ + "The format for delete is: \"delete ; ; []\".";
+ String actualStringFive = fifthE.getMessage();
+ assertEquals(expectedStringFive, actualStringFive);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/command/DoneCommandTest.java b/src/test/java/seedu/duke/command/DoneCommandTest.java
new file mode 100644
index 0000000000..95e8bb16dd
--- /dev/null
+++ b/src/test/java/seedu/duke/command/DoneCommandTest.java
@@ -0,0 +1,174 @@
+package seedu.duke.command;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.data.UserData;
+import seedu.duke.exception.DukeException;
+import seedu.duke.exception.InvalidEventDateException;
+import seedu.duke.exception.InvalidIndexException;
+import seedu.duke.exception.MissingSemicolonException;
+import seedu.duke.exception.WrongNumberFormatException;
+import seedu.duke.exception.WrongNumberOfArgumentsException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class DoneCommandTest {
+ private final PrintStream standardOut = System.out;
+ private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
+
+ private final UserData data = new UserData();
+ private final Ui ui = new Ui();
+ private final Storage storage = new Storage("data", ui);
+
+ @BeforeEach
+ void setUp() throws DukeException {
+ // Add Personal events to data
+ String personalInput = "personal; Go out for dinner; 05/05/20; 12:00";
+ Command addCommand = new AddCommand(personalInput);
+ addCommand.execute(data, ui, storage);
+
+ personalInput = "personal; Stay at home; 04/05/20";
+ addCommand = new AddCommand(personalInput);
+ addCommand.execute(data, ui, storage);
+
+ // Add Zoom event to data
+ String zoomInput = "zoom; CS2113T tutorial; zoom.com/blahblah; 03/10/2020; 1330";
+ addCommand = new AddCommand(zoomInput);
+ addCommand.execute(data, ui, storage);
+
+ // Repeat Zoom event
+ String repeatZoomInput = "zoom; 1; weekly; 1";
+ Command repeatCommand = RepeatCommand.parse(repeatZoomInput);
+ repeatCommand.execute(data, ui, storage);
+
+ //Add Timetable Event to Data
+ String timeTableInput = "timetable; Science class; S17; 4/5/2020; 3 pm";
+ addCommand = new AddCommand(timeTableInput);
+ addCommand.execute(data, ui, storage);
+
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @AfterEach
+ void tearDown() {
+ System.setOut(standardOut);
+ }
+
+ @Test
+ void execute_markValidEventsAsDone_eventsMarkedDone() throws DukeException {
+ // First valid event
+ String inputStringOne = "personal; 1";
+ Command doneCommand = DoneCommand.parse(inputStringOne);
+ doneCommand.execute(data, ui, storage);
+
+ String expectedString = "You have successfully marked this event as done!" + System.lineSeparator()
+ + "[P][O] Go out for dinner on 2020-05-05, 12:00";
+ assertEquals(expectedString, outputStreamCaptor.toString().trim());
+ String actualStatus = data.getEventList("Personal").getEventByIndex(0).getStatus();
+ assertEquals("O", actualStatus);
+
+ // Second valid event
+ String inputStringTwo = "zoom; 1; 10/10/2020";
+ doneCommand = DoneCommand.parse(inputStringTwo);
+ doneCommand.execute(data, ui, storage);
+
+ expectedString = expectedString + System.lineSeparator()
+ + "You have successfully marked this event as done!" + System.lineSeparator()
+ + "[Z][O] CS2113T tutorial, Link: zoom.com/blahblah on 2020-10-10, 13:30";
+ assertEquals(expectedString, outputStreamCaptor.toString().trim());
+ actualStatus = data.getEventList("Zoom").getEventByIndex(0).getRepeatEventList().get(0).getStatus();
+ assertEquals("O", actualStatus);
+
+ // Third valid event
+ String inputStringThree = "timetable; 1; extra accidental entries";
+ doneCommand = DoneCommand.parse(inputStringThree);
+ doneCommand.execute(data, ui, storage);
+
+ expectedString = expectedString + System.lineSeparator()
+ + "You have successfully marked this event as done!" + System.lineSeparator()
+ + "[T][O] Science class, Location: S17 on 2020-05-04, 15:00";
+ assertEquals(expectedString, outputStreamCaptor.toString().trim());
+ actualStatus = data.getEventList("Timetable").getEventByIndex(0).getStatus();
+ assertEquals("O", actualStatus);
+
+ // Fourth valid event
+ String inputStringFour = "zoom; 1; 3/10/2020";
+ doneCommand = DoneCommand.parse(inputStringFour);
+ doneCommand.execute(data, ui, storage);
+
+ expectedString = expectedString + System.lineSeparator()
+ + "You have successfully marked this event as done!" + System.lineSeparator()
+ + "[Z][O] CS2113T tutorial, Link: zoom.com/blahblah on 2020-10-03, 13:30";
+ assertEquals(expectedString, outputStreamCaptor.toString().trim());
+ actualStatus = data.getEventList("Zoom").getEventByIndex(0).getStatus();
+ assertEquals("O", actualStatus);
+ }
+
+ @Test
+ void execute_markInvalidEventsAsDone_correspondingExceptionThrown() {
+ // Event index exceeds max event index
+ String inputStringOne = "personal; 3";
+ Exception firstE = assertThrows(InvalidIndexException.class, () -> {
+ Command doneCommand = DoneCommand.parse(inputStringOne);
+ doneCommand.execute(data, ui, storage);
+ });
+
+ String expectedStringOne = "Error, no such index is available!";
+ String actualString = firstE.getMessage();
+ assertEquals(expectedStringOne, actualString);
+
+ // Event does not fall on the provided date
+ String inputStringTwo = "zoom; 1; 5/10/2020";
+ Exception secondE = assertThrows(InvalidEventDateException.class, () -> {
+ Command doneCommand = DoneCommand.parse(inputStringTwo);
+ doneCommand.execute(data, ui, storage);
+ });
+
+ String expectedStringTwo = "This event does not occur on this date.";
+ String actualStringTwo = secondE.getMessage();
+ assertEquals(expectedStringTwo, actualStringTwo);
+
+ // Semicolons not used to separate fields
+ String inputStringThree = "personal 1";
+ Exception thirdE = assertThrows(MissingSemicolonException.class, () -> {
+ Command doneCommand = DoneCommand.parse(inputStringThree);
+ doneCommand.execute(data, ui, storage);
+ });
+
+ String expectedStringThree = "Remember to separate input fields with a ';'." + System.lineSeparator()
+ + "The format for done is: \"done ; ; []\".";
+ String actualStringThree = thirdE.getMessage();
+ assertEquals(expectedStringThree, actualStringThree);
+
+ // Event index not given
+ String inputStringFour = "zoom;";
+ Exception fourthE = assertThrows(WrongNumberOfArgumentsException.class, () -> {
+ Command doneCommand = DoneCommand.parse(inputStringFour);
+ doneCommand.execute(data, ui, storage);
+ });
+
+ String expectedStringFour = "Event type or index is missing." + System.lineSeparator()
+ + "The format for done is: \"done ; ; []\".";
+ String actualStringFour = fourthE.getMessage();
+ assertEquals(expectedStringFour, actualStringFour);
+
+ // Event index is not an integer
+ String inputStringFive = "zoom; a";
+ Exception fifthE = assertThrows(WrongNumberFormatException.class, () -> {
+ Command doneCommand = DoneCommand.parse(inputStringFive);
+ doneCommand.execute(data, ui, storage);
+ });
+
+ String expectedStringFive = "Event index given is not an integer." + System.lineSeparator()
+ + "The format for done is: \"done ; ; []\".";
+ String actualStringFive = fifthE.getMessage();
+ assertEquals(expectedStringFive, actualStringFive);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/command/ExtractCommandTest.java b/src/test/java/seedu/duke/command/ExtractCommandTest.java
new file mode 100644
index 0000000000..612a91d6cf
--- /dev/null
+++ b/src/test/java/seedu/duke/command/ExtractCommandTest.java
@@ -0,0 +1,291 @@
+package seedu.duke.command;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import seedu.duke.data.UserData;
+import seedu.duke.exception.DukeException;
+import seedu.duke.exception.InvalidExtractCommandException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class ExtractCommandTest {
+ private final PrintStream standardOut = System.out;
+ private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
+
+ @Nested
+ class TestEmptyTextSubject {
+ public void setUp() {
+ String userInput = " \r\n extractend \r\n";
+ ByteArrayInputStream inStream = new ByteArrayInputStream(userInput.getBytes());
+ System.setIn(inStream);
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Test
+ void execute_emptyTextSubjectOfEvent_InvalidExtractCommandException() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+ String extractInput = ";";
+ assertThrows(InvalidExtractCommandException.class, () -> {
+ ExtractCommand extractCommand = new ExtractCommand(extractInput);
+ extractCommand.execute(data, ui, storage);
+ });
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(standardOut);
+ }
+ }
+
+ @Nested
+ class TestEmptyTextBody {
+ @BeforeEach
+ public void setUp() {
+ String userInput = " \r\n extractend \r\n";
+ ByteArrayInputStream inStream = new ByteArrayInputStream(userInput.getBytes());
+ System.setIn(inStream);
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Test
+ void execute_emptyTextBodyOfEvent_InvalidExtractCommandException() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+ String extractInput = "quiz; ";
+ assertThrows(InvalidExtractCommandException.class, () -> {
+ ExtractCommand extractCommand = new ExtractCommand(extractInput);
+ extractCommand.execute(data, ui, storage);
+ });
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(standardOut);
+ }
+ }
+
+ @Nested
+ class TestPersonalEventNoDateNoTime {
+ @BeforeEach
+ public void setUp() {
+ String userInput = "Hi class, please note there will be a meeting soon \r\n extractend \r\n";
+ ByteArrayInputStream inStream = new ByteArrayInputStream(userInput.getBytes());
+ System.setIn(inStream);
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Test
+ void execute_CreatePersonalEventWithNoDateOrTime_printSuccessfulAdd() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+
+ String extractInput = "Meeting;";
+ Command extractCommand = new ExtractCommand(extractInput);
+ extractCommand.execute(data, ui, storage);
+ assertEquals("Copy and paste or enter the body of the text you want to extract from!"
+ + System.lineSeparator()
+ + "At the end of your text, press enter to go to the next line, enter 'extractend' "
+ + "with no quotation marks and press enter once more." + System.lineSeparator()
+ + "_________________________________" + System.lineSeparator()
+ + "No date detected for this text body!" + System.lineSeparator()
+ + "No timing detected for this text body!" + System.lineSeparator()
+ + "Since no date was detected in the text body, "
+ + "the personal event will only contain the description." + System.lineSeparator()
+ + "You have successfully added this event to your list!" + System.lineSeparator()
+ + "[P][X] Meeting" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(standardOut);
+ }
+ }
+
+
+ @Nested
+ class TestPersonalEventDateOnly {
+ @BeforeEach
+ public void setUp() {
+ String userInput = "Hi class, please note there will be a makeup lesson on 17th nov"
+ + " and 32 jan \r\n extractend \r\n";
+ ByteArrayInputStream inStream = new ByteArrayInputStream(userInput.getBytes());
+ System.setIn(inStream);
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Test
+ void execute_CreatePersonalEventWithDateOnly_printSuccessfulAdd() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+
+ String extractInput = "CS2113T Makeup Lesson;";
+ Command extractCommand = new ExtractCommand(extractInput);
+ extractCommand.execute(data, ui, storage);
+ assertEquals("Copy and paste or enter the body of the text you want to extract from!"
+ + System.lineSeparator()
+ + "At the end of your text, press enter to go to the next line, enter 'extractend' "
+ + "with no quotation marks and press enter once more." + System.lineSeparator()
+ + "_________________________________" + System.lineSeparator()
+ + "One date detected and chosen: 2020-11-17" + System.lineSeparator()
+ + "No timing detected for this text body!" + System.lineSeparator()
+ + "Since no time was detected in the text body, "
+ + "the personal event will only contain the description and the date."
+ + System.lineSeparator()
+ + "You have successfully added this event to your list!" + System.lineSeparator()
+ + "[P][X] CS2113T Makeup Lesson on 2020-11-17" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(standardOut);
+ }
+ }
+
+
+ @Nested
+ class TestPersonalEventOneDateAndTime {
+ @BeforeEach
+ public void setUp() {
+ String userInput = "Hi class, please note there will be a quiz \r\n on oct 5 2020 "
+ + "at 4pm \r\n extractend \r\n";
+ ByteArrayInputStream inStream = new ByteArrayInputStream(userInput.getBytes());
+ System.setIn(inStream);
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Test
+ void execute_CreatePersonalEventOneDateAndTime_printSuccessfulAdd() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+ Command extractCommand = new ExtractCommand("CG2271 Quiz;");
+ extractCommand.execute(data, ui, storage);
+ assertEquals("Copy and paste or enter the body of the text you want to extract from!"
+ + System.lineSeparator()
+ + "At the end of your text, press enter to go to the next line, enter 'extractend' "
+ + "with no quotation marks and press enter once more." + System.lineSeparator()
+ + "_________________________________" + System.lineSeparator()
+ + "One date detected and chosen: 2020-10-05" + System.lineSeparator()
+ + "One timing detected and chosen: 16:00" + System.lineSeparator()
+ + "You have successfully added this event to your list!" + System.lineSeparator()
+ + "[P][X] CG2271 Quiz on 2020-10-05, 16:00" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(standardOut);
+ }
+ }
+
+
+ @Nested
+ class TestPersonalEventMultipleDateAndTime {
+ @BeforeEach
+ public void setUp() {
+ String userInput = "Hi class, please note there will be a quiz \r\n "
+ + "on jan 30 or may 15 at 06:00 am or 7pm \r\n extractend \r\n 2 \r\n 2 \r\n";
+ ByteArrayInputStream inStream = new ByteArrayInputStream(userInput.getBytes());
+ System.setIn(inStream);
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Test
+ void execute_CreatePersonalEventMultipleDateAndTime_printSuccessfulAdd() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+ Command extractCommand = new ExtractCommand("GEH quiz;");
+ extractCommand.execute(data, ui, storage);
+ assertEquals("Copy and paste or enter the body of the text you want to extract from!"
+ + System.lineSeparator()
+ + "At the end of your text, press enter to go to the next line, enter 'extractend' "
+ + "with no quotation marks and press enter once more." + System.lineSeparator()
+ + "_________________________________" + System.lineSeparator()
+ + "We have detected 2 dates in this text body!" + System.lineSeparator()
+ + "Please select the date you want for this event from the list below!"
+ + System.lineSeparator()
+ + "_________________________________" + System.lineSeparator()
+ + "1. 2020-01-30" + System.lineSeparator()
+ + "2. 2020-05-15" + System.lineSeparator()
+ + "_________________________________" + System.lineSeparator()
+ + "We have detected 2 timings in this text body!" + System.lineSeparator()
+ + "Please select the time you want for this event from the list below!"
+ + System.lineSeparator()
+ + "_________________________________" + System.lineSeparator()
+ + "1. 06:00" + System.lineSeparator()
+ + "2. 19:00" + System.lineSeparator()
+ + "_________________________________" + System.lineSeparator()
+ + "You have successfully added this event to your list!" + System.lineSeparator()
+ + "[P][X] GEH quiz on 2020-05-15, 19:00" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(standardOut);
+ }
+ }
+
+ @Nested
+ class TestZoomEventLinkOnly {
+ @BeforeEach
+ public void setUp() {
+ String userInput = "The seminar will be held via Zoom. You may tune in using this link:\n"
+ + "\n" + "https://nus-sg.zoom.us/j/9290988107?pwd=dDZSQ0lTa0loaTRPc1F1d01hbWVFZz09"
+ + " \r\n extractend \r\n";
+ ByteArrayInputStream inStream = new ByteArrayInputStream(userInput.getBytes());
+ System.setIn(inStream);
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Test
+ void execute_CreateZoomEventLinkOnly_printSuccessfulAdd() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+ Command extractCommand = new ExtractCommand("Seminar;");
+ extractCommand.execute(data, ui, storage);
+ assertEquals("Copy and paste or enter the body of the text you want to extract from!"
+ + System.lineSeparator()
+ + "At the end of your text, press enter to go to the next line, enter 'extractend' "
+ + "with no quotation marks and press enter once more." + System.lineSeparator()
+ + "_________________________________" + System.lineSeparator()
+ + "One zoom link detected and chosen:"
+ + " https://nus-sg.zoom.us/j/9290988107?pwd=dDZSQ0lTa0loaTRPc1F1d01hbWVFZz09"
+ + System.lineSeparator()
+ + "No date detected for this text body!" + System.lineSeparator()
+ + "No timing detected for this text body!" + System.lineSeparator()
+ + "Since no date was detected in the text body,"
+ + " the zoom event will only contain the description and zoom link."
+ + System.lineSeparator()
+ + "You have successfully added this event to your list!" + System.lineSeparator()
+ + "[Z][X] Seminar, Link:"
+ + " https://nus-sg.zoom.us/j/9290988107?pwd=dDZSQ0lTa0loaTRPc1F1d01hbWVFZz09"
+ + System.lineSeparator(),
+ outputStreamCaptor.toString());
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(standardOut);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/command/GoalCommandTest.java b/src/test/java/seedu/duke/command/GoalCommandTest.java
new file mode 100644
index 0000000000..5943f1887e
--- /dev/null
+++ b/src/test/java/seedu/duke/command/GoalCommandTest.java
@@ -0,0 +1,87 @@
+package seedu.duke.command;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.data.UserData;
+import seedu.duke.exception.DukeException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class GoalCommandTest {
+ private final PrintStream standardOut = System.out;
+ private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
+ private final Ui ui = new Ui();
+ private Storage store = new Storage("goalStoreTest", ui);
+
+ @BeforeEach
+ public void setUp() {
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Test
+ void execute_oneString_printChangeGoal() throws DukeException {
+ String inputString = "Fly like a butterfly";
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Command command = new GoalCommand(inputString);
+ command.execute(data, ui, store);
+ StringWriter expectedStringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(expectedStringWriter);
+ printWriter.println("Goal changed to: " + inputString);
+ printWriter.close();
+ String expected = expectedStringWriter.toString();
+ assertEquals(expected, outputStreamCaptor.toString());
+ }
+
+ @Test
+ void execute_blankInput_printGoal() throws DukeException {
+ String inputString = "";
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Command command = new GoalCommand(inputString);
+ command.execute(data, ui, store);
+ StringWriter expectedStringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(expectedStringWriter);
+ printWriter.println("You have no goal! Why not set one now?");
+ printWriter.close();
+ String expected = expectedStringWriter.toString();
+ assertEquals(expected, outputStreamCaptor.toString());
+ }
+
+ @Test
+ void execute_twoSeparateStringThenBlankInput_printChangeGoal() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ String inputString1 = "Fly like a butterfly";
+ Command command = new GoalCommand(inputString1);
+ command.execute(data, ui, store);
+ String inputString2 = "Float like a bumblebee";
+ command = new GoalCommand(inputString2);
+ command.execute(data, ui, store);
+ String blankString = "";
+ command = new GoalCommand(blankString);
+ command.execute(data, ui, store);
+ StringWriter expectedStringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(expectedStringWriter);
+ printWriter.println("Goal changed to: " + inputString1);
+ printWriter.println("Goal changed to: " + inputString2);
+ printWriter.println("Goal: " + inputString2);
+ printWriter.close();
+ String expected = expectedStringWriter.toString();
+ assertEquals(expected, outputStreamCaptor.toString());
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(standardOut);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/command/ListCommandTest.java b/src/test/java/seedu/duke/command/ListCommandTest.java
new file mode 100644
index 0000000000..7eba1d80cc
--- /dev/null
+++ b/src/test/java/seedu/duke/command/ListCommandTest.java
@@ -0,0 +1,97 @@
+package seedu.duke.command;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.data.UserData;
+import seedu.duke.event.EventList;
+import seedu.duke.exception.DukeException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ListCommandTest {
+ private final PrintStream standardOut = System.out;
+ private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
+
+ @Test
+ void execute_noArguments_listEventListNames() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+
+ System.setOut(new PrintStream(outputStreamCaptor));
+ Command listCommand = ListCommand.parse("");
+ listCommand.execute(data, ui, storage);
+ assertEquals("Following are all existing lists:" + System.lineSeparator()
+ + "Personal" + System.lineSeparator()
+ + "Timetable" + System.lineSeparator()
+ + "Zoom" + System.lineSeparator()
+ + "'list All' will list all existing lists." + System.lineSeparator(),
+ outputStreamCaptor.toString());
+ }
+
+ @Test
+ void execute_zoomEvent_listZoomEvents() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+
+ // Add zoom event to data
+ String zoomInput = "zoom; Math class; zoom.com; 09/10/2000; 1300";
+ Command addCommand = new AddCommand(zoomInput);
+ addCommand.execute(data, ui, storage);
+
+ // Execute list command
+ String inputString = "zoom";
+ System.setOut(new PrintStream(outputStreamCaptor));
+
+ Command listCommand = ListCommand.parse(inputString);
+ listCommand.execute(data, ui, storage);
+ assertEquals("Here is a list of your Zoom events:" + System.lineSeparator()
+ + "1. [Z][X] Math class, Link: zoom.com on 2000-10-09, 13:00" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+ }
+
+ @Test
+ void execute_repeatEvent_listRepeatEvents() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+
+ String personalInput = "personal; party; 09/10/2000; 1300";
+ Command addCommand = new AddCommand(personalInput);
+ addCommand.execute(data, ui, storage);
+
+ String repeatInput = "personal; 1; monthly; 4;";
+ Command repeatCommand = RepeatCommand.parse(repeatInput);
+ repeatCommand.execute(data, ui, storage);
+
+ // Execute list command
+ String inputString = "all";
+ System.setOut(new PrintStream(outputStreamCaptor));
+
+ Command listCommand = ListCommand.parse(inputString);
+ listCommand.execute(data, ui, storage);
+ assertEquals("Here is a list of your Personal events:" + System.lineSeparator()
+ + "1. [P][X] party on 2000-10-09, 13:00 is also on:" + System.lineSeparator()
+ + " 1. 2000-11-09 13:00 [X]" + System.lineSeparator()
+ + " 2. 2000-12-09 13:00 [X]" + System.lineSeparator()
+ + " 3. 2001-01-09 13:00 [X]" + System.lineSeparator()
+ + " 4. 2001-02-09 13:00 [X]" + System.lineSeparator()
+ + "_________________________________" + System.lineSeparator()
+ + "You have no Timetable events!" + System.lineSeparator()
+ + "_________________________________" + System.lineSeparator()
+ + "You have no Zoom events!" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(standardOut);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/command/NoteCommandTest.java b/src/test/java/seedu/duke/command/NoteCommandTest.java
new file mode 100644
index 0000000000..c7a7bd1d49
--- /dev/null
+++ b/src/test/java/seedu/duke/command/NoteCommandTest.java
@@ -0,0 +1,216 @@
+package seedu.duke.command;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import seedu.duke.data.UserData;
+import seedu.duke.exception.DukeException;
+import seedu.duke.exception.InvalidIndexException;
+import seedu.duke.exception.InvalidListException;
+import seedu.duke.exception.WrongNumberFormatException;
+import seedu.duke.exception.WrongNumberOfArgumentsException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class NoteCommandTest {
+
+ private final PrintStream standardOut = System.out;
+ private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
+
+ @Nested
+ class TestCreateNote {
+
+ @BeforeEach
+ public void setUp() {
+ String extractInput = "Meeting is held in NUS COM 2 \r\n Attire is black t-shirt \r\n noteend \r\n";
+ ByteArrayInputStream inStream = new ByteArrayInputStream(extractInput.getBytes());
+ System.setIn(inStream);
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Test
+ void execute_CreateNote_printSuccessfulCreate() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+ String input = "personal; sleep";
+ Command addPersonalEvent = new AddCommand(input);
+ addPersonalEvent.execute(data, ui, storage);
+
+ NoteCommand testCreateNote = new NoteCommand("personal; 1");
+ testCreateNote.execute(data, ui, storage);
+ StringWriter expectedStringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(expectedStringWriter);
+ printWriter.println("You have successfully added this event to your list!");
+ printWriter.println("[P][X] sleep");
+ printWriter.println("Please type in your notes."
+ + " To stop note taking, ensure that you are in a new line"
+ + " and type 'noteend', and press enter");
+ printWriter.println("You have successfully written the note for this event!");
+ printWriter.println("[P][X] sleep");
+ printWriter.println("Meeting is held in NUS COM 2");
+ printWriter.println("Attire is black t-shirt");
+ printWriter.close();
+ String expected = expectedStringWriter.toString();
+ String actual = outputStreamCaptor.toString();
+ String[] remove = actual.split("---------");
+
+ StringWriter actualString = new StringWriter();
+ PrintWriter printWriterActual = new PrintWriter(actualString);
+ for (int i = 0; i < remove.length; i++) {
+ if (i % 2 == 0) {
+ printWriterActual.println(remove[i].trim());
+ }
+
+ }
+ String withoutTimestamp = actualString.toString();
+ assertEquals(expected, withoutTimestamp);
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(standardOut);
+ }
+ }
+
+ @Nested
+ class TestWrongIndex {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+
+ @BeforeEach
+ public void setUp() {
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Test
+ public void execute_withWrongIndex_invalidIndexException() throws DukeException {
+ String input = "personal; sleep";
+ Command addPersonalEvent = new AddCommand(input);
+ addPersonalEvent.execute(data, ui, storage);
+
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+ assertThrows(InvalidIndexException.class, () -> {
+ NoteCommand testNotesWithInvalidIndex = new NoteCommand("personal; 7");
+ testNotesWithInvalidIndex.execute(data, ui, storage);
+ });
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(standardOut);
+ }
+ }
+
+ @Nested
+ class TestWithWrongList {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+
+ @BeforeEach
+ public void setUp() {
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Test
+ public void execute_withWrongList_invalidListException() throws DukeException {
+ String input = "personal; sleep";
+ Command addPersonalEvent = new AddCommand(input);
+ addPersonalEvent.execute(data, ui, storage);
+
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+ assertThrows(InvalidListException.class, () -> {
+ NoteCommand testNotesWithInvalidIndex = new NoteCommand("persona; 1");
+ testNotesWithInvalidIndex.execute(data, ui, storage);
+ });
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(standardOut);
+ }
+ }
+
+ @Nested
+ class TestWithoutListTypeOrIndex {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+
+ @BeforeEach
+ public void setUp() {
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Test
+ public void execute_withoutListTypeorIndex_wrongNumberOfArgumentsException() throws DukeException {
+ String input = "personal; sleep";
+ Command addPersonalEvent = new AddCommand(input);
+ addPersonalEvent.execute(data, ui, storage);
+
+
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+ assertThrows(WrongNumberOfArgumentsException.class, () -> {
+ NoteCommand testNotesWithMissingArguments = new NoteCommand("personal");
+ testNotesWithMissingArguments.execute(data, ui, storage);
+ });
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(standardOut);
+ }
+ }
+
+ @Nested
+ class TestIndexIsNotaNumber {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+
+ @BeforeEach
+ public void setUp() {
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Test
+ public void execute_withIndexIsNotaNumber_wrongNumberFormatException() throws DukeException {
+ String input = "personal; sleep";
+ Command addPersonalEvent = new AddCommand(input);
+ addPersonalEvent.execute(data, ui, storage);
+
+
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+ assertThrows(WrongNumberFormatException.class, () -> {
+ NoteCommand testNotesWithInvalidIndex = new NoteCommand("personal; a");
+ testNotesWithInvalidIndex.execute(data, ui, storage);
+ });
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(standardOut);
+ }
+ }
+
+}
diff --git a/src/test/java/seedu/duke/command/ReminderCommandTest.java b/src/test/java/seedu/duke/command/ReminderCommandTest.java
new file mode 100644
index 0000000000..450d189a0a
--- /dev/null
+++ b/src/test/java/seedu/duke/command/ReminderCommandTest.java
@@ -0,0 +1,96 @@
+package seedu.duke.command;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.data.UserData;
+import seedu.duke.exception.DukeException;
+import seedu.duke.parser.DateTimeParser;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.time.LocalDate;
+
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ReminderCommandTest {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+
+ private final PrintStream standardOut = System.out;
+ private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
+
+ @BeforeEach
+ public void setUp() {
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Test
+ public void execute_withoutEvents_printNoEvents() throws DukeException {
+ ReminderCommand testWithEvents = new ReminderCommand();
+ testWithEvents.execute(data, ui, storage);
+ StringWriter expectedStringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(expectedStringWriter);
+ printWriter.println("You have no events today!");
+ printWriter.close();
+ String expected = expectedStringWriter.toString();
+ assertEquals(expected, outputStreamCaptor.toString());
+ }
+
+ @Test
+ public void execute_withEvents_printEventsToday() throws DukeException {
+ LocalDate current = LocalDate.now();
+
+ String input = "timetable; wakeup; " + current.toString() + "; 1000";
+ Command addTimetableEvent = new AddCommand(input);
+ addTimetableEvent.execute(data, ui, storage);
+
+ ReminderCommand testWithEvents = new ReminderCommand();
+ testWithEvents.execute(data, ui, storage);
+ LocalDate printDate = DateTimeParser.dateParser(current.toString());
+ StringWriter expectedStringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(expectedStringWriter);
+ printWriter.println("You have successfully added this event to your list!");
+ printWriter.println("[T][X] wakeup on " + printDate + ", 10:00");
+ printWriter.println("You have the following events today: ");
+ printWriter.println("_________Events With Time________");
+ printWriter.println("[T][X] wakeup on " + printDate + ", 10:00");
+ printWriter.close();
+ String expected = expectedStringWriter.toString();
+ assertEquals(expected, outputStreamCaptor.toString());
+ }
+
+ @Test
+ public void execute_withRepeatedEvents_printEventsToday() throws DukeException {
+ LocalDate current = LocalDate.now();
+ LocalDate last = current.minusWeeks(1);
+
+ String input = "timetable; wakeup; " + last.toString() + "; 1000";
+ Command addTimetableEvent = new AddCommand(input);
+ addTimetableEvent.execute(data, ui, storage);
+ String repeatInput = "timetable; 1; weekly; 1";
+ Command repeatCommand = RepeatCommand.parse(repeatInput);
+ repeatCommand.execute(data, ui, storage);
+ ReminderCommand testWithEvents = new ReminderCommand();
+ testWithEvents.execute(data, ui, storage);
+ LocalDate printLastDate = DateTimeParser.dateParser(last.toString());
+ LocalDate printDate = DateTimeParser.dateParser(current.toString());
+ StringWriter expectedStringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(expectedStringWriter);
+ printWriter.println("You have successfully added this event to your list!");
+ printWriter.println("[T][X] wakeup on " + printLastDate + ", 10:00");
+ printWriter.println("[T][X] wakeup on " + printLastDate + ", 10:00");
+ printWriter.println("is now repeating weekly for 1 times.");
+ printWriter.println("You have the following events today: ");
+ printWriter.println("_________Events With Time________");
+ printWriter.println("[T][X] wakeup on " + printDate + ", 10:00");
+ printWriter.close();
+ String expected = expectedStringWriter.toString();
+ assertEquals(expected, outputStreamCaptor.toString());
+ }
+}
diff --git a/src/test/java/seedu/duke/command/RepeatCommandTest.java b/src/test/java/seedu/duke/command/RepeatCommandTest.java
new file mode 100644
index 0000000000..79ab844ef5
--- /dev/null
+++ b/src/test/java/seedu/duke/command/RepeatCommandTest.java
@@ -0,0 +1,395 @@
+package seedu.duke.command;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.data.UserData;
+import seedu.duke.exception.DukeException;
+import seedu.duke.exception.InvalidIndexException;
+import seedu.duke.exception.InvalidListException;
+import seedu.duke.exception.InvalidTimeUnitException;
+import seedu.duke.exception.MissingDeadlineRepeatException;
+import seedu.duke.exception.MissingRepeatListException;
+import seedu.duke.exception.WrongNumberFormatException;
+import seedu.duke.exception.WrongNumberOfArgumentsException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+class RepeatCommandTest {
+
+ private final PrintStream standardOut = System.out;
+ private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
+
+ private UserData data = new UserData();
+ private Ui ui = new Ui();
+ private Storage storage = new Storage("data", ui);
+
+ @BeforeEach
+ void setupEventLists() throws DukeException {
+ // Add Personal event to data
+ String personalInput = "personal; party; 09/10/2000; 1300";
+ Command addCommand = new AddCommand(personalInput);
+ addCommand.execute(data, ui, storage);
+
+ personalInput = "personal; surprise";
+ addCommand = new AddCommand(personalInput);
+ addCommand.execute(data, ui, storage);
+
+ personalInput = "personal; hello; 29/02/2020";
+ addCommand = new AddCommand(personalInput);
+ addCommand.execute(data, ui, storage);
+
+ personalInput = "personal; leap ahead; 31/01/2019";
+ addCommand = new AddCommand(personalInput);
+ addCommand.execute(data, ui, storage);
+
+ // Add zoom event to data
+ String zoomInput = "zoom; Math class; zoom.com; 09/10/2000; 1300";
+ addCommand = new AddCommand(zoomInput);
+ addCommand.execute(data, ui, storage);
+
+ //Add Timetable Event to Data
+ String timeTableInput = "timetable; Science class; S17; 17/10/2000; 1500";
+ addCommand = new AddCommand(timeTableInput);
+ addCommand.execute(data, ui, storage);
+
+
+ }
+
+
+ @Test
+ void repeat_personalEventMonthly_personalEventRepeatedMonthly() throws DukeException {
+
+
+ // Create Repeat Command
+ String inputString = "personal; 1; monthly; 4;";
+ System.setOut(new PrintStream(outputStreamCaptor));
+
+ Command repeatCommand = RepeatCommand.parse(inputString);
+ repeatCommand.execute(data, ui, storage);
+ assertEquals("[P][X] party on 2000-10-09, 13:00" + System.lineSeparator()
+ + "is now repeating monthly for 4 times." + System.lineSeparator(),
+ outputStreamCaptor.toString());
+
+ //check the dates reported and erase previous output
+ inputString = "personal; 1;";
+ outputStreamCaptor.reset();
+
+ repeatCommand = RepeatCommand.parse(inputString);
+ repeatCommand.execute(data, ui, storage);
+
+ assertEquals("[P][X] party on 2000-10-09, 13:00 is also on:"
+ + System.lineSeparator()
+ + " 1. 2000-11-09 13:00 [X]" + System.lineSeparator()
+ + " 2. 2000-12-09 13:00 [X]" + System.lineSeparator()
+ + " 3. 2001-01-09 13:00 [X]" + System.lineSeparator()
+ + " 4. 2001-02-09 13:00 [X]" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+ }
+
+ @Test
+ void repeat_personalEventMonthlyNoTime_personalEventRepeatedMonthly() throws DukeException {
+
+
+ // Create Repeat Command
+ String inputString = "personal; 3; monthly; 4;";
+ System.setOut(new PrintStream(outputStreamCaptor));
+
+ Command repeatCommand = RepeatCommand.parse(inputString);
+ repeatCommand.execute(data, ui, storage);
+ assertEquals("[P][X] hello on 2020-02-29" + System.lineSeparator()
+ + "is now repeating monthly for 4 times." + System.lineSeparator(),
+ outputStreamCaptor.toString());
+
+ //check the dates reported and erase previous output
+ inputString = "personal; 3;";
+ outputStreamCaptor.reset();
+
+ repeatCommand = RepeatCommand.parse(inputString);
+ repeatCommand.execute(data, ui, storage);
+
+ assertEquals("[P][X] hello on 2020-02-29 is also on:"
+ + System.lineSeparator()
+ + " 1. 2020-03-29 [X]" + System.lineSeparator()
+ + " 2. 2020-04-29 [X]" + System.lineSeparator()
+ + " 3. 2020-05-29 [X]" + System.lineSeparator()
+ + " 4. 2020-06-29 [X]" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+ }
+
+ @Test
+ void repeat_personalEventMonthlyLeapYearFinalDay_personalEventRepeatedMonthly() throws DukeException {
+
+
+ // Create Repeat Command
+ String inputString = "personal; 4; monthly; 24;";
+ System.setOut(new PrintStream(outputStreamCaptor));
+
+ Command repeatCommand = RepeatCommand.parse(inputString);
+ repeatCommand.execute(data, ui, storage);
+ assertEquals("[P][X] leap ahead on 2019-01-31" + System.lineSeparator()
+ + "is now repeating monthly for 24 times." + System.lineSeparator(),
+ outputStreamCaptor.toString());
+
+ //check the dates reported and erase previous output
+ inputString = "personal; 4;";
+ outputStreamCaptor.reset();
+
+ repeatCommand = RepeatCommand.parse(inputString);
+ repeatCommand.execute(data, ui, storage);
+
+ assertEquals("[P][X] leap ahead on 2019-01-31 is also on:"
+ + System.lineSeparator()
+ + " 1. 2019-02-28 [X]" + System.lineSeparator()
+ + " 2. 2019-03-31 [X]" + System.lineSeparator()
+ + " 3. 2019-04-30 [X]" + System.lineSeparator()
+ + " 4. 2019-05-31 [X]" + System.lineSeparator()
+ + " 5. 2019-06-30 [X]" + System.lineSeparator()
+ + " 6. 2019-07-31 [X]" + System.lineSeparator()
+ + " 7. 2019-08-31 [X]" + System.lineSeparator()
+ + " 8. 2019-09-30 [X]" + System.lineSeparator()
+ + " 9. 2019-10-31 [X]" + System.lineSeparator()
+ + " 10. 2019-11-30 [X]" + System.lineSeparator()
+ + " 11. 2019-12-31 [X]" + System.lineSeparator()
+ + " 12. 2020-01-31 [X]" + System.lineSeparator()
+ + " 13. 2020-02-29 [X]" + System.lineSeparator()
+ + " 14. 2020-03-31 [X]" + System.lineSeparator()
+ + " 15. 2020-04-30 [X]" + System.lineSeparator()
+ + " 16. 2020-05-31 [X]" + System.lineSeparator()
+ + " 17. 2020-06-30 [X]" + System.lineSeparator()
+ + " 18. 2020-07-31 [X]" + System.lineSeparator()
+ + " 19. 2020-08-31 [X]" + System.lineSeparator()
+ + " 20. 2020-09-30 [X]" + System.lineSeparator()
+ + " 21. 2020-10-31 [X]" + System.lineSeparator()
+ + " 22. 2020-11-30 [X]" + System.lineSeparator()
+ + " 23. 2020-12-31 [X]" + System.lineSeparator()
+ + " 24. 2021-01-31 [X]" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+ }
+
+ @Test
+ void repeat_personalPrintRepeatNoExist_MissingRepeatListExceptionThrown() throws DukeException {
+
+
+ try {
+ // Create Repeat Command
+ String inputString = "personal; 4;";
+ System.setOut(new PrintStream(outputStreamCaptor));
+
+ Command repeatCommand = RepeatCommand.parse(inputString);
+ repeatCommand.execute(data, ui, storage);
+ fail("This command should have thrown an exception");
+ } catch (MissingRepeatListException e) {
+ assertTrue(true);
+ } catch (Exception e) {
+ fail("The wrong exception type was thrown");
+ }
+
+ }
+
+ @Test
+ void repeat_zoomEventWeekly_zoomEventRepeatedWeekly() throws DukeException {
+
+ //creating repeat command
+ String inputString = "zoom; 1; weekly; 3;";
+ System.setOut(new PrintStream(outputStreamCaptor));
+
+ Command repeatCommand = RepeatCommand.parse(inputString);
+ repeatCommand.execute(data, ui, storage);
+ assertEquals("[Z][X] Math class, Link: zoom.com on 2000-10-09, 13:00" + System.lineSeparator()
+ + "is now repeating weekly for 3 times." + System.lineSeparator(),
+ outputStreamCaptor.toString());
+ //check the dates reported and erase previous output
+ inputString = "zoom; 1;";
+ outputStreamCaptor.reset();
+
+ repeatCommand = RepeatCommand.parse(inputString);
+ repeatCommand.execute(data, ui, storage);
+
+ assertEquals("[Z][X] Math class, Link: zoom.com on 2000-10-09, 13:00 is also on:"
+ + System.lineSeparator()
+ + " 1. 2000-10-16 13:00 [X]" + System.lineSeparator()
+ + " 2. 2000-10-23 13:00 [X]" + System.lineSeparator()
+ + " 3. 2000-10-30 13:00 [X]" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+
+ }
+
+ @Test
+ void repeat_timeTableEventDaily_timeTableEventRepeatedDaily() throws DukeException {
+
+ //creating repeat command
+ String inputString = "timetable; 1; daily; 3;";
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+ Command repeatCommand = RepeatCommand.parse(inputString);
+ repeatCommand.execute(data, ui, storage);
+ assertEquals("[T][X] Science class, Location: S17 on 2000-10-17, 15:00" + System.lineSeparator()
+ + "is now repeating daily for 3 times." + System.lineSeparator(),
+ outputStreamCaptor.toString());
+
+ //check the dates reported and erase previous output
+ inputString = "timetable; 1;";
+ outputStreamCaptor.reset();
+
+ repeatCommand = RepeatCommand.parse(inputString);
+ repeatCommand.execute(data, ui, storage);
+
+ assertEquals("[T][X] Science class, Location: S17 on 2000-10-17, 15:00 is also on:"
+ + System.lineSeparator()
+ + " 1. 2000-10-18 15:00 [X]" + System.lineSeparator()
+ + " 2. 2000-10-19 15:00 [X]" + System.lineSeparator()
+ + " 3. 2000-10-20 15:00 [X]" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+ }
+
+ @Test
+ void repeat_personalEventRepeatDailyMonthlyChange_personalEventDailyToMonthly() throws DukeException {
+
+ //create repeat command for daily
+ String inputString = "personal; 1; daily; 4;";
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+ Command repeatCommand = RepeatCommand.parse(inputString);
+ repeatCommand.execute(data, ui, storage);
+ assertEquals("[P][X] party on 2000-10-09, 13:00" + System.lineSeparator()
+ + "is now repeating daily for 4 times." + System.lineSeparator(),
+ outputStreamCaptor.toString());
+
+ //clear screen and now test changing repeat from daily 4 to monthly 2
+ outputStreamCaptor.reset();
+ inputString = "personal; 1; monthly; 2;";
+
+ repeatCommand = RepeatCommand.parse(inputString);
+ repeatCommand.execute(data, ui, storage);
+
+ assertEquals("[P][X] party on 2000-10-09, 13:00" + System.lineSeparator()
+ + "is now repeating monthly for 2 times." + System.lineSeparator(),
+ outputStreamCaptor.toString());
+
+ //clear screen and check if monthly repeats were successfully recorded
+ outputStreamCaptor.reset();
+ inputString = "personal; 1;";
+
+ repeatCommand = RepeatCommand.parse(inputString);
+ repeatCommand.execute(data, ui, storage);
+
+ assertEquals("[P][X] party on 2000-10-09, 13:00 is also on:"
+ + System.lineSeparator()
+ + " 1. 2000-11-09 13:00 [X]" + System.lineSeparator()
+ + " 2. 2000-12-09 13:00 [X]" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+
+
+
+ }
+
+ @Test
+ void repeat_repeatingEventNoDeadline_missingDeadlineExceptionThrown() {
+ //create repeat command for event with no deadline
+ String inputString = "personal; 2; daily; 4;";
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+
+
+ assertThrows(MissingDeadlineRepeatException.class, () -> {
+ Command repeatCommand = RepeatCommand.parse(inputString);
+ repeatCommand.execute(data, ui, storage);
+ });
+
+
+
+
+ }
+
+ @Test
+ void repeat_repeatingEventWrongIndex_indexOutOfBoundsExceptionThrown() {
+ //create repeat command for an index which does not exist
+ String inputString = "personal; 5; daily; 4;";
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+
+
+ assertThrows(InvalidIndexException.class, () -> {
+ Command repeatCommand = RepeatCommand.parse(inputString);
+ repeatCommand.execute(data, ui, storage);
+ });
+
+
+ }
+
+ @Test
+ void repeat_repeatingEventWrongUnit_invalidTimeUnitExceptionThrown() {
+ //create repeat command for event using wrong time unit
+ String inputString = "personal; 1; fortnightly; 4;";
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+
+
+ assertThrows(InvalidTimeUnitException.class, () -> {
+ Command repeatCommand = RepeatCommand.parse(inputString);
+ repeatCommand.execute(data, ui, storage);
+ });
+
+ }
+
+ @Test
+ void repeat_tooLittleArgumentsProvided_wrongNumberOfArgumentsExceptionThrown() {
+ //create repeat command for event with too little information
+ String inputString = "personal;";
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+
+ assertThrows(WrongNumberOfArgumentsException.class, () -> {
+ Command repeatCommand = RepeatCommand.parse(inputString);
+ });
+
+ }
+
+ @Test
+ void repeat_repeatingEventWrongType_invalidEventListTypeExceptionThrown() {
+
+ //create repeat command for an event type that does not exist
+ String inputString = "holiday; 5; daily; 4;";
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+
+
+ assertThrows(InvalidListException.class, () -> {
+ Command repeatCommand = RepeatCommand.parse(inputString);
+ repeatCommand.execute(data, ui, storage);
+ });
+ }
+
+ @Test
+ void repeat_userTypeWrongNumber_NumberFormatExceptionThrown() {
+
+ //create repeat command using wrong number writing format
+ String inputString = "personal; 1; weekly; three;";
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+
+ assertThrows(WrongNumberFormatException.class, () -> {
+ Command repeatCommand = RepeatCommand.parse(inputString);
+ repeatCommand.execute(data, ui, storage);
+ });
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/command/UndoneCommandTest.java b/src/test/java/seedu/duke/command/UndoneCommandTest.java
new file mode 100644
index 0000000000..4fb8c1fe45
--- /dev/null
+++ b/src/test/java/seedu/duke/command/UndoneCommandTest.java
@@ -0,0 +1,179 @@
+package seedu.duke.command;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.data.UserData;
+import seedu.duke.exception.DukeException;
+import seedu.duke.exception.InvalidEventDateException;
+import seedu.duke.exception.InvalidIndexException;
+import seedu.duke.exception.MissingSemicolonException;
+import seedu.duke.exception.WrongNumberFormatException;
+import seedu.duke.exception.WrongNumberOfArgumentsException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class UndoneCommandTest {
+ private final PrintStream standardOut = System.out;
+ private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
+
+ private final UserData data = new UserData();
+ private final Ui ui = new Ui();
+ private final Storage storage = new Storage("data", ui);
+
+ @BeforeEach
+ void setUp() throws DukeException {
+ // Add Personal events to data
+ String personalInput = "personal; Go out for dinner; 05/05/20; 12:00";
+ Command addCommand = new AddCommand(personalInput);
+ addCommand.execute(data, ui, storage);
+ data.getEventList("Personal").getEventByIndex(0).markAsDone();
+
+ personalInput = "personal; Stay at home; 04/05/20";
+ addCommand = new AddCommand(personalInput);
+ addCommand.execute(data, ui, storage);
+ data.getEventList("Personal").getEventByIndex(1).markAsDone();
+
+ // Add Zoom event to data
+ String zoomInput = "zoom; CS2113T tutorial; zoom.com/blahblah; 03/10/2020; 1330";
+ addCommand = new AddCommand(zoomInput);
+ addCommand.execute(data, ui, storage);
+ data.getEventList("Zoom").getEventByIndex(0).markAsDone();
+
+ // Repeat Zoom event
+ String repeatZoomInput = "zoom; 1; weekly; 1";
+ Command repeatCommand = RepeatCommand.parse(repeatZoomInput);
+ repeatCommand.execute(data, ui, storage);
+ data.getEventList("Zoom").getEventByIndex(0).getRepeatEventList().get(0).markAsDone();
+
+ //Add Timetable Event to Data
+ String timeTableInput = "timetable; Science class; S17; 4/5/2020; 3 pm";
+ addCommand = new AddCommand(timeTableInput);
+ addCommand.execute(data, ui, storage);
+ data.getEventList("Timetable").getEventByIndex(0).markAsDone();
+
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @AfterEach
+ void tearDown() {
+ System.setOut(standardOut);
+ }
+
+ @Test
+ void execute_markValidEventsAsUndone_eventsMarkedUndone() throws DukeException {
+ // First valid event
+ String inputStringOne = "personal; 1";
+ Command undoneCommand = UndoneCommand.parse(inputStringOne);
+ undoneCommand.execute(data, ui, storage);
+
+ String expectedString = "You have successfully marked this event as undone!" + System.lineSeparator()
+ + "[P][X] Go out for dinner on 2020-05-05, 12:00";
+ assertEquals(expectedString, outputStreamCaptor.toString().trim());
+ String actualStatus = data.getEventList("Personal").getEventByIndex(0).getStatus();
+ assertEquals("X", actualStatus);
+
+ // Second valid event
+ String inputStringTwo = "zoom; 1; 10/10/2020";
+ undoneCommand = UndoneCommand.parse(inputStringTwo);
+ undoneCommand.execute(data, ui, storage);
+
+ expectedString = expectedString + System.lineSeparator()
+ + "You have successfully marked this event as undone!" + System.lineSeparator()
+ + "[Z][X] CS2113T tutorial, Link: zoom.com/blahblah on 2020-10-10, 13:30";
+ assertEquals(expectedString, outputStreamCaptor.toString().trim());
+ actualStatus = data.getEventList("Zoom").getEventByIndex(0).getRepeatEventList().get(0).getStatus();
+ assertEquals("X", actualStatus);
+
+ // Third valid event
+ String inputStringThree = "timetable; 1; extra accidental entries";
+ undoneCommand = UndoneCommand.parse(inputStringThree);
+ undoneCommand.execute(data, ui, storage);
+
+ expectedString = expectedString + System.lineSeparator()
+ + "You have successfully marked this event as undone!" + System.lineSeparator()
+ + "[T][X] Science class, Location: S17 on 2020-05-04, 15:00";
+ assertEquals(expectedString, outputStreamCaptor.toString().trim());
+ actualStatus = data.getEventList("Timetable").getEventByIndex(0).getStatus();
+ assertEquals("X", actualStatus);
+
+ // Fourth valid event
+ String inputStringFour = "zoom; 1; 3/10/2020";
+ undoneCommand = UndoneCommand.parse(inputStringFour);
+ undoneCommand.execute(data, ui, storage);
+
+ expectedString = expectedString + System.lineSeparator()
+ + "You have successfully marked this event as undone!" + System.lineSeparator()
+ + "[Z][X] CS2113T tutorial, Link: zoom.com/blahblah on 2020-10-03, 13:30";
+ assertEquals(expectedString, outputStreamCaptor.toString().trim());
+ actualStatus = data.getEventList("Zoom").getEventByIndex(0).getStatus();
+ assertEquals("X", actualStatus);
+ }
+
+ @Test
+ void execute_markInvalidEventsAsUndone_correspondingExceptionThrown() {
+ // Event index exceeds max event index
+ String inputStringOne = "personal; 3";
+ Exception firstE = assertThrows(InvalidIndexException.class, () -> {
+ Command undoneCommand = UndoneCommand.parse(inputStringOne);
+ undoneCommand.execute(data, ui, storage);
+ });
+
+ String expectedStringOne = "Error, no such index is available!";
+ String actualStringOne = firstE.getMessage();
+ assertEquals(expectedStringOne, actualStringOne);
+
+ // Event does not fall on the provided date
+ String inputStringTwo = "zoom; 1; 5/10/2020";
+ Exception secondE = assertThrows(InvalidEventDateException.class, () -> {
+ Command undoneCommand = UndoneCommand.parse(inputStringTwo);
+ undoneCommand.execute(data, ui, storage);
+ });
+
+ String expectedStringTwo = "This event does not occur on this date.";
+ String actualStringTwo = secondE.getMessage();
+ assertEquals(expectedStringTwo, actualStringTwo);
+
+ // Semicolons not used to separate fields
+ String inputStringThree = "personal 1";
+ Exception thirdE = assertThrows(MissingSemicolonException.class, () -> {
+ Command undoneCommand = UndoneCommand.parse(inputStringThree);
+ undoneCommand.execute(data, ui, storage);
+ });
+
+ String expectedStringThree = "Remember to separate input fields with a ';'." + System.lineSeparator()
+ + "The format for undone is: \"undone ; ; []\".";
+ String actualStringThree = thirdE.getMessage();
+ assertEquals(expectedStringThree, actualStringThree);
+
+ // Event index not given
+ String inputStringFour = "zoom;";
+ Exception fourthE = assertThrows(WrongNumberOfArgumentsException.class, () -> {
+ Command undoneCommand = UndoneCommand.parse(inputStringFour);
+ undoneCommand.execute(data, ui, storage);
+ });
+
+ String expectedStringFour = "Event type or index is missing." + System.lineSeparator()
+ + "The format for undone is: \"undone ; ; []\".";
+ String actualStringFour = fourthE.getMessage();
+ assertEquals(expectedStringFour, actualStringFour);
+
+ // Event index is not an integer
+ String inputStringFive = "zoom; a";
+ Exception fifthE = assertThrows(WrongNumberFormatException.class, () -> {
+ Command undoneCommand = UndoneCommand.parse(inputStringFive);
+ undoneCommand.execute(data, ui, storage);
+ });
+
+ String expectedStringFive = "Event index given is not an integer." + System.lineSeparator()
+ + "The format for undone is: \"undone ; ; []\".";
+ String actualStringFive = fifthE.getMessage();
+ assertEquals(expectedStringFive, actualStringFive);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/command/ViewCommandTest.java b/src/test/java/seedu/duke/command/ViewCommandTest.java
new file mode 100644
index 0000000000..32a049dc38
--- /dev/null
+++ b/src/test/java/seedu/duke/command/ViewCommandTest.java
@@ -0,0 +1,166 @@
+package seedu.duke.command;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import seedu.duke.data.UserData;
+import seedu.duke.exception.DukeException;
+import seedu.duke.exception.InvalidIndexException;
+import seedu.duke.exception.WrongNumberFormatException;
+import seedu.duke.exception.WrongNumberOfArgumentsException;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.Ui;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class ViewCommandTest {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+
+ private final PrintStream standardOut = System.out;
+ private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
+
+ @BeforeEach
+ public void setUp() {
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Nested
+ class TestViewNote {
+
+
+ @BeforeEach
+ public void setUp() {
+ String extractInput = "Meeting is held in NUS COM 2 \r\n Attire is black t-shirt \r\n noteend \r\n";
+ ByteArrayInputStream inStream = new ByteArrayInputStream(extractInput.getBytes());
+ System.setIn(inStream);
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Test
+ void execute_ViewNote_printSuccessfulView() throws DukeException {
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ Storage storage = new Storage("data", ui);
+ String input = "personal; sleep";
+ Command addPersonalEvent = new AddCommand(input);
+ addPersonalEvent.execute(data, ui, storage);
+
+ NoteCommand createNote = new NoteCommand("personal; 1");
+ createNote.execute(data, ui, storage);
+
+ ViewCommand testViewNote = new ViewCommand("personal; 1");
+ testViewNote.execute(data, ui, storage);
+
+ StringWriter expectedStringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(expectedStringWriter);
+ printWriter.println("You have successfully added this event to your list!");
+ printWriter.println("[P][X] sleep");
+ printWriter.println("Please type in your notes."
+ + " To stop note taking, ensure that you are in a new line and type 'noteend', and press enter");
+ printWriter.println("You have successfully written the note for this event!");
+ printWriter.println("[P][X] sleep");
+ printWriter.println("Meeting is held in NUS COM 2");
+ printWriter.println("Attire is black t-shirt");
+ printWriter.println("These are the notes that you have taken:");
+ printWriter.println("Meeting is held in NUS COM 2");
+ printWriter.println("Attire is black t-shirt");
+ printWriter.close();
+ String expected = expectedStringWriter.toString();
+ String actual = outputStreamCaptor.toString();
+ String[] remove = actual.split("---------");
+
+ StringWriter actualString = new StringWriter();
+ PrintWriter printWriterActual = new PrintWriter(actualString);
+ for (int i = 0; i < remove.length; i++) {
+ if (i % 2 == 0) {
+ printWriterActual.println(remove[i].trim());
+ }
+
+ }
+ String withoutTimestamp = actualString.toString();
+ assertEquals(expected, withoutTimestamp);
+ }
+ }
+
+ @Test
+ public void execute_validEventWithoutNotes_printDeadline() throws DukeException {
+ String input = "personal; sleep";
+ Command addPersonalEvent = new AddCommand(input);
+ addPersonalEvent.execute(data, ui, storage);
+
+ ViewCommand testViewWithoutNotes = new ViewCommand("personal; 1");
+ testViewWithoutNotes.execute(data, ui, storage);
+ StringWriter expectedStringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(expectedStringWriter);
+ printWriter.println("You have successfully added this event to your list!");
+ printWriter.println("[P][X] sleep");
+ printWriter.println("You have not written any notes for this event!");
+ printWriter.close();
+ String expected = expectedStringWriter.toString();
+ assertEquals(expected,
+ outputStreamCaptor.toString());
+ }
+
+ @Test
+ public void execute_withoutIndex_wrongNumberOfArgumentsException() throws DukeException {
+ String input = "personal; sleep";
+ Command addPersonalEvent = new AddCommand(input);
+ addPersonalEvent.execute(data, ui, storage);
+
+
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+ assertThrows(WrongNumberOfArgumentsException.class, () -> {
+ ViewCommand testViewWithoutIndex = new ViewCommand("personal");
+ testViewWithoutIndex.execute(data, ui, storage);
+ });
+ }
+
+ @Test
+ public void execute_withInvalidIndex_invalidIndexException() throws DukeException {
+ String input = "personal; sleep";
+ Command addPersonalEvent = new AddCommand(input);
+ addPersonalEvent.execute(data, ui, storage);
+
+
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+ assertThrows(InvalidIndexException.class, () -> {
+ ViewCommand testViewWithInvalidIndex = new ViewCommand("personal; 7");
+ testViewWithInvalidIndex.execute(data, ui, storage);
+ });
+ }
+
+ @Test
+ public void execute_withIndexIsNotaNumber_wrongNumberFormatException() throws DukeException {
+ String input = "personal; sleep";
+ Command addPersonalEvent = new AddCommand(input);
+ addPersonalEvent.execute(data, ui, storage);
+
+
+ PrintStream outputLoc = new PrintStream(outputStreamCaptor);
+ System.setOut(outputLoc);
+
+ assertThrows(WrongNumberFormatException.class, () -> {
+ ViewCommand testViewWithInvalidIndex = new ViewCommand("personal; abc");
+ testViewWithInvalidIndex.execute(data, ui, storage);
+ });
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(standardOut);
+ }
+}
diff --git a/src/test/java/seedu/duke/storage/StorageTest.java b/src/test/java/seedu/duke/storage/StorageTest.java
new file mode 100644
index 0000000000..90db5e0aac
--- /dev/null
+++ b/src/test/java/seedu/duke/storage/StorageTest.java
@@ -0,0 +1,182 @@
+package seedu.duke.storage;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.command.AddCommand;
+import seedu.duke.command.Command;
+import seedu.duke.command.GoalCommand;
+import seedu.duke.command.ListCommand;
+import seedu.duke.data.UserData;
+import seedu.duke.exception.DukeException;
+import seedu.duke.ui.Ui;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+class StorageTest {
+
+ private final PrintStream standardOut = System.out;
+ private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
+ private Ui ui;
+
+ @BeforeEach
+ public void setUp() {
+ ui = new Ui();
+
+ Storage store = new Storage("src,test,storagetester", ui);
+ UserData data = new UserData();
+ store.loadAll(data);
+ String inputString = "personal";
+ System.setOut(new PrintStream(outputStreamCaptor));
+ }
+
+ @Test
+ void storageLoadAll_LoadFilesFromDirectory_allFilesLoaded() throws DukeException {
+ Storage store = new Storage("src,test,storagetester", ui);
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ store.loadAll(data);
+ String inputString = "personal";
+ System.setOut(new PrintStream(outputStreamCaptor));
+
+ Command listCommand = ListCommand.parse(inputString);
+ listCommand.execute(data, ui, store);
+
+ assertEquals("3/3 loaded successfully for Personal" + System.lineSeparator()
+ + "2/2 loaded successfully for Zoom" + System.lineSeparator()
+ + "3/3 loaded successfully for Timetable" + System.lineSeparator()
+ + "The file has successfully been loaded!" + System.lineSeparator()
+ + "Here is a list of your Personal events:" + System.lineSeparator()
+ + "1. [P][X] stuff on 2010-01-01, 12:00 is also on:" + System.lineSeparator()
+ + " 1. 2010-01-08 12:00 [X]" + System.lineSeparator()
+ + " 2. 2010-01-15 12:00 [X]" + System.lineSeparator()
+ + " 3. 2010-01-22 12:00 [O]" + System.lineSeparator()
+ + " 4. 2010-01-29 12:00 [X]" + System.lineSeparator()
+ + "2. [P][O] birthday celebration on 2010-01-01, 12:00" + System.lineSeparator()
+ + "3. [P][X] others" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+
+ outputStreamCaptor.reset();
+
+ inputString = "zoom";
+ listCommand = ListCommand.parse(inputString);
+ listCommand.execute(data, ui, store);
+
+ assertEquals("Here is a list of your Zoom events:" + System.lineSeparator()
+ + "1. [Z][X] math, Link: www.zoom.com/blah on 2010-01-01, 12:00 is also on:"
+ + System.lineSeparator()
+ + " 1. 2010-01-02 12:00 [X]" + System.lineSeparator()
+ + " 2. 2010-01-03 12:00 [X]" + System.lineSeparator()
+ + " 3. 2010-01-04 12:00 [O]" + System.lineSeparator()
+ + " 4. 2010-01-05 12:00 [X]" + System.lineSeparator()
+ + "2. [Z][O] computing, Link: www.zoom.com/hello on 2010-01-01, 12:00" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+
+ outputStreamCaptor.reset();
+
+ inputString = "timetable";
+ listCommand = ListCommand.parse(inputString);
+ listCommand.execute(data, ui, store);
+
+ assertEquals("Here is a list of your Timetable events:" + System.lineSeparator()
+ + "1. [T][X] math, Location: S17 on 2010-01-01, 12:00 is also on:" + System.lineSeparator()
+ + " 1. 2010-02-01 12:00 [X]" + System.lineSeparator()
+ + " 2. 2010-03-01 12:00 [X]" + System.lineSeparator()
+ + " 3. 2010-04-01 12:00 [O]" + System.lineSeparator()
+ + " 4. 2010-05-01 12:00 [X]" + System.lineSeparator()
+ + "2. [T][O] computing, Location: COM2 on 2010-01-01, 12:00" + System.lineSeparator()
+ + "3. [T][O] history on 2009-08-01, 03:00" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+
+ outputStreamCaptor.reset();
+
+ GoalCommand goalCheck = new GoalCommand("");
+ goalCheck.execute(data, ui, store);
+
+ assertEquals("Goal: hello there" + System.lineSeparator(),
+ outputStreamCaptor.toString());
+
+
+
+ }
+
+ @Test
+ void storageSaveAll_saveFilesIntoComputer_allFilesSaved() {
+ try {
+ String[] modelPersonalLoc = {"src", "test", "storagetestermodelans", "personal.txt"};
+ String[] modelZoomLoc = {"src", "test", "storagetestermodelans", "zoom.txt"};
+ String[] modelTimetableLoc = {"src", "test", "storagetestermodelans", "timetable.txt"};
+ String[] modelGoalLoc = {"src", "test", "storagetestermodelans", "goal.txt"};
+
+ Path personalPath = createPath(modelPersonalLoc);
+ Path zoomPath = createPath(modelZoomLoc);
+ Path timetablePath = createPath(modelTimetableLoc);
+ Path goalPath = createPath(modelGoalLoc);
+
+ final List personalModel = Files.readAllLines(personalPath);
+ final List zoomModel = Files.readAllLines(zoomPath);
+ final List timetableModel = Files.readAllLines(timetablePath);
+ final List goalModel = Files.readAllLines(goalPath);
+
+ Storage store = new Storage("src,test,storagetester", ui);
+ UserData data = new UserData();
+ Ui ui = new Ui();
+ store.loadAll(data);
+ store.saveAll(data);
+
+ String[] actualPersonalLoc = {"src", "test", "storagetester", "personal.txt"};
+ String[] actualZoomLoc = {"src", "test", "storagetester", "zoom.txt"};
+ String[] actualTimetableLoc = {"src", "test", "storagetester", "timetable.txt"};
+ String[] actualGoalLoc = {"src", "test", "storagetester", "goal.txt"};
+
+ Path actualPersonalPath = createPath(actualPersonalLoc);
+ Path actualZoomPath = createPath(actualZoomLoc);
+ Path actualTimetablePath = createPath(actualTimetableLoc);
+ Path actualGoalPath = createPath(actualGoalLoc);
+
+ final List personalActual = Files.readAllLines(actualPersonalPath);
+ final List zoomActual = Files.readAllLines(actualZoomPath);
+ final List timetableActual = Files.readAllLines(actualTimetablePath);
+ final List goalActual = Files.readAllLines(actualGoalPath);
+
+ assertEquals(personalActual, personalModel);
+ assertEquals(zoomActual, zoomModel);
+ assertEquals(timetableActual, timetableModel);
+ assertEquals(goalActual, goalModel);
+
+
+ } catch (IOException e) {
+ fail("IO error! File was not written to");
+ }
+
+
+
+
+
+
+ }
+
+ /**
+ * Function accepts a string and creates a path object originating from the user directory.
+ *
+ * @param pathName is a string array which accepts in the path name words, each word represents a folder
+ * @return Path object indicating the location of the pathName keyed in initially.
+ */
+ private Path createPath(String[] pathName) {
+
+ String origin = System.getProperty("user.dir");
+ Path newPath = Paths.get(origin, pathName);
+ return newPath;
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/test/storagetester/goal.txt b/src/test/storagetester/goal.txt
new file mode 100644
index 0000000000..c7c7da3c64
--- /dev/null
+++ b/src/test/storagetester/goal.txt
@@ -0,0 +1 @@
+hello there
diff --git a/src/test/storagetester/personal.txt b/src/test/storagetester/personal.txt
new file mode 100644
index 0000000000..fafbf3c513
--- /dev/null
+++ b/src/test/storagetester/personal.txt
@@ -0,0 +1,3 @@
+stuff | 2010-01-01 | 1200 | WEEKLY | 4 | | F | F | F | T | F
+birthday celebration | 2010-01-01 | 1200 | 0 | 0 | | T
+others | 0 | 0 | 0 | 0 | | F
diff --git a/src/test/storagetester/timetable.txt b/src/test/storagetester/timetable.txt
new file mode 100644
index 0000000000..3b6f1ee106
--- /dev/null
+++ b/src/test/storagetester/timetable.txt
@@ -0,0 +1,3 @@
+math | 2010-01-01 | 1200 | MONTHLY | 4 | S17 | | F | F | F | T | F
+computing | 2010-01-01 | 1200 | 0 | 0 | COM2 | | T
+history | 2009-08-01 | 0300 | 0 | 0 | | | T
diff --git a/src/test/storagetester/zoom.txt b/src/test/storagetester/zoom.txt
new file mode 100644
index 0000000000..7b162d9b2f
--- /dev/null
+++ b/src/test/storagetester/zoom.txt
@@ -0,0 +1,2 @@
+math | 2010-01-01 | 1200 | DAILY | 4 | www.zoom.com/blah | | F | F | F | T | F
+computing | 2010-01-01 | 1200 | 0 | 0 | www.zoom.com/hello | | T
diff --git a/src/test/storagetestermodelans/goal.txt b/src/test/storagetestermodelans/goal.txt
new file mode 100644
index 0000000000..c7c7da3c64
--- /dev/null
+++ b/src/test/storagetestermodelans/goal.txt
@@ -0,0 +1 @@
+hello there
diff --git a/src/test/storagetestermodelans/personal.txt b/src/test/storagetestermodelans/personal.txt
new file mode 100644
index 0000000000..a269721532
--- /dev/null
+++ b/src/test/storagetestermodelans/personal.txt
@@ -0,0 +1,3 @@
+stuff | 2010-01-01 | 1200 | WEEKLY | 4 | | F | F | F | T | F
+birthday celebration | 2010-01-01 | 1200 | 0 | 0 | | T
+others | 0 | 0 | 0 | 0 | | F
\ No newline at end of file
diff --git a/src/test/storagetestermodelans/timetable.txt b/src/test/storagetestermodelans/timetable.txt
new file mode 100644
index 0000000000..3b6f1ee106
--- /dev/null
+++ b/src/test/storagetestermodelans/timetable.txt
@@ -0,0 +1,3 @@
+math | 2010-01-01 | 1200 | MONTHLY | 4 | S17 | | F | F | F | T | F
+computing | 2010-01-01 | 1200 | 0 | 0 | COM2 | | T
+history | 2009-08-01 | 0300 | 0 | 0 | | | T
diff --git a/src/test/storagetestermodelans/zoom.txt b/src/test/storagetestermodelans/zoom.txt
new file mode 100644
index 0000000000..bf1ad089ea
--- /dev/null
+++ b/src/test/storagetestermodelans/zoom.txt
@@ -0,0 +1,2 @@
+math | 2010-01-01 | 1200 | DAILY | 4 | www.zoom.com/blah | | F | F | F | T | F
+computing | 2010-01-01 | 1200 | 0 | 0 | www.zoom.com/hello | | T
\ No newline at end of file
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 892cb6cae7..603e0c326e 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,9 +1,19 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
-
-What is your name?
-Hello James Gosling
+_________________________________
+Welcome to scheduler--;!
+What can I do for you?
+_________________________________
+File Created: Personal
+0/0 loaded successfully for Personal
+File Created: Zoom
+0/0 loaded successfully for Zoom
+File Created: Timetable
+0/0 loaded successfully for Timetable
+File Created: Goal
+The file has successfully been loaded!
+_________________________________
+You have no events today!
+_________________________________
+_________________________________
+Thank you for using scheduler--;!
+We hope to see you soon!
+_________________________________
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index f6ec2e9f95..b023018cab 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -1 +1 @@
-James Gosling
\ No newline at end of file
+bye
diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh
index 1dcbd12021..f8177afc4d 100755
--- a/text-ui-test/runtest.sh
+++ b/text-ui-test/runtest.sh
@@ -13,6 +13,7 @@ java -jar $(find ../build/libs/ -mindepth 1 -print -quit) < input.txt > ACTUAL.
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!"