diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
index fd8c44d086..e2062c4201 100644
--- a/.github/workflows/gradle.yml
+++ b/.github/workflows/gradle.yml
@@ -33,18 +33,18 @@ jobs:
- name: Build and check with Gradle
run: ./gradlew check
- - name: Perform IO redirection test (*NIX)
- if: runner.os == 'Linux'
- working-directory: ${{ github.workspace }}/text-ui-test
- run: ./runtest.sh
-
- - name: Perform IO redirection test (MacOS)
- if: always() && runner.os == 'macOS'
- working-directory: ${{ github.workspace }}/text-ui-test
- run: ./runtest.sh
-
- - name: Perform IO redirection test (Windows)
- if: always() && runner.os == 'Windows'
- working-directory: ${{ github.workspace }}/text-ui-test
- shell: cmd
- run: runtest.bat
\ No newline at end of file
+# - name: Perform IO redirection test (*NIX)
+# if: runner.os == 'Linux'
+# working-directory: ${{ github.workspace }}/text-ui-test
+# run: ./runtest.sh
+#
+# - name: Perform IO redirection test (MacOS)
+# if: always() && runner.os == 'macOS'
+# working-directory: ${{ github.workspace }}/text-ui-test
+# run: ./runtest.sh
+#
+# - name: Perform IO redirection test (Windows)
+# if: always() && runner.os == 'Windows'
+# working-directory: ${{ github.workspace }}/text-ui-test
+# shell: cmd
+# run: runtest.bat
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index f69985ef1f..889ad94867 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,5 +13,8 @@ src/main/resources/docs/
*.iml
bin/
-/text-ui-test/ACTUAL.txt
-text-ui-test/EXPECTED-UNIX.TXT
+/text-ui-eventTest/ACTUAL.txt
+text-ui-eventTest/EXPECTED-UNIX.TXT
+
+/data/
+logfile.txt
diff --git a/README.md b/README.md
index 698b938529..d057931cee 100644
--- a/README.md
+++ b/README.md
@@ -35,11 +35,11 @@ Prerequisites: JDK 11 (use the exact version), update Intellij to the most recen
### I/O redirection tests
-* To run _I/O redirection_ tests (aka _Text UI tests_), navigate to the `text-ui-test` and run the `runtest(.bat/.sh)` script.
+* To run _I/O redirection_ tests (aka _Text UI tests_), navigate to the `text-ui-eventTest` and run the `runtest(.bat/.sh)` script.
### JUnit tests
-* A skeleton JUnit test (`src/test/java/seedu/duke/DukeTest.java`) is provided with this project template.
+* A skeleton JUnit eventTest (`src/eventTest/java/seedu/duke/DukeTest.java`) is provided with this project template.
* If you are new to JUnit, refer to the [JUnit Tutorial at se-education.org/guides](https://se-education.org/guides/tutorials/junit.html).
## Checkstyle
diff --git a/build.gradle b/build.gradle
index b0c5528fb5..af3286e5d0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -12,6 +12,7 @@ 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'
+ compile "com.dorkbox:Notify:3.7"
}
test {
@@ -29,11 +30,11 @@ test {
}
application {
- mainClassName = "seedu.duke.Duke"
+ mainClassName = "seedu.duke.Main"
}
shadowJar {
- archiveBaseName = "duke"
+ archiveBaseName = "plan"
archiveClassifier = null
}
@@ -41,6 +42,7 @@ checkstyle {
toolVersion = '8.23'
}
-run{
+run {
standardInput = System.in
+ enableAssertions = true
}
diff --git a/dataevents.txt b/dataevents.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 0f072953ea..e2ca039260 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -2,8 +2,8 @@
Display | Name | Github Profile | Portfolio
--------|:----:|:--------------:|:---------:
-![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
+ | Elizabeth | [Github](https://github.com/elizabethcwt) | [Portfolio](/team/elizabethcwt.md)
+ | Chen Jinran | [Github](https://github.com/untitle4) | [Portfolio](/team/untitle4.md)
+ | Chan Xu Hui | [Github](https://github.com/durianpancakes) | [Portfolio](/team/durianpancakes.md)
+ | Andre Wong | [Github](https://github.com/AndreWongZH) | [Portfolio](/team/andrewongzh.md)
+ | Alicia Ho | [Github](https://github.com/Aliciaho) | [Portfolio](/team/aliciaho.md)
\ No newline at end of file
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 0ec3db103d..e63cb7c968 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,34 +1,568 @@
# Developer Guide
-## Design & implementation
+* Table of Contents
+{:toc}
-{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.}
+## Introduction
+### Purpose
+This document describes the architecture and system design of Plan&Score, which will evolve throughout future releases.
+
+Each release will have an edition of the document, and the current edition of the document for the first public release is v2.1.
+
+The goal of this document is to cover the high-level system architecture and design. This document is divided into three major parts: design, implementation, product proposition.
+
+## Design
+
+### Architecture
+
+![diagram](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/ArchitectureDiagram.png)
+
+Figure 1. Architecture Diagram
+
+The Architecture Diagram given above explains the high-level design of the App. Given below is a quick overview of each component.
+
+The `Main` class is responsible for,
+* At app launch: Initializes the main components in the correct sequence
+* At shut down: Terminates the continuous loop and shut down the components
+
+`Common` represents a collection of classes used by multiple other components.
+
+The rest of the App consists of four components.
+* `UserInterface`: The user interface of the App.
+* `Controller`: User input parser and command executor
+* `Model`: Holds the data of the App in memory
+* `Storage`: Reads data from, and writes data to, the hard disk.
+
+Each of the four components:
+Exposes its functionality using a concrete {Component Name}Manager class.
+
+The sections below give more details of each component.
+
+
+
+#### UserInterface component
+
+![userinterfacecomponent](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/UserInterfaceComponent.png)
+
+Figure 2. Class Diagram of the UserInterface component
+
+API: UserInterface.java
+
+The UserInterface component,
+* Prompts commands from the user.
+* Creates the Controller component and execute user commands.
+* Displays information based on changes to Model data.
+* Prints the user’s events of the week in a timetable format.
+
+This component uses the singleton design, meaning that there is only an instance required throughout the entire lifetime of the application, obtained with the following command
+`UserInterface userInterface = UserInterface.getInstance()`
+
+
+
+
+
+
+
+#### Controller component
+
+![controllercomponent](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/ControllerComponent.png)
+
+Figure 3. Class Diagram of the Controller component
+
+API: ControlManager.java
+
+The Controller component,
+* Receives user input from the user interface.
+* Extracts the command and model type from the user input.
+* Generate the required command from the CommandFactory.java class
+* Extract the required model to be accessed and modified.
+* Executes the command with the corresponding model.
+* The sequence diagram below illustrates the above mentioned steps.
+
+![controllercomponent](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/ControllerSequence.png)
+
+Figure 4. Sequence Diagram of the Controller component
+
+
+
+
+#### Model component
+
+![modelcomponent](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/ModelComponent.png)
+
+Figure 5. Class Diagram of the Model component
+
+API: Model.java
+
+The Model component,
+* Holds all the in-memory data of type event, quiz, config and contact.
+* Each data type has a corresponding manager that the controller can interface with. This is named as Manager.java.
+* During a command execution, the manager will handle the modification and reading of its data type.
+* After the execution, the corresponding output is passed on to the User Interface component to be shown to the user.
+
+
+
+
+
+#### Storage component
+![storagecomponent](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/StorageComponent.png)
+
+Figure 6. Class Diagram of the Storage component
+
+API: StorageManager.java
+
+The Storage component,
+* Creates the necessary data files for the operation of Plan&Score.
+* Reads encoded data from Plan&Score’s data files.
+* Writes encoded data to Plan&Score’s data files.
+
+There are 3 categories of data stored by Plan&Score: `event`, `quiz` and `config`
+
+Plan&Score loads data automatically from .txt files in the `data` directory.
+Each `StorageManager` reads in their respective data files through a `decoder` and writes to the same file through an `encoder`
+
+##### Event Storage
+![eventstorage](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/EventStorageManager.png)
+
+Figure 7. Class Diagram of EventStorageManager
+
+API: EventStorageManager.java
+`EventStorageManager` is responsible for the reading and writing of data from Plan&Score’s `events.txt` file, located in the `{root}/data` directory.
+
+It utilises a decoder (`EventListDecoder.java`) for the reading of data, and an encoder (`EventListEncoder.java`) for the writing of data.
+
+##### Reading events
+
+`EventListDecoder` is responsible for the decoding of data from `events.txt`.
+
+It returns an `EventParameter` to `EventStorageManager`.
+
+![eventreadstorage](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/EventStorageReadSequence.png)
+
+Figure 8. Sequence Diagram of the reading of data
+
+##### Writing events
+
+`EventListEncoder` is responsible for the creation of the encoded strings for `EventStorageManager` to write to `events.txt`.
+
+It returns a `String` to `EventStorageManager` for further writing.
+
+![eventwritestorage](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/EventStorageSaveSequence.png)
+
+Figure 9. Sequence Diagram of the writing of data
+
+
+
+##### Quiz Storage
+API: QuizStorageManager.java
+
+![quizwritestorage](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/QuizWriteStorage.png)
+
+Figure 10. Sequence Diagram of the writing of data
+
+![quizreadstorage](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/QuizReadStorage.png)
+
+Figure 11. Sequence Diagram of the reading of data
+
+The Quiz Storage,
+* Checks existence of the quiz data file in the `data` directory. If the file does not exist, create a new data file for quiz storage.
+* Invokes the `quizListEncoder` class to encode the ArrayList of type Quiz into its String representations and writes them into the quiz data file.
+* Invokes the `quizListDecoder` class to decode the String representations of quizzes in the quiz data file and add the quizzes back into the ArrayList of type Quiz.
+
+
+
+##### Config Storage
+API: ConfigStorageManager.java
+![configstoragecomponent](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/ConfigStorage.png)
+
+Figure 12. Class Diagram of the ConfigStorageManager
+
+The Config Storage,
+* Checks existence of the config data file in the `data` directory. If the file does not exist, create a new data file for config storage.
+* Invokes the `configEncoder` class to encode the String of user name, the integer number of recommended hours and finally, the boolean which checks if the program has run before. The encoded string is written into a config data file.
+* Invokes the `configDecoder` class to decode the String user name, the integer number of recommended hours and the boolean which checks if the program has run before from a config data file. The three variables are used to show a different welcome message.
+
+
+The Config Parameter,
+* Helps to store the String of user name, the integer number of recommended hours and finally, the boolean which checks if the program has run before.
+* Contents stored in the parameter are subsequently used by configEncoder to encode them into a string.
+
+
+---
+
+## Implementation
+
+This section describes some noteworthy details on how certain features are implemented.
+
+
+### User Interface
+The user interface of Plan&Score uses the singleton design. There is only one instance of UserInterface to be used throughout the application. The API for the user interface is UserInterface.java.
+
+#### Printing to user
+Instead of using the default method `System.out.println()` provided by Java to display messages to the user, use `showToUser(String …)` where multiple strings can be added into the arguments. Each string provided that is separated by `,` will be printed on the next line.
+
+Example input:
+```
+UserInterface userInterface = UserInterface.getInstance();
+
+String string1 = "Hello";
+String string2 = "How are you?";
+
+userInterface.showToUser(string1, string2);
+```
+
+Example output:
+```
+Hello
+How are you?
+```
+
+Code:
+```
+public void showToUser(String... message) {
+ for (String m : message) {
+ out.println(m);
+ }
+}
+```
+
+
+
+#### Printing arrays to user
+
+The method `printArray(ArrayList stringArrayList)` is provided for the printing of any arraylists.
+
+Example input:
+```
+ArrayList stringArrayList = new ArrayList<>();
+
+// Adding items into the stringArrayList
+stringArrayList.add("Hello");
+stringArrayList.add("How are you?);
+
+// Obtaining user interface instance
+UserInterface userInterface = UserInterface.getInstance();
+userInterface.printArray(stringArrayList);
+```
+
+Example output:
+```
+Hello
+How are you?
+```
+
+Code:
+```
+public void printArray(ArrayList stringArrayList) {
+ assert stringArrayList != null;
+ for (String line : stringArrayList) {
+ userInterface.showToUser(line);
+ }
+}
+```
+
+### Help feature
+
+![helpcomponent](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/Help.png)
+
+Figure 13. Sequence Diagram of the help feature
+
+When a user enters ‘help’, the input will be read in by the UI class.
+The UI class will then parse the user input into the ControlManager class, which calls the runLogic() method.
+The extractCommand() method of the CommandParser class is then called, extracting and returning the command type based on the user’s input.
+In this case, the command type would be ‘help’.
+The corresponding actionableCommand will be generated via the generateActionableCommand() method in the CommandFactory class.
+Lastly, the execute() method in the HelpCommand class is called, which in turn calls its own handleHelp() method.
+This displays the help message via the showToUser() method of the userInterface.
+
+Upon completion of this feature, it returns a boolean value “true” to the active flag in UserInterface.java to allow the continuous usage of the program.
+
+### Set Hours feature
+
+![sethourscomponent](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/SetHours.png)
+
+Figure 14. Sequence Diagram of the set hours feature
+
+When a user enters ‘set hours’, the input will be read in by the UI class.
+The UI class will then parse the user input into the ControlManager class, which calls the runLogic() method.
+The extractCommand() method of the CommandParser class is then called, extracting and returning the command type based on the user’s input.
+In this case, the command type would be ‘set hours’.
+The corresponding actionableCommand will be generated via the generateActionableCommand() method in the CommandFactory class.
+Lastly, the execute() method in the SetHoursCommand class is called, which in turn calls the editHours() function from the configManager class.
+
+
+Upon completion of this feature, it returns a boolean value “true” to the active flag in UserInterface.java to allow the continuous usage of the program.
+
+### Add feature
+
+![addcomponent](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/Add.png)
+
+Figure 15. Sequence Diagram of the add feature
+
+Firstly, when the user enters add .. /n .. /s .. /e .. , the input will be read in by the UI class.
+The UI class will then parse the user input into the ControlManager class where the command will be extracted and processed by the CommandParser class.
+According to which category they belong to i.e class,cca,test,tuition etc, they are sent to their respective category managers.
+In each manager, the user input is processed and made into a new Event item by the Event class.
+The result is subsequently outputted by the UI class to the user.
+
+Upon completion of this feature, it returns a boolean value “true” to the active flag in UserInterface.java to allow the continuous usage of the program.
+
+
+### Delete feature
+
+![deletecomponent](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/DeleteFeatureDiagram.png)
+
+Figure 16. Sequence Diagram of the delete feature
+
+Firstly, when the user enters delete , the input will be read in by the UI class.
+The UI class will then parse the user input into the ControlManager class where the command will be extracted and processed by the CommandParser class. The result is passed into ModelExtractor Class.
+According to which category they belong to i.e. class, cca, test, tuition etc, they are sent to their respective category managers. In each manager, the user input is processed and the Event item would be deleted by the Event class.
+The result is subsequently outputted by the UI class to the user.
+
+Upon completion of this feature, it returns a boolean value “true” to the active flag in UserInterface.java to allow the continuous usage of the program.
+
+### List feature
+
+#### List contact / quiz
+
+![listcontact](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/ListContact.png)
+
+Figure 17. Sequence diagram for listing quizzes
+
+The list command will invoke the `ContactManager` or `QuizManager` class’s list() method respectively.
+If the ArrayList is empty, it will inform the user that there is no data to display.
+Else the method will then loop through the ArrayList as show in the loop box in figure 17 and convert it into its string representation.
+This is then passed to the `UserInterface#printArray()` to be printed out to the user.
+
+#### List event (date / today / week)
+While the back-end data processing is the same for all three types of list requests, the list event week request requires a different front-end class to display to the user.
+As such, we divide this section into 2 sub-sections, with List event (/today) in the first subsection, and List event week in the next subsection.
+
+The execution of the ListCommand will cause the `EventManager` class to invoke `EventManager#listSchedule()`.
+This will then create an instance of the `ListSchedule` class, with all the classes, ccas, tests and tuitions data
+as its attributes.
+
+#### List event (date / today)
+
+![listevent](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/ListEvent.png)
+
+Figure 18. Sequence diagram for listing out events.
+
+Inside this `EventManager#listSchedule()`, we then call the `ListSchedule#getPrintableEvents()` and this will start
+to convert ArrayList of type Event into its corresponding `toString()` representation.
+This is then padded with numbers and an ArrayList of type string is returned to `EventManager#listSchedule()`.
+
+It is then passed to the `UserInterface#printArray()` to be printed out to the user.
+
+Special parameters:
+
+In the case where the parameters passed in is ‘today’, the `ListSchedule#checkAndConvertToday()` will
+check if the user passed in ‘today’ as a parameter.
+If that is true, then it converts the userInput attribute to `LocalDate.now()`, which is the current date.
+When filtering the events to be converted, the start time of the event is compared with the parameter date.
+If it is equal, the event will be converted and be printed out.
+
+
+
+
+
+#### List event week/nextweek
+
+![listeventweek](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/ListWeekSequence.png)
+
+Figure 19. Sequence diagram for listing out events for week/nextweek.
+
+Inside this `EventManager`, we then call the `UserInterface#printWeekSchedule(EventManager, ListWeekCommand)`, passing
+in the current instance of EventManager into this method and a ListWeekCommand that determines if the current or next
+week is to be printed.
+`UserInterface#printWeekSchedule(EventManager)` will then construct an instance of `CalendarWeekRenderer(EventManager)`
+which will display the week schedule to the user.
+
+As of V2.1, the CalendarWeekRenderer does not support printing the location parameter of the Tuition class.
+
+Example code snippet:
+
+```
+UserInterface userInterface = UserInterface.getInstance();
+userInterface.printWeekSchedule(this, ListWeekCommand.CURRENT_WEEK); // the EventManager instance is passed into the method call
+```
+
+
+
+### Find feature
+
+#### Find event
+
+The execution of FindCommand will cause the `EventManager` class to invoke `EventManager#find()`. Inside this `EventManager#find()`, it creates an instance of the `FindSchedule` class, with all the classes, ccas, tests and tuitions data as its attributes.
+
+We then call the `FindSchedule#getFilteredEvents()` and this will start to convert ArrayList of type Event into its corresponding `toString()` representation. An ArrayList of type String is returned to `EventManager#find()`.
+
+This is then passed to the `UserInterface#printArray()` to be printed out to the user.
+
+The diagram below shows the execution flow explained below.
+
+![findevent](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/FindEvent.png)
+
+Figure 20. Sequence diagram for finding events.
+
+
+
+#### Find quiz
+
+The execution of FindCommand will cause the `QuizManager` class to create an instance of the `FindQuiz` class, with all the quizzes data as its attributes.
+
+We then call the `findQuiz#filterQuizzes()` and this will start to convert ArrayList of type Quiz into its corresponding `toString()` representation. An ArrayList of type String is returned to `QuizManager#find()`.
+
+This is then passed to the `UserInterface#printArray()` to be printed out to the user.
+
+The diagram below shows the execution flow explained below.
+
+![findquiz](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/diagram/FindQuiz.png)
+
+Figure 21. Sequence diagram for finding quizzes.
+
+
+### Quiz feature
+
+#### Take quiz
+
+This execution of the quiz command will invoke the QuizManager class’s quiz() method.
+The variable correctCounter will be initialized to be 0.
+If the input is:
+
+- Less than 1 (including negative numbers), or
+- more than the total number of questions in the current quiz list,
+
+the program will inform the user that their input is invalid, and provide the range of questions the user can attempt.
+
+If the input is not an integer:
+- the program will inform the user that their input is of the wrong format
+ and probe them to enter a value of integer type.
+- The method will then randomly select the input number of quiz questions in the quiz ArrayList
+ and convert them into their string representation.
+- This is then passed to the UserInterface#printArray() to be printed out to the user.
+- When each quiz question is printed, the user needs to input an answer to this question.
+- The input answer will then be compared to the answer of that quiz question.
+- If the answers are the same, the variable correctCounter in QuizManager() will be incremented by 1.
+- Upon completion of this feature, it returns a boolean value “true” to the active flag in UserInterface.java,
+ which allows the continuous usage of the program.
## Product scope
### Target user profile
-{Describe the target user profile}
+ Target User Profile:
+* Is a Primary 6 student with a packed schedule
+* prefer desktop apps over other types
+* prefers typing to mouse interactions
+* is reasonably comfortable using CLI apps
-### Value proposition
-{Describe the value proposition: what problem does it solve?}
+### Value proposition
+One-stop application for Primary 6 students to plan their schedule and do revision tests at the same time.
## 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|As a forgetful Primary 6 Student|to be able to remember all the classes I have in school|plan my class schedule accordingly|
+|v1.0|Primary 6 student|to be able to remember all the extra-curricular activities I have in school |Plan my cca schedule accordingly. |
+|v1.0|Primary 6 student|to be able to remember all the tuition classes I have outside of school and remember the locations of my tuition centres |Plan my tuition schedule accordingly and would not get lost. |
+|v1.0|Primary 6 student|I have many class tests and examinations leading up to PSLE, and would like to keep track of all my upcoming test dates|I know the test dates in advance and can plan my revision schedule well.|
+|v1.0|Primary 6 student|I would like to list all my classes, extra-curricular activities, tuition classes and test dates|I can have a overview of what I schedules I have|
+|v1.0|Primary 6 student|I would like to be able to remember all the commands used in this program|I do not need to refer to the user guide all the time|
+|v1.0|Primary 6 student|I would like to be able to remember all the commands used in this program|I do not need to refer to the user guide all the time|
+|v2.0|Primary 6 student|to remember the contact details of my teachers|I can contact them in case there is an emergency.|
+|v2.0|Primary 6 student|to see the list of questions I have added|I can keep track of the questions I need.|
+|v2.0|Primary 6 student|to practice via short and interactive online trivia|I can revise in an entertaining manner.|
+|v2.1|Primary 6 student|set a number of productive hours per day|I can work efficiently and effective without overloading.|
+
## Non-Functional Requirements
-{Give non-functional requirements}
+* Should work on any Windows, Mac and Linux operating system with Java 11 installed.
+* Should update the storage every time a command changes the data.
+* Users should be able to view the output of their command within 10 seconds.
+* Accessing and loading of data should take less than 5 seconds when we have less than 10000 data entries.
+
## Glossary
-* *glossary item* - Definition
+Data entries:
+ Event, Quiz or contact data that are either in storage or in their respective Manager class
+
## 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}
+Given below are instructions to test the app manually
+
+### Adding of CCAs
+1. Test case: `add`
+ * Expected: Inform the user that the category type is missing.
+1. Test case: `add cca`
+ * Expected: Inform the user to include all /n /s and /e inputs.
+1. Test case: `add cca /n abc /s date1 /e date 2`
+ * Expected: Inform the user to enter a valid date time format.
+1. Test case: `add test /n Math test /s 2020-10-03 1300 /e 2020-10-03 1400`
+ * Expected: Inform the user that cca has been added. Running `list event` command will show the added cca inside.
+1. Test case: `add test /n Math test /s 2020-10-03 1300 /e 2020-10-03 1000`
+ * Expected: Inform the user that start time is later than end time.
+
+### Listing of events
+1. Test case: `list`
+ * Expected: Inform the user to list either event, quiz or contact
+1. Test case: `list event`
+ * Expected: Show a list of events and categorized into its event type. If there are no events, inform the user that the schedule is empty.
+1. Test case: `list event 2020-10-03`
+ * Expected: Show a list of events that matches with the specified date. If no events that match, inform the user that the schedule is not found.
+1. Test case: `list event date`
+ * Expected: Inform the user to enter a valid date time format.
+1. Test case: `list event 2020-10-3 2pm`
+ * Expected: Inform the user not to enter extra parameters.
+
+### Finding of contacts
+1. Test case: `find`
+ * Expected: Inform the user to find either event, quiz or contact
+1. Test case: `find contact math`
+ * Expected: Show a list of contacts that matches the keywords. If there are no contacts found,
+ inform the user that the search has no result.
+1. Test case: `find contact`
+ * Expected: Remind the user to include the keyword in the command.
+1. Test case: `find testing`
+ * Expected: Inform user that the program does not recognise the category.
+
+
+
+### Saving data
+#### Dealing with missing data files
+
+There are two ways we use to define a missing file:
+1. When the “data” directory in the project root directory is missing.
+1. When any of the critical data .txt files (i.e events.txt, quiz.txt) are missing from the “data” directory.
+
+We can simulate this by deleting any of the critical data .txt file, or the “data” directory as a whole.
+
+Expected: The “data” directory as well as “event.txt“ and “quiz.txt” are recreated.
+However, any prior data stored will be lost.
+
+
+#### Dealing with corrupted data files
+
+We define a file to be corrupted when our decoders are unable to decode the data .txt files.
+
+We can simulate this by changing the encoded text in the file. For example, a Class that has the following parameters:
+
+* Description: Math tutorial
+* isDone: false
+* Start: 2020-09-21 1500
+* End: 2020-09-21 1600
+
+should be encoded as “[CLASS]\|false\|Math tutorial\|2020-09-21 1500\|2020-09-21 1600” in the events.txt file.
+
+Corrupted forms can be in the following forms, but not limited to:
+* “[CLASS]\|Math tutorial\|2020-09-21 1500\|2020-09-21 1600”: the isDone parameter is missing from the encoded string.
+* “[CLASS]\|false\|Math tutorial\|2020-09-21 1600”: one of the date-time parameter is missing from the encoded string
+* “[CLASS]+false+Math tutorial+2020-09-21 1500+2020-09-21 1600”: the parameter separator “+” is not recognized by our decoder.
+
+Expected: the program will not be able to start, with the error message “): Storage file corrupted." shown.
+
\ No newline at end of file
diff --git a/docs/README.md b/docs/README.md
index bbcc99c1e7..ff653cb469 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,6 +1,10 @@
-# Duke
+# Plan&Score
+
+Plan&Score is a Java command-line application that allows Primary 6 students
+to plan and track their classes, CCAs and test dates.
+This enables the students to remember their schedule,
+so they can plan well in advance for their tests and score better.
-{Give product intro here}
Useful links:
* [User Guide](UserGuide.md)
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index abd9fbe891..70a5b882f5 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -1,42 +1,737 @@
# User Guide
+- [1. Introduction](#introduction-chan-wan-ting-elizabeth)
+- [2. About this User Guide](#about-this-user-guide-chan-xu-hui)
+- [3. How to user this User Guide](#how-to-use-this-user-guide-andre-wong-zhi-hua)
+- [4. Quick Start](#quick-start-chan-xu-hui)
+- [5. Initialisation](#initialisation-alicia-ho-shimin)
+- [6. Commands](#commands)
+ - [Command Format](#command-format)
+ - [Category Types](#category-types-chan-wan-ting-elizabeth)
+ - [Help:](#viewing-help-help-chan-wan-ting-elizabeth) `help`
+ - [PLAN component of Plan&Score](#plan-component-of-planscore)
+ - [Set Recommended Hours](#set-recommended-hours-set-hours-alicia-ho-shimin) `set hours`
+ - [Class Category](#category-class-chan-wan-ting-elizabeth)
+ - [Add a class:](#adding-a-class-add-class) `add class`
+ - [Delete a class:](#deleting-a-class-delete-class) `delete class`
+ - [Cca Category](#category-cca-chen-jinran)
+ - [Add a cca:](#adding-a-cca-add-cca) `add cca`
+ - [Delete a cca:](#deleting-a-cca-delete-cca) `delete cca`
+ - [Test Category](#category-test-alicia-ho-shimin)
+ - [Add a test:](#adding-a-test-add-test) `add test`
+ - [Delete a test:](#deleting-a-test-delete-test) `delete test`
+ - [Tuition Category](#category-tuition-chan-xu-hui)
+ - [Add a tuition:](#adding-a-tuition-add-tuition) `add tuition`
+ - [Delete a tuition:](#deleting-a-tuition-delete-tuition) `delete tuition`
+ - [List schedule:](#listing-out-schedule-list-event-todaydateweeknextweek-andre-wong-zhi-hua) `list event `
+ - [Find event:](#finding-events-find-event-andre-wong-zhi-hua) `find event`
+ - [SCORE component of Plan&Score:](#score-component-of-planscore)
+ - [Take a quiz:](#taking-a-quiz-quiz-chan-wan-ting-elizabeth)`quiz`
+ - [Add a quiz question:](#adding-a-quiz-question-add-quiz-andre-wong-zhi-hua) `add quiz`
+ - [Delete a quiz question:](#deleting-a-quiz-question-delete-quiz-chen-jinran) `delete quiz`
+ - [List quiz questions:](#listing-out-all-quiz-questions-list-quiz-andre-wong-zhi-hua) `list quiz`
+ - [Find a quiz by keyword:](#finding-a-quiz-find-quiz-chen-jinran) `find quiz`
+ - [Search for former incorrect quiz questions:](#searching-for-former-incorrect-quiz-questions-quiz-record-chen-jinran) `quiz record`
+ - [Contact component of Plan&Score:](#contact-component-of-planscore)
+ - [Add a contact:](#adding-a-contact-add-contact-chen-jinran) `add contact`
+ - [Delete a contact:](#deleting-a-contact-delete-contact-chen-jinran) `delete contact`
+ - [List a contact:](#listing-out-contacts-list-contact-chen-jinran) `list contact`
+ - [Find a contact:](#finding-a-contact-find-contact-andre-wong-zhi-hua) `find contact`
+ - [Exit the program:](#exits-program-bye-alicia-ho-shimin) `bye`
+- [7. Saving Data](#saving-data-chan-xu-hui)
+- [8. FAQ](#faq)
+- [9. Troubleshooting](#troubleshooting-chan-xu-hui)
+- [10. Command Summary](#command-summary-team)
-## Introduction
+## Introduction (Chan Wan Ting Elizabeth)
-{Give a product intro}
+Plan&Score is an application consisting of 2 key components:
-## Quick Start
+* Event scheduler
+* Quiz component
-{Give steps to get started quickly}
+and a sub-component:
-1. Ensure that you have Java 11 or above installed.
-1. Down the latest version of `Duke` from [here](http://link.to/duke).
+* Contact list
-## Features
-{Give detailed description of each feature}
+These components aim to tackle the issue of poor planning and revision most Primary 6 students in Singapore face.
-### Adding a todo: `todo`
-Adds a new item to the list of todo items.
+This application uses a command line interface, meaning that you operate the application by typing commands into a Command Box.
-Format: `todo n/TODO_NAME d/DEADLINE`
+![initialization](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/initialization.png)
-* The `DEADLINE` can be in a natural language format.
-* The `TODO_NAME` cannot contain punctuation.
+Figure 1. The graphical user interface for Plan&Score
-Example of usage:
+## About this User Guide (Chan Xu Hui)
+This User Guide serves to provide an in-depth explanation of Plan&Score’s usage and functionalities, as well as troubleshooting steps you can take if problems are encountered.
-`todo n/Write the rest of the User Guide d/next week`
+## How to use this User Guide (Andre Wong Zhi Hua)
+To navigate to the contents of your desired feature, simply click on the hyperlinks provided in the contents page above.
-`todo n/Refactor the User Guide to remove passive voice d/13/04/2020`
+The highlights and symbols used in this document are as follows:
+
+![information](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/information.png)
+
+![warning](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/warning.png)
+
+`Add class` A grey highlight is used to denote text to be entered into the
+ command line.
+
+
+## Quick Start (Chan Xu Hui)
+
+1. Ensure that you have Java `11` or above installed.
+2. Download the latest version of `Plan&Score` from [here](https://github.com/AY2021S1-CS2113T-W12-4/tp/releases).
+3. Run the program by entering `java -jar \[CS2113T-W12-4\]\[\ Plan\&Score\].jar` in your terminal.
+4. You can exit the program by running `bye`.
+
+
+## Initialisation (Alicia Ho Shimin)
+
+When you first open Plan&Score, you will be greeted by a welcome message as well as
+a prompt asking for your name. This is as shown below.
+
+![introduction](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/intro_screen.png)
+
+Type in your name and press 'Enter' to proceed.
+
+![keyname](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/name.png)
+
+Afterwards, you will be prompted to enter the number of recommended hours that
+you wish to accomplish per day. Key in your desired number of hours
+and press 'Enter' to proceed.
+
+![keyhours](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/hours.png)
+
+This is the end of the initialisation process. You can now enjoy the rest of Plan&Score's
+amazing features!
+
+![finishintro](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/finish_intro.png)
+
+
+
+## Commands
+
+#### Command Format
+* A command can contain multiple parameters.
+ * A `test` contains a `description`, `start` and `end`, where `description`, `start` and `end` are parameters that make up a `test`.
+* Command parameters cannot be swapped.
+ * `add test /n [name of test] /s [start date-time of test] /e [end date-time of test]` will work but `add test /s [start date-time of test] /e [end date-time of test] /n [name of test]` will give an error
+* Extra spaces in the command will be sanitized.
+ * ` list event ` will be sanitized to `list event`
+* Command will be converted as lowercase.
+ * `LIST EVENT` will be modified to `list event`
+* Commands with extra parameters provided will give an error
+ * `delete class 1 2 3 abc` will be give an error.
+
+
+#### Category Types (Chan Wan Ting Elizabeth)
+* Plan&Score has the following categories types:
+
+ 1. `event`
+ 1. `class`
+ 1. `cca`
+ 1. `test`
+ 1. `tuition`
+
+ 1. `contact`
+ 1. `quiz`
+
+* The category type often follows after the command word (E.g `class` follows after `add` to form `add class`).
+
+### Viewing help: `help` (Chan Wan Ting Elizabeth)
+Shows all available commands that you can use
+
+Firstly, type `help` in the command line as seen below, and press 'Enter' to execute it.
+
+![help1](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/help1.png)
+
+The output containing all the different commands is seen in the console.
+
+![help2](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/help_command_1.png)
+![help2](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/help_command_2.png)
+
+### Plan component of Plan&Score
+It allows you to plan and track the following category types:
+* Class
+* Cca
+* Test
+* Tuition
+
+![indexinfo](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/indexinfo.png)
+
+
+#### Set Recommended Hours: `set hours` (Alicia Ho Shimin)
+Allows you to change the number of recommended hours you would want to have per day.
+
+Firstly, type `set hours` in the command line as seen below. Press 'Enter' to execute it.
+
+![sethours1](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/set_hours_1.png)
+
+Afterwards, Plan&Score will prompt you to enter the number of recommended hours that
+you wish to accomplish per day.
+
+![sethours2](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/set_hours_2.png)
+
+Key in your new desired number of hours and press 'Enter' to proceed.
+The output is then seen in the console as shown below.
+
+![sethours3](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/set_hours_3.png)
+
+
+WARNING:
+
+
+* `[number of hours]` must be written in numerals.
+
+![erroraddclass](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/erroraddclass.png)
+
+![sethourserror](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/set_hours_error.png)
+
+
+#### Category: Class (Chan Wan Ting Elizabeth)
+##### Adding a class: `add class`
+Allows you to add a new class with a name, date and time.
+
+Firstly, type `add` in the command line as seen below, followed by the category type which is the class. Afterwards, key in the description of the class as well as its start and end date and time. Press 'Enter' to execute it.
+
+![addclass1](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/addclass1.png)
+
+The output is seen in the console. The date and time is converted to a more readable form, enabling you to read it more pleasantly.
+
+![addclass2](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/add_class_2.png)
+
+
+WARNING:
+
+
+* `[name of class]` can be in a natural language format and
+cannot contain '/'.
+
+* `[start date-time of class]` and `[end date-time of class]` must be in yyyy-mm-dd HHMM format with HHMM in 24-hour format.
+
+* Parameters `/n`, `/s`, `/e` cannot be swapped.
+
+
+![erroraddclass](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/erroraddclass.png)
+
+![addclasserror](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/class_error.png)
+
+
+
+
+##### Deleting a class: `delete class`
+Allows you to delete a class based on its index in the list.
+
+Firstly, type ‘delete’ in the command line as seen below, followed by the category type which is the class. Afterwards, key in the corresponding index of the class you would like to delete. Press 'Enter' to execute it.
+![deleteclass1](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/delete_class_1.png)
+
+The output is seen in the console. The date and time is converted to a more readable form, enabling you to read it more pleasantly.
+![deleteclass2](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/delete_class_2.png)
+
+
+WARNING:
+
+
+* `[class number]` must be written in numerals.
+
+![erroraddclass](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/erroraddclass.png)
+
+![deleteclasserror](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/delete_class_error.png)
+
+
+
+#### Category: Cca (Chen Jinran)
+##### Adding a cca: `add cca`
+Allow you to add a new cca with a name, date and time.
+
+Firstly, type `add` in the command line as seen below, followed by the category type which is the cca. Afterwards, key in the description of the cca as well as its start and end date and time. Press 'Enter' to execute it.
+
+![addCca1](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/add_cca_1.png)
+
+The output is seen in the console. The date and time is converted to a more readable form, enabling you to read it more pleasantly.
+
+![addCca2](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/add_cca_2.png)
+
+
+WARNING:
+
+
+* `[name of cca]` can be in a natural language format and
+cannot contain '/'.
+
+* `[start date-time of cca]` and `[end date-time of cca]` must be in yyyy-mm-dd HHMM format with HHMM in 24-hour format.
+
+* Parameters `/n`, `/s`, `/e` cannot be swapped.
+
+![erroraddclass](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/erroraddclass.png)
+
+![addccaerror](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/add_cca_error.png)
+
+
+
+##### Deleting a cca: `delete cca`
+Allows you to delete a cca based on its index in the list.
+
+Firstly, type `delete` in the command line as seen below, followed by the category type which is the cca. Afterwards, key in the corresponding index of the cca you would like to delete. Press 'Enter' to execute it.
+
+![deleteCca1](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/delete_cca_1.png)
+
+The output is seen in the console. The date and time is converted to a more readable form, enabling you to read it more pleasantly.
+
+![deleteCca2](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/delete_cca_2.png)
+
+
+WARNING:
+
+
+* `[cca number]` must be written in numerals.
+
+![erroraddclass](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/erroraddclass.png)
+
+![deleteccaerror](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/delete_cca_error.png)
+
+
+
+
+#### Category: Test (Alicia Ho Shimin)
+##### Adding a test: `add test`
+Allows you to add a new test with a name, date and time.
+
+Firstly, type `add` in the command line as seen below, followed by the category type which is the test. Afterwards, key in the description of the test as well as its start and end date and time. Press 'Enter' to execute it.
+
+![addtest1](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/add_test_1.png)
+
+The output is seen in the console. The date and time is converted to a more readable form, enabling you to read it more pleasantly.
+
+![addtest2](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/add_test_2.png)
+
+
+WARNING:
+
+
+* `[name of test]` can be in a natural language format and
+cannot contain '/'.
+
+* `[start date-time of test]` and `[end date-time of test]` must be in yyyy-mm-dd HHMM format with HHMM in 24-hour format.
+
+* Parameters `/n`, `/s`, `/e` cannot be swapped.
+
+![erroraddclass](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/erroraddclass.png)
+
+![addtesterror](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/add_test_error.png)
+
+
+
+##### Deleting a test: `delete test`
+Allows you to delete a test event based on its index in the list.
+
+Firstly, type `delete` in the command line as seen below, followed by the category type which is the test. Afterwards, key in the corresponding index of the test you would like to delete. Press 'Enter' to execute it.
+
+![deletetest1](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/delete_test_1.png)
+
+The output is seen in the console. The date and time is converted to a more readable form, enabling you to read it more pleasantly.
+
+![deletetest2](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/delete_test_2.png)
+
+
+WARNING:
+
+
+* `[test number]` must be written in numerals.
+
+![erroraddclass](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/erroraddclass.png)
+
+![deletetesterror](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/delete_test_error.png)
+
+
+
+
+
+#### Category: Tuition (Chan Xu Hui)
+##### Adding a tuition: `add tuition`
+Allows you to add a new test with a name, date, time, location.
+
+Firstly, type `add` in the command line as seen below, followed by the category type which is the tuition. Afterwards, key in the description of the tuition, its start and end date and time as well as its location. Press 'Enter' to execute it.
+
+![addtuition1](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/add_tuition_1.png)
+
+The output is seen in the console. The date and time is converted to a more readable form, enabling you to read it more pleasantly.
+
+![addtuition2](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/add_tuition_2.png)
+
+
+WARNING:
+
+
+* `[name of tuition]` and `[location of tuition]` can be in a natural language format and
+cannot contain '/'.
+
+* `[start date-time of tuition]` and `[end date-time of tuition]` must be in yyyy-mm-dd HHMM format with HHMM in 24-hour format.
+
+* Parameters `/n`, `/s`, `/e`, `/l` cannot be swapped.
+
+![erroraddclass](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/erroraddclass.png)
+
+![addtuitionerror](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/add_tuition_error.png)
+
+
+
+##### Deleting a tuition: `delete tuition`
+Allows you to delete a tuition based on its index in the list.
+
+Firstly, type `delete` in the command line as seen below, followed by the category type which is the tuition. Afterwards, key in the corresponding index of the tuition you would like to delete. Press 'Enter' to execute it.
+
+![deletetuition1](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/delete_tuition_1.png)
+
+The output is seen in the console. The date and time is converted to a more readable form, enabling you to read it more pleasantly.
+
+![deletetuition2](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/delete_tuition_2.png)
+
+
+WARNING:
+
+
+* `[tuition number]` must be written in numerals.
+
+![erroraddclass](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/erroraddclass.png)
+
+![deletetuitionerror](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/delete_tuition_error.png)
+
+
+
+
+
+#### Listing out schedule: `list event ` (Andre Wong Zhi Hua)
+
+Allows you to list out the entire schedule for classes, ccas and tests.
+You can also choose to list today's schedule, the schedule
+for the week or the schedule for a specific date.
+
+##### List event: `list event`
+Firstly, to list out all events, type ‘list event’ in the command line, as seen below. Press 'Enter' to execute it.
+
+![listevent1](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/list_event_1.png)
+
+The output is seen in the console. The list is arranged by their respective categories. The date and time is converted to a more readable form, enabling you to read it more pleasantly.
+
+![listevent2](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/list_event_2.png)
+
+
+
+
+
+##### List event today: `list event today` (Alicia Ho Shimin)
+
+Secondly, to list out all events today, type `list event today` in the command line, as seen below. Press 'Enter' to execute it.
+
+![listeventtoday1](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/list_event_today_1.png)
+
+The output is seen in the console. The list is arranged by their respective categories. The date and time is converted to a more readable form, enabling you to read it more pleasantly. The index of the event is corresponding to its index in the entire list.
+
+![listeventtoday2](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/list_event_today_2.png)
+
+
+
+
+
+##### List event week: `list event week` (Chan Xu Hui)
+
+Thirdly, to list out all events in the current week, type `list event week` in the command line, as seen below. Press 'Enter' to execute it.
+
+![listeventweek1](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/list_event_week_1.png)
+
+The output is seen in the console. It is displayed in a calendar format for easier readability. The events are listed according to time. The date and time is converted to a more readable form, enabling you to read it more pleasantly.
+
+![listeventweek2](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/list_event_week_2.png)
+
+
+
+##### List event next week: `list event nextweek` (Chan Xu Hui)
+
+Fourthly, to list out all events in the next week, type `list event nextweek` in the command line, as seen below. Press 'Enter' to execute it.
+
+![listeventnextweek1](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/list_event_nextweek_1.png)
+
+The output is seen in the console. It is displayed in a calendar format for easier readability. The events are listed according to time. The date and time is converted to a more readable form, enabling you to read it more pleasantly.
+
+![listeventnextweek2](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/list_event_nextweek_2.png)
+
+
+
+
+
+##### List event date: `list event [desired date]` (Alicia Ho Shimin)
+
+Lastly, to list out all events on a particular date, type `list event [desired date]` in the command line, as seen below. Press 'Enter' to execute it.
+
+![listeventdate1](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/list_event_date_1.png)
+
+The output is seen in the console. The list is arranged by their respective categories. The date and time is converted to a more readable form, enabling you to read it more pleasantly. The index of the event is corresponding to its index in the entire list.
+
+![listeventdate2](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/list_event_date_2.png)
+
+
+
+
+
+#### Finding events: `find event` (Andre Wong Zhi Hua)
+Allows you to look for classes, ccas and tests and tuitions that match your given keyphrase.
+
+In the case when you have many events in your schedule and you need to find a specific event to check the date. You can use the `find event` command to look for the event you want by entering keywords related to it.
+
+Let's say we need to find out when my vocabulary test is on.
+We can first type `find event vocabulary` into the command box, and press 'Enter' to execute it.
+
+![find1](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/find1.JPG)
+
+The output can then be seen in the console.
+
+![find2](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/find2.JPG)
+
+And we can find the date we are looking for as pointed out by the yellow arrow below.
+
+![find3](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/find3.JPG)
+
+
+
+
+
+
+### Score component of Plan&Score
+Enables the following category type:
+quiz
+
+The quiz feature enables you to spend the extra time resulting from your productive scheduling to hone your Mathematics for your upcoming PSLE.
+
+#### Taking a Quiz: `quiz` (Chan Wan Ting Elizabeth)
+Taking a Mathematics quiz with any number of questions you want, ranging from just 1 question to the total number of questions in the quiz list.
+
+First, enter `quiz`, followed by the number of questions you would like to take in your quiz. For example, you could enter `quiz 1` to attempt a quiz with 1 question.
+
+The following output would then be displayed.
+
+![quiz1](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/quiz1.png)
+
+
+
+#### Adding a Quiz Question: `add quiz` (Andre Wong Zhi Hua)
+Allows you to add a Mathematics multiple-choice quiz question to the quiz list.
+
+Note that the `/e (explanation)` is an optional parameter. You can add a quiz question with or without an explanation.
+
+First, enter `add quiz`, follow by `/q`.
+
+Then, enter the question you would like to add.
+
+Next, enter `/o1`, followed by the answer option you would like to enter. Repeat this for the rest of the three options.
+
+Now, enter `/a`, followed by the number corresponding to the correct answer option.
+
+Lastly, enter `/e`, followed by the explanation to the solution of the question. This last part is optional.
+
+An example of such a command would be: `add quiz /q What is 26+5? /o1 28 /o2 31 /o3 38 /o4 41 /a 2 /exp Adding 5 to 26 gives us 31.`.
+
+The corresponding output would then be displayed as shown below.
+
+![quizadded](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/quizadded.png)
+
+
+
+#### Deleting a Quiz Question: `delete quiz` (Chen Jinran)
+Allows you to delete a quiz question from the quiz list.
+
+Simply enter `delete quiz`, followed by the index number corresponding to the quiz question you would like to delete in your quiz list.
+
+For example, you could enter `delete quiz 1`.
+
+You would expect to see this output.
+
+![deletequiz1](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/deletequiz1.png)
+
+
+#### Listing out All Quiz Questions: `list quiz` (Andre Wong Zhi Hua)
+Allows you to list out all the questions in the quiz list.
+
+All you have to enter is `list quiz` for this command.
+
+This output should then be seen.
+
+![listquiz](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/listquiz.png)
+
+
+#### Finding a quiz: `find quiz` (Chen Jinran)
+Look for quizzes that match the given keyword(s) that you want.
+
+Note that searches are case-insensitive and user can provide multiple words to compare with
+
+The format for this command is `find quiz`, followed by the keyword(s) you would like to search for in your list of quiz questions.
+
+For example, you could enter `find quiz 26`.
+
+Then, you would expect to see this in your output.
+
+![findquiz](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/findquiz.png)
+
+#### Searching for former incorrect quiz questions: `quiz record` (Chen Jinran)
+Allows you to list out the incorrect quizzes in your last quiz attempt.
+
+Simply enter `quiz record`.
+
+You should see the full list of questions you have answered wrongly in your previous quiz attempt, as shown below.
+
+![quizrecord](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/quizrecord.png)
+
+
+### Contact component of Plan&Score
+
+#### Adding a contact `add contact` (Chen Jinran)
+Allows you to add a teacher's contact details to the contact list.
+
+Firstly, type `add` in the command line as seen below, followed by the category type which is the contact.
+Afterwards, key in the description of the contact. Press 'Enter' to execute it.
+
+![add_contact_command](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/add_contact_command.png)
+
+The output is seen in the console.
+
+![add_contact](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/add_contact.png)
+
+WARNING:
+* `[subject of contact]`, `[name of contact]`, `[phone number of contact]` and
+`[email of contact]` can be in a natural language format and cannot contain '/'.
+* Parameters `/s`, `/n`, `/p`, `/e` cannot be swapped.
+
+![erroraddclass](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/erroraddclass.png)
+
+![add_contact_wrongly](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/add_contact_wrongly.png)
+
+#### Deleting a contact `delete contact` (Chen Jinran)
+Allows you to delete a contact from the contact list.
+
+Firstly, type `delete` in the command line as seen below, followed by the category type which is the contact.
+Afterwards, key in the corresponding index of the contact you would like to delete. Press 'Enter' to execute it.
+
+![delete_contact_command](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/delete_contact_command.png)
+
+The output is seen in the console.
+
+![delete_contact](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/delete_contact.png)
+
+WARNING:
+* `[contact number]` must be written in numerals.
+
+![erroraddclass](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/erroraddclass.png)
+
+![delete_contact_wrongly](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/delete_contact_wrongly.png)
+
+#### Listing out contacts `list contact` (Chen Jinran)
+Allows you to list out all the contacts in the contact list
+
+To list out all contacts, type `list contact` in the command line, as seen below.
+Press `Enter` to execute it.
+
+![list_contact_command](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/list_contact_command.png)
+
+The output is seen in the console.
+
+![list_contact](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/list_contact.png)
+
+
+#### Finding a contact `find contact` (Andre Wong Zhi Hua)
+Look for contacts that match the given keywords you want.
+
+In the case when you have many contacts in your list and you need to find a specific contact, you can use the
+`find contact` command to look for the contact you want by entering keywords related to it.
+
+Let's say we need to find out the contact of our math teacher. We can type `find contact math`
+into the command box, and press 'Enter' to execute it.
+
+![find_contact_command](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/find_contact_command.png)
+
+The output can then be seen in the console.
+
+![find_contact](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/find_contact.png)
+
+
+
+
+
+### Exits Program: `bye` (Alicia Ho Shimin)
+Exits the program once you have finished using it.
+
+Type the command and press 'Enter' to execute it.
+
+![bye1](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/bye_1.png)
+
+The output is shown in the console. It contains a simple goodbye message.
+
+![bye2](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/bye_2.png)
+
+
+
+## Saving Data (Chan Xu Hui)
+Plan&Score saves all your data automatically after every command. There is no need to save manually.
+However, if your storage file is detected to be unreadable, Plan&Score will offer to reset itself to its default settings,
+wiping all data stored in the text file.
+
+![factoryreset](https://raw.githubusercontent.com/AY2021S1-CS2113T-W12-4/tp/master/docs/images/storage_corrupted_factory_reset.png)
+
+
## FAQ
-**Q**: How do I transfer my data to another computer?
+**Q**: How do I transfer my data to another computer?
+
+**A**: We recommend you to transfer the entire folder containing `[CS2113T-W12-4][ Plan&Score].jar` and the `data` directory. Then, follow instructions given in our `Quick Start` section to set up Plan&Score.
+
+## Troubleshooting (Chan Xu Hui)
+
+**Q**: Plan&Score is unable to start, and I do not want to lose my data through factory resetting. How can I fix this?
+
+**A**: Your data files might have been corrupted. To fix this, you can attempt the following steps:
+
+**We recommend adult supervision for this process.**
+
+1. Locate the `data` directory. It should be in the same directory as where Plan&Score is located.
+1. Open the `events.txt` file.
+1. Check if any of the data are violating our decoding formats:
+ * For class/cca/test, the format should be: `[IDENTIFIER]|[true/false]|[DESCRIPTION]|[DATE IN YYYY-MM-DD HHMM]|[DATE IN YYYY-MM-DD HHMM]`
+ * For tuition, the format should be: `[IDENTIFIER]|[true/false]|[DESCRIPTION]|[DATE IN YYYY-MM-DD HHMM]|[DATE IN YYYY-MM-DD HHMM]|location`
+1. Edit the file to the correct formats shown above
+1. Launch Plan&Score
+1. Confirm that Plan&Score runs without any error
-**A**: {your answer here}
+In the unfortunate event where the error persists, please reset Plan&Score to factory settings.
+We recommend keeping a duplicate of the contents in `events.txt` to assist with the re-adding of events.
+1. Ensure you have duplicated `events.txt`
+1. Delete `events.txt`
+1. Launch Plan&Score
+1. Confirm that Plan&Score runs without any error
+1. `events.txt` should appear in the `data` directory
+1. Copy any unaffected events from the duplicated file in Step 1
+1. Re-add affected events through the command line interface
+
-## Command Summary
+## Command Summary (Team)
-{Give a 'cheat sheet' of commands here}
-* Add todo `todo n/TODO_NAME d/DEADLINE`
+Action | Format | Examples
+-------|--------|--------
+help|`help`
+add event|`add [class/cca/test] /n [description] /s [start-date-time] /e [end date-time]`, `add [tuition] /n [description] /s [start-date-time] /e [end-date-time] /l [location]`|`add class /n Math /s 2020-09-06 1300 /e 2020-09-06 1400`, `add tuition /n Math /s 2020-09-06 1300 /e 2020-09-06 1400 /l home`
+add quiz|`add quiz /q [question] /o1 [option 1] /o2 [option 2] /o3 [option 3] /o4 [option 4] /a [option answer] /e (explanation)`|`add quiz /q 1 + 1 = ? /o1 1 /o2 2 /o3 3 /o4 4 /a 2 /exp no explanation needed`
+add contact|`add contact /s [subject] /n [name of contact person] /p [phone number] /e [email address]`|`add contact /s math /n thomas /p 91779977 /e thomas@gmail.com`
+delete event|`delete [class/cca/test/tuition] [item number]`|`delete class 1`, `delete test 1`, `delete cca 1`
+delete quiz|`delete quiz [item number]`|`delete quiz 1`
+delete contact|`delete contact [item number]`|`delete contact 1`
+list event|`list event ()`|`list event 2020-09-06`
+list quiz|`list quiz`
+list contact| `list contact`
+Find event|`find event [keyword(s)]`|`find event English`
+Find quiz|`find quiz [keyword(s)]`|`find quiz 2 + 2 = 4`
+Find contact|`find contact [keyword(s)]`|`find contact jonny@gmail.com`
+quiz|`quiz [no of questions]`|`quiz 15`
+display quiz record|`quiz record`
+set hours|`set hours`
+bye|`bye`
diff --git a/docs/_config.yml b/docs/_config.yml
new file mode 100644
index 0000000000..a0be1d94bc
--- /dev/null
+++ b/docs/_config.yml
@@ -0,0 +1,3 @@
+theme: jekyll-theme-cayman
+title: CS2113T Team Project
+description: Plan&Score
\ No newline at end of file
diff --git a/docs/diagram/Add.png b/docs/diagram/Add.png
new file mode 100644
index 0000000000..73f3520f68
Binary files /dev/null and b/docs/diagram/Add.png differ
diff --git a/docs/diagram/ArchitectureDiagram.png b/docs/diagram/ArchitectureDiagram.png
new file mode 100644
index 0000000000..da13039e31
Binary files /dev/null and b/docs/diagram/ArchitectureDiagram.png differ
diff --git a/docs/diagram/ConfigStorage.png b/docs/diagram/ConfigStorage.png
new file mode 100644
index 0000000000..5757d1af30
Binary files /dev/null and b/docs/diagram/ConfigStorage.png differ
diff --git a/docs/diagram/ControllerComponent.png b/docs/diagram/ControllerComponent.png
new file mode 100644
index 0000000000..83290889a1
Binary files /dev/null and b/docs/diagram/ControllerComponent.png differ
diff --git a/docs/diagram/ControllerSequence.png b/docs/diagram/ControllerSequence.png
new file mode 100644
index 0000000000..9c5be3ef4d
Binary files /dev/null and b/docs/diagram/ControllerSequence.png differ
diff --git a/docs/diagram/DeleteFeatureDiagram.png b/docs/diagram/DeleteFeatureDiagram.png
new file mode 100644
index 0000000000..3e74f367fc
Binary files /dev/null and b/docs/diagram/DeleteFeatureDiagram.png differ
diff --git a/docs/diagram/EventStorageManager.png b/docs/diagram/EventStorageManager.png
new file mode 100644
index 0000000000..fc564cb8d4
Binary files /dev/null and b/docs/diagram/EventStorageManager.png differ
diff --git a/docs/diagram/EventStorageReadSequence.png b/docs/diagram/EventStorageReadSequence.png
new file mode 100644
index 0000000000..f03fadc066
Binary files /dev/null and b/docs/diagram/EventStorageReadSequence.png differ
diff --git a/docs/diagram/EventStorageSaveSequence.png b/docs/diagram/EventStorageSaveSequence.png
new file mode 100644
index 0000000000..f6b4406667
Binary files /dev/null and b/docs/diagram/EventStorageSaveSequence.png differ
diff --git a/docs/diagram/ExampleSequence.png b/docs/diagram/ExampleSequence.png
new file mode 100644
index 0000000000..d4424b8111
Binary files /dev/null and b/docs/diagram/ExampleSequence.png differ
diff --git a/docs/diagram/FindEvent.png b/docs/diagram/FindEvent.png
new file mode 100644
index 0000000000..e754ca2789
Binary files /dev/null and b/docs/diagram/FindEvent.png differ
diff --git a/docs/diagram/FindQuiz.png b/docs/diagram/FindQuiz.png
new file mode 100644
index 0000000000..0667e34392
Binary files /dev/null and b/docs/diagram/FindQuiz.png differ
diff --git a/docs/diagram/Help.png b/docs/diagram/Help.png
new file mode 100644
index 0000000000..5221496ede
Binary files /dev/null and b/docs/diagram/Help.png differ
diff --git a/docs/diagram/ListContact.png b/docs/diagram/ListContact.png
new file mode 100644
index 0000000000..3ee45aa249
Binary files /dev/null and b/docs/diagram/ListContact.png differ
diff --git a/docs/diagram/ListEvent.png b/docs/diagram/ListEvent.png
new file mode 100644
index 0000000000..67f392904b
Binary files /dev/null and b/docs/diagram/ListEvent.png differ
diff --git a/docs/diagram/ListEvent2.png b/docs/diagram/ListEvent2.png
new file mode 100644
index 0000000000..5ee63e6489
Binary files /dev/null and b/docs/diagram/ListEvent2.png differ
diff --git a/docs/diagram/ListWeekSequence.png b/docs/diagram/ListWeekSequence.png
new file mode 100644
index 0000000000..56a78db3cd
Binary files /dev/null and b/docs/diagram/ListWeekSequence.png differ
diff --git a/docs/diagram/ModelComponent.png b/docs/diagram/ModelComponent.png
new file mode 100644
index 0000000000..d07a3afcf0
Binary files /dev/null and b/docs/diagram/ModelComponent.png differ
diff --git a/docs/diagram/QuizReadStorage.png b/docs/diagram/QuizReadStorage.png
new file mode 100644
index 0000000000..ffddc6af8d
Binary files /dev/null and b/docs/diagram/QuizReadStorage.png differ
diff --git a/docs/diagram/QuizWriteStorage.png b/docs/diagram/QuizWriteStorage.png
new file mode 100644
index 0000000000..c1b252922e
Binary files /dev/null and b/docs/diagram/QuizWriteStorage.png differ
diff --git a/docs/diagram/SetHours.png b/docs/diagram/SetHours.png
new file mode 100644
index 0000000000..05a68415e8
Binary files /dev/null and b/docs/diagram/SetHours.png differ
diff --git a/docs/diagram/StorageComponent.png b/docs/diagram/StorageComponent.png
new file mode 100644
index 0000000000..8df23ce8f1
Binary files /dev/null and b/docs/diagram/StorageComponent.png differ
diff --git a/docs/diagram/StorageSequence.png b/docs/diagram/StorageSequence.png
new file mode 100644
index 0000000000..20dd46fb72
Binary files /dev/null and b/docs/diagram/StorageSequence.png differ
diff --git a/docs/diagram/UserInterfaceComponent.png b/docs/diagram/UserInterfaceComponent.png
new file mode 100644
index 0000000000..a9af7bad84
Binary files /dev/null and b/docs/diagram/UserInterfaceComponent.png differ
diff --git a/docs/images/add_cca_1.png b/docs/images/add_cca_1.png
new file mode 100644
index 0000000000..8c899bf603
Binary files /dev/null and b/docs/images/add_cca_1.png differ
diff --git a/docs/images/add_cca_2.png b/docs/images/add_cca_2.png
new file mode 100644
index 0000000000..05ab37cceb
Binary files /dev/null and b/docs/images/add_cca_2.png differ
diff --git a/docs/images/add_cca_error.png b/docs/images/add_cca_error.png
new file mode 100644
index 0000000000..724bf2aaad
Binary files /dev/null and b/docs/images/add_cca_error.png differ
diff --git a/docs/images/add_class_2.png b/docs/images/add_class_2.png
new file mode 100644
index 0000000000..75237ab070
Binary files /dev/null and b/docs/images/add_class_2.png differ
diff --git a/docs/images/add_contact.png b/docs/images/add_contact.png
new file mode 100644
index 0000000000..84ddd70b7d
Binary files /dev/null and b/docs/images/add_contact.png differ
diff --git a/docs/images/add_contact_command.png b/docs/images/add_contact_command.png
new file mode 100644
index 0000000000..4652aa8bbb
Binary files /dev/null and b/docs/images/add_contact_command.png differ
diff --git a/docs/images/add_contact_wrongly.png b/docs/images/add_contact_wrongly.png
new file mode 100644
index 0000000000..083b280c58
Binary files /dev/null and b/docs/images/add_contact_wrongly.png differ
diff --git a/docs/images/add_test_1.png b/docs/images/add_test_1.png
new file mode 100644
index 0000000000..f5f912a02a
Binary files /dev/null and b/docs/images/add_test_1.png differ
diff --git a/docs/images/add_test_2.png b/docs/images/add_test_2.png
new file mode 100644
index 0000000000..70c968ee30
Binary files /dev/null and b/docs/images/add_test_2.png differ
diff --git a/docs/images/add_test_error.png b/docs/images/add_test_error.png
new file mode 100644
index 0000000000..e36790cef1
Binary files /dev/null and b/docs/images/add_test_error.png differ
diff --git a/docs/images/add_tuition_1.png b/docs/images/add_tuition_1.png
new file mode 100644
index 0000000000..2ced37a58f
Binary files /dev/null and b/docs/images/add_tuition_1.png differ
diff --git a/docs/images/add_tuition_2.png b/docs/images/add_tuition_2.png
new file mode 100644
index 0000000000..f4cf628e6f
Binary files /dev/null and b/docs/images/add_tuition_2.png differ
diff --git a/docs/images/add_tuition_error.png b/docs/images/add_tuition_error.png
new file mode 100644
index 0000000000..c97b1f88fa
Binary files /dev/null and b/docs/images/add_tuition_error.png differ
diff --git a/docs/images/addclass1.png b/docs/images/addclass1.png
new file mode 100644
index 0000000000..32be062784
Binary files /dev/null and b/docs/images/addclass1.png differ
diff --git a/docs/images/bye_1.png b/docs/images/bye_1.png
new file mode 100644
index 0000000000..9efa26c599
Binary files /dev/null and b/docs/images/bye_1.png differ
diff --git a/docs/images/bye_2.png b/docs/images/bye_2.png
new file mode 100644
index 0000000000..c0e8f4a4c2
Binary files /dev/null and b/docs/images/bye_2.png differ
diff --git a/docs/images/class_error.png b/docs/images/class_error.png
new file mode 100644
index 0000000000..4cc91c11cc
Binary files /dev/null and b/docs/images/class_error.png differ
diff --git a/docs/images/delete_cca_1.png b/docs/images/delete_cca_1.png
new file mode 100644
index 0000000000..d611624d58
Binary files /dev/null and b/docs/images/delete_cca_1.png differ
diff --git a/docs/images/delete_cca_2.png b/docs/images/delete_cca_2.png
new file mode 100644
index 0000000000..fc0859ea43
Binary files /dev/null and b/docs/images/delete_cca_2.png differ
diff --git a/docs/images/delete_cca_error.png b/docs/images/delete_cca_error.png
new file mode 100644
index 0000000000..3b4be70146
Binary files /dev/null and b/docs/images/delete_cca_error.png differ
diff --git a/docs/images/delete_class_1.png b/docs/images/delete_class_1.png
new file mode 100644
index 0000000000..410c329a1b
Binary files /dev/null and b/docs/images/delete_class_1.png differ
diff --git a/docs/images/delete_class_2.png b/docs/images/delete_class_2.png
new file mode 100644
index 0000000000..9cfb8643a0
Binary files /dev/null and b/docs/images/delete_class_2.png differ
diff --git a/docs/images/delete_class_error.png b/docs/images/delete_class_error.png
new file mode 100644
index 0000000000..e52c07831d
Binary files /dev/null and b/docs/images/delete_class_error.png differ
diff --git a/docs/images/delete_contact.png b/docs/images/delete_contact.png
new file mode 100644
index 0000000000..72ab943145
Binary files /dev/null and b/docs/images/delete_contact.png differ
diff --git a/docs/images/delete_contact_command.png b/docs/images/delete_contact_command.png
new file mode 100644
index 0000000000..65a34961f4
Binary files /dev/null and b/docs/images/delete_contact_command.png differ
diff --git a/docs/images/delete_contact_wrongly.png b/docs/images/delete_contact_wrongly.png
new file mode 100644
index 0000000000..1d68497080
Binary files /dev/null and b/docs/images/delete_contact_wrongly.png differ
diff --git a/docs/images/delete_test_1.png b/docs/images/delete_test_1.png
new file mode 100644
index 0000000000..125bf84b5f
Binary files /dev/null and b/docs/images/delete_test_1.png differ
diff --git a/docs/images/delete_test_2.png b/docs/images/delete_test_2.png
new file mode 100644
index 0000000000..6a5cd51d1d
Binary files /dev/null and b/docs/images/delete_test_2.png differ
diff --git a/docs/images/delete_test_error.png b/docs/images/delete_test_error.png
new file mode 100644
index 0000000000..847f6886cc
Binary files /dev/null and b/docs/images/delete_test_error.png differ
diff --git a/docs/images/delete_tuition_1.png b/docs/images/delete_tuition_1.png
new file mode 100644
index 0000000000..22128d13ce
Binary files /dev/null and b/docs/images/delete_tuition_1.png differ
diff --git a/docs/images/delete_tuition_2.png b/docs/images/delete_tuition_2.png
new file mode 100644
index 0000000000..2b3d39ed95
Binary files /dev/null and b/docs/images/delete_tuition_2.png differ
diff --git a/docs/images/delete_tuition_error.png b/docs/images/delete_tuition_error.png
new file mode 100644
index 0000000000..5e7af19d20
Binary files /dev/null and b/docs/images/delete_tuition_error.png differ
diff --git a/docs/images/deletequiz1.png b/docs/images/deletequiz1.png
new file mode 100644
index 0000000000..7d52f5899a
Binary files /dev/null and b/docs/images/deletequiz1.png differ
diff --git a/docs/images/erroraddclass.png b/docs/images/erroraddclass.png
new file mode 100644
index 0000000000..3f6f1eae20
Binary files /dev/null and b/docs/images/erroraddclass.png differ
diff --git a/docs/images/find note.jpg b/docs/images/find note.jpg
new file mode 100644
index 0000000000..12334cc60d
Binary files /dev/null and b/docs/images/find note.jpg differ
diff --git a/docs/images/find1.JPG b/docs/images/find1.JPG
new file mode 100644
index 0000000000..cf747fd530
Binary files /dev/null and b/docs/images/find1.JPG differ
diff --git a/docs/images/find2.JPG b/docs/images/find2.JPG
new file mode 100644
index 0000000000..1f1c987889
Binary files /dev/null and b/docs/images/find2.JPG differ
diff --git a/docs/images/find3.JPG b/docs/images/find3.JPG
new file mode 100644
index 0000000000..3dac71aca3
Binary files /dev/null and b/docs/images/find3.JPG differ
diff --git a/docs/images/find_contact.png b/docs/images/find_contact.png
new file mode 100644
index 0000000000..3c1de5648d
Binary files /dev/null and b/docs/images/find_contact.png differ
diff --git a/docs/images/find_contact_command.png b/docs/images/find_contact_command.png
new file mode 100644
index 0000000000..233be508c9
Binary files /dev/null and b/docs/images/find_contact_command.png differ
diff --git a/docs/images/findquiz.png b/docs/images/findquiz.png
new file mode 100644
index 0000000000..0df821e812
Binary files /dev/null and b/docs/images/findquiz.png differ
diff --git a/docs/images/finish_intro.png b/docs/images/finish_intro.png
new file mode 100644
index 0000000000..686765dc67
Binary files /dev/null and b/docs/images/finish_intro.png differ
diff --git a/docs/images/help1.png b/docs/images/help1.png
new file mode 100644
index 0000000000..436cd0e0d3
Binary files /dev/null and b/docs/images/help1.png differ
diff --git a/docs/images/help_command_1.png b/docs/images/help_command_1.png
new file mode 100644
index 0000000000..c9b99bb71b
Binary files /dev/null and b/docs/images/help_command_1.png differ
diff --git a/docs/images/help_command_2.png b/docs/images/help_command_2.png
new file mode 100644
index 0000000000..00b289b2a8
Binary files /dev/null and b/docs/images/help_command_2.png differ
diff --git a/docs/images/hours.png b/docs/images/hours.png
new file mode 100644
index 0000000000..24ae0e8a8e
Binary files /dev/null and b/docs/images/hours.png differ
diff --git a/docs/images/indexinfo.png b/docs/images/indexinfo.png
new file mode 100644
index 0000000000..8bf0e71e41
Binary files /dev/null and b/docs/images/indexinfo.png differ
diff --git a/docs/images/information.png b/docs/images/information.png
new file mode 100644
index 0000000000..f7a3f30ef1
Binary files /dev/null and b/docs/images/information.png differ
diff --git a/docs/images/initialization.png b/docs/images/initialization.png
new file mode 100644
index 0000000000..aa08152319
Binary files /dev/null and b/docs/images/initialization.png differ
diff --git a/docs/images/intro_screen.png b/docs/images/intro_screen.png
new file mode 100644
index 0000000000..edeb1914b0
Binary files /dev/null and b/docs/images/intro_screen.png differ
diff --git a/docs/images/list_contact.png b/docs/images/list_contact.png
new file mode 100644
index 0000000000..dc3101ad6b
Binary files /dev/null and b/docs/images/list_contact.png differ
diff --git a/docs/images/list_contact_command.png b/docs/images/list_contact_command.png
new file mode 100644
index 0000000000..8dd7d72b82
Binary files /dev/null and b/docs/images/list_contact_command.png differ
diff --git a/docs/images/list_event_1.png b/docs/images/list_event_1.png
new file mode 100644
index 0000000000..ef9965bf59
Binary files /dev/null and b/docs/images/list_event_1.png differ
diff --git a/docs/images/list_event_2.png b/docs/images/list_event_2.png
new file mode 100644
index 0000000000..b21810be07
Binary files /dev/null and b/docs/images/list_event_2.png differ
diff --git a/docs/images/list_event_date_1.png b/docs/images/list_event_date_1.png
new file mode 100644
index 0000000000..61c1e14282
Binary files /dev/null and b/docs/images/list_event_date_1.png differ
diff --git a/docs/images/list_event_date_2.png b/docs/images/list_event_date_2.png
new file mode 100644
index 0000000000..b0df06d9bc
Binary files /dev/null and b/docs/images/list_event_date_2.png differ
diff --git a/docs/images/list_event_nextweek_1.png b/docs/images/list_event_nextweek_1.png
new file mode 100644
index 0000000000..e6c0618849
Binary files /dev/null and b/docs/images/list_event_nextweek_1.png differ
diff --git a/docs/images/list_event_nextweek_2.png b/docs/images/list_event_nextweek_2.png
new file mode 100644
index 0000000000..197a675486
Binary files /dev/null and b/docs/images/list_event_nextweek_2.png differ
diff --git a/docs/images/list_event_today_1.png b/docs/images/list_event_today_1.png
new file mode 100644
index 0000000000..18ff41d8bf
Binary files /dev/null and b/docs/images/list_event_today_1.png differ
diff --git a/docs/images/list_event_today_2.png b/docs/images/list_event_today_2.png
new file mode 100644
index 0000000000..ae380b417a
Binary files /dev/null and b/docs/images/list_event_today_2.png differ
diff --git a/docs/images/list_event_week_1.png b/docs/images/list_event_week_1.png
new file mode 100644
index 0000000000..10e16d99e3
Binary files /dev/null and b/docs/images/list_event_week_1.png differ
diff --git a/docs/images/list_event_week_2.png b/docs/images/list_event_week_2.png
new file mode 100644
index 0000000000..3e3979a817
Binary files /dev/null and b/docs/images/list_event_week_2.png differ
diff --git a/docs/images/listquiz.png b/docs/images/listquiz.png
new file mode 100644
index 0000000000..4e7630cf39
Binary files /dev/null and b/docs/images/listquiz.png differ
diff --git a/docs/images/name.png b/docs/images/name.png
new file mode 100644
index 0000000000..16695eb47f
Binary files /dev/null and b/docs/images/name.png differ
diff --git a/docs/images/quiz1.png b/docs/images/quiz1.png
new file mode 100644
index 0000000000..a80872edb9
Binary files /dev/null and b/docs/images/quiz1.png differ
diff --git a/docs/images/quizadded.png b/docs/images/quizadded.png
new file mode 100644
index 0000000000..5c77c2fba1
Binary files /dev/null and b/docs/images/quizadded.png differ
diff --git a/docs/images/quizrecord.png b/docs/images/quizrecord.png
new file mode 100644
index 0000000000..aa7e9196a0
Binary files /dev/null and b/docs/images/quizrecord.png differ
diff --git a/docs/images/set_hours_1.png b/docs/images/set_hours_1.png
new file mode 100644
index 0000000000..a1c23c3bff
Binary files /dev/null and b/docs/images/set_hours_1.png differ
diff --git a/docs/images/set_hours_2.png b/docs/images/set_hours_2.png
new file mode 100644
index 0000000000..40af5ba92e
Binary files /dev/null and b/docs/images/set_hours_2.png differ
diff --git a/docs/images/set_hours_3.png b/docs/images/set_hours_3.png
new file mode 100644
index 0000000000..afb6858a13
Binary files /dev/null and b/docs/images/set_hours_3.png differ
diff --git a/docs/images/set_hours_error.png b/docs/images/set_hours_error.png
new file mode 100644
index 0000000000..6ea50fb5c1
Binary files /dev/null and b/docs/images/set_hours_error.png differ
diff --git a/docs/images/storage_corrupted_factory_reset.png b/docs/images/storage_corrupted_factory_reset.png
new file mode 100644
index 0000000000..5626f8be70
Binary files /dev/null and b/docs/images/storage_corrupted_factory_reset.png differ
diff --git a/docs/images/warning.png b/docs/images/warning.png
new file mode 100644
index 0000000000..bbf7fe220a
Binary files /dev/null and b/docs/images/warning.png differ
diff --git a/docs/plantuml/Add.puml b/docs/plantuml/Add.puml
new file mode 100644
index 0000000000..388bec8053
--- /dev/null
+++ b/docs/plantuml/Add.puml
@@ -0,0 +1,40 @@
+@startuml
+
+skinparam ParticipantPadding 10
+skinparam BoxPadding 5
+hide footbox
+
+box "Controller" #LightSalmon
+participant ControlManager
+participant AddCommand
+end box
+
+box "Model" #YellowGreen
+participant DataManager
+end box
+
+box "UserInterface" #skyblue
+participant UserInterface
+end box
+
+ControlManager -> AddCommand: execute()
+activate ControlManager
+
+activate AddCommand
+AddCommand -> DataManager: add()
+activate DataManager
+DataManager -> DataManager: getClassStatement()
+activate DataManager #salmon
+DataManager -> UserInterface: showToUser()
+activate UserInterface
+UserInterface --> DataManager
+deactivate UserInterface
+DataManager --> DataManager
+deactivate DataManager
+DataManager --> AddCommand
+deactivate DataManager
+AddCommand --> ControlManager
+deactivate AddCommand
+
+deactivate ControlManager
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/ArchitectureDiagram.puml b/docs/plantuml/ArchitectureDiagram.puml
new file mode 100644
index 0000000000..ea6d9fbb0f
--- /dev/null
+++ b/docs/plantuml/ArchitectureDiagram.puml
@@ -0,0 +1,48 @@
+@startuml
+allowmixing
+skinparam actorStyle awesome
+skinparam rectangle {
+ roundCorner 25
+}
+skinparam MinClassWidth 100
+hide circle
+hide members
+scale 450 width
+skinparam Class {
+ BorderColor Azure
+}
+skinparam Shadowing false
+skinparam Actor {
+ BorderColor Olive
+ BackgroundColor Khaki
+}
+
+actor user
+
+rectangle #Azure {
+ class UserInterface #SkyBlue {
+ }
+ class Model #YellowGreen {
+ }
+ class Controller #LightSalmon {
+ }
+ class Main #Silver {
+ }
+ class Storage #Tan {
+ }
+}
+
+package Data <> #LightSalmon {
+}
+
+user ..down> UserInterface
+UserInterface -down[bold,#SkyBlue]-> Controller
+Controller -[Bold,#LightSalmon]-> Model : manipulate data
+Model --[bold,#YellowGreen]> UserInterface : update view
+UserInterface <-[#Silver] Main : initialize
+Main -up[#Silver]> Model : initialize
+Main -right[#Silver]> Storage : initialize
+Storage ..[#Peru]> Data
+Controller -[bold,#LightSalmon]> Storage
+
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/ConfigStorage.puml b/docs/plantuml/ConfigStorage.puml
new file mode 100644
index 0000000000..cae8c9bb9b
--- /dev/null
+++ b/docs/plantuml/ConfigStorage.puml
@@ -0,0 +1,45 @@
+@startuml
+hide circles
+skinparam classAttributeIconSize 0
+skinparam classBackgroundColor Tan
+
+class StorageManager <> #Tan {
+ # fileName:String
+ + StorageManager(fileName)
+ + StorageManager(directory, fileName)
+ # createDataFile():boolean
+}
+
+class ConfigStorageManager #Tan {
+ + ConfigStorageManager(:String)
+ + saveData(:ConfigParameter)
+ + loadData()(:ConfigParameter)
+}
+
+ConfigStorageManager --|> StorageManager
+
+class ConfigParameter {
+ - name(:String)
+ - recommendedHours(:int)
+ - hasProgramRan(:boolean)
+}
+
+class ConfigEncoder {
+ - name(:String)
+ - recommendedHours(:int)
+ - hasProgramRan(:boolean)
+}
+
+class ConfigDecoder {
+ - splitEncodedConfig(:StringArray)
+ - name(:String)
+ - recommendedHours(:int)
+ - hasProgramRan(:boolean)
+}
+
+ConfigStorageManager --> ConfigEncoder
+ConfigStorageManager --> ConfigDecoder
+ConfigParameter --> ConfigEncoder
+ConfigDecoder --> ConfigParameter
+
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/ControllerComponent.puml b/docs/plantuml/ControllerComponent.puml
new file mode 100644
index 0000000000..7bd2ca0aeb
--- /dev/null
+++ b/docs/plantuml/ControllerComponent.puml
@@ -0,0 +1,82 @@
+@startuml
+skinparam Shadowing false
+skinparam MinClassWidth 100
+skinparam classBackgroundColor LightSalmon
+skinparam packageStyle rectangle
+skinparam ArrowColor Salmon
+hide circle
+hide members
+
+package Controller {
+
+ class ControlManager {
+ userInput: String
+ model: Model
+ runLogic()
+ }
+
+ Package Parser {
+ class ModelExtractor {
+ model: Model
+ modelType: ModelType
+ retriveModel()
+ }
+ class CommandParser {
+ separatedInputs: String[]
+ extractCommand() : CommandType
+ }
+ class ModelParser {
+ separatedInputs: String[]
+ extractModel() : ModelType
+ }
+
+' enum "<>\nCommandType" {
+' ADD
+' DELETE
+' DONE
+' LIST
+' HELP
+' BYE
+' }
+'
+' enum "<>\nModelType" {
+' EVENT
+' CLASS
+' CCA
+' TEST
+' TUITION
+' QUIZ
+' CONTACT
+' }
+ }
+
+ Package Command {
+ class CommandFactory {
+ userInput: String
+ generateActionableCommand()
+ }
+ class ChildCommand
+ class "{abstract}\nCommand" {
+ userInput: String
+ execute(DataManager) { abstract }
+ }
+ }
+}
+
+Package Model {
+}
+
+ControlManager .down> CommandParser
+ControlManager ..down> ModelParser
+ControlManager ..> CommandFactory
+ControlManager ---> Model
+ControlManager .left> ModelExtractor
+CommandFactory .right> ChildCommand : creates >
+ControlManager .> "{abstract}\nCommand" : executes >
+ChildCommand -|> "{abstract}\nCommand"
+"{abstract}\nCommand" ..up> Model
+'CommandParser -> "<>\nCommandType"
+'ModelParser -left> "<>\nModelType"
+'CommandFactory -> "<>\nCommandType"
+
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/ControllerSequence.puml b/docs/plantuml/ControllerSequence.puml
new file mode 100644
index 0000000000..e70b9ac2a1
--- /dev/null
+++ b/docs/plantuml/ControllerSequence.puml
@@ -0,0 +1,63 @@
+@startuml
+skinparam ParticipantPadding 10
+skinparam BoxPadding 5
+hide footbox
+
+box "Controller" #LightSalmon
+participant ":ControlManager"
+participant ":CommandParser"
+participant ":CommandFactory"
+participant ":ModelParser"
+participant ":ModelExtractor"
+end box
+
+-> ":ControlManager" : runLogic()
+activate ":ControlManager"
+":ControlManager" -> ":CommandParser" ** : constructor()
+activate ":CommandParser"
+":CommandParser" --> ":ControlManager"
+deactivate ":CommandParser"
+":ControlManager" -> ":CommandParser" : extractCommand()
+activate ":CommandParser"
+":CommandParser" --> ":ControlManager" : Command Type
+deactivate ":CommandParser"
+
+":ControlManager" -> ":CommandFactory" ** : constructor()
+activate ":CommandFactory"
+":CommandFactory" --> ":ControlManager"
+deactivate ":CommandFactory"
+":ControlManager" -> ":CommandFactory" : generateActionableCommand()
+activate ":CommandFactory"
+":CommandFactory" --> ":ControlManager" : Command
+deactivate ":CommandFactory"
+
+":ControlManager" -> ":ModelParser" ** : constructor()
+activate ":ModelParser"
+":ModelParser" --> ":ControlManager"
+deactivate ":ModelParser"
+":ControlManager" -> ":ModelParser" : extractModel()
+activate ":ModelParser"
+":ModelParser" --> ":ControlManager" : Model Type
+deactivate ":ModelParser"
+
+":ControlManager" -> ":ModelExtractor" ** : constructor()
+activate ":ModelExtractor"
+":ModelExtractor" --> ":ControlManager"
+deactivate ":ModelExtractor"
+":ControlManager" -> ":ModelExtractor" : retrieveModel()
+activate ":ModelExtractor"
+":ModelExtractor" --> ":ControlManager" : Model
+deactivate ":ModelExtractor"
+
+":ControlManager" -> : execute()
+":ControlManager" <--
+
+<-- ":ControlManager"
+deactivate ":ControlManager"
+
+destroy ":CommandParser"
+destroy ":CommandFactory"
+destroy ":ModelParser"
+destroy ":ModelExtractor"
+
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/DeleteFeatureDiagram.puml b/docs/plantuml/DeleteFeatureDiagram.puml
new file mode 100644
index 0000000000..450727b050
--- /dev/null
+++ b/docs/plantuml/DeleteFeatureDiagram.puml
@@ -0,0 +1,69 @@
+@startuml
+'activate UserInterface
+'UserInterface -> ControlManager: runLogic()
+'activate ControlManager
+
+'activate ControlManager
+'ControlManager -> CommandParser: extractCommand()
+'activate CommandParser
+'ControlManager <-- CommandParser: return command type
+'deactivate CommandParser
+'deactivate ControlManager
+'
+'ControlManager -> CommandFactory: generateActionableCommand()
+'activate ControlManager
+'activate CommandFactory
+'CommandFactory --> ControlManager: return actionable command
+'deactivate CommandFactory
+'deactivate ControlManager
+'
+'ControlManager -> ModelParser: extractModel()
+'activate ControlManager
+'activate ModelParser
+'ModelParser --> ControlManager: return model type
+'deactivate ModelParser
+'deactivate ControlManager
+'
+'ControlManager -> ModelExtractor: retrieveModel()
+'activate ControlManager
+'activate ModelExtractor
+'ModelExtractor --> ControlManager: return model
+'deactivate ControlManager
+'deactivate ModelExtractor
+
+skinparam ParticipantPadding 10
+skinparam BoxPadding 5
+hide footbox
+
+box "Controller" #LightSalmon
+participant ControlManager
+participant DeleteCommand
+end box
+
+box "Model" #YellowGreen
+participant DataManager
+end box
+
+box "UserInterface" #skyblue
+participant UserInterface
+end box
+
+ControlManager -> DeleteCommand: execute()
+activate ControlManager
+activate DeleteCommand
+DeleteCommand -> DataManager: delete()
+activate DataManager
+DataManager -> DataManager: getClassStatement()
+activate DataManager #salmon
+DataManager -> UserInterface: showToUser()
+activate UserInterface
+UserInterface --> DataManager
+DataManager --> DataManager
+deactivate DataManager
+deactivate UserInterface
+DataManager --> DeleteCommand
+deactivate DataManager
+DeleteCommand --> ControlManager
+deactivate DeleteCommand
+deactivate ControlManager
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/DoneFeatureDiagram.puml b/docs/plantuml/DoneFeatureDiagram.puml
new file mode 100644
index 0000000000..ed7bceea86
--- /dev/null
+++ b/docs/plantuml/DoneFeatureDiagram.puml
@@ -0,0 +1,40 @@
+@startuml
+
+skinparam ParticipantPadding 10
+skinparam BoxPadding 5
+
+box "Controller" #LightSalmon
+participant ControlManager
+participant DoneCommand
+end box
+
+box "Model" #YellowGreen
+participant DataManager
+end box
+
+box "UserInterface" #skyblue
+participant UserInterface
+end box
+
+ControlManager -> DoneCommand: execute()
+activate ControlManager
+activate DoneCommand
+DoneCommand -> DataManager: setDone()
+activate DataManager
+DataManager -> DataManager: getClassStatement()
+activate DataManager
+
+
+
+DataManager -> UserInterface: showToUser()
+activate UserInterface
+
+UserInterface --> DataManager
+deactivate DataManager
+deactivate UserInterface
+DataManager --> DoneCommand
+deactivate DataManager
+DoneCommand --> ControlManager
+deactivate DoneCommand
+deactivate ControlManager
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/EventStorageManager.puml b/docs/plantuml/EventStorageManager.puml
new file mode 100644
index 0000000000..98802092ec
--- /dev/null
+++ b/docs/plantuml/EventStorageManager.puml
@@ -0,0 +1,51 @@
+@startuml
+hide circles
+skinparam classAttributeIconSize 0
+
+class StorageManager <> #Tan {
+ # fileName:String
+ + StorageManager(fileName)
+ + StorageManager(directory, fileName)
+ # createDataFile():boolean
+}
+
+class EventStorageManager #Tan {
+ + EventStorageManager(:String)
+ + saveData(:EventParameter)
+ + loadData():EventParameter
+}
+
+EventStorageManager --|> StorageManager
+
+class EventListDecoder #Tan {
+ + EventListDecoder(:String)
+ + EventListDecoder(:String, :String)
+ + decodeEventFromString(:String)
+ - parseClass(:String[])
+ - parseCca(:String[])
+ - parseTest(:String[])
+ - parseTuition(:String[])
+}
+
+EventStorageManager -> EventListDecoder
+
+class EventListEncoder #Tan {
+ + EventListEncoder()
+ + encodeEventList(:ArrayList eventList)
+ - encodeEventToString(:Event)
+}
+
+EventStorageManager -> EventListEncoder
+
+class EventParameter #YellowGreen {
+ + ccas:ArrayList
+ + classes:ArrayList
+ + tuitions:ArrayList
+ + tests:ArrayList
+ + EventParameter(ccas:ArrayList, classes:ArrayList,
+ tests:ArrayList, tuitions:ArrayList)
+}
+
+EventListDecoder --> EventParameter : creates
+
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/EventStorageReadSequence.puml b/docs/plantuml/EventStorageReadSequence.puml
new file mode 100644
index 0000000000..13b24a893f
--- /dev/null
+++ b/docs/plantuml/EventStorageReadSequence.puml
@@ -0,0 +1,40 @@
+@startuml
+hide footbox
+
+-> ":EventStorageManager" : loadData()
+activate ":EventStorageManager"
+":EventStorageManager" -> ":EventListDecoder" : decodeEventList(encodedEventList)
+activate ":EventListDecoder"
+
+loop number of strings
+":EventListDecoder" -> ":EventListDecoder" : decodeEventFromString(encodedEvent)
+activate ":EventListDecoder" #salmon
+alt cca
+":EventListDecoder" -> ":EventListDecoder" : parseCca(splitData)
+activate ":EventListDecoder"
+":EventListDecoder" --> ":EventListDecoder" : eventCca
+deactivate ":EventListDecoder"
+else tuition
+":EventListDecoder" -> ":EventListDecoder" : parseTuition(splitData)
+activate ":EventListDecoder"
+":EventListDecoder" --> ":EventListDecoder" : eventTuition
+deactivate ":EventListDecoder"
+else class
+":EventListDecoder" -> ":EventListDecoder" : parseClass(splitData)
+activate ":EventListDecoder"
+":EventListDecoder" --> ":EventListDecoder" : eventClass
+deactivate ":EventListDecoder"
+else test
+":EventListDecoder" -> ":EventListDecoder" : parseTest(splitData)
+activate ":EventListDecoder"
+":EventListDecoder" --> ":EventListDecoder" : eventTest
+deactivate ":EventListDecoder"
+end alt
+":EventListDecoder" --> ":EventListDecoder"
+deactivate ":EventListDecoder"
+":EventListDecoder" --> ":EventStorageManager" : decodedEventList
+deactivate ":EventListDecoder"
+end loop
+<-- ":EventStorageManager" : eventParameter
+deactivate ":EventStorageManager"
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/EventStorageSaveSequence.puml b/docs/plantuml/EventStorageSaveSequence.puml
new file mode 100644
index 0000000000..4e045c3665
--- /dev/null
+++ b/docs/plantuml/EventStorageSaveSequence.puml
@@ -0,0 +1,20 @@
+@startuml
+hide footbox
+
+-> ":EventStorageManager" : saveData(eventList)
+activate ":EventStorageManager"
+":EventStorageManager" -> ":EventListEncoder" : encodeEventList(eventList)
+activate ":EventListEncoder"
+
+loop number of events
+":EventListEncoder" -> ":EventListEncoder" : encodeEventToString(event)
+activate ":EventListEncoder" #salmon
+":EventListEncoder" --> ":EventListEncoder" : data
+deactivate ":EventListEncoder"
+":EventListEncoder" --> ":EventStorageManager"
+deactivate ":EventListEncoder"
+end loop
+<-- ":EventStorageManager"
+deactivate ":EventStorageManager"
+
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/ExampleSequence.puml b/docs/plantuml/ExampleSequence.puml
new file mode 100644
index 0000000000..77e17faa49
--- /dev/null
+++ b/docs/plantuml/ExampleSequence.puml
@@ -0,0 +1,51 @@
+@startuml
+
+skinparam ParticipantPadding 10
+skinparam BoxPadding 5
+hide footbox
+
+participant User
+
+box "UserInterface" #skyblue
+participant UserInterface
+end box
+
+box "Controller" #LightSalmon
+participant Controller
+end box
+
+box "Model" #YellowGreen
+participant Model
+end box
+
+box "Storage" #Tan
+participant Storage
+end box
+
+
+User -> UserInterface: "add test /n CS2113T Finals /s 2020-12-01 1300 /e 2020-12-01 1400
+activate UserInterface
+UserInterface -> UserInterface :runUI()
+activate UserInterface #salmon
+UserInterface -> Controller: runLogic()
+activate Controller
+Controller -> Model: add(userInput)
+activate Model
+Model --> Controller
+deactivate Model
+Controller -> Storage: refreshEvents()
+activate Storage
+Storage -> Storage: saveData()
+activate Storage #salmon
+Storage --> Storage
+deactivate Storage
+Storage --> Controller
+deactivate Storage
+Controller --> UserInterface
+deactivate Controller
+UserInterface --> UserInterface : isActive
+deactivate UserInterface
+UserInterface --> User
+deactivate UserInterface
+deactivate User
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/FindEvent.puml b/docs/plantuml/FindEvent.puml
new file mode 100644
index 0000000000..9000269d9f
--- /dev/null
+++ b/docs/plantuml/FindEvent.puml
@@ -0,0 +1,44 @@
+@startuml
+
+skinparam ParticipantPadding 10
+skinparam BoxPadding 5
+hide footbox
+
+box "Controller" #LightSalmon
+participant FindCommand
+end box
+
+box "Model" #YellowGreen
+participant EventManager
+participant FindSchedule
+end box
+
+box "UserInterface" #skyblue
+participant UserInterface
+end box
+
+FindCommand -> EventManager : execute()
+activate FindCommand
+activate EventManager
+EventManager -> EventManager : findSchedule()
+activate EventManager #DarkSalmon
+EventManager -> FindSchedule ** : constructor()
+activate FindSchedule
+FindSchedule --> EventManager
+deactivate FindSchedule
+EventManager -> FindSchedule : getFilteredEvents()
+activate FindSchedule
+FindSchedule --> EventManager : filteredEvents
+deactivate FindSchedule
+EventManager -> UserInterface : printArray()
+activate UserInterface
+UserInterface --> EventManager
+deactivate UserInterface
+EventManager --> EventManager
+deactivate EventManager
+EventManager --> FindCommand
+deactivate EventManager
+deactivate FindCommand
+destroy FindSchedule
+
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/FindQuiz.puml b/docs/plantuml/FindQuiz.puml
new file mode 100644
index 0000000000..119592f94b
--- /dev/null
+++ b/docs/plantuml/FindQuiz.puml
@@ -0,0 +1,44 @@
+@startuml
+
+skinparam ParticipantPadding 10
+skinparam BoxPadding 5
+hide footbox
+
+box "Controller" #LightSalmon
+participant ":FindCommand"
+end box
+
+box "Model" #YellowGreen
+participant ":QuizManager"
+participant ":FindQuiz"
+end box
+
+box "UserInterface" #skyblue
+participant ":UserInterface"
+end box
+
+":FindCommand" -> ":QuizManager" : execute()
+activate ":FindCommand"
+activate ":QuizManager"
+":QuizManager" -> ":QuizManager" : find()
+activate ":QuizManager" #DarkSalmon
+":QuizManager" -> ":FindQuiz" ** : constructor()
+activate ":FindQuiz"
+":FindQuiz" --> ":QuizManager"
+deactivate ":FindQuiz"
+":QuizManager" -> ":FindQuiz" : filteredQuizzes()
+activate ":FindQuiz"
+":FindQuiz" --> ":QuizManager" : filteredQuizzes
+deactivate ":FindQuiz"
+":QuizManager" -> ":UserInterface" : printArray()
+activate ":UserInterface"
+":UserInterface" --> ":QuizManager"
+deactivate ":UserInterface"
+":QuizManager" --> ":QuizManager"
+deactivate ":QuizManager"
+":QuizManager" --> ":FindCommand"
+deactivate ":QuizManager"
+deactivate ":FindCommand"
+destroy ":FindQuiz"
+
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/Help.puml b/docs/plantuml/Help.puml
new file mode 100644
index 0000000000..33d9944557
--- /dev/null
+++ b/docs/plantuml/Help.puml
@@ -0,0 +1,40 @@
+@startuml
+
+skinparam ParticipantPadding 10
+skinparam BoxPadding 5
+hide footbox
+
+box "Controller" #LightSalmon
+participant ControlManager
+participant CommandParser
+participant HelpCommand
+end box
+
+box "UserInterface" #skyblue
+participant UserInterface
+end box
+
+
+ControlManager -> CommandParser: extractCommand()
+activate ControlManager
+activate CommandParser
+CommandParser --> ControlManager: command type
+deactivate CommandParser
+
+ControlManager -> HelpCommand: execute()
+activate HelpCommand
+HelpCommand -> HelpCommand: handleHelp()
+activate HelpCommand #salmon
+HelpCommand --> HelpCommand
+deactivate HelpCommand
+
+HelpCommand -> UserInterface: showToUser()
+activate UserInterface
+UserInterface --> HelpCommand
+deactivate UserInterface
+HelpCommand --> ControlManager
+
+deactivate ControlManager
+deactivate HelpCommand
+
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/ListContact.puml b/docs/plantuml/ListContact.puml
new file mode 100644
index 0000000000..16a00ebd37
--- /dev/null
+++ b/docs/plantuml/ListContact.puml
@@ -0,0 +1,45 @@
+@startuml
+
+skinparam ParticipantPadding 10
+skinparam SequenceGroup 200
+skinparam BoxPadding 5
+hide footbox
+
+box "Controller" #LightSalmon
+participant ":ListCommand"
+end box
+
+box "Model" #YellowGreen
+participant ":QuizManager"
+end box
+
+box "UserInterface" #skyblue
+participant ":UserInterface"
+end box
+
+":ListCommand" -> ":QuizManager" : execute()
+activate ":ListCommand"
+activate ":QuizManager"
+":QuizManager" -> ":QuizManager" : list()
+activate ":QuizManager" #DarkSalmon
+alt empty ArrayList
+ ":QuizManager" -> ":UserInterface" : showToUser()
+ activate ":UserInterface"
+ ":UserInterface" --> ":QuizManager"
+ deactivate ":UserInterface"
+ ":QuizManager" --> ":ListCommand"
+else else
+ loop until end of ArrayList
+ ":QuizManager" -> ":UserInterface" : showToUser()
+ activate ":UserInterface"
+ ":UserInterface" --> ":QuizManager"
+ deactivate ":UserInterface"
+ ":QuizManager" --> ":QuizManager"
+ deactivate ":QuizManager"
+ |||
+ end
+ ":QuizManager" --> ":ListCommand"
+ deactivate ":QuizManager"
+ deactivate ":ListCommand"
+end
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/ListEvent.puml b/docs/plantuml/ListEvent.puml
new file mode 100644
index 0000000000..471fb6003b
--- /dev/null
+++ b/docs/plantuml/ListEvent.puml
@@ -0,0 +1,41 @@
+@startuml
+
+skinparam ParticipantPadding 10
+skinparam BoxPadding 5
+hide footbox
+
+box "Controller" #LightSalmon
+participant ":ListCommand"
+end box
+
+box "Model" #YellowGreen
+participant ":EventManager"
+participant ":ListSchedule"
+end box
+
+box "UserInterface" #skyblue
+participant ":UserInterface"
+end box
+
+":ListCommand" -> ":EventManager" : execute()
+activate ":EventManager"
+":EventManager" -> ":EventManager" : list()
+activate ":EventManager" #DarkSalmon
+":EventManager" -> ":ListSchedule" ** : constructor()
+activate ":ListSchedule"
+":ListSchedule" --> ":EventManager"
+deactivate ":ListSchedule"
+":EventManager" -> ":ListSchedule" : getPrintableEvents()
+activate ":ListSchedule"
+":ListSchedule" --> ":EventManager" : string array
+deactivate ":ListSchedule"
+":EventManager" -> ":UserInterface" : printArray()
+activate ":UserInterface"
+":UserInterface" --> ":EventManager"
+deactivate ":UserInterface"
+deactivate ":EventManager"
+":EventManager" --> ":ListCommand"
+deactivate ":EventManager"
+destroy ":ListSchedule"
+
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/ListWeekSequence.puml b/docs/plantuml/ListWeekSequence.puml
new file mode 100644
index 0000000000..d97764d362
--- /dev/null
+++ b/docs/plantuml/ListWeekSequence.puml
@@ -0,0 +1,38 @@
+@startuml
+hide footbox
+skinparam BoxPadding 5
+
+box "Controller" #LightSalmon
+participant ":ListCommand"
+end box
+
+box "Model" #YellowGreen
+participant ":EventManager"
+end box
+
+box "UserInterface" #skyblue
+participant ":UserInterface"
+participant ":CalendarWeekRenderer"
+end box
+
+":ListCommand" -> ":EventManager" : execute()
+activate ":ListCommand"
+activate ":EventManager"
+":EventManager" -> ":EventManager" : listSchedule()
+activate ":EventManager" #Salmon
+":EventManager" -> ":UserInterface" : printWeekSchedule(this)
+activate ":UserInterface"
+":UserInterface" -> ":CalendarWeekRenderer"** : constructor()
+activate ":CalendarWeekRenderer"
+":CalendarWeekRenderer" -> ":UserInterface"
+deactivate ":CalendarWeekRenderer"
+":UserInterface" --> ":EventManager"
+deactivate ":UserInterface"
+":EventManager" --> ":EventManager"
+deactivate ":EventManager"
+":EventManager" --> ":ListCommand"
+deactivate
+deactivate ":ListCommand"
+destroy ":CalendarWeekRenderer"
+
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/ModelComponent.puml b/docs/plantuml/ModelComponent.puml
new file mode 100644
index 0000000000..a36f0eb98f
--- /dev/null
+++ b/docs/plantuml/ModelComponent.puml
@@ -0,0 +1,51 @@
+@startuml
+
+skinparam Shadowing false
+skinparam MinClassWidth 100
+skinparam classBackgroundColor YellowGreen
+skinparam packageStyle rectangle
+skinparam ArrowColor Salmon
+hide circle
+hide members
+
+package Model {
+ class Model
+
+ Package Quiz {
+ class QuizManager
+ class "<>\nQuizInteractable"
+ }
+
+ Package Event {
+ class EventManager
+ class IndividualManager
+ class "<>\nEventManagerInteractable"
+ class "<>\nEventInteractable"
+ }
+
+ Package Contact {
+ class ContactManager
+ class "<>\nContactInteractable"
+ }
+
+ Package Config {
+ class ConfigManager
+ class "<>\nConfigInteractable"
+ }
+}
+
+
+QuizManager ..|> "<>\nQuizInteractable"
+ContactManager ..|> "<>\nContactInteractable"
+
+Model ---> ContactManager
+Model ---> EventManager
+Model ---> QuizManager
+Model ---> ConfigManager
+EventManager --> IndividualManager
+EventManager ..|> "<>\nEventManagerInteractable"
+IndividualManager ..|> "<>\nEventInteractable"
+ConfigManager ..|> "<>\nConfigInteractable"
+note bottom of IndividualManager : IndividualManager can be either\nClass, CCA, Test or Tuition
+
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/QuizReadStorage.puml b/docs/plantuml/QuizReadStorage.puml
new file mode 100644
index 0000000000..510edf4010
--- /dev/null
+++ b/docs/plantuml/QuizReadStorage.puml
@@ -0,0 +1,46 @@
+@startuml
+
+skinparam ParticipantPadding 10
+skinparam BoxPadding 5
+hide footbox
+
+box "Storage" #YellowGreen
+participant ":QuizStorageManager"
+participant ":QuizListDecoder"
+end box
+
+box "File" #orange
+participant ":File"
+end box
+
+-> ":QuizStorageManager" : loadData()
+activate ":QuizStorageManager"
+":QuizStorageManager" -> ":QuizStorageManager" : createDataFile()
+activate ":QuizStorageManager" #salmon
+":QuizStorageManager" -> ":File" ** : constructor()
+activate ":File"
+":File" --> ":QuizStorageManager"
+deactivate ":File"
+":QuizStorageManager" -> ":File" : createNewFile()
+activate ":File"
+":File" --> ":QuizStorageManager" : fileCreated
+deactivate ":File"
+
+opt !fileCreated
+":QuizStorageManager" -> ":QuizListDecoder" : decodeQuizList()
+activate ":QuizListDecoder"
+":QuizListDecoder" -> ":QuizListDecoder" : decodeQuizFromString()
+activate ":QuizListDecoder" #salmon
+":QuizListDecoder" --> ":QuizListDecoder" : Quiz
+deactivate ":QuizListDecoder"
+":QuizListDecoder" --> ":QuizStorageManager" : decodedQuizzes
+deactivate ":QuizListDecoder"
+<-- ":QuizStorageManager" : decodedQuizzes
+deactivate ":QuizStorageManager"
+end opt
+
+<-- ":QuizStorageManager" : new ArrayList<>()
+deactivate ":QuizStorageManager"
+destroy ":File"
+
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/QuizWriteStorage.puml b/docs/plantuml/QuizWriteStorage.puml
new file mode 100644
index 0000000000..40719ec301
--- /dev/null
+++ b/docs/plantuml/QuizWriteStorage.puml
@@ -0,0 +1,42 @@
+@startuml
+
+skinparam ParticipantPadding 10
+skinparam BoxPadding 5
+hide footbox
+
+box "Controller" #LightSalmon
+participant ":ControlManager"
+end box
+
+box "Storage" #YellowGreen
+participant ":QuizStorageManager"
+participant ":QuizListEncoder"
+end box
+
+box "File" #skyblue
+participant ":File"
+end box
+
+-> ":ControlManager"
+activate ":ControlManager"
+":ControlManager" -> ":ControlManager" : refreshQuizzes()
+activate ":ControlManager" #salmon
+":ControlManager" -> ":QuizStorageManager" : saveData()
+activate ":QuizStorageManager"
+":QuizStorageManager" -> ":QuizListEncoder" : encodeQuizList()
+activate ":QuizListEncoder"
+":QuizListEncoder" --> ":QuizStorageManager" : encodedQuizzes
+deactivate ":QuizListEncoder"
+":QuizStorageManager" --> ":ControlManager"
+deactivate ":QuizStorageManager"
+":ControlManager" -> ":File" : write(encodedQuizzes)
+activate ":File"
+":File" --> ":ControlManager"
+deactivate ":File"
+":ControlManager" --> ":ControlManager"
+deactivate ":ControlManager"
+<-- ":ControlManager"
+deactivate ":ControlManager"
+destroy ":File"
+
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/SetHours.puml b/docs/plantuml/SetHours.puml
new file mode 100644
index 0000000000..d599db29ea
--- /dev/null
+++ b/docs/plantuml/SetHours.puml
@@ -0,0 +1,40 @@
+@startuml
+
+skinparam ParticipantPadding 10
+skinparam BoxPadding 5
+hide footbox
+
+box "Controller" #LightSalmon
+participant ControlManager
+participant SetHoursCommand
+end box
+
+box "Model" #YellowGreen
+participant ConfigManager
+end box
+
+box "UserInterface" #skyblue
+participant UserInterface
+end box
+
+ControlManager -> SetHoursCommand: execute()
+activate ControlManager
+
+activate SetHoursCommand
+SetHoursCommand -> ConfigManager: editHours()
+activate ConfigManager
+ConfigManager -> ConfigManager: getInputHours()
+activate ConfigManager #salmon
+ConfigManager -> UserInterface: showToUser()
+activate UserInterface
+UserInterface --> ConfigManager
+deactivate UserInterface
+ConfigManager --> ConfigManager
+deactivate ConfigManager
+ConfigManager --> SetHoursCommand
+deactivate ConfigManager
+SetHoursCommand --> ControlManager
+deactivate SetHoursCommand
+
+deactivate ControlManager
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/StorageComponent.puml b/docs/plantuml/StorageComponent.puml
new file mode 100644
index 0000000000..4041fc970f
--- /dev/null
+++ b/docs/plantuml/StorageComponent.puml
@@ -0,0 +1,78 @@
+@startuml
+skinparam Shadowing false
+skinparam classBackgroundColor Tan
+skinparam MinClassWidth 120
+
+package storage <> {
+ abstract class StorageManager {
+ + StorageManager(:String)
+ - createDataFile():boolean
+ }
+
+ class EventStorageManager extends StorageManager {
+ + EventStorageManager(fileName)
+ + EventStorageManager(directory, fileName)
+ + saveData(eventList)
+ + loadData():eventList
+ - separateEventsIntoList(:ArrayList):EventParameter
+ }
+
+ class EventListDecoder {
+ + EventListDecoder()
+ + decodeEventList(encodedEventList):decodedEvents
+ - decodeEventFromString(encodedEvent):decodedEvent
+ - parseCca(data):eventCca
+ - parseClass(data):eventClass
+ - parseTest(data):eventTest
+ - parseTuition(data):eventTuition
+ }
+
+ class EventListEncoder {
+ + EventListEncoder()
+ + encodeEventList(eventList):encodedEvents
+ - encodeEventToString(event):encodedEvent
+ }
+
+ EventStorageManager --> EventListDecoder
+ EventStorageManager --> EventListEncoder
+
+
+ class QuizStorageManager extends StorageManager {
+ + QuizStorageManager(fileName)
+ + saveData(quizList, filePath)
+ + loadData():data
+ }
+
+ class QuizListDecoder {
+ + decodeQuizList(data):quizList
+ - decodeQuizFromString(data):quiz
+ }
+
+ class QuizListEncoder {
+ + encodeQuizList(:ArrayList):ArrayList
+ }
+
+ QuizStorageManager --> QuizListDecoder
+ QuizStorageManager --> QuizListEncoder
+
+
+ class ConfigStorageManager extends StorageManager {
+
+ }
+
+ class ConfigDecoder {
+
+ }
+
+ class ConfigEncoder {
+
+ }
+
+ ConfigStorageManager --> ConfigDecoder
+ ConfigStorageManager --> ConfigEncoder
+}
+
+hide circle
+hide members
+
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/StorageSequence.puml b/docs/plantuml/StorageSequence.puml
new file mode 100644
index 0000000000..184f60ad5e
--- /dev/null
+++ b/docs/plantuml/StorageSequence.puml
@@ -0,0 +1,79 @@
+@startuml
+
+skinparam ParticipantPadding 10
+skinparam BoxPadding 5
+hide footbox
+
+participant Main
+
+box "UserInterface" #skyblue
+participant ":UserInterface"
+end box
+
+box "Controller" #LightSalmon
+participant ":ControlManager"
+end box
+
+box "Storage" #Tan
+participant ":EventStorageManager"
+participant ":QuizStorageManager"
+end box
+
+activate Main
+Main -> ":EventStorageManager"** : constructor()
+activate ":EventStorageManager"
+":EventStorageManager" --> Main
+deactivate ":EventStorageManager"
+
+Main -> ":EventStorageManager" : loadData()
+activate ":EventStorageManager"
+":EventStorageManager" --> Main : eventParameter
+deactivate ":EventStorageManager"
+
+Main -> ":QuizStorageManager"** : constructor()
+activate ":QuizStorageManager"
+":QuizStorageManager" --> Main
+deactivate ":QuizStorageManager"
+
+Main -> ":QuizStorageManager" : loadData()
+activate ":QuizStorageManager"
+":QuizStorageManager" --> Main : quizList
+deactivate ":QuizStorageManager"
+
+Main -> ":UserInterface" : runUI()
+activate ":UserInterface"
+":UserInterface" -> ":UserInterface" : getUserCommand()
+activate ":UserInterface"
+":UserInterface" --> ":UserInterface"
+deactivate ":UserInterface"
+":UserInterface" -> ":ControlManager" : runLogic()
+activate ":ControlManager"
+":ControlManager" -> ":ControlManager" : refreshEvents()
+activate ":ControlManager"
+":ControlManager" -> ":EventStorageManager" : saveData()
+activate ":EventStorageManager"
+":EventStorageManager" --> ":ControlManager"
+deactivate ":EventStorageManager"
+":ControlManager" --> ":ControlManager"
+deactivate ":ControlManager"
+":ControlManager" -> ":ControlManager" : refreshQuizzes()
+activate ":ControlManager"
+":ControlManager" --> ":ControlManager"
+":ControlManager" -> ":QuizStorageManager" : saveData()
+activate ":QuizStorageManager"
+":QuizStorageManager" --> ":ControlManager"
+deactivate ":QuizStorageManager"
+deactivate ":ControlManager"
+":ControlManager" --> ":UserInterface"
+deactivate ":ControlManager"
+deactivate ":ControlManager"
+":UserInterface" -> ":UserInterface" : checkIfProgramEnds(:CommandType)
+activate ":UserInterface"
+":UserInterface" --> ":UserInterface"
+deactivate ":UserInterface"
+deactivate ":ControlManager"
+":UserInterface" --> Main
+deactivate ":UserInterface"
+deactivate Main
+
+@enduml
\ No newline at end of file
diff --git a/docs/plantuml/UserInterfaceComponent.puml b/docs/plantuml/UserInterfaceComponent.puml
new file mode 100644
index 0000000000..a338239474
--- /dev/null
+++ b/docs/plantuml/UserInterfaceComponent.puml
@@ -0,0 +1,30 @@
+@startuml
+skinparam Shadowing false
+skinparam MinClassWidth 120
+skinparam classBackgroundColor #SkyBlue
+skinparam packageStyle rectangle
+hide circle
+hide members
+
+package UserInterface {
+ class "<> \nUserInterface"
+ class CalendarWeekRenderer
+ class CalendarWeekRendererUtils
+
+ "<> \nUserInterface" -[#SkyBlue]-> CalendarWeekRenderer : creates
+ CalendarWeekRenderer -[#SkyBlue][dotted]-> CalendarWeekRendererUtils : creates
+}
+
+package Model {
+
+}
+
+package Controller {
+
+}
+
+Model <..[#YellowGreen]. UserInterface
+UserInterface -[#SkyBlue]--> Controller :creates
+Controller -[#Salmon]----> UserInterface :call prints errors to >
+
+@enduml
diff --git a/docs/team/aliciaho.md b/docs/team/aliciaho.md
new file mode 100644
index 0000000000..0b5deef407
--- /dev/null
+++ b/docs/team/aliciaho.md
@@ -0,0 +1,79 @@
+# Alicia Ho Shimin - Project Portfolio Page
+## Project: Plan&Score
+
+Plan&Score is a Java command-line application that allows Primary 6 students to plan and track their classes, CCAs and test dates. This enables the students to remember their schedule, so they can plan well in advance for their tests and score better.
+
+## Contributions
+Given below are my contributions to the project.
+* Features:
+ * Add test
+
+ What it does: Allow users to add a test to their schedule.
+
+ Justification: This feature improves the product because the users can tests to their schedule to better plan for it. Hence, allowing them to revise for their tests well in advance.
+
+ * Delete test
+
+ What it does: Allow users to delete a test to their schedule.
+
+ Justification: This feature improves the product because the users can delete any past tests or tests that have been cancelled.
+ Hence, allowing them to better plan their schedule.
+
+ * Set Recommended hours
+
+ What it does: Allow users to set their recommended hours per day.
+
+ Justification: This feature improves the product because the users are able to set the amount of productive hours they want to accomplish.
+ This allows users to remain within their recommended hours thus preventing them from overloading.
+
+ Highlights: This enhancement affects existing commands and commands to be added in the future e.g checking if time exceeded.
+ The implementation was challenging as it required changes to existing commands and classes.
+
+ * List event today/[date]
+
+ What it does: Allow users to list their events for today or the date they desire.
+
+ Justification: This feature improves the product because users are able to check the schedule for that day.
+
+ * Time left
+
+ What it does: Allow users to see the amount of productive hours they have for a particular day.
+
+ Justification: This feature improves the product because users are able to know how
+ many productive hours they have left for that day thus, they are able to better plan their schedule and not overload.
+
+ Highlights: This enhancement affects existing commands e.g add/delete/list event.
+ The implementation was challenging as it required changes to existing commands and classes.
+
+ * Date Time conversion
+
+ What it does: Converts date time from yyyy-MM-dd HHmm to date time in natural language format.
+
+ Justification: This feature improves the product because users can intuitively know the date time of their events.
+
+ Highlights: This enhancement affects existing commands e.g add/delete.
+ The implementation was challenging as it required changes to existing commands and classes.
+
+ Credits: [To find out ordinal numbers](https://stackoverflow.com/questions/4011075/how-do-you-format-the-day-of-the-month-to-say-11th-21st-or-23rd-ordinal)
+
+* Code contributed: [RepoSense link](https://nus-cs2113-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=&sort=groupTitle&sortWithin=title&since=2020-09-27&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other&tabOpen=true&tabType=authorship&zFR=false&tabAuthor=AliciaHo&tabRepo=AY2021S1-CS2113T-W12-4%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other)
+* Project management:
+ * Reviewed PRs: [CS2113-W12-4](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/355#pullrequestreview-525631892)
+
+* Community:
+ * Reported bugs and suggestions for other teams: [CS2113-T13-2](https://github.com/AY2021S1-CS2113-T13-2/tp)
+ * Review DG: [#16](https://github.com/nus-cs2113-AY2021S1/tp/pull/16)
+
+* Contribution to user guide
+ * Wrote documentation and did diagrams for `add test`,`delete test`,`list event today`,
+ `list event [date]`,`bye`
+ * Help to enhance documentation and did diagrams for `help`, `add class`, `delete class`, `add cca`,
+ `delete cca`, `add tuition`, `delete tuition`, `list event`, `list event week`, `list event nextweek`
+ * Wrote documentation and did diagrams for Initialisation
+ * Update table for `command summary`
+
+
+* Contribution to developer guide
+ * Wrote documentation and did UML for `configStorage`, `set hours`
+ * Wrote documentation for `help`, `product scope`
+ * Did UML for `add` and `delete` feature
\ No newline at end of file
diff --git a/docs/team/andrewongzh.md b/docs/team/andrewongzh.md
new file mode 100644
index 0000000000..52d2683e1b
--- /dev/null
+++ b/docs/team/andrewongzh.md
@@ -0,0 +1,63 @@
+## Project: Plan&Score
+
+Plan&Score is a Java command-line application that allows Primary 6 students to plan and track their classes, CCAs and test dates. This enables the students to remember their schedule, so they can plan well in advance for their tests and score better.
+
+## Contributions by Andre Wong
+Given below are my contributions to the project.
+* Features:
+ * List events [#18](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/18)
+ * What it does: allows the user to list out all their classes, ccas, tests and tuitions.
+ * Justification: Allow users to view all their schedule at a glance. The numbering that appears here also corresponds to which events to delete later.
+ * Find events [#178](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/178)
+ * What it does: allows the user to search for all the events by keywords.
+ * Justification: Allows user to look for events in which they cannot remember the exact details such as start date.
+ * Add quiz [#123](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/123)
+ * What it does: allows the user to add a quiz question to quiz manager
+ * Justification: Users can create their own question freely to test themselves afterwards. This helps to improve learning.
+ * Find contacts [#240](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/240)
+ * What it does: allows the user to find contacts.
+ * Justification: Users can search by email, phone number , name or subject if they cannot remember them all.
+ * Contacts storage [#347](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/347)
+ * What it does: allows the application to store the contacts in a text file.
+ * Justification: The data will now be persistent even after the application closes.
+
+* Unused Feature:
+ * Quiz Notification [#217](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/217)
+ * What it does: shows notification to user if quiz has not been attempted since two days ago.
+ * Justification: Reminds users to attempt quiz if they forgot to do their quiz.
+
+* Code contributed: [RepoSense link](https://nus-cs2113-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=&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=AndreWongZH&tabRepo=AY2021S1-CS2113T-W12-4%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other)
+
+* Enhancements:
+ * Refactor code in controller package to have a command and model parser to extract out commands and models so to adhere to single responsibility principle
+ * Create a command factory that generates the correct command class to execute on
+ * All commands inherit from abstract class command have method execute()
+ * EventManager class to add another layer of abstraction
+ * Storage class inherit from abstract StorageManager to merge duplicated methods [#125](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/125)
+ * Logger class follows a singleton pattern [#146](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/146)
+ * Add encapsulation to models by adding an interface to all the modelManagers
+ * Command sanitized to ensure cases where there are extra or swapped parameters, command in uppercase and command with extra whitespaces are handled
+ * Factory reset to ensure the program can still loop after factory reset.
+
+* Project management:
+ * Update code using forking workflow when doing pull requests
+ * Set up issue tracker and milestones for `v1.0`, `v2.0` and `v2.1`
+ * Set up github project dashboard to track progress
+ * Released `v2.0` onto github
+ * Reviewed PRs: [#17](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/17) [#355](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/355) [#363](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/363) [#375](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/375)
+
+* Community:
+ * Contributed to forum discussions: [#29](https://github.com/nus-cs2113-AY2021S1/forum/issues/29) [#37](https://github.com/nus-cs2113-AY2021S1/forum/issues/87)
+ * Reported bugs and suggestions for other teams: [#2](https://github.com/nus-cs2113-AY2021S1/tp/pull/2)
+
+* Contribution to user guide
+ * Wrote documentation for `Finding events`, `Finding contacts`, `Listing events`, `Adding quiz`
+ * Added images initially for initialization, help, add and delete class
+ * Added images under section `Finding events` and enhanced table for `command summary`
+
+* Contribution to developer guide
+ * Add colors and removed footboxes to UML diagrams
+ * Add UML diagrams for figure 1, 3, 4, 5, 17, 18
+ * Wrote documentation for `Architecture`, `Controller component` and `Model component` in the Design section
+ * Wrote documentation for `List contact/quiz`, `List event (date /today)` and `Find event ` feature
+ * Wrote documentation for manual testing for sections on `Adding of CCAs`, `Listing of events` and `Finding of contacts` and for Non-Functional Requirements
\ No newline at end of file
diff --git a/docs/team/durianpancakes.md b/docs/team/durianpancakes.md
new file mode 100644
index 0000000000..816ba4f22c
--- /dev/null
+++ b/docs/team/durianpancakes.md
@@ -0,0 +1,44 @@
+# Chan Xu Hui - Project Portfolio Page
+
+## Overview
+Plan&Score is a Command Line Interface (CLI) Application that possesses an in-built timetable,
+quiz and contacts tracker. It is written in Java.
+
+### Summary of Contributions
+* New Feature: Added the ability for Plan&Score to perform I/O operations with .txt files.
+
+* New Feature: Added the ability for Plan&Score to support Tuition events.
+
+* Code Contributed: [RepoSense link](https://nus-cs2113-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=&sort=groupTitle&sortWithin=title&since=2020-09-27&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other&tabOpen=true&tabType=authorship&zFR=false&tabAuthor=durianpancakes&tabRepo=AY2021S1-CS2113T-W12-4%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other)
+
+* Project Management:
+ * Reviewed PRs:
+ * [#356](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/356)
+ * [#346](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/346)
+
+* Community:
+ * Reported bugs and suggestions for other teams:
+ * [#57](https://github.com/nus-cs2113-AY2021S1/tp/pull/57)
+
+* Enhancement to existing features:
+ * Added the ability for Plan&Score to display current and next week's events in a Week View format.
+ * What it does: Instead of listing events in a vertical list format, Plan&Score can list them in a
+ more intuitive and eye-pleasing way.
+ * Justification: Listing a long list of events in a list format can lead to inconvenience in
+ scrolling by the user. By displaying it in a horizontal timetable manner,
+ Plan&Score makes efficient use of the Command Line Interface's display real
+ estate.
+ * Highlights: The implementation was challenging as it required me to come up with the entire
+ logic of processing the events to be printed in the right format. The lack of
+ experience in this also added to the challenge.
+ * Assisted in the migration of String date-times used in Plan&Score to Calendar type. [#206](https://github.com/AY2021S1-CS2113T-W12-4/tp/issues/206)
+
+* Documentation:
+ * User Guide:
+ * Added documentations for `Tuition`, `list event week`, `list event nextweek` and `Troubleshooting` sections.
+ * Added initial version of the `Command Summary`
+ * Developer Guide:
+ * Added implementation details of the `UserInterface`, `list event week/nextweek` and `Storage` features.
+ * Added manual testing instructions for missing and corrupted files.
+ * Added UML diagrams for figures 2, 6, 7, 8, 9, 19
+
diff --git a/docs/team/elizabethcwt.md b/docs/team/elizabethcwt.md
new file mode 100644
index 0000000000..0b12a538e8
--- /dev/null
+++ b/docs/team/elizabethcwt.md
@@ -0,0 +1,72 @@
+## Elizabeth Chan's Project Portfolio Page
+### Project: Plan&Score
+
+#### Overview
+Plan&Score is a Java command-line application that does two main things:
+1. Plan
+
+Allows students to plan and track their:
+
+ - School classes
+ - CCAs
+ - Tests
+ - Tuition
+
+
+ This inculcates effective scheduling habits in these students from a young age, which they will carry with them as they grow older.
+
+2. Score
+
+Allows students to practice quizzes regularly and consistently, honing their required skills for their upcoming PSLE.
+
+Apart from that, Plan&Score also allows students to save contact details of their teachers, cca coaches, or any other relevant person in a highly convenient way.
+
+In essence, Plan&Score is not merely a scheduling application like many other applications in the market. It is a one-of-a-kind and all-encompassing application, tailored for Primary 6 students in Singapore (for now).
+
+
+#### Summary of Contributions
+
+Link to my code contribution (via RepoSense):
+[My contributions](https://nus-cs2113-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=elizabethcwt&sort=groupTitle&sortWithin=title&since=2020-09-27&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+
+Given below are my contributions to Plan&Score.
+
+- Feature: Help
+ - What it does: Displays the updated list of available features and corresponding commands for user
+ - Justification: By including the required format to enter the input via quotation marks, provides a convenient way for users to remember how to input commands. It also allows them to view and track new features during application updates.
+ - Highlights: This feature is usually what the user first accesses when launching the application, and provides a bird’s eye view of the whole application before delving into each individual feature.
+
+- Feature: Class
+ - What it does: Stores user’s classes, attains class list size, allows adding and deleting of classes
+ - Justification: This feature allows users to schedule well, by entering their school classes into the application. Users can then remove any of these classes once they’re done, or no longer relevant. Users can then view exactly how many classes, as well as which classes they have, to manage their workload and time better.
+ - Highlights: This feature affects existing commands and commands to be added in future. It requires an in-depth analysis of design alternatives.
+ - Relevant classes in source code: EventClass.java class and EventClassManager.java
+
+- Feature: Take Quiz
+ - What it does: Displays a number of quiz questions, as specified by the user, in randomised order. Then, reviews the correctness of the user’s answers, an explanation for each question, as well as the user’s score.
+ - Justification: This feature allows users to score well, by allowing them to take quizzes of any size they’d like, according to how much time they have. Since it is integrated into this application, along with the scheduler, it is highly accessible for users.
+ - Highlights: This feature requires the use of a manager, to manage multiple arraylists. It also requires a lot of checks to handle different user inputs for each question, valid or invalid.
+ - Relevant classes in source code: Quiz.java, QuizManager.java and UserAnswerManager.java
+
+- Project Management
+ - Managed release `v1.0` on GitHub
+ - Reviewed PRs: [#363](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/363)
+
+- Documentation
+ - User Guide
+ - Created structure and added hyperlinks
+ - Created photos of tips and warnings boxes
+ - Added photos for score component features/commands
+ - Added details for `help`, `add class`, `delete class`, and `quiz` commands/features
+ - Added `help` and `quiz` components in the Command Summary
+
+ - Developer's Guide
+ - Added explanation for help feature
+ - Added UML diagrams for the `quiz storage` and `find quiz` features.
+
+- Community
+ - Reported bugs and gave suggestions to other teams based on their newest release (jarfile):
+ [T13-3](https://github.com/AY2021S1-CS2113-T13-3/tp/releases)
+
+ - Reviewed and gave comments and suggestions to the Developer's Guide for another team:
+ [T13-2](https://ay2021s1-cs2113t-w13-2.github.io/tp/DeveloperGuide.html)
\ No newline at end of file
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/untitle4.md b/docs/team/untitle4.md
new file mode 100644
index 0000000000..f71ead6434
--- /dev/null
+++ b/docs/team/untitle4.md
@@ -0,0 +1,53 @@
+# Chen Jinran's Project Portfolio Page
+
+## Project: Plan&Score
+
+Plan&Score is a Command Line Interface application for primary 6 students to
+possess a schedule, quiz and contact tracker. It is written in Java.
+
+Given below are my contributions to the project.
+
+* Features:
+ * Add/Delete ccas [#25](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/25)
+ * What it does: Allows the user to add an event of type cca in the schedule.
+ Allows the user to delete the existing ccas in the schedule.
+ * Justification: This is a basic feature for constructing the schedule.
+ * Quiz storage [#121](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/121)
+ * What it does: Allows the application to store the quizzes in a text file automatically.
+ * Delete quiz [#121](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/121)
+ * What it does: Allows the user to delete the existing quizzes in the quiz list.
+ * Find quiz [#185](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/185)
+ * What it does: Allows users to find quizzes in the quiz list with input keyword(s).
+ * List quiz [#127](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/127)
+ * What it does: Allows users to list out all quiz questions in the quiz list.
+ * Quiz record [#213](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/213)
+ * What it does: Allows users to review the incorrect quizzes in their last quiz attempt.
+ * Add/Delete contacts [#131](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/131)
+ * What it does: Allows users to add a contact in the contact list.
+ Allows the user to delete the existing contacts in the contact list.
+
+* Unused Features:
+ * Set ccas done [#34](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/34)
+ * What it does: Allows users to set a cca from `NOT DONE` to `DONE`.
+
+* Code contributed: [RepoSense link](https://nus-cs2113-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=&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=untitle4&tabRepo=AY2021S1-CS2113T-W12-4%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code)
+
+* Project management:
+ * PRs reviewed: [#363](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/363)
+
+* Enhancements to existing features:
+ * Added an optional parameter `explanation` for a quiz question: [#150](https://github.com/AY2021S1-CS2113T-W12-4/tp/pull/150)
+
+* Documentation:
+ * User Guide:
+ * Added implementation details of the `add`, `delete`,
+ `list` and `find` features for quiz.
+ * Added implementation details of the `add` and `delete` for cca.
+ * Added picture demonstration of the `add`, `delete`, `list` and `find` command for contacts
+ and update the corresponding implementation details.
+ * Developer Guide:
+ * Drafted the description of the `quiz` feature.
+ * Added UML diagrams for the `quiz storage` and `find quiz` features.
+
+* Community:
+ * Reported bugs and suggestions for other teams in the class (example: [Reviewing T14-3](https://github.com/untitle4/ped/issues/4))
\ No newline at end of file
diff --git a/logfile.txt.1 b/logfile.txt.1
new file mode 100644
index 0000000000..b1c3829287
--- /dev/null
+++ b/logfile.txt.1
@@ -0,0 +1,274 @@
+Nov 08, 2020 8:27:49 PM seedu.duke.storage.config.ConfigStorageManager loadData
+INFO: Loading storage...
+Nov 08, 2020 8:27:49 PM seedu.duke.storage.StorageManager createDataFile
+INFO: Directory found...
+Nov 08, 2020 8:27:49 PM seedu.duke.storage.config.ConfigStorageManager loadData
+INFO: Data file found, reading data file...
+Nov 08, 2020 8:27:49 PM seedu.duke.storage.config.ConfigStorageManager loadData
+INFO: Load successful
+Nov 08, 2020 8:27:49 PM seedu.duke.storage.contact.ContactStorageManager loadData
+INFO: Loading storage...
+Nov 08, 2020 8:27:49 PM seedu.duke.storage.StorageManager createDataFile
+INFO: Directory found...
+Nov 08, 2020 8:27:49 PM seedu.duke.storage.contact.ContactStorageManager loadData
+INFO: Data file found, reading data file...
+Nov 08, 2020 8:27:49 PM seedu.duke.storage.contact.ContactStorageManager loadData
+INFO: Load successful
+Nov 08, 2020 8:27:49 PM seedu.duke.storage.quiz.QuizStorageManager loadData
+INFO: Loading storage...
+Nov 08, 2020 8:27:49 PM seedu.duke.storage.StorageManager createDataFile
+INFO: Directory found...
+Nov 08, 2020 8:27:49 PM seedu.duke.storage.quiz.QuizStorageManager loadData
+INFO: Data file found, reading data file...
+Nov 08, 2020 8:27:49 PM seedu.duke.storage.quiz.QuizStorageManager loadData
+INFO: Load successful
+Nov 08, 2020 8:27:49 PM seedu.duke.storage.event.EventStorageManager loadData
+INFO: Loading storage...
+Nov 08, 2020 8:27:49 PM seedu.duke.storage.StorageManager createDataFile
+INFO: Directory found...
+Nov 08, 2020 8:27:49 PM seedu.duke.storage.event.EventStorageManager loadData
+INFO: Data file found, reading data file...
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.controller.parser.DateTimeParser convertStringToCalendar
+INFO: converting string to calendar
+Nov 08, 2020 8:27:49 PM seedu.duke.storage.event.EventStorageManager loadData
+INFO: Load successful
+Nov 08, 2020 8:27:49 PM seedu.duke.ui.ConfigManager getIntroductoryVariables
+INFO: getting username and recommended hours from user
+Nov 08, 2020 8:27:55 PM seedu.duke.controller.ControlManager runLogic
+INFO: Running controller logic now
+Nov 08, 2020 8:27:55 PM seedu.duke.controller.ControlManager runLogic
+INFO: Extracting command
+Nov 08, 2020 8:27:55 PM seedu.duke.controller.parser.CommandParser extractCommand
+INFO: Extracting command now...
+Nov 08, 2020 8:27:55 PM seedu.duke.controller.ControlManager runLogic
+INFO: Extracting model
+Nov 08, 2020 8:27:55 PM seedu.duke.controller.parser.ModelParser extractModel
+INFO: Extracting model now...
+Nov 08, 2020 8:27:55 PM seedu.duke.controller.ControlManager runLogic
+INFO: Executing command
+Nov 08, 2020 8:27:55 PM seedu.duke.ui.ConfigManager editHours
+INFO: editing hours to take in new hours input
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 8:27:57 PM seedu.duke.controller.parser.DateTimeParser convertCalendarToString
+INFO: converting calendar to string
+Nov 08, 2020 10:49:00 PM seedu.duke.Main run
+SEVERE: Program ended undexpectedly
diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java
deleted file mode 100644
index 5c74e68d59..0000000000
--- a/src/main/java/seedu/duke/Duke.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package seedu.duke;
-
-import java.util.Scanner;
-
-public class Duke {
- /**
- * Main entry-point for the java.duke.Duke application.
- */
- public static void main(String[] args) {
- String logo = " ____ _ \n"
- + "| _ \\ _ _| | _____ \n"
- + "| | | | | | | |/ / _ \\\n"
- + "| |_| | |_| | < __/\n"
- + "|____/ \\__,_|_|\\_\\___|\n";
- System.out.println("Hello from\n" + logo);
- System.out.println("What is your name?");
-
- Scanner in = new Scanner(System.in);
- System.out.println("Hello " + in.nextLine());
- }
-}
diff --git a/src/main/java/seedu/duke/Main.java b/src/main/java/seedu/duke/Main.java
new file mode 100644
index 0000000000..efa874007a
--- /dev/null
+++ b/src/main/java/seedu/duke/Main.java
@@ -0,0 +1,109 @@
+package seedu.duke;
+
+import seedu.duke.common.LogManager;
+import seedu.duke.common.Messages;
+import seedu.duke.exception.StorageCorruptedException;
+import seedu.duke.model.Model;
+import seedu.duke.model.contact.ContactManager;
+import seedu.duke.model.event.EventManager;
+import seedu.duke.model.event.EventParameter;
+import seedu.duke.model.quiz.QuizManager;
+import seedu.duke.storage.StorageExceptionHandler;
+import seedu.duke.storage.contact.ContactStorageManager;
+import seedu.duke.storage.quiz.QuizStorageManager;
+import seedu.duke.storage.event.EventStorageManager;
+import seedu.duke.model.config.ConfigManager;
+import seedu.duke.ui.UserInterface;
+
+import java.util.NoSuchElementException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static seedu.duke.storage.StorageManager.CONTACT_FILE_NAME;
+import static seedu.duke.storage.StorageManager.EVENT_FILE_NAME;
+import static seedu.duke.storage.StorageManager.QUIZ_FILE_NAME;
+
+public class Main {
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+
+ private static UserInterface userInterface;
+ private static boolean isRunning = true;
+
+ private EventStorageManager eventStorageManager;
+ private QuizStorageManager quizStorageManager;
+ private ContactStorageManager contactStorageManager;
+ private ConfigManager configManager;
+ private Model model;
+
+ private boolean isActive;
+
+ public Main() throws StorageCorruptedException {
+ userInterface = UserInterface.getInstance();
+ initializeStorageManagers();
+ isActive = true;
+ loadModel();
+ }
+
+ /**
+ * Main entry-point for the java.duke.Duke application.
+ */
+ public static void main(String[] args) {
+ while (isRunning) {
+ try {
+ new Main().run();
+ } catch (StorageCorruptedException e) {
+ userInterface.showToUser(Messages.MESSAGE_STORAGE_CORRUPTED);
+ StorageExceptionHandler storageExceptionHandler = new StorageExceptionHandler();
+ isRunning = storageExceptionHandler.handleCorruptedStorage();
+ }
+ }
+ }
+
+ /**
+ * Loops continuously through and lets the user interface ask user for input.
+ *
+ * @throws StorageCorruptedException If there is error when reading config values.
+ */
+ public void run() throws StorageCorruptedException {
+ try {
+ userInterface.showToUser(Messages.MESSAGE_HELLO_FROM_DUKE);
+ configManager.getIntroductoryVariables(configManager.getConfig());
+ userInterface.showWelcomeMessage(configManager.getConfig());
+
+ while (isActive) {
+ isActive = userInterface.runUi(model, eventStorageManager, quizStorageManager, contactStorageManager);
+ isRunning = isActive;
+ }
+ } catch (NoSuchElementException e) {
+ logger.log(Level.SEVERE, "Program ended unexpectedly");
+ }
+
+ // Exit Message
+ userInterface.showToUser(Messages.MESSAGE_BYE);
+ }
+
+ //@@author AndreWongZH
+ /**
+ * Sets up the eventManager, quizManager, contactManager and configManager to be saved into model.
+ *
+ * @throws StorageCorruptedException If unable to load data correctly.
+ */
+ private void loadModel() throws StorageCorruptedException {
+ ContactManager contactManager = new ContactManager(contactStorageManager.loadData());
+ QuizManager quizManager = new QuizManager(quizStorageManager.loadData());
+ EventParameter eventParameter = eventStorageManager.loadData();
+ EventManager eventManager = new EventManager(eventParameter, configManager.getConfig());
+ model = new Model(eventManager, contactManager, quizManager, configManager);
+ }
+
+ //@@author AndreWongZH
+ /**
+ * Sets up storage managers to get data from text file storage.
+ */
+ private void initializeStorageManagers() {
+ eventStorageManager = new EventStorageManager(EVENT_FILE_NAME);
+ quizStorageManager = new QuizStorageManager(QUIZ_FILE_NAME);
+ contactStorageManager = new ContactStorageManager(CONTACT_FILE_NAME);
+ configManager = ConfigManager.getInstance();
+ }
+}
diff --git a/src/main/java/seedu/duke/common/LogManager.java b/src/main/java/seedu/duke/common/LogManager.java
new file mode 100644
index 0000000000..84d7ad586f
--- /dev/null
+++ b/src/main/java/seedu/duke/common/LogManager.java
@@ -0,0 +1,63 @@
+package seedu.duke.common;
+
+import seedu.duke.ui.UserInterface;
+
+import java.io.IOException;
+import java.util.logging.FileHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.logging.SimpleFormatter;
+
+//@@author AndreWongZH
+/**
+ * Represents the objects that logs all the processes of the program at each step.
+ */
+public class LogManager {
+ public static final String LOG_FILE_LOCATION = "./logfile.txt";
+ private static LogManager logManager = null;
+ private static final Logger LOGGER = Logger.getLogger("log");
+
+ /**
+ * The constructor here is only called once when program runs to initialize a new LogManager instance.
+ */
+ private LogManager() {
+ UserInterface userInterface = UserInterface.getInstance();
+
+ try {
+ FileHandler fileHandler = new FileHandler(LOG_FILE_LOCATION);
+ fileHandler.setFormatter(new SimpleFormatter());
+
+ // set log level, defaults to Level.INFO
+ LOGGER.setLevel(Level.INFO);
+
+ // prevents printing of log messages to console
+ LOGGER.setUseParentHandlers(false);
+
+ // redirects log messages to a file instead
+ LOGGER.addHandler(fileHandler);
+ } catch (IOException e) {
+ userInterface.showToUser(Messages.MESSAGE_INITIALIZATION_LOGGER_FAILED);
+ }
+ }
+
+ /**
+ * Returns an instance of the LogManager class for users to get the logger instance.
+ *
+ * @return LogManager instance.
+ */
+ public static LogManager getLogManagerInstance() {
+ if (logManager == null) {
+ logManager = new LogManager();
+ }
+ return logManager;
+ }
+
+ /**
+ * Returns a logger instance that the programmer can use to log process info.
+ *
+ * @return A logger instance.
+ */
+ public Logger getLogger() {
+ return LOGGER;
+ }
+}
diff --git a/src/main/java/seedu/duke/common/Messages.java b/src/main/java/seedu/duke/common/Messages.java
new file mode 100644
index 0000000000..76f7b760d8
--- /dev/null
+++ b/src/main/java/seedu/duke/common/Messages.java
@@ -0,0 +1,236 @@
+package seedu.duke.common;
+
+public class Messages {
+ //@@author durianpancakes
+ // Intro related messages
+ public static final String MESSAGE_LOGO = " ------ - -----\n"
+ + "| _ | | | | ____|\n"
+ + "| | | | | | | |___\n"
+ + "| |_| | | | ----- ----- & |____ | ------ ----- ----- -----\n"
+ + "| | | | / - \\ | _ | | | | _____| / - \\ / ___\\ / -- \\\n"
+ + "| ---- | | | | | | | | | | ___| | | | | | | | | / | ___|\n"
+ + "| | | | | |_| \\ | | | | | | | |____ | |_| | | | | |____\n"
+ + "|_| |_| \\____/\\_\\ |_| |_| |_____| |______| \\_____/ |_| \\_____/";
+ //@@author
+ public static final String MESSAGE_HELLO_FROM_DUKE = "Hello from\n"
+ + Messages.MESSAGE_LOGO;
+ public static final String MESSAGE_PROMPT_NAME = "What is your name?";
+ public static final String MESSAGE_PROMPT_HOURS = "Key in the number of hours you would like "
+ + "to be productive each day (not more than 12)?";
+ public static final String MESSAGE_SHOW_HOURS = "This is your recommended hours per day: ";
+ public static final String MESSAGE_SHOW_NEW_HOURS = "This is your new recommended hours per day: ";
+ public static final String MESSAGE_HELLO = "Hello ";
+ public static final String MESSAGE_PROMPT_COMMAND = "What can we do for you? "
+ + "(Enter 'help' for the list of available commands!)\n";
+ public static final String MESSAGE_HOURS_ERROR_NON_NUMBER = "Please indicate in NUMERALS, "
+ + "how many hours you would like to set!";
+
+ // General error messages
+ //@@author durianpancakes
+ public static final String MESSAGE_STORAGE_READ_ERROR = "There was an error loading your files.";
+ public static final String MESSAGE_STORAGE_INITIALIZATION_ERROR = "STORAGE: There was an error";
+ public static final String MESSAGE_STORAGE_CORRUPTED = "): Storage file corrupted.";
+ public static final String MESSAGE_FACTORY_RESET_PROMPT = "Would you like to reset Plan&Score? [y/n]"
+ + "(NOTE: This will result in the loss of all data!)";
+ public static final String MESSAGE_MANUAL_TROUBLESHOOT_PROMPT = "Otherwise, consider going through the "
+ + "troubleshooting steps provided in our User Guide to fix the problem manually.";
+ public static final String MESSAGE_FACTORY_RESET_INVALID_INPUT_PROMPT = "Please enter [y/n] only!";
+ public static final String MESSAGE_FACTORY_RESET_SUCCESSFUL = "Reset successful!";
+ public static final String MESSAGE_FACTORY_RESET_FAILED_OR_CANCELLED = "Reset failed/cancelled. "
+ + "Please follow troubleshooting steps provided in our User Guide to reset Plan&Score.";
+ public static final String MESSAGE_INCOMPLETE_LIST_PARAMETERS = "Please tell me what you want to be listed! "
+ + "You can either 'list event' or 'list quiz' or 'list contact'";
+ public static final String MESSAGE_RECOMMENDED_TIME_EXCEEDED = "Recommended time exceeded!";
+ public static final String MESSAGE_PROMPT_CHECK_START_END_INPUTS = "Please check the start and end inputs again!";
+ public static final String MESSAGE_ERROR_START_AFTER_END = "The start time given is later "
+ + "than the end time given!\n"
+ + "Please check your inputs again!";
+ public static final String MESSAGE_ERROR_EQUALS_END = "The start time given is the same as "
+ + "the end time given!\n"
+ + "Please check your inputs again!";
+ //@@author AndreWongZH
+ public static final String MESSAGE_EMPTY_SCHEDULE_LIST = "Schedule is %s. Add some!";
+ public static final String MESSAGE_INITIALIZATION_LOGGER_FAILED = "Failed to set up logger";
+ public static final String MESSAGE_MISSING_MODEL = ":( Oops! Category type is missing! "
+ + "Enter 'help' if needed!";
+ public static final String MESSAGE_INCOMPLETE_FIND_PARAMETERS = "Please tell me what you want to be listed! "
+ + "You can either 'find event' or 'find quiz' or 'find contact'";
+ public static final String MESSAGE_SWAPPED_PARAMETERS = ":( Please do not swap the parameters";
+ //@@author elizabethcwt
+ public static final String MESSAGE_MISSING_PARAMETERS = ":( OOPS!!! "
+ + "Remember to include ALL %s inputs!";
+ public static final String MESSAGE_EMPTY_PARAMETERS = ":( OOPS!!! Ensure ALL parameters are filled up!";
+ public static final String MESSAGE_INVALID_COMMAND = ":( Oops! I did not recognize that command! "
+ + "Enter 'help' if needed!";
+ public static final String MESSAGE_INVALID_MODEL = ":( Oops! I did not recognize that category type"
+ + " or category not compatible with command! "
+ + "Enter 'help' if needed!";
+ public static final String MESSAGE_INVALID_EXTRA_PARAM = ":( OOPS!!! Please do not enter extra inputs";
+ //@@author Aliciaho
+ //Messages for date time
+ public static final String MESSAGE_LIST_INVALID_DATE = ":( OOPS!!! Please enter today/week/valid date"
+ + "and time in format yyyy-mm-dd!";
+ public static final String MESSAGE_INVALID_DATE = ":( OOPS!!! "
+ + "Please enter valid date and time in format yyyy-mm-dd HHMM!";
+
+ //@@author durianpancakes
+ // Messages from CalendarWeekRenderer
+ public static final String MESSAGE_MONDAY_LABEL = "[MON]";
+ public static final String MESSAGE_TUESDAY_LABEL = "[TUE]";
+ public static final String MESSAGE_WEDNESDAY_LABEL = "[WED]";
+ public static final String MESSAGE_THURSDAY_LABEL = "[THU]";
+ public static final String MESSAGE_FRIDAY_LABEL = "[FRI]";
+ public static final String MESSAGE_SATURDAY_LABEL = "[SAT]";
+ public static final String MESSAGE_SUNDAY_LABEL = "[SUN]";
+
+ //@@author untitle4
+ // Messages from Contact related classes
+ public static final String MESSAGE_CONTACT_ADD_SUCCESS = "Got it. I've added this contact: ";
+ public static final String MESSAGE_CONTACT_DELETE_SUCCESS = "Noted. I've removed this contact: ";
+ public static final String MESSAGE_CONTACT_DELETE_ERROR_NON_NUMBER = ":( OOPS!!! "
+ + "Please indicate in NUMERALS, which contact you'd like to delete!";
+ public static final String MESSAGE_CONTACT_DELETE_ERROR_NO_NUMBER_GIVEN = ":( OOPS!!! "
+ + "Please indicate which contact you'd like to delete!";
+ public static final String MESSAGE_INVALID_CONTACT_INDEX = ":( OOPS!!! Please indicate a valid contact index!";
+ public static final String MESSAGE_EMPTY_CONTACT_LIST = "Contact list is empty. Add some!!";
+ public static final String MESSAGE_CONTACT_INDEX_OUT_OF_BOUNDS = "There is not such a contact in your list!";
+
+ //@@author elizabethcwt
+ // Messages from Class related classes
+ public static final String MESSAGE_CLASS_ADD_SUCCESS = "Got it. I've added this class: ";
+ public static final String MESSAGE_CLASS_DELETE_SUCCESS = "Noted. I've removed this class: ";
+ public static final String MESSAGE_CLASS_DELETE_ERROR_NON_NUMBER = ":( OOPS!!! "
+ + "Please indicate in NUMERALS, which class you'd like to delete!";
+ public static final String MESSAGE_CLASS_DELETE_ERROR_NO_NUMBER_GIVEN = ":( OOPS!!! "
+ + "Please indicate which class you'd like to delete!";
+ public static final String MESSAGE_CLASS_DONE_SUCCESS = "Nice! I've marked this class as done:";
+ public static final String MESSAGE_CLASS_DONE_ERROR_NON_NUMBER = ":( OOPS!!! "
+ + "Please indicate in NUMERALS, which class you'd like to set as Done!";
+ public static final String MESSAGE_CLASS_DONE_ERROR_NO_NUMBER_GIVEN = ":( OOPS!!! "
+ + "Please indicate which class you'd like to set as Done!";
+ public static final String MESSAGE_INVALID_CLASS_INDEX = ":( OOPS!!! Please indicate a valid class index!";
+
+ //@@author untitle4
+ // Messages from Cca related classes
+ public static final String MESSAGE_CCA_ADD_SUCCESS = "Got it. I've added this cca: ";
+ public static final String MESSAGE_CCA_DELETE_SUCCESS = "Noted. I've removed this cca: ";
+ public static final String MESSAGE_CCA_DELETE_ERROR_NON_NUMBER = ":( OOPS!!! "
+ + "Please indicate in NUMERALS, which cca you'd like to delete!";
+ public static final String MESSAGE_CCA_DELETE_ERROR_NO_NUMBER_GIVEN = ":( OOPS!!! "
+ + "Please indicate which cca you'd like to delete!";
+ public static final String MESSAGE_CCA_DONE_SUCCESS = "Nice! I've marked this cca as done:";
+ public static final String MESSAGE_CCA_DONE_ERROR_NON_NUMBER = ":( OOPS!!! "
+ + "Please indicate in NUMERALS, which cca you'd like to set as Done!";
+ public static final String MESSAGE_CCA_DONE_ERROR_NO_NUMBER_GIVEN = ":( OOPS!!! "
+ + "Please indicate which cca you'd like to set as Done!";
+ public static final String MESSAGE_INVALID_CCA_INDEX = ":( OOPS!!! Please indicate a valid cca index!";
+
+ //@@author Aliciaho
+ // Messages from Test related classes
+ public static final String MESSAGE_TEST_ADD_SUCCESS = "Got it. I've added this test: ";
+ public static final String MESSAGE_TEST_DELETE_SUCCESS = "Noted. I've removed this test: ";
+ public static final String MESSAGE_TEST_DELETE_ERROR_NON_NUMBER = ":( OOPS!!! "
+ + "Please indicate in NUMERALS, which test you'd like to delete!";
+ public static final String MESSAGE_TEST_DELETE_ERROR_NO_NUMBER_GIVEN = ":( OOPS!!! "
+ + "Please indicate which test you'd like to delete!";
+ public static final String MESSAGE_TEST_DONE_SUCCESS = "Nice! I've marked this test as done:";
+ public static final String MESSAGE_TEST_DONE_ERROR_NON_NUMBER = ":( OOPS!!! "
+ + "Please indicate in NUMERALS, which test you'd like to set as Done!";
+ public static final String MESSAGE_TEST_DONE_ERROR_NO_NUMBER_GIVEN = ":( OOPS!!! "
+ + "Please indicate which test you'd like to set as Done!";
+ public static final String MESSAGE_INVALID_TEST_INDEX = ":( OOPS!!! Please indicate a valid test index!";
+ public static final String MESSAGE_TIME_LEFT_HEADER = "Time left for this day: ";
+
+ //@@author durianpancakes
+ // Messages from Tuition related classes
+ public static final String MESSAGE_MISSING_TUITION_SUFFIX = ":( OOPS!!! "
+ + "Remember to include ALL '/n', '/s', '/e', '/l' suffixes!";
+ public static final String MESSAGE_MISSING_TUITION_INPUT = ":( OOPS!!! "
+ + "Remember to include ALL '/n', '/s', '/e', '/l' inputs!";
+ public static final String MESSAGE_TUITION_ADD_SUCCESS = "Got it. I've added this tuition: ";
+ public static final String MESSAGE_TUITION_DELETE_SUCCESS = "Noted. I've removed this tuition: ";
+ public static final String MESSAGE_TUITION_DELETE_ERROR_NON_NUMBER = ":( OOPS!!! "
+ + "Please indicate in NUMERALS, which tuition you'd like to delete!";
+ public static final String MESSAGE_TUITION_DELETE_ERROR_NO_NUMBER_GIVEN = ":( OOPS!!! "
+ + "Please indicate which tuition you'd like to delete!";
+ public static final String MESSAGE_TUITION_DONE_SUCCESS = "Nice! I've marked this tuition as done:";
+ public static final String MESSAGE_TUITION_DONE_ERROR_NON_NUMBER = ":( OOPS!!! "
+ + "Please indicate in NUMERALS, which tuition you'd like to set as Done!";
+ public static final String MESSAGE_TUITION_DONE_ERROR_NO_NUMBER_GIVEN = ":( OOPS!!! "
+ + "Please indicate which tuition you'd like to set as Done!";
+ public static final String MESSAGE_INVALID_TUITION_INDEX = ":( OOPS!!! Please indicate a valid tuition index!";
+
+ //@@author elizabethcwt
+ // Messages from Quiz related classes
+ public static final String MESSAGE_QUIZ_DELETE_ERROR_NON_NUMBER = ":( OOPS!!! Please indicate in NUMERALS, "
+ + "which quiz you'd like to delete!\n";
+ public static final String MESSAGE_INVALID_QUIZ_COMMAND = ":( OOPS!!! Are you trying to take a quiz or add/delete a"
+ + " quiz question? Enter 'help' to check the correct format!";
+ public static final String MESSAGE_EMPTY_QUIZ_LIST = "Quiz list is empty. Add some!";
+ public static final String MESSAGE_QUIZ_INDEX_OUT_OF_BOUND = "There is no such question in your quiz list!";
+ public static final String MESSAGE_QUIZ_QUESTION_NOT_FOUND = "No question provided";
+ public static final String MESSAGE_QUIZ_ANSWER_NOT_FOUND = "No answer provided";
+ public static final String MESSAGE_QUIZ_OPTIONS_NOT_FOUND = "Options not provided";
+ public static final String MESSAGE_QUIZ_ADD_SUCCESSFUL = "Quiz question added!";
+ public static final String MESSAGE_QUIZ_INVALID_ANS_PROVIDED = ":( OOPS! Incorrect answer format! "
+ + "Your answer can only be either 1, 2, 3 or 4!";
+ public static final String MESSAGE_QUIZ_FULL_MARKS = "Congratulations! You got full marks in your last attempt!";
+ public static final String MESSAGE_QUIZ_WRONG_QUESTIONS_HEADER = "Here are the incorrect questions "
+ + "in your last quiz attempt:";
+ public static final String MESSAGE_QUIZ_LIST_HEADER = "Here are the questions in your quiz list:";
+ public static final String MESSAGE_QUIZ_MISSING_ANSWER = ":( OOPS! Please enter your answer for the "
+ + "question above!";
+ public static final String MESSAGE_NO_QUIZ_ATTEMPTS = "You have not taken a quiz yet!";
+
+ public static String invalid_number_of_quiz_questions_message(int size) {
+ return ":( OOPS!!! Please enter a valid number of quiz questions to attempt! (1~" + size + ")";
+ }
+
+ public static final String MESSAGE_MISSING_QUIZ_PARAM = "Please indicate the command you would like to apply on"
+ + " quiz!";
+
+ public static final String MESSAGE_QUIZ_NON_NUMBER = "Please enter a valid number or "
+ + "enter 'quiz record' to see your incorrect questions in your previous quiz!";
+
+ public static String print_quiz_score(int correctCounter, int noOfQues) {
+ return "You scored " + correctCounter + " out of " + noOfQues + "!"
+ + "Scroll up to review your quiz.";
+ }
+
+ //@@author elizabethcwt
+ public static final String MESSAGE_HELP = "Hello! Here is a list of commands you can try:\n\n"
+ + "\t1. Add class: 'add class /n [name of class] /s [start date-time of class] /e"
+ + " [end date-time of class]'\n"
+ + "\t2. Delete class: 'delete class [class number]'\n\n"
+ + "\t3. Add cca: 'add cca /n [name of cca] /s [start date-time of cca] /e [end date-time of cca]'\n"
+ + "\t4. Delete cca: 'delete cca [cca number]'\n\n"
+ + "\t5. Add test: 'add test /n [name of test] /s [start date-time of test] /e "
+ + "[end date-time of test]'\n"
+ + "\t6. Delete test: 'delete test [test number]'\n\n"
+ + "\t7. Add tuition: 'add tuition /n [name of tuition] /s [start date-time of tuition] /e "
+ + "start date-time of tuition] /l [location of tuition]'\n"
+ + "\t8. Delete tuition: 'delete tuition [tuition number]'\n\n"
+ + "\t9. List events (class, test, cca, tuition): 'list'\n\n"
+ + "\t10. Find relevant event(s): 'find [keyword(s)]'\n\n"
+ + "\t11. Add contact: 'add contact /s [subject] /n [name of contact person] /p [phone number]"
+ + " /e [email address]'\n"
+ + "\t12. Delete contact: 'delete contact [contact number]'\n"
+ + "\t13. List contact: 'list contact'\n"
+ + "\t14. Find contact: 'find contact [keyword(s)]'\n\n"
+ + "\t15. Take Mathematics quiz: 'quiz [no. of questions]'\n"
+ + "\t16. Add quiz question: 'add quiz /q [question] /o1 [option 1] /o2 [option 2] /o3 [option 3]"
+ + " /o4 [option 4] /a [option answer] /exp (explanation)'\n"
+ + "\t17. Delete quiz question: 'delete quiz [question number]'\n"
+ + "\t18. List quiz questions: 'list quiz'\n"
+ + "\t19. Find quiz questions: 'find quiz [keyword(s)]'\n"
+ + "\t20. Display former incorrect quiz question records: 'quiz record'\n\n"
+ + "\t21. Exit program: 'bye'\n\n"
+ + "\n\tNOTE:\n\t1. Please enter the date-time in the following format: YYYY-MM-DD "
+ + "[time in 24hr format]\n\te.g. 2020-08-19 1300\n\n"
+ + "\t2. For command 16 (Add quiz question), the 'explanation' field is OPTIONAL\n\n";
+
+ public static final String MESSAGE_BYE = "BYE BYE! SEE YOU NEXT TIME! :3";
+ public static final String MESSAGE_EXTRA_HELP_PARAM = "OOPS! Were you trying to ask for help? Just enter 'help'!";
+ public static final String MESSAGE_NO_EVENTS_FOUND = "Sorry but your searches yield no results!";
+ public static final String MESSAGE_NO_QUIZZES_FOUND = "Sorry but there is not such a quiz in your list!";
+}
diff --git a/src/main/java/seedu/duke/controller/ControlManager.java b/src/main/java/seedu/duke/controller/ControlManager.java
new file mode 100644
index 0000000000..c766835255
--- /dev/null
+++ b/src/main/java/seedu/duke/controller/ControlManager.java
@@ -0,0 +1,220 @@
+package seedu.duke.controller;
+
+import seedu.duke.common.LogManager;
+import seedu.duke.common.Messages;
+
+import seedu.duke.controller.command.Command;
+import seedu.duke.controller.command.CommandFactory;
+import seedu.duke.controller.parser.ModelExtractor;
+import seedu.duke.controller.parser.ModelParser;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.ExtraParameterException;
+import seedu.duke.exception.IncompleteFindCommandException;
+import seedu.duke.exception.IncompleteListCommandException;
+import seedu.duke.exception.InvalidCommandException;
+import seedu.duke.exception.InvalidHelpCommandException;
+import seedu.duke.exception.InvalidModelException;
+import seedu.duke.exception.MissingModelException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.model.Model;
+import seedu.duke.controller.parser.CommandParser;
+import seedu.duke.controller.parser.CommandType;
+import seedu.duke.controller.parser.ModelType;
+import seedu.duke.model.ModelMain;
+import seedu.duke.model.event.Event;
+import seedu.duke.model.quiz.Quiz;
+import seedu.duke.storage.contact.ContactStorageManager;
+import seedu.duke.storage.event.EventStorageManager;
+import seedu.duke.storage.quiz.QuizStorageManager;
+import seedu.duke.ui.UserInterface;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static seedu.duke.storage.StorageManager.CONTACT_FILE_NAME;
+import static seedu.duke.storage.StorageManager.QUIZ_FILE_NAME;
+
+//@@author AndreWongZH
+/**
+ * Manages the parsing of commands and models and the execution of commands.
+ */
+public class ControlManager {
+ private String userInput;
+ private final Model model;
+ private final UserInterface userInterface;
+ private final EventStorageManager eventStorageManager;
+ private final QuizStorageManager quizStorageManager;
+ private final ContactStorageManager contactStorageManager;
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+
+ public ControlManager(String userInput, Model model, EventStorageManager eventStorageManager,
+ QuizStorageManager quizStorageManager, ContactStorageManager contactStorageManager) {
+ assert userInput != null : "ControlManager must not accept null userInput";
+ this.userInput = userInput;
+ this.model = model;
+ userInterface = UserInterface.getInstance();
+ this.eventStorageManager = eventStorageManager;
+ this.quizStorageManager = quizStorageManager;
+ this.contactStorageManager = contactStorageManager;
+ }
+
+ /**
+ * Executes generated command class with a model.
+ *
+ * @return CommandType back to user interface to determine if program exits.
+ */
+ public CommandType runLogic() {
+ CommandType commandType = null;
+ ModelType modelType = null;
+ ModelMain dataModel = null;
+
+ try {
+ logger.log(Level.INFO, "Running controller logic now");
+ trimWhitespace();
+ logger.log(Level.INFO, "Extracting command");
+ commandType = new CommandParser(userInput).extractCommand();
+ final Command actionableCommand = new CommandFactory(commandType, userInput).generateActionableCommand();
+
+ if (commandType == CommandType.BYE) {
+ logger.log(Level.INFO, "User terminates program");
+ return commandType;
+ }
+
+ // Only extract model for certain commands(add, delete, list, find, quiz, done, set).
+ if (doesRequireModel(commandType)) {
+ logger.log(Level.INFO, "Extracting model");
+ modelType = new ModelParser(userInput).extractModel();
+ dataModel = new ModelExtractor(model, modelType).retrieveModel();
+ }
+ checkInvalidModels(commandType, modelType);
+
+ logger.log(Level.INFO, "Executing command");
+ actionableCommand.execute(dataModel);
+ } catch (InvalidHelpCommandException e) {
+ userInterface.showToUser(Messages.MESSAGE_EXTRA_HELP_PARAM);
+ } catch (InvalidCommandException e) {
+ userInterface.showToUser(Messages.MESSAGE_INVALID_COMMAND);
+ } catch (InvalidModelException e) {
+ userInterface.showToUser(Messages.MESSAGE_INVALID_MODEL);
+ } catch (MissingParameterException e) {
+ userInterface.showToUser(String.format(Messages.MESSAGE_MISSING_PARAMETERS, e.getMessage()));
+ } catch (EmptyParameterException e) {
+ userInterface.showToUser(Messages.MESSAGE_EMPTY_PARAMETERS);
+ } catch (IncompleteListCommandException e) {
+ userInterface.showToUser(Messages.MESSAGE_INCOMPLETE_LIST_PARAMETERS);
+ } catch (MissingModelException e) {
+ userInterface.showToUser(Messages.MESSAGE_MISSING_MODEL);
+ } catch (IncompleteFindCommandException e) {
+ userInterface.showToUser(Messages.MESSAGE_INCOMPLETE_FIND_PARAMETERS);
+ } catch (ExtraParameterException e) {
+ userInterface.showToUser(Messages.MESSAGE_INVALID_EXTRA_PARAM);
+ } catch (IndexOutOfBoundsException e) {
+ userInterface.showToUser(Messages.MESSAGE_CONTACT_INDEX_OUT_OF_BOUNDS);
+ } catch (SwappedParameterException e) {
+ userInterface.showToUser(Messages.MESSAGE_SWAPPED_PARAMETERS);
+ } finally {
+ refreshEvents();
+ refreshQuizzes();
+ refreshContacts();
+ }
+
+ return commandType;
+ }
+
+ /**
+ * Replaces all multiple spaces with only a single space and trims spaces at the start and end.
+ */
+ private void trimWhitespace() {
+ userInput = userInput.trim().replaceAll(" +", " ");
+ }
+
+ /**
+ * Checks if user entered an invalid model after the command.
+ * Commands such as add, delete and done cannot be paired with a model type event.
+ *
+ * @param commandType Command Type to be checked.
+ * @param modelType Model Type to be compared against.
+ * @throws InvalidModelException If add, delete or done is followed after event model.
+ */
+ private void checkInvalidModels(CommandType commandType, ModelType modelType) throws InvalidModelException {
+ if ((commandType == CommandType.ADD || commandType == CommandType.DELETE || commandType == CommandType.DONE)
+ && modelType == ModelType.EVENT) {
+ throw new InvalidModelException();
+ }
+
+ if ((commandType == CommandType.FIND || commandType == CommandType.LIST)
+ && (modelType != ModelType.EVENT && modelType != ModelType.CONTACT
+ && modelType != ModelType.QUIZ && modelType != null)) {
+ throw new InvalidModelException();
+ }
+
+ if ((commandType == CommandType.SET) && (modelType != ModelType.HOURS)) {
+ throw new InvalidModelException();
+ }
+ }
+
+ /**
+ * Checks if a modelType is required to be extracted out.
+ * Help and Bye command need not require any model.
+ *
+ * @param commandType The command type to be checked.
+ * @return Boolean to inform a need for model extraction.
+ */
+ private boolean doesRequireModel(CommandType commandType) {
+ boolean isAdd = commandType == CommandType.ADD;
+ boolean isDelete = commandType == CommandType.DELETE;
+ boolean isDone = commandType == CommandType.DONE;
+ boolean isList = commandType == CommandType.LIST;
+ boolean isFind = commandType == CommandType.FIND;
+ boolean isQuiz = commandType == CommandType.QUIZ;
+ boolean isSet = commandType == CommandType.SET;
+
+ return isAdd || isDelete || isDone || isList || isFind || isQuiz || isSet;
+ }
+
+ /**
+ * Saves the contacts data in ContactManager into contact.txt
+ */
+ private void refreshContacts() {
+ try {
+ contactStorageManager.saveData(model.getContactManager().getContacts(), CONTACT_FILE_NAME);
+ } catch (IOException e) {
+ userInterface.showToUser(Messages.MESSAGE_STORAGE_INITIALIZATION_ERROR);
+ }
+ }
+
+ //@@author durianpancakes
+ /**
+ * Obtains the latest Events from the data file.
+ */
+ private void refreshEvents() {
+ ArrayList events = new ArrayList<>();
+
+ events.addAll(model.getEventManager().getCcaManager().getCcas());
+ events.addAll(model.getEventManager().getTestManager().getTests());
+ events.addAll(model.getEventManager().getClassManager().getClasses());
+ events.addAll(model.getEventManager().getTuitionManager().getTuitions());
+
+ try {
+ eventStorageManager.saveData(events);
+ } catch (IOException e) {
+ userInterface.showToUser(Messages.MESSAGE_STORAGE_INITIALIZATION_ERROR);
+ }
+ }
+
+ /**
+ * Obtains the latest Quizzes from the data file.
+ */
+ private void refreshQuizzes() {
+ ArrayList quizzes = model.getQuizManager().getQuizList();
+
+ try {
+ quizStorageManager.saveData(quizzes, QUIZ_FILE_NAME);
+ } catch (IOException e) {
+ userInterface.showToUser(Messages.MESSAGE_STORAGE_INITIALIZATION_ERROR);
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/controller/command/AddCommand.java b/src/main/java/seedu/duke/controller/command/AddCommand.java
new file mode 100644
index 0000000000..82110833b4
--- /dev/null
+++ b/src/main/java/seedu/duke/controller/command/AddCommand.java
@@ -0,0 +1,31 @@
+package seedu.duke.controller.command;
+
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.model.ModelMain;
+import seedu.duke.model.ModelManager;
+
+//@@author AndreWongZH
+/**
+ * Represents the command for adding a class, cca, test, tuition, quiz and contact.
+ */
+public class AddCommand extends Command {
+ public AddCommand(String userInput) {
+ super(userInput);
+ }
+
+ /**
+ * Runs add command on corresponding model type.
+ *
+ * @param modelMain A model type to be modified or read.
+ * @throws EmptyParameterException If command if has empty parameters
+ * @throws MissingParameterException If command is lacking prefixes.
+ */
+ @Override
+ public void execute(ModelMain modelMain) throws EmptyParameterException, MissingParameterException,
+ SwappedParameterException {
+ ModelManager modelManager = (ModelManager) modelMain;
+ modelManager.add(userInput);
+ }
+}
diff --git a/src/main/java/seedu/duke/controller/command/Command.java b/src/main/java/seedu/duke/controller/command/Command.java
new file mode 100644
index 0000000000..7025dfee63
--- /dev/null
+++ b/src/main/java/seedu/duke/controller/command/Command.java
@@ -0,0 +1,32 @@
+package seedu.duke.controller.command;
+
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.ExtraParameterException;
+import seedu.duke.exception.IncompleteFindCommandException;
+import seedu.duke.exception.IncompleteListCommandException;
+import seedu.duke.exception.InvalidHelpCommandException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.model.ModelMain;
+
+//@@author AndreWongZH
+/**
+ * Represents the base of all command types.
+ */
+public abstract class Command {
+ protected final String userInput;
+
+ /**
+ * Runs if the execution requires passing the user input to the model.
+ *
+ * @param userInput String given from the user.
+ */
+ protected Command(String userInput) {
+ this.userInput = userInput;
+ }
+
+ public abstract void execute(ModelMain modelMain) throws InvalidHelpCommandException,
+ EmptyParameterException, MissingParameterException,
+ IncompleteListCommandException, IncompleteFindCommandException,
+ ExtraParameterException, SwappedParameterException;
+}
diff --git a/src/main/java/seedu/duke/controller/command/CommandFactory.java b/src/main/java/seedu/duke/controller/command/CommandFactory.java
new file mode 100644
index 0000000000..4cd772769f
--- /dev/null
+++ b/src/main/java/seedu/duke/controller/command/CommandFactory.java
@@ -0,0 +1,53 @@
+package seedu.duke.controller.command;
+
+import seedu.duke.controller.parser.CommandType;
+import seedu.duke.unused.DoneCommand;
+
+//@@author AndreWongZH
+/**
+ * Represents a generator that returns the corresponding Command class
+ * based on the commandType.
+ */
+public class CommandFactory {
+ private final CommandType commandType;
+ private final String userInput;
+
+ public CommandFactory(CommandType commandType, String userInput) {
+ assert commandType != null : "commandType must not be null";
+ this.commandType = commandType;
+ this.userInput = userInput;
+ }
+
+ /**
+ * Returns a Command class back to the ControlManager
+ * based on the commandType.
+ *
+ * @return Command class which is to be executed.
+ */
+ public Command generateActionableCommand() {
+ switch (commandType) {
+ case HELP:
+ return new HelpCommand(userInput);
+ case ADD:
+ return new AddCommand(userInput);
+ case DELETE:
+ return new DeleteCommand(userInput);
+ case DONE:
+ return new DoneCommand(userInput);
+ case LIST:
+ return new ListCommand(userInput);
+ case FIND:
+ return new FindCommand(userInput);
+ case QUIZ:
+ return new QuizCommand(userInput);
+ case SET:
+ return new SetHoursCommand(userInput);
+ case BYE:
+ // bye does not have a corresponding Command class
+ break;
+ default:
+ assert false : "all commandType should be handled";
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/seedu/duke/controller/command/DeleteCommand.java b/src/main/java/seedu/duke/controller/command/DeleteCommand.java
new file mode 100644
index 0000000000..72175530e5
--- /dev/null
+++ b/src/main/java/seedu/duke/controller/command/DeleteCommand.java
@@ -0,0 +1,36 @@
+package seedu.duke.controller.command;
+
+import seedu.duke.exception.ExtraParameterException;
+import seedu.duke.model.ModelMain;
+import seedu.duke.model.ModelManager;
+
+//@@author AndreWongZH
+/**
+ * Represents the command for deleting a class, cca, test, tuition, quiz and contact.
+ */
+public class DeleteCommand extends Command {
+
+ public static final String INPUT_SPACE = " ";
+ public static final int INPUT_LENGTH_THREE = 3;
+
+ public DeleteCommand(String userInput) {
+ super(userInput);
+ }
+
+ /**
+ * Runs delete on the required model.
+ *
+ * @param modelMain A model type to be modified or read.
+ * @throws ExtraParameterException If user adds extra parameters to delete [model] [number].
+ */
+ @Override
+ public void execute(ModelMain modelMain) throws ExtraParameterException {
+ String[] separatedInputs = userInput.split(INPUT_SPACE);
+ if (separatedInputs.length > INPUT_LENGTH_THREE) {
+ throw new ExtraParameterException();
+ }
+
+ ModelManager modelManager = (ModelManager) modelMain;
+ modelManager.delete(separatedInputs);
+ }
+}
diff --git a/src/main/java/seedu/duke/controller/command/FindCommand.java b/src/main/java/seedu/duke/controller/command/FindCommand.java
new file mode 100644
index 0000000000..f83e5938f9
--- /dev/null
+++ b/src/main/java/seedu/duke/controller/command/FindCommand.java
@@ -0,0 +1,43 @@
+package seedu.duke.controller.command;
+
+import seedu.duke.exception.IncompleteFindCommandException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.model.ModelMain;
+import seedu.duke.model.contact.ContactManager;
+import seedu.duke.model.event.EventManager;
+import seedu.duke.model.quiz.QuizManager;
+
+//@@author AndreWongZH
+/**
+ * Represents the command for searching via event description.
+ */
+public class FindCommand extends Command {
+ public FindCommand(String userInput) {
+ super(userInput);
+ }
+
+ /**
+ * Runs find command on corresponding model type.
+ *
+ * @param modelMain A model type to be modified or read.
+ * @throws MissingParameterException If command is lacking keywords.
+ * @throws IncompleteFindCommandException If model is missing.
+ */
+ @Override
+ public void execute(ModelMain modelMain) throws MissingParameterException, IncompleteFindCommandException {
+ if (modelMain == null) {
+ throw new IncompleteFindCommandException();
+ }
+
+ if (modelMain instanceof EventManager) {
+ EventManager eventManager = (EventManager) modelMain;
+ eventManager.find(userInput);
+ } else if (modelMain instanceof QuizManager) {
+ QuizManager quizManager = (QuizManager) modelMain;
+ quizManager.find(userInput);
+ } else if (modelMain instanceof ContactManager) {
+ ContactManager contactManager = (ContactManager) modelMain;
+ contactManager.find(userInput);
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/controller/command/HelpCommand.java b/src/main/java/seedu/duke/controller/command/HelpCommand.java
new file mode 100644
index 0000000000..b21e3a7f6f
--- /dev/null
+++ b/src/main/java/seedu/duke/controller/command/HelpCommand.java
@@ -0,0 +1,45 @@
+package seedu.duke.controller.command;
+
+import seedu.duke.common.LogManager;
+import seedu.duke.common.Messages;
+import seedu.duke.exception.InvalidHelpCommandException;
+import seedu.duke.model.ModelMain;
+import seedu.duke.ui.UserInterface;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+//@@author AndreWongZH
+/**
+ * Represents the command for asking for help.
+ */
+public class HelpCommand extends Command {
+ private final UserInterface userInterface = UserInterface.getInstance();
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+
+ public HelpCommand(String userInput) {
+ super(userInput);
+ }
+
+ @Override
+ public void execute(ModelMain modelMain) throws InvalidHelpCommandException {
+ handleHelp();
+ }
+
+ //@@author elizabethcwt
+ /**
+ * handleHelp()
+ * Prints out all available features users can use.
+ * @exception InvalidHelpCommandException to inform the user if their help input is invalid.
+ */
+ private void handleHelp() throws InvalidHelpCommandException {
+ String[] userInputs = userInput.split(" ");
+ if (userInputs.length == 1) {
+ logger.log(Level.INFO, "printing out all features users can use");
+ userInterface.showToUser(Messages.MESSAGE_HELP);
+ } else {
+ logger.log(Level.WARNING, "invalid help command");
+ throw new InvalidHelpCommandException();
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/controller/command/ListCommand.java b/src/main/java/seedu/duke/controller/command/ListCommand.java
new file mode 100644
index 0000000000..c02e9e05f2
--- /dev/null
+++ b/src/main/java/seedu/duke/controller/command/ListCommand.java
@@ -0,0 +1,50 @@
+package seedu.duke.controller.command;
+
+import seedu.duke.exception.ExtraParameterException;
+import seedu.duke.exception.IncompleteListCommandException;
+import seedu.duke.model.ModelMain;
+import seedu.duke.model.contact.ContactManager;
+import seedu.duke.model.event.EventManager;
+import seedu.duke.model.quiz.QuizManager;
+
+//@@author AndreWongZH
+/**
+ * Represents the command for listing events, quizzes and contacts.
+ */
+public class ListCommand extends Command {
+ public static final String INPUT_SPACE = " ";
+ public static final int INPUT_LENGTH_TWO = 2;
+
+ public ListCommand(String userInput) {
+ super(userInput);
+ }
+
+ /**
+ * Runs list command on corresponding model type.
+ *
+ * @param modelMain A model type to be modified or read.
+ * @throws IncompleteListCommandException If model is missing.
+ * @throws ExtraParameterException If user adds extra parameters to list contacts or quiz.
+ */
+ @Override
+ public void execute(ModelMain modelMain) throws IncompleteListCommandException, ExtraParameterException {
+ if (modelMain == null) {
+ throw new IncompleteListCommandException();
+ }
+
+ String[] separatedInputs = userInput.split(INPUT_SPACE);
+
+ if (modelMain instanceof EventManager) {
+ EventManager eventManager = (EventManager) modelMain;
+ eventManager.list(userInput);
+ } else if (modelMain instanceof QuizManager && separatedInputs.length == INPUT_LENGTH_TWO) {
+ QuizManager quizManager = (QuizManager) modelMain;
+ quizManager.list();
+ } else if (modelMain instanceof ContactManager && separatedInputs.length == INPUT_LENGTH_TWO) {
+ ContactManager contactManager = (ContactManager) modelMain;
+ contactManager.list();
+ } else {
+ throw new ExtraParameterException();
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/controller/command/QuizCommand.java b/src/main/java/seedu/duke/controller/command/QuizCommand.java
new file mode 100644
index 0000000000..8150a29ff0
--- /dev/null
+++ b/src/main/java/seedu/duke/controller/command/QuizCommand.java
@@ -0,0 +1,46 @@
+package seedu.duke.controller.command;
+
+import seedu.duke.exception.ExtraParameterException;
+import seedu.duke.model.ModelMain;
+import seedu.duke.model.quiz.QuizManager;
+
+import static seedu.duke.common.Messages.MESSAGE_MISSING_QUIZ_PARAM;
+import static seedu.duke.common.Messages.MESSAGE_QUIZ_NON_NUMBER;
+
+//@@author AndreWongZH
+/**
+ * Represents the command for taking and recording of quizzes.
+ */
+public class QuizCommand extends Command {
+
+ public static final String INPUT_SPACE = " ";
+ public static final int MAX_PARAM_SIZE = 2;
+ public static final String INPUT_RECORD = "record";
+
+ public QuizCommand(String userInput) {
+ super(userInput);
+ }
+
+ //@@author untitle4
+ @Override
+ public void execute(ModelMain modelMain) throws ExtraParameterException {
+ QuizManager quizManager = (QuizManager) modelMain;
+ assert userInput != null;
+ String[] separatedInputs = userInput.trim().split(INPUT_SPACE);
+ if (separatedInputs.length > MAX_PARAM_SIZE) {
+ throw new ExtraParameterException();
+ }
+
+ try {
+ if (separatedInputs[1].toLowerCase().equals(INPUT_RECORD)) {
+ quizManager.recordedQuizzes();
+ } else {
+ quizManager.checkQuizSizeValidity(separatedInputs);
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.out.println(MESSAGE_MISSING_QUIZ_PARAM);
+ } catch (NumberFormatException e) {
+ System.out.println(MESSAGE_QUIZ_NON_NUMBER);
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/controller/command/SetHoursCommand.java b/src/main/java/seedu/duke/controller/command/SetHoursCommand.java
new file mode 100644
index 0000000000..d29e76afa2
--- /dev/null
+++ b/src/main/java/seedu/duke/controller/command/SetHoursCommand.java
@@ -0,0 +1,28 @@
+package seedu.duke.controller.command;
+
+import seedu.duke.exception.ExtraParameterException;
+import seedu.duke.model.ModelMain;
+import seedu.duke.model.config.ConfigManager;
+
+//@@author Aliciaho
+public class SetHoursCommand extends Command {
+ public static final String INPUT_SPACE = " ";
+
+ /**
+ * Runs if the execution requires passing the user input to the model.
+ *
+ * @param userInput String given from the user.
+ */
+ protected SetHoursCommand(String userInput) {
+ super(userInput);
+ }
+
+ @Override
+ public void execute(ModelMain modelMain) throws NumberFormatException, ExtraParameterException {
+ if (userInput.split(INPUT_SPACE).length > 2) {
+ throw new ExtraParameterException();
+ }
+ ConfigManager configManager = (ConfigManager) modelMain;
+ configManager.editHours();
+ }
+}
diff --git a/src/main/java/seedu/duke/controller/parser/CommandParser.java b/src/main/java/seedu/duke/controller/parser/CommandParser.java
new file mode 100644
index 0000000000..ad167b47c7
--- /dev/null
+++ b/src/main/java/seedu/duke/controller/parser/CommandParser.java
@@ -0,0 +1,103 @@
+package seedu.duke.controller.parser;
+
+import seedu.duke.common.LogManager;
+import seedu.duke.exception.ExtraParameterException;
+import seedu.duke.exception.InvalidCommandException;
+import seedu.duke.exception.MissingModelException;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+//@@author AndreWongZH
+/**
+ * Represents the process of extracting out the commands.
+ */
+public class CommandParser {
+ public static final String INPUT_ADD = "add";
+ public static final String INPUT_DELETE = "delete";
+ public static final String INPUT_DONE = "done";
+ public static final String INPUT_LIST = "list";
+ public static final String INPUT_BYE = "bye";
+ public static final String INPUT_HELP = "help";
+ public static final String INPUT_FIND = "find";
+ public static final String INPUT_SPACES = " ";
+ public static final int LENGTH_SINGLE_WORD = 1;
+ public static final String INPUT_QUIZ = "quiz";
+ public static final int MAIN_COMMAND_INDEX = 0;
+ public static final String INPUT_SET = "set";
+
+ private final String[] separatedInputs;
+
+ private CommandType commandType;
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+
+ public CommandParser(String userInput) {
+ separatedInputs = userInput.toLowerCase().split(INPUT_SPACES);
+ commandType = null;
+ }
+
+ /**
+ * Checks if the first word in the input string matches any command word.
+ * If it contains any command word, returns the respective commandType.
+ *
+ * @return CommandType corresponding to the command.
+ * @throws InvalidCommandException If no command word matches the first word.
+ * @throws MissingModelException If command is missing a model word.
+ * @throws ExtraParameterException If command contains extra parameters.
+ */
+ public CommandType extractCommand() throws InvalidCommandException, MissingModelException, ExtraParameterException {
+ logger.log(Level.INFO, "Extracting command now...");
+
+ switch (separatedInputs[MAIN_COMMAND_INDEX]) {
+ case INPUT_ADD:
+ checkSingleLengthWord();
+ commandType = CommandType.ADD;
+ break;
+ case INPUT_DELETE:
+ checkSingleLengthWord();
+ commandType = CommandType.DELETE;
+ break;
+ case INPUT_DONE:
+ checkSingleLengthWord();
+ commandType = CommandType.DONE;
+ break;
+ case INPUT_LIST:
+ commandType = CommandType.LIST;
+ break;
+ case INPUT_BYE:
+ if (separatedInputs.length > LENGTH_SINGLE_WORD) {
+ throw new ExtraParameterException();
+ }
+ commandType = CommandType.BYE;
+ break;
+ case INPUT_HELP:
+ commandType = CommandType.HELP;
+ break;
+ case INPUT_FIND:
+ commandType = CommandType.FIND;
+ break;
+ case INPUT_QUIZ:
+ commandType = CommandType.QUIZ;
+ break;
+ case INPUT_SET:
+ checkSingleLengthWord();
+ commandType = CommandType.SET;
+ break;
+ default:
+ throw new InvalidCommandException();
+ }
+
+ return commandType;
+ }
+
+ /**
+ * Throws error if user input only have one word.
+ *
+ * @throws MissingModelException If user input only have one word.
+ */
+ private void checkSingleLengthWord() throws MissingModelException {
+ if (separatedInputs.length == LENGTH_SINGLE_WORD) {
+ throw new MissingModelException();
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/controller/parser/CommandType.java b/src/main/java/seedu/duke/controller/parser/CommandType.java
new file mode 100644
index 0000000000..1ef2430079
--- /dev/null
+++ b/src/main/java/seedu/duke/controller/parser/CommandType.java
@@ -0,0 +1,9 @@
+package seedu.duke.controller.parser;
+
+//@@author AndreWongZH
+/**
+ * Represents all possible commands available to the user.
+ */
+public enum CommandType {
+ ADD, DELETE, DONE, HELP, LIST, BYE, QUIZ, FIND, SET
+}
diff --git a/src/main/java/seedu/duke/controller/parser/DateTimeParser.java b/src/main/java/seedu/duke/controller/parser/DateTimeParser.java
new file mode 100644
index 0000000000..037925255b
--- /dev/null
+++ b/src/main/java/seedu/duke/controller/parser/DateTimeParser.java
@@ -0,0 +1,270 @@
+package seedu.duke.controller.parser;
+
+import seedu.duke.common.LogManager;
+
+import java.time.Duration;
+import java.text.SimpleDateFormat;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.text.ParseException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+//@@author Aliciaho
+/**
+ * To configure the Date and Time of the events from yyyy-mm-dd HHMM format to dd suffix mm yyyy, hh:mm aa format.
+ */
+public class DateTimeParser {
+
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+ public static final int MAX_HHMM = 2400;
+
+ //@@author Aliciaho-reused
+ /**
+ * Get the suffix for each day.
+ * ref to: https://stackoverflow.com/questions/4011075/how-do-you-format-the-day-of-the-month-to-say-11th-21st-or-23rd-ordinal
+ * @param day day number of the month
+ * @return respective suffix for the day inputted
+ */
+ private String getDayNumberSuffix(int day) {
+ assert day > 0;
+ assert day < 31;
+
+ if (day >= 11 && day <= 13) {
+ return "th";
+ }
+ switch (day % 10) {
+ case 1:
+ return "st";
+ case 2:
+ return "nd";
+ case 3:
+ return "rd";
+ default:
+ return "th";
+ }
+ }
+
+ /**
+ * Convert time from HH:mm format to hh:mma format.
+ *
+ * @param calendar date time inputted
+ * @return the time in hh:mma format
+ */
+ public String parseTime(Calendar calendar) {
+ logger.log(Level.INFO, "converting time to hh:mma");
+ SimpleDateFormat sdf = new SimpleDateFormat("hh:mma");
+ return sdf.format(calendar.getTime());
+ }
+
+ /**
+ * Convert day and month into MM-yyyy format.
+ *
+ * @param calendar date time inputted
+ * @return the date in MM-yyyy format
+ */
+ public String parseDayAndMonth(Calendar calendar) {
+ logger.log(Level.INFO, "converting date to MM-yyyy");
+ SimpleDateFormat sdf = new SimpleDateFormat("MM-yyyy");
+ return sdf.format(calendar.getTime());
+ }
+
+ /**
+ * Convert a string input to date time format yyyy-MM-dd HHmm in Calendar form.
+ *
+ * @param string user input string
+ * @return calendar date time in yyyy-MM-dd HHmm in Calendar form
+ * @exception ParseException exception thrown when valid date and time is not inputted
+ */
+ public Calendar convertStringToCalendar(String string) throws ParseException {
+ logger.log(Level.INFO, "converting string to calendar");
+ checkDateTimeFormat(string);
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HHmm");
+ Calendar calendar = Calendar.getInstance();
+
+ Date date = sdf.parse(string);
+ calendar.setTime(date);
+
+ return calendar;
+ }
+
+ //@@author AndreWongZH
+ /**
+ * Validates if the datetime string is following the correct format (yyyy-MM-dd HHmm).
+ *
+ * @param string User input string.
+ * @throws ParseException If date is not according to the format.
+ */
+ private void checkDateTimeFormat(String string) throws ParseException {
+ Pattern pattern = Pattern.compile("^\\d\\d\\d\\d-\\d\\d-\\d\\d (\\d\\d\\d\\d)$");
+ Matcher matcher = pattern.matcher(string);
+ if (!matcher.find()) {
+ throw new ParseException("Invalid datetime", 0);
+ }
+
+ if (Integer.parseInt(matcher.group(1)) > MAX_HHMM) {
+ throw new ParseException("Invalid datetime", 0);
+ }
+ }
+
+ //@@author Aliciaho
+ /**
+ * Convert a string input to date time format yyyy-MM-dd in Calendar form.
+ *
+ * @param string user input string
+ * @return calendar date time in yyyy-MM-dd in Calendar form
+ * @exception ParseException exception thrown when valid date and time is not inputted
+ */
+ public Calendar convertStringToCalendarByDate(String string) throws ParseException {
+ logger.log(Level.INFO, "converting string to calendar");
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ Calendar calendar = Calendar.getInstance();
+
+ Date date = sdf.parse(string);
+ calendar.setTime(date);
+
+ return calendar;
+ }
+
+ /**
+ * Convert a Calendar form in date time format yyyy-MM-dd HHmm to String.
+ *
+ * @param calendar date time input in Calendar form
+ * @return calendar date time input in String form
+ */
+ public String convertCalendarToString(Calendar calendar) {
+ logger.log(Level.INFO, "converting calendar to string");
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HHmm");
+
+ return sdf.format(calendar.getTime());
+ }
+
+ /**
+ * Add day suffix to date time string.
+ *
+ * @param calendar date time in calendar form
+ * @return date time string with day suffix
+ */
+ public String obtainFormattedDateTimeString(Calendar calendar) {
+ logger.log(Level.INFO, "converting to MMM yyyy, hh:mma format");
+ SimpleDateFormat sdf = new SimpleDateFormat("MMM yyyy, hh:mma");
+ int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
+ logger.log(Level.INFO, "getting day suffix");
+ String dayOfMonthString = dayOfMonth + getDayNumberSuffix(dayOfMonth);
+ String monthAndYearString = sdf.format(calendar.getTime());
+
+ return dayOfMonthString + " " + monthAndYearString;
+ }
+
+ //@@author durianpancakes
+ /**
+ * Convert date time in Calendar form to dd-MM in String form.
+ *
+ * @param calendar date time in Calendar form
+ * @return dd-MM in String form
+ */
+ public String obtainFormattedDayAndMonthString(Calendar calendar) {
+ logger.log(Level.INFO, "converting to dd-MM format");
+ SimpleDateFormat sdf = new SimpleDateFormat("dd-MM");
+
+ return sdf.format(calendar.getTime());
+ }
+
+ //@@author Aliciaho
+ /**
+ * Check if two dates are equal.
+ *
+ * @param listInput date time input from List
+ * @param userInput date time input by user
+ * @return true if both dates are equal
+ * @throws DateTimeParseException if valid datetime is not inputted
+ */
+ public boolean isDateEqual(Calendar listInput, Calendar userInput) throws DateTimeParseException {
+ logger.log(Level.INFO, "checking if dates are equal");
+ int listDay = listInput.get(Calendar.DAY_OF_MONTH);
+ int userDay = userInput.get(Calendar.DAY_OF_MONTH);
+ int listMonth = listInput.get(Calendar.MONTH);
+ int userMonth = userInput.get(Calendar.MONTH);
+ int listYear = listInput.get(Calendar.YEAR);
+ int userYear = userInput.get(Calendar.YEAR);
+ return (listDay == userDay)
+ && (listMonth == userMonth)
+ && (listYear == userYear);
+ }
+
+ /**
+ * Get the dates for this week.
+ *
+ * @return dateCalendars ArrayList containing the dates for this week
+ */
+ public ArrayList getDaysOfWeek(Calendar calendar) {
+ logger.log(Level.INFO, "getting days of the week");
+ ArrayList dateCalendars = new ArrayList<>();
+
+ switch (calendar.get(Calendar.DAY_OF_WEEK)) {
+ case Calendar.MONDAY:
+ calendar.add(Calendar.DAY_OF_MONTH, 0);
+ break;
+ case Calendar.TUESDAY:
+ calendar.add(Calendar.DAY_OF_MONTH, -1);
+ break;
+ case Calendar.WEDNESDAY:
+ calendar.add(Calendar.DAY_OF_MONTH, -2);
+ break;
+ case Calendar.THURSDAY:
+ calendar.add(Calendar.DAY_OF_MONTH, -3);
+ break;
+ case Calendar.FRIDAY:
+ calendar.add(Calendar.DAY_OF_MONTH, -4);
+ break;
+ case Calendar.SATURDAY:
+ calendar.add(Calendar.DAY_OF_MONTH, -5);
+ break;
+ case Calendar.SUNDAY:
+ calendar.add(Calendar.DAY_OF_MONTH, -6);
+ break;
+ default:
+ break;
+ }
+
+ logger.log(Level.INFO, "adding days of week to dateCalendars");
+ for (int i = 0; i < 7; i++) {
+ Calendar newCalendar = (Calendar) calendar.clone();
+ dateCalendars.add(newCalendar);
+ calendar.add(Calendar.DAY_OF_MONTH, 1);
+ }
+ return dateCalendars;
+ }
+
+ /**
+ * Get duration between two Calendar dates.
+ *
+ * @param startDateCalendar starting date
+ * @param endDateCalendar ending date
+ * @return duration between the two dates in minutes
+ */
+ public long getDuration(Calendar startDateCalendar, Calendar endDateCalendar) {
+ logger.log(Level.INFO, "getting duration between two dates");
+ long timeDurationInMinutes;
+ timeDurationInMinutes = Duration.between(startDateCalendar.toInstant(),
+ endDateCalendar.toInstant()).toMinutes();
+ return timeDurationInMinutes;
+ }
+
+ public ArrayList getCurrentDaysOfWeek() {
+ Calendar calendar = Calendar.getInstance();
+ return getDaysOfWeek(calendar);
+ }
+
+ //@@author durianpancakes
+ public ArrayList getNextDaysOfWeek() {
+ Calendar calendar = Calendar.getInstance();
+ calendar.add(Calendar.DAY_OF_MONTH, 7);
+ return getDaysOfWeek(calendar);
+ }
+}
+
diff --git a/src/main/java/seedu/duke/controller/parser/ModelExtractor.java b/src/main/java/seedu/duke/controller/parser/ModelExtractor.java
new file mode 100644
index 0000000000..2c922e76e6
--- /dev/null
+++ b/src/main/java/seedu/duke/controller/parser/ModelExtractor.java
@@ -0,0 +1,54 @@
+package seedu.duke.controller.parser;
+
+import seedu.duke.exception.InvalidModelException;
+import seedu.duke.model.Model;
+import seedu.duke.model.ModelMain;
+
+//@@author AndreWongZH
+/**
+ * Represents a extractor that returns the corresponding Model Manager
+ * based on the modelType.
+ */
+public class ModelExtractor {
+ private final Model model;
+ private final ModelType modelType;
+
+ public ModelExtractor(Model model, ModelType modelType) {
+ this.model = model;
+ this.modelType = modelType;
+ }
+
+ /**
+ * Returns the Model Manager based on the modelType.
+ * Returns null if modelType is EVENT or null.
+ *
+ * @return Model Manager to be read or modified.
+ * @throws InvalidModelException If modelType does not match any of the Model Managers.
+ */
+ public ModelMain retrieveModel() throws InvalidModelException {
+ if (modelType == null) {
+ return null;
+ }
+
+ switch (modelType) {
+ case CLASS:
+ return model.getEventManager().getClassManager();
+ case CCA:
+ return model.getEventManager().getCcaManager();
+ case TEST:
+ return model.getEventManager().getTestManager();
+ case TUITION:
+ return model.getEventManager().getTuitionManager();
+ case QUIZ:
+ return model.getQuizManager();
+ case CONTACT:
+ return model.getContactManager();
+ case EVENT:
+ return model.getEventManager();
+ case HOURS:
+ return model.getConfigManager();
+ default:
+ throw new InvalidModelException();
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/controller/parser/ModelParser.java b/src/main/java/seedu/duke/controller/parser/ModelParser.java
new file mode 100644
index 0000000000..788b9e9707
--- /dev/null
+++ b/src/main/java/seedu/duke/controller/parser/ModelParser.java
@@ -0,0 +1,85 @@
+package seedu.duke.controller.parser;
+
+import seedu.duke.common.LogManager;
+import seedu.duke.exception.InvalidModelException;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+//@@author AndreWongZH
+/**
+ * Represents the process of extracting out the model to be performed on.
+ */
+public class ModelParser {
+ public static final String INPUT_SCHEDULE_CLASS = "class";
+ public static final String INPUT_SCHEDULE_TEST = "test";
+ public static final String INPUT_SCHEDULE_CCA = "cca";
+ public static final String INPUT_SCHEDULE_TUITION = "tuition";
+ public static final String INPUT_QUIZ = "quiz";
+ public static final String INPUT_CONTACT = "contact";
+ public static final String INPUT_EVENT = "event";
+ public static final String INPUT_HOURS = "hours";
+ public static final int SUB_COMMAND_INDEX = 1;
+ public static final String INPUT_SPACES = " ";
+ public static final int MAIN_COMMAND_INDEX = 0;
+
+ private final String[] separatedInputs;
+
+ private ModelType modelType;
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+
+ public ModelParser(String userInput) {
+ separatedInputs = userInput.toLowerCase().split(INPUT_SPACES);
+ modelType = null;
+ }
+
+ /**
+ * Checks if the second word in the input string matches any model word.
+ * If it contains any model word, returns the respective modelType.
+ *
+ * @return ModelType corresponding to the model.
+ * @throws InvalidModelException If the second word does not match any model word.
+ */
+ public ModelType extractModel() throws InvalidModelException {
+ logger.log(Level.INFO, "Extracting model now...");
+
+ if (separatedInputs[MAIN_COMMAND_INDEX].equals("quiz")) {
+ return modelType = ModelType.QUIZ;
+ }
+
+ if (separatedInputs.length <= 1) {
+ return null;
+ }
+
+ switch (separatedInputs[SUB_COMMAND_INDEX]) {
+ case INPUT_SCHEDULE_CLASS:
+ modelType = ModelType.CLASS;
+ break;
+ case INPUT_SCHEDULE_CCA:
+ modelType = ModelType.CCA;
+ break;
+ case INPUT_SCHEDULE_TEST:
+ modelType = ModelType.TEST;
+ break;
+ case INPUT_SCHEDULE_TUITION:
+ modelType = ModelType.TUITION;
+ break;
+ case INPUT_CONTACT:
+ modelType = ModelType.CONTACT;
+ break;
+ case INPUT_QUIZ:
+ modelType = ModelType.QUIZ;
+ break;
+ case INPUT_EVENT:
+ modelType = ModelType.EVENT;
+ break;
+ case INPUT_HOURS:
+ modelType = ModelType.HOURS;
+ break;
+ default:
+ throw new InvalidModelException();
+ }
+
+ return modelType;
+ }
+}
diff --git a/src/main/java/seedu/duke/controller/parser/ModelType.java b/src/main/java/seedu/duke/controller/parser/ModelType.java
new file mode 100644
index 0000000000..58651c74de
--- /dev/null
+++ b/src/main/java/seedu/duke/controller/parser/ModelType.java
@@ -0,0 +1,9 @@
+package seedu.duke.controller.parser;
+
+//@@author AndreWongZH
+/**
+ * Represents all possible models available to the user.
+ */
+public enum ModelType {
+ CLASS, CCA, TEST, TUITION, QUIZ, CONTACT, EVENT, HOURS;
+}
diff --git a/src/main/java/seedu/duke/exception/EmptyListException.java b/src/main/java/seedu/duke/exception/EmptyListException.java
new file mode 100644
index 0000000000..359e5f97c4
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/EmptyListException.java
@@ -0,0 +1,10 @@
+package seedu.duke.exception;
+
+/**
+ * Represents the error when list to be displayed is empty.
+ */
+public class EmptyListException extends Exception {
+ public EmptyListException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/EmptyParameterException.java b/src/main/java/seedu/duke/exception/EmptyParameterException.java
new file mode 100644
index 0000000000..7d7d5372fb
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/EmptyParameterException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exception;
+
+/**
+ * Represents the error when user does not give any input after a prefix.
+ */
+public class EmptyParameterException extends Exception {
+}
diff --git a/src/main/java/seedu/duke/exception/ExtraParameterException.java b/src/main/java/seedu/duke/exception/ExtraParameterException.java
new file mode 100644
index 0000000000..28e42b5237
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/ExtraParameterException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exception;
+
+/**
+ * Represents the error when there are unnecessary parameters in the user input.
+ */
+public class ExtraParameterException extends Exception {
+}
diff --git a/src/main/java/seedu/duke/exception/IncompleteFindCommandException.java b/src/main/java/seedu/duke/exception/IncompleteFindCommandException.java
new file mode 100644
index 0000000000..5cf606f9cf
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/IncompleteFindCommandException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exception;
+
+/**
+ * Represents the error when user only inputs find.
+ */
+public class IncompleteFindCommandException extends Exception {
+}
diff --git a/src/main/java/seedu/duke/exception/IncompleteListCommandException.java b/src/main/java/seedu/duke/exception/IncompleteListCommandException.java
new file mode 100644
index 0000000000..08c63f8c81
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/IncompleteListCommandException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exception;
+
+/**
+ * Represents the error when the user only inputs list.
+ */
+public class IncompleteListCommandException extends Exception {
+}
diff --git a/src/main/java/seedu/duke/exception/InvalidCommandException.java b/src/main/java/seedu/duke/exception/InvalidCommandException.java
new file mode 100644
index 0000000000..1539a5f96b
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/InvalidCommandException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exception;
+
+/**
+ * Represents the error when user inputs an unknown command.
+ */
+public class InvalidCommandException extends Exception {
+}
diff --git a/src/main/java/seedu/duke/exception/InvalidDateException.java b/src/main/java/seedu/duke/exception/InvalidDateException.java
new file mode 100644
index 0000000000..d30e4afc66
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/InvalidDateException.java
@@ -0,0 +1,17 @@
+package seedu.duke.exception;
+
+//@@author durianpancakes
+/**
+ * Represents the error when the start date or end date is not valid.
+ */
+public class InvalidDateException extends Exception {
+ private final InvalidDateType errorType;
+
+ public InvalidDateException(InvalidDateType errorType) {
+ this.errorType = errorType;
+ }
+
+ public InvalidDateType getErrorType() {
+ return this.errorType;
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/InvalidDateType.java b/src/main/java/seedu/duke/exception/InvalidDateType.java
new file mode 100644
index 0000000000..e8e7c58138
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/InvalidDateType.java
@@ -0,0 +1,6 @@
+package seedu.duke.exception;
+
+//@@author durianpancakes
+public enum InvalidDateType {
+ START_AFTER_END, START_EQUALS_END
+}
diff --git a/src/main/java/seedu/duke/exception/InvalidHelpCommandException.java b/src/main/java/seedu/duke/exception/InvalidHelpCommandException.java
new file mode 100644
index 0000000000..32bf570dea
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/InvalidHelpCommandException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exception;
+
+/**
+ * Represents the error when there are extra parameters in help command.
+ */
+public class InvalidHelpCommandException extends Exception {
+}
diff --git a/src/main/java/seedu/duke/exception/InvalidModelException.java b/src/main/java/seedu/duke/exception/InvalidModelException.java
new file mode 100644
index 0000000000..859b1c7d13
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/InvalidModelException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exception;
+
+/**
+ * Represents the error when model type is not compatible with command.
+ */
+public class InvalidModelException extends Exception {
+}
diff --git a/src/main/java/seedu/duke/exception/MissingModelException.java b/src/main/java/seedu/duke/exception/MissingModelException.java
new file mode 100644
index 0000000000..fcde8b1ff1
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/MissingModelException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exception;
+
+/**
+ * Represents the error when user input does not contain a required model type.
+ */
+public class MissingModelException extends Exception {
+}
diff --git a/src/main/java/seedu/duke/exception/MissingParameterException.java b/src/main/java/seedu/duke/exception/MissingParameterException.java
new file mode 100644
index 0000000000..3793e1ae1d
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/MissingParameterException.java
@@ -0,0 +1,10 @@
+package seedu.duke.exception;
+
+/**
+ * Represents the error when there are missing required prefixes.
+ */
+public class MissingParameterException extends Exception {
+ public MissingParameterException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/duke/exception/StorageCorruptedException.java b/src/main/java/seedu/duke/exception/StorageCorruptedException.java
new file mode 100644
index 0000000000..a0f9b15e46
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/StorageCorruptedException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exception;
+
+/**
+ * Represents the error during the loading of data from txt file storage.
+ */
+public class StorageCorruptedException extends Exception {
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/duke/exception/StorageSeparatorException.java b/src/main/java/seedu/duke/exception/StorageSeparatorException.java
new file mode 100644
index 0000000000..b14616b085
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/StorageSeparatorException.java
@@ -0,0 +1,8 @@
+package seedu.duke.exception;
+
+/**
+ * Represents the error when the separator used between each parameter of an Event is not "|".
+ */
+public class StorageSeparatorException extends Exception {
+}
+
diff --git a/src/main/java/seedu/duke/exception/SwappedParameterException.java b/src/main/java/seedu/duke/exception/SwappedParameterException.java
new file mode 100644
index 0000000000..2a160fd86a
--- /dev/null
+++ b/src/main/java/seedu/duke/exception/SwappedParameterException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exception;
+
+/**
+ * Represents the error when the parameters are swapped around.
+ */
+public class SwappedParameterException extends Exception {
+}
diff --git a/src/main/java/seedu/duke/model/ConfigParameter.java b/src/main/java/seedu/duke/model/ConfigParameter.java
new file mode 100644
index 0000000000..2cbfa8c357
--- /dev/null
+++ b/src/main/java/seedu/duke/model/ConfigParameter.java
@@ -0,0 +1,45 @@
+package seedu.duke.model;
+
+//@@author Aliciaho
+public class ConfigParameter {
+ private String name;
+ private int recommendedHours;
+ private boolean hasProgramRan;
+
+ public ConfigParameter(String name, int recommendedHours, boolean hasProgramRan) {
+ this.name = name;
+ this.recommendedHours = recommendedHours;
+ this.hasProgramRan = hasProgramRan;
+ }
+
+ public ConfigParameter() {
+ this.name = "";
+ this.recommendedHours = 1;
+ this.hasProgramRan = false;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getRecommendedHours() {
+ return recommendedHours;
+ }
+
+ public void setRecommendedHours(int recommendedHours) {
+ this.recommendedHours = recommendedHours;
+ }
+
+ public boolean getHasProgramRan() {
+ return hasProgramRan;
+ }
+
+ public void setHasProgramRan(boolean hasProgramRan) {
+ this.hasProgramRan = hasProgramRan;
+ }
+
+}
diff --git a/src/main/java/seedu/duke/model/Interactable.java b/src/main/java/seedu/duke/model/Interactable.java
new file mode 100644
index 0000000000..7f13f6b787
--- /dev/null
+++ b/src/main/java/seedu/duke/model/Interactable.java
@@ -0,0 +1,28 @@
+package seedu.duke.model;
+
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.exception.SwappedParameterException;
+
+//@@author AndreWongZH
+/**
+ * Represents the public api methods across all ModelManager that the controller can call.
+ */
+public interface Interactable {
+
+ /**
+ * Adds an object to a ModelManager.
+ *
+ * @param userInput The input entered by the user.
+ * @throws MissingParameterException If command is missing parameters prefix.
+ * @throws EmptyParameterException If no parameter inputs are found after the prefix.
+ */
+ void add(String userInput) throws MissingParameterException, EmptyParameterException, SwappedParameterException;
+
+ /**
+ * Deletes an object from a ModelManager.
+ *
+ * @param userInputs The input entered by the user.
+ */
+ void delete(String[] userInputs);
+}
diff --git a/src/main/java/seedu/duke/model/Model.java b/src/main/java/seedu/duke/model/Model.java
new file mode 100644
index 0000000000..8487480dd5
--- /dev/null
+++ b/src/main/java/seedu/duke/model/Model.java
@@ -0,0 +1,42 @@
+package seedu.duke.model;
+
+import seedu.duke.model.contact.ContactManager;
+import seedu.duke.model.event.EventManager;
+import seedu.duke.model.quiz.QuizManager;
+import seedu.duke.model.config.ConfigManager;
+
+//@@author AndreWongZH
+/**
+ * Represents a model object that stores volatile memory of the program data.
+ * This consist of the eventManager, contactManager, quizManager and configManager.
+ */
+public class Model {
+ EventManager eventManager;
+ ContactManager contactManager;
+ QuizManager quizManager;
+ ConfigManager configManager;
+
+ public Model(EventManager eventManager, ContactManager contactManager,
+ QuizManager quizManager, ConfigManager configManager) {
+ this.eventManager = eventManager;
+ this.contactManager = contactManager;
+ this.quizManager = quizManager;
+ this.configManager = configManager;
+ }
+
+ public EventManager getEventManager() {
+ return eventManager;
+ }
+
+ public ContactManager getContactManager() {
+ return contactManager;
+ }
+
+ public QuizManager getQuizManager() {
+ return quizManager;
+ }
+
+ public ConfigManager getConfigManager() {
+ return configManager;
+ }
+}
diff --git a/src/main/java/seedu/duke/model/ModelMain.java b/src/main/java/seedu/duke/model/ModelMain.java
new file mode 100644
index 0000000000..10934a635b
--- /dev/null
+++ b/src/main/java/seedu/duke/model/ModelMain.java
@@ -0,0 +1,14 @@
+package seedu.duke.model;
+
+import seedu.duke.model.config.ConfigManager;
+
+//@@author AndreWongZH
+/**
+ * Represents the main model class which is inherited by all Managers.
+ *
+ * @see seedu.duke.model.event.EventManager
+ * @see ModelManager
+ * @see ConfigManager
+ */
+public abstract class ModelMain {
+}
diff --git a/src/main/java/seedu/duke/model/ModelManager.java b/src/main/java/seedu/duke/model/ModelManager.java
new file mode 100644
index 0000000000..08fdc7562e
--- /dev/null
+++ b/src/main/java/seedu/duke/model/ModelManager.java
@@ -0,0 +1,12 @@
+package seedu.duke.model;
+
+//@@author AndreWongZH
+/**
+ * Represents the main model class which is inherited by Contact, Quiz, Class, Test, CCA, Tuition Manager.
+ *
+ * @see seedu.duke.model.contact.ContactManager
+ * @see seedu.duke.model.quiz.QuizManager
+ * @see seedu.duke.model.event.classlesson.EventClassManager
+ */
+public abstract class ModelManager extends ModelMain implements Interactable {
+}
diff --git a/src/main/java/seedu/duke/model/config/ConfigInteractable.java b/src/main/java/seedu/duke/model/config/ConfigInteractable.java
new file mode 100644
index 0000000000..0f3b86d3ee
--- /dev/null
+++ b/src/main/java/seedu/duke/model/config/ConfigInteractable.java
@@ -0,0 +1,16 @@
+package seedu.duke.model.config;
+
+import seedu.duke.model.ConfigParameter;
+
+//@@author AndreWongZH
+/**
+ * Represents the public api methods for ConfigManager that the controller can call.
+ */
+public interface ConfigInteractable {
+ /**
+ * Changes the recommended hours a day.
+ */
+ void editHours();
+
+ void getIntroductoryVariables(ConfigParameter configParameter);
+}
diff --git a/src/main/java/seedu/duke/model/config/ConfigManager.java b/src/main/java/seedu/duke/model/config/ConfigManager.java
new file mode 100644
index 0000000000..67698d86b5
--- /dev/null
+++ b/src/main/java/seedu/duke/model/config/ConfigManager.java
@@ -0,0 +1,130 @@
+package seedu.duke.model.config;
+
+import seedu.duke.common.LogManager;
+import seedu.duke.common.Messages;
+import seedu.duke.exception.StorageCorruptedException;
+import seedu.duke.model.ConfigParameter;
+import seedu.duke.model.ModelMain;
+import seedu.duke.storage.config.ConfigStorageManager;
+import seedu.duke.ui.UserInterface;
+
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+//@@author Aliciaho
+/**
+ * Represents a handler that manages the recommended hours features.
+ */
+public class ConfigManager extends ModelMain implements ConfigInteractable {
+ public static final String CONFIG_FILE_NAME = "/config.txt";
+ private final ConfigStorageManager configStorageManager;
+ private static UserInterface userInterface;
+ private ConfigParameter configParameter;
+ private static ConfigManager INSTANCE = null;
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+
+ /**
+ * Get the instance of ConfigManager.
+ *
+ * @return INSTANCE instance of ConfigManager
+ */
+ public static ConfigManager getInstance() {
+ if (INSTANCE == null) {
+ INSTANCE = new ConfigManager();
+ }
+ return INSTANCE;
+ }
+
+ /**
+ * Constructor for ConfigManager.
+ */
+ private ConfigManager() {
+ userInterface = UserInterface.getInstance();
+ this.configStorageManager = new ConfigStorageManager(CONFIG_FILE_NAME);
+ this.configParameter = null;
+ }
+
+ /**
+ * Load data from configStorageManager to configParam.
+ *
+ * @return configParam containing data from configStorageManager
+ */
+ public ConfigParameter getConfig() throws StorageCorruptedException {
+ ConfigParameter configParameter;
+ configParameter = configStorageManager.loadData();
+ return configParameter;
+ }
+
+ /**
+ * Get the username and recommended hours from user.
+ *
+ * @param configParameter configParameter class
+ */
+ @Override
+ public void getIntroductoryVariables(ConfigParameter configParameter) {
+ logger.log(Level.INFO, "getting username and recommended hours from user");
+ if (!configParameter.getHasProgramRan()) {
+ userInterface.showToUser(Messages.MESSAGE_PROMPT_NAME);
+ String userName = userInterface.getUserCommand();
+ configParameter.setName(userName);
+ int recommendedHours;
+ recommendedHours = getInputHours();
+ configParameter.setRecommendedHours(recommendedHours);
+ configParameter.setHasProgramRan(true);
+ }
+ saveConfigParameter(configParameter);
+ }
+
+ /**
+ * Prompt the user to key in the number of recommended hours.
+ *
+ * @return recommendedHours hours that the user has inputted which is not less than 0 or more than 12
+ * @exception NumberFormatException exception thrown when non-integer is inputted
+ */
+ private int getInputHours() {
+ int recommendedHours = 0;
+ do {
+ try {
+ userInterface.showToUser(Messages.MESSAGE_PROMPT_HOURS);
+ recommendedHours = Integer.parseInt(userInterface.getUserCommand());
+ } catch (NumberFormatException e) {
+ userInterface.showToUser(Messages.MESSAGE_HOURS_ERROR_NON_NUMBER);
+ }
+ } while (recommendedHours <= 0 || recommendedHours > 12);
+ return recommendedHours;
+ }
+
+
+ /**
+ * Save the new ConfigParam into ConfigStorageManager.
+ *
+ * @param configParameter configParam storing the new inputs
+ */
+ private void saveConfigParameter(ConfigParameter configParameter) {
+ try {
+ configStorageManager.saveData(configParameter);
+ } catch (IOException e) {
+ userInterface.showToUser(Messages.MESSAGE_STORAGE_INITIALIZATION_ERROR);
+ }
+ }
+
+ /**
+ * Prompt and edit hours according to user's new recommended hours.
+ */
+ @Override
+ public void editHours() {
+ logger.log(Level.INFO, "editing hours to take in new hours input");
+ int newHours;
+ newHours = getInputHours();
+ userInterface.showToUser(Messages.MESSAGE_SHOW_NEW_HOURS + newHours);
+ try {
+ configParameter = getConfig();
+ configParameter.setRecommendedHours(newHours);
+ saveConfigParameter(configParameter);
+ } catch (StorageCorruptedException e) {
+ // This should not occur at this point in time
+ userInterface.showToUser(Messages.MESSAGE_STORAGE_CORRUPTED);
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/model/contact/Contact.java b/src/main/java/seedu/duke/model/contact/Contact.java
new file mode 100644
index 0000000000..f60c0d820a
--- /dev/null
+++ b/src/main/java/seedu/duke/model/contact/Contact.java
@@ -0,0 +1,47 @@
+package seedu.duke.model.contact;
+
+//@@author untitle4
+/**
+ * Class class
+ * Contains details of contact.
+ */
+public class Contact {
+ private final String subject;
+ private final String name;
+ private final String phoneNumber;
+ private final String email;
+
+ public Contact(String subject, String name, String phoneNumber, String email) {
+ this.subject = subject;
+ this.name = name;
+ this.phoneNumber = phoneNumber;
+ this.email = email;
+ }
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getPhoneNumber() {
+ return phoneNumber;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ @Override
+ public String toString() {
+ return subject + " teacher: " + name
+ + "\nPhone number: " + phoneNumber
+ + "\nemail address: " + email + "\n";
+ }
+
+ public String convertToData() {
+ return subject + "|" + name + "|" + phoneNumber + "|" + email;
+ }
+}
diff --git a/src/main/java/seedu/duke/model/contact/ContactInteractable.java b/src/main/java/seedu/duke/model/contact/ContactInteractable.java
new file mode 100644
index 0000000000..27e18270c0
--- /dev/null
+++ b/src/main/java/seedu/duke/model/contact/ContactInteractable.java
@@ -0,0 +1,23 @@
+package seedu.duke.model.contact;
+
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.model.Interactable;
+
+//@@author AndreWongZH
+/**
+ * Represents the public api methods for ContactManager that the controller can call.
+ */
+public interface ContactInteractable extends Interactable {
+ /**
+ * List all contacts in ContactManager.
+ */
+ void list();
+
+ /**
+ * Find a list of contacts that matches with the keyword.
+ *
+ * @param userInput The input entered by the user.
+ * @throws MissingParameterException If keyword is missing from the command.
+ */
+ void find(String userInput) throws MissingParameterException;
+}
diff --git a/src/main/java/seedu/duke/model/contact/ContactManager.java b/src/main/java/seedu/duke/model/contact/ContactManager.java
new file mode 100644
index 0000000000..3f9db6746c
--- /dev/null
+++ b/src/main/java/seedu/duke/model/contact/ContactManager.java
@@ -0,0 +1,212 @@
+package seedu.duke.model.contact;
+
+import seedu.duke.common.LogManager;
+import seedu.duke.common.Messages;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.model.ModelManager;
+import seedu.duke.ui.UserInterface;
+
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class ContactManager extends ModelManager implements ContactInteractable {
+ public static final int BEGIN_INDEX = 0;
+ public static final int END_INDEX = 1;
+ private static final int USER_INPUT_OFFSET = 12;
+ private static final int EMPTY_SIZE = 0;
+ private static final String INPUT_SPACE = " ";
+ private static final int S_INDEX = 1;
+ private static final int N_INDEX = 2;
+ private static final int P_INDEX = 3;
+ private static final int E_INDEX = 4;
+ private static final String S_PREFIX = "s";
+ private static final String N_PREFIX = "n";
+ private static final String P_PREFIX = "p";
+ private static final String E_PREFIX = "e";
+ private final ArrayList contacts;
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+ private final UserInterface userInterface;
+
+ public ContactManager(ArrayList contacts) {
+ userInterface = UserInterface.getInstance();
+ this.contacts = contacts;
+ }
+
+ public int getContactListSize() {
+ return contacts.size();
+ }
+
+ public ArrayList getContacts() {
+ return contacts;
+ }
+
+ //@@author untitle4
+ /**
+ * Adds a contact with provided details.
+ * @param userInput The input entered by the user.
+ * @throws EmptyParameterException if no parameter are provided for each section.
+ * @throws MissingParameterException if sections symbols are missing.
+ */
+ @Override
+ public void add(String userInput) throws EmptyParameterException, MissingParameterException,
+ SwappedParameterException {
+ if (!userInput.contains("/s") || !userInput.contains("/n")
+ || !userInput.contains("/p") || !userInput.contains("/e")) {
+ throw new MissingParameterException("'/s', '/n', '/p' and '/e'");
+ }
+
+ String[] separatedInputs = userInput.trim().split("/");
+
+ validateSwappedParameters(separatedInputs);
+
+ logger.log(Level.INFO, "splitting user input into subject, name, phone number"
+ + "and email address.");
+ String subject = separatedInputs[1].substring(1).trim();
+ String name = separatedInputs[2].substring(1).trim();
+ String phoneNumber = separatedInputs[3].substring(1).trim();
+ String emailAddress = separatedInputs[4].substring(1).trim();
+
+ if (subject.equals("") || name.equals("")
+ || phoneNumber.equals("") || emailAddress.equals("")) {
+ logger.log(Level.WARNING, "subject/name/phone number/email address is empty");
+ throw new EmptyParameterException();
+ }
+
+ contacts.add(new Contact(subject, name, phoneNumber, emailAddress));
+
+ userInterface.showToUser(Messages.MESSAGE_CONTACT_ADD_SUCCESS,
+ contacts.get(getContactListSize() - 1).toString());
+ getContactStatement();
+ }
+
+ //@@author untitle4
+ /**
+ * Delete a contact indicated by the user input.
+ * @param userInput the input provided by the user.
+ * @throws IndexOutOfBoundsException if there is not such a contact in the list.
+ */
+ @Override
+ public void delete(String[] userInput) throws IndexOutOfBoundsException {
+ int contactIndex;
+
+ try {
+ contactIndex = Integer.parseInt(userInput[2]);
+ } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
+ userInterface.showToUser(Messages.MESSAGE_CONTACT_DELETE_ERROR_NON_NUMBER);
+ return;
+ } catch (IndexOutOfBoundsException e) {
+ userInterface.showToUser(Messages.MESSAGE_CONTACT_DELETE_ERROR_NO_NUMBER_GIVEN);
+ return;
+ }
+
+ if ((contactIndex <= 0) || (contactIndex > getContactListSize())) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ userInterface.showToUser(Messages.MESSAGE_CONTACT_DELETE_SUCCESS,
+ contacts.get(contactIndex - 1).toString());
+
+ contacts.remove(contactIndex - 1);
+ getContactStatement();
+ }
+
+ //@@author untitle4
+ /**
+ * Provide a list of contacts for the user.
+ */
+ @Override
+ public void list() {
+ if (contacts.size() == 0) {
+ userInterface.showToUser(Messages.MESSAGE_EMPTY_CONTACT_LIST);
+ } else {
+ for (int i = 0; i < getContactListSize(); i++) {
+ userInterface.showToUser("Contact " + (i + 1) + ":",
+ contacts.get(i).toString());
+ }
+ }
+ }
+
+ //@@author AndreWongZH
+ /**
+ * Prints to user all the found events that matches with keyword provided.
+ *
+ * @param userInput The input entered by the user.
+ * @throws MissingParameterException If input supplied does not contain any keywords.
+ */
+ @Override
+ public void find(String userInput) throws MissingParameterException {
+ String param = userInput.substring(USER_INPUT_OFFSET).trim();
+
+ if (param.length() == EMPTY_SIZE) {
+ throw new MissingParameterException("keywords as");
+ }
+
+ ArrayList filteredContacts = filterContacts(userInput);
+
+ if (filteredContacts.size() == EMPTY_SIZE) {
+ userInterface.showToUser(Messages.MESSAGE_NO_EVENTS_FOUND);
+ return;
+ }
+
+ userInterface.printArray(filteredContacts);
+ }
+
+ //@@author AndreWongZH
+ /**
+ * Searches for a match in the contacts list against the keyword.
+ *
+ * @param userInput The input entered by the user.
+ * @return An ArrayList of contacts after filtering.
+ */
+ private ArrayList filterContacts(String userInput) {
+ ArrayList filteredContacts = new ArrayList<>();
+ String[] separatedInputs = userInput.split(INPUT_SPACE);
+
+ for (Contact contact : contacts) {
+ for (String keyword: separatedInputs) {
+ keyword = keyword.toLowerCase();
+ boolean matchName = contact.getName().toLowerCase().contains(keyword);
+ boolean matchEmail = contact.getEmail().toLowerCase().contains(keyword);
+ boolean matchSubject = contact.getSubject().toLowerCase().contains(keyword);
+ boolean matchPhoneNumber = contact.getPhoneNumber().toLowerCase().contains(keyword);
+
+ if (matchName || matchEmail || matchSubject || matchPhoneNumber) {
+ filteredContacts.add(contact.toString());
+ break;
+ }
+ }
+ }
+
+ return filteredContacts;
+ }
+
+ //@@author AndreWongZH
+ /**
+ * Validates if the parameters are swapped.
+ *
+ * @param userInputs An arraylist of type string of the user input.
+ * @throws SwappedParameterException If letter does not match up with the required prefix.
+ */
+ private void validateSwappedParameters(String[] userInputs) throws SwappedParameterException {
+ boolean hasS = userInputs[S_INDEX].substring(BEGIN_INDEX, END_INDEX).contentEquals(S_PREFIX);
+ boolean hasN = userInputs[N_INDEX].substring(BEGIN_INDEX, END_INDEX).contentEquals(N_PREFIX);
+ boolean hasP = userInputs[P_INDEX].substring(BEGIN_INDEX, END_INDEX).contentEquals(P_PREFIX);
+ boolean hasE = userInputs[E_INDEX].substring(BEGIN_INDEX, END_INDEX).contentEquals(E_PREFIX);
+
+ if (!hasS || !hasN || !hasP || !hasE) {
+ throw new SwappedParameterException();
+ }
+ }
+
+ //@@author untitle4
+ /**
+ * A simple method to show contact(s) in the text box.
+ */
+ private void getContactStatement() {
+ String contactStatement = getContactListSize() <= 1 ? " contact" : " contacts";
+ userInterface.showToUser("Now you have " + getContactListSize() + contactStatement + " in your list.");
+ }
+}
diff --git a/src/main/java/seedu/duke/model/event/Event.java b/src/main/java/seedu/duke/model/event/Event.java
new file mode 100644
index 0000000000..8cde4a6dff
--- /dev/null
+++ b/src/main/java/seedu/duke/model/event/Event.java
@@ -0,0 +1,82 @@
+package seedu.duke.model.event;
+
+import java.util.Calendar;
+
+//@@author elizabethcwt
+public class Event implements Comparable {
+
+ protected String description;
+ protected boolean isDone;
+ protected Calendar start;
+ protected Calendar end;
+
+ public Event(String description, Calendar start, Calendar end) {
+ this.description = description;
+ this.start = start;
+ this.end = end;
+ isDone = false;
+ }
+
+ //@@author durianpancakes
+ public Event(String description, Calendar start, Calendar end, boolean isDone) {
+ this.description = description;
+ this.start = start;
+ this.end = end;
+ this.isDone = isDone;
+ }
+
+ //@@author
+ public String getIcon() {
+ return null;
+ }
+
+ public Calendar getStart() {
+ return start;
+ }
+
+ public Calendar getEnd() {
+ return end;
+ }
+
+ public boolean isDone() {
+ return isDone;
+ }
+
+ public void setDone() {
+ this.isDone = true;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ @Override
+ public String toString() {
+ return description;
+ }
+
+ //@@author durianpancakes
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+
+ if (obj == null || obj.getClass() != this.getClass()) {
+ return false;
+ }
+
+ Event otherEvent = (Event) obj;
+ return this.description.equals(otherEvent.description)
+ && this.isDone == otherEvent.isDone();
+ }
+
+ @Override
+ public int compareTo(Event otherEvent) {
+ if (this.getStart().before(otherEvent.start)) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/model/event/EventDataManager.java b/src/main/java/seedu/duke/model/event/EventDataManager.java
new file mode 100644
index 0000000000..b4186c22ff
--- /dev/null
+++ b/src/main/java/seedu/duke/model/event/EventDataManager.java
@@ -0,0 +1,43 @@
+package seedu.duke.model.event;
+
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.model.ModelManager;
+import seedu.duke.model.event.tuition.EventTuitionManager;
+
+//@@author AndreWongZH
+/**
+ * Represents the model class which is inherited by Class, Test, CCA, Tuition Manager.
+ */
+public abstract class EventDataManager extends ModelManager implements EventInteractable {
+ public static final int BEGIN_INDEX = 0;
+ public static final int END_INDEX = 1;
+ public static final String N_PREFIX = "n";
+ public static final String S_PREFIX = "s";
+ public static final String E_PREFIX = "e";
+ public static final String L_PREFIX = "l";
+ public static final int N_INDEX = 1;
+ public static final int S_INDEX = 2;
+ public static final int E_INDEX = 3;
+ public static final int L_INDEX = 4;
+
+ /**
+ * Validates if the parameters are swapped.
+ *
+ * @param userInputs An arraylist of type string of the user input.
+ * @throws SwappedParameterException If letter does not match up with the required prefix.
+ */
+ protected void validateSwappedParameters(String[] userInputs) throws SwappedParameterException {
+ boolean hasN = userInputs[N_INDEX].substring(BEGIN_INDEX, END_INDEX).contentEquals(N_PREFIX);
+ boolean hasS = userInputs[S_INDEX].substring(BEGIN_INDEX, END_INDEX).contentEquals(S_PREFIX);
+ boolean hasE = userInputs[E_INDEX].substring(BEGIN_INDEX, END_INDEX).contentEquals(E_PREFIX);
+ boolean hasL = true;
+
+ if (this instanceof EventTuitionManager) {
+ hasL = userInputs[L_INDEX].substring(BEGIN_INDEX, END_INDEX).contentEquals(L_PREFIX);
+ }
+
+ if (!hasN || !hasS || !hasE || !hasL) {
+ throw new SwappedParameterException();
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/model/event/EventInteractable.java b/src/main/java/seedu/duke/model/event/EventInteractable.java
new file mode 100644
index 0000000000..3de06df1bd
--- /dev/null
+++ b/src/main/java/seedu/duke/model/event/EventInteractable.java
@@ -0,0 +1,16 @@
+package seedu.duke.model.event;
+
+import seedu.duke.model.Interactable;
+
+//@@author AndreWongZH
+/**
+ * Represents the public api methods for Class, CCA, Test and Tuition Manager that the controller can call.
+ */
+public interface EventInteractable extends Interactable {
+ /**
+ * Set a particular event to be done.
+ *
+ * @param userInputs The input entered by the user.
+ */
+ void setDone(String[] userInputs);
+}
diff --git a/src/main/java/seedu/duke/model/event/EventManager.java b/src/main/java/seedu/duke/model/event/EventManager.java
new file mode 100644
index 0000000000..23603b3a14
--- /dev/null
+++ b/src/main/java/seedu/duke/model/event/EventManager.java
@@ -0,0 +1,350 @@
+package seedu.duke.model.event;
+
+import seedu.duke.common.LogManager;
+import seedu.duke.controller.parser.DateTimeParser;
+import seedu.duke.exception.EmptyListException;
+import seedu.duke.exception.InvalidDateException;
+import seedu.duke.exception.InvalidDateType;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.model.ConfigParameter;
+import seedu.duke.model.ModelMain;
+import seedu.duke.model.event.cca.EventCcaManager;
+import seedu.duke.model.event.classlesson.EventClassManager;
+import seedu.duke.common.Messages;
+import seedu.duke.model.event.test.EventTestManager;
+import seedu.duke.model.event.tuition.EventTuitionManager;
+import seedu.duke.ui.UserInterface;
+
+import java.text.ParseException;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+//@@author AndreWongZH
+/**
+ * Represents a handler that manages the four different event managers.
+ * This provides access to each individual event managers and
+ * also performs listing and searches for the entire events data set.
+ */
+public class EventManager extends ModelMain implements EventManagerInteractable {
+ public static final int EMPTY_SIZE = 0;
+ public static final int USER_INPUT_OFFSET = 10;
+ public static final String INPUT_SPACE = " ";
+ public static final String INPUT_WEEK = "week";
+ public static final int INPUT_LENGTH_NO_PARAMS = 2;
+ public static final int INPUT_LENGTH_ONE_PARAM = 3;
+ public static final int DATE_PARAM_INDEX = 2;
+ public static final String INPUT_NEXT_WEEK = "nextweek";
+
+ private static EventClassManager eventClassManager;
+ private static EventTestManager eventTestManager;
+ private static EventCcaManager eventCcaManager;
+ private static EventTuitionManager eventTuitionManager;
+ private final ConfigParameter configParameter;
+ private final UserInterface userInterface;
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+ DateTimeParser dateTimeParser = new DateTimeParser();
+
+ public EventManager(EventParameter eventParameter, ConfigParameter configParameter) {
+ eventClassManager = new EventClassManager(eventParameter.getClasses(), this);
+ eventTestManager = new EventTestManager(eventParameter.getTests(), this);
+ eventCcaManager = new EventCcaManager(eventParameter.getCcas(), this);
+ eventTuitionManager = new EventTuitionManager(eventParameter.getTuitions(), this);
+ userInterface = UserInterface.getInstance();
+ this.configParameter = configParameter;
+ }
+
+ public EventClassManager getClassManager() {
+ return eventClassManager;
+ }
+
+ public EventTestManager getTestManager() {
+ return eventTestManager;
+ }
+
+ public EventCcaManager getCcaManager() {
+ return eventCcaManager;
+ }
+
+ public EventTuitionManager getTuitionManager() {
+ return eventTuitionManager;
+ }
+
+ //@@author AndreWongZH
+ /**
+ * Prints to user all the found events that matches with keyword provided.
+ *
+ * @param userInput Input supplied by the user that contains the keywords.
+ * @throws MissingParameterException If input supplied does not contain any keywords.
+ */
+ @Override
+ public void find(String userInput) throws MissingParameterException {
+ String param = userInput.substring(USER_INPUT_OFFSET).trim();
+
+ if (param.length() == EMPTY_SIZE) {
+ throw new MissingParameterException("keywords as");
+ }
+
+ FindSchedule findSchedule = new FindSchedule(param, eventClassManager.getClasses(),
+ eventCcaManager.getCcas(), eventTestManager.getTests(), eventTuitionManager.getTuitions());
+ ArrayList filteredEvents = findSchedule.getFilteredEvents();
+
+ if (filteredEvents.size() == EMPTY_SIZE) {
+ userInterface.showToUser(Messages.MESSAGE_NO_EVENTS_FOUND);
+ return;
+ }
+
+ userInterface.printArray(filteredEvents);
+ }
+
+ //@@author AndreWongZH
+ /**
+ * Lists out user's events based on everything, date or week.
+ * Checks if there are any extra parameters and will inform user if there is.
+ * The dateParam here passed into listSchedule can be null to indicate list everything.
+ *
+ * @param userInput The input entered by the user.
+ */
+ @Override
+ public void list(String userInput) {
+ try {
+ String[] separatedInputs = userInput.split(INPUT_SPACE);
+
+ // check if user entered extra parameters
+ if (separatedInputs.length > INPUT_LENGTH_ONE_PARAM) {
+ userInterface.showToUser(Messages.MESSAGE_INVALID_EXTRA_PARAM);
+ return;
+ }
+
+ String dateParam = separatedInputs.length == INPUT_LENGTH_NO_PARAMS ? null
+ : separatedInputs[DATE_PARAM_INDEX];
+
+ ListSchedule listSchedule = new ListSchedule(dateParam, eventClassManager.getClasses(),
+ eventCcaManager.getCcas(), eventTestManager.getTests(),
+ eventTuitionManager.getTuitions(), configParameter);
+
+ printToUser(separatedInputs, listSchedule);
+ } catch (EmptyListException e) {
+ userInterface.showToUser(String.format(Messages.MESSAGE_EMPTY_SCHEDULE_LIST, e.getMessage()));
+ } catch (DateTimeParseException e) {
+ userInterface.showToUser(Messages.MESSAGE_LIST_INVALID_DATE);
+ } catch (ParseException e) {
+ logger.log(Level.WARNING, "valid datetime not inputted");
+ userInterface.showToUser(Messages.MESSAGE_LIST_INVALID_DATE);
+ }
+ }
+
+ /** Prints list to user based on the different inputs. */
+ private void printToUser(String[] separatedInputs, ListSchedule listSchedule)
+ throws EmptyListException, ParseException {
+ ArrayList printedEvents;
+
+ if (separatedInputs.length == INPUT_LENGTH_ONE_PARAM
+ && separatedInputs[DATE_PARAM_INDEX].contentEquals(INPUT_NEXT_WEEK)) {
+ userInterface.printWeekSchedule(this, ListWeekCommand.NEXT_WEEK);
+ } else if (separatedInputs.length == INPUT_LENGTH_ONE_PARAM
+ && separatedInputs[DATE_PARAM_INDEX].contentEquals(INPUT_WEEK)) {
+ userInterface.printWeekSchedule(this, ListWeekCommand.CURRENT_WEEK);
+ } else {
+ printedEvents = listSchedule.getPrintableEvents();
+ userInterface.printArray(printedEvents);
+ }
+ }
+
+ //@@author durianpancakes
+ public ArrayList> getCurrentWeekEventMasterList() {
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ ArrayList eventMasterList = getEventMasterList();
+ ArrayList daysOfWeek = dateTimeParser.getCurrentDaysOfWeek();
+ ArrayList> result = new ArrayList<>();
+
+ for (int i = 0; i < 7; i++) {
+ result.add(getDayEventList(eventMasterList, daysOfWeek.get(i)));
+ }
+
+ return result;
+ }
+
+ public ArrayList> getNextWeekEventMasterList() {
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ ArrayList eventMasterList = getEventMasterList();
+ ArrayList daysOfWeek = dateTimeParser.getNextDaysOfWeek();
+ ArrayList> result = new ArrayList<>();
+
+ for (int i = 0; i < 7; i++) {
+ result.add(getDayEventList(eventMasterList, daysOfWeek.get(i)));
+ }
+
+ return result;
+ }
+
+ //@@author Aliciaho
+ /**
+ * Adds the relevant events whose date correspond to the date inputted in the masterList.
+ *
+ * @param masterList ArrayList containing all the events
+ * @param date Date inputted to filter out the corresponding events
+ * @return result ArrayList contain the relevant events for that date
+ */
+ private ArrayList getDayEventList(ArrayList masterList, Calendar date) {
+ assert masterList.size() >= 0;
+ assert date != null;
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ ArrayList result = new ArrayList<>();
+
+ for (Event event : masterList) {
+ Calendar startCalendar = event.getStart();
+ if (dateTimeParser.isDateEqual(date, startCalendar)) {
+ result.add(event);
+ }
+ }
+
+ return result;
+ }
+
+ //@@author Aliciaho
+ /**
+ * Adds all the ccas, classes, tests and tuitions into one Master ArrayList.
+ *
+ * @return masterList ArrayList containing all the events
+ */
+ public ArrayList getEventMasterList() {
+ logger.log(Level.INFO, "getting all ccas, classes, tests and tuitions");
+ ArrayList ccas = eventCcaManager.getCcas();
+ ArrayList tests = eventTestManager.getTests();
+ ArrayList classes = eventClassManager.getClasses();
+ ArrayList tuitions = eventTuitionManager.getTuitions();
+
+ ArrayList masterList = new ArrayList<>(ccas);
+ masterList.addAll(tests);
+ masterList.addAll(classes);
+ masterList.addAll(tuitions);
+ logger.log(Level.INFO, "added all ccas, classes, tests and tuitions");
+
+ return masterList;
+ }
+
+ //@@author durianpancakes
+ /**
+ * Checks if the input Event clashes with the master list of Events.
+ *
+ * @param event Event input from user.
+ * @return ArrayList of Events that clashes with the input Event.
+ */
+ public ArrayList checkEventClash(Event event) {
+ ArrayList relevantEvents = getDayEventList(getEventMasterList(), event.getStart());
+ ArrayList results = new ArrayList<>();
+
+ for (Event e : relevantEvents) {
+ if (isTimeClash(e, event)) {
+ results.add(e);
+ }
+ }
+
+ return results;
+ }
+
+ /**
+ * Prints out the right error message for the event that is invalid.
+ *
+ * @param errorCode InvalidDateType START_AFTER_END or START_EQUALS_END.
+ */
+ public void processInvalidDateException(InvalidDateType errorCode) {
+ switch (errorCode) {
+ case START_AFTER_END:
+ userInterface.showToUser(Messages.MESSAGE_ERROR_START_AFTER_END);
+ break;
+ case START_EQUALS_END:
+ userInterface.showToUser(Messages.MESSAGE_ERROR_EQUALS_END);
+ break;
+ default:
+ // No default cases needed here
+ }
+ }
+
+ // Check if start time given is before end time
+ public void checkValidTimeGiven(Event inputEvent) throws InvalidDateException {
+ Calendar startCalendar = inputEvent.getStart();
+ Calendar endCalendar = inputEvent.getEnd();
+
+ if (startCalendar.equals(endCalendar)) {
+ throw new InvalidDateException(InvalidDateType.START_EQUALS_END);
+ }
+
+ if (startCalendar.after(endCalendar)) {
+ throw new InvalidDateException(InvalidDateType.START_AFTER_END);
+ }
+ }
+
+ // Check if there are any clashes with other events
+ private boolean isTimeClash(Event referenceEvent, Event inputEvent) {
+ Calendar startInputCalendar = inputEvent.getStart();
+ Calendar endInputCalendar = inputEvent.getEnd();
+ Calendar startReferenceCalendar = referenceEvent.getStart();
+ Calendar endReferenceCalendar = referenceEvent.getEnd();
+
+ if (startInputCalendar.after(startReferenceCalendar)
+ && startInputCalendar.before(endReferenceCalendar)) {
+ return true;
+ }
+
+ if (endInputCalendar.after((startReferenceCalendar))
+ && endInputCalendar.before(endReferenceCalendar)) {
+ return true;
+ }
+
+ return startInputCalendar.equals(startReferenceCalendar)
+ || endInputCalendar.equals(endReferenceCalendar);
+ }
+
+ /**
+ * Checks if the recommended time for that day exceeded.
+ *
+ * @param event Event that user is trying to add
+ * @return true if the time did exceed, vice versa.
+ */
+ public boolean didTimeExceed(Event event) {
+ logger.log(Level.INFO, "checking if time exceeded");
+ ArrayList eventArrayList = getEventMasterList();
+ long noOfMinutes = dateTimeParser.getDuration(event.getStart(),event.getEnd());
+ noOfMinutes += getNoOfMinutes(event, eventArrayList);
+ return noOfMinutes > (configParameter.getRecommendedHours() * 60);
+ }
+
+ /**
+ * Get the total number of productive minutes for a particular day.
+ *
+ * @param event Event that user is trying to add
+ * @param eventArrayList Master List containing all the events
+ * @return total number of minutes for that day
+ */
+ private long getNoOfMinutes(Event event, ArrayList eventArrayList) {
+ long noOfMinutes = 0;
+ for (Event value : eventArrayList) {
+ if (dateTimeParser.isDateEqual(value.getStart(),
+ event.getStart())) {
+ noOfMinutes += dateTimeParser.getDuration(value.getStart(),
+ value.getEnd());
+ }
+ }
+ return noOfMinutes;
+ }
+
+ /**
+ * Get the time left for each day.
+ *
+ * @param event Event that user is trying to add
+ * @return string containing the time left for that particular day
+ */
+ public String getTimeLeft(Event event) {
+ logger.log(Level.INFO, "checking time left for that day");
+ ArrayList eventArrayList = getEventMasterList();
+ long noOfMinutes = getNoOfMinutes(event, eventArrayList);
+ long noOfMinutesLeft = (configParameter.getRecommendedHours() * 60) - noOfMinutes;
+ int hoursLeft = Math.toIntExact(noOfMinutesLeft / 60);
+ int actualNoOfMinutesLeft = Math.toIntExact(noOfMinutesLeft - (hoursLeft * 60));
+ return hoursLeft + "hr " + actualNoOfMinutesLeft + "mins";
+ }
+}
diff --git a/src/main/java/seedu/duke/model/event/EventManagerInteractable.java b/src/main/java/seedu/duke/model/event/EventManagerInteractable.java
new file mode 100644
index 0000000000..cfd5ea77ef
--- /dev/null
+++ b/src/main/java/seedu/duke/model/event/EventManagerInteractable.java
@@ -0,0 +1,24 @@
+package seedu.duke.model.event;
+
+import seedu.duke.exception.MissingParameterException;
+
+//@@author AndreWongZH
+/**
+ * Represents the public api methods for EventManager that the controller can call.
+ */
+public interface EventManagerInteractable {
+ /**
+ * List all events in EventManager.
+ *
+ * @param userInput The input entered by the user.
+ */
+ void list(String userInput);
+
+ /**
+ * Find a list of events that matches with the keyword.
+ *
+ * @param userInput The input entered by the user.
+ * @throws MissingParameterException If keyword is missing from the command.
+ */
+ void find(String userInput) throws MissingParameterException;
+}
diff --git a/src/main/java/seedu/duke/model/event/EventParameter.java b/src/main/java/seedu/duke/model/event/EventParameter.java
new file mode 100644
index 0000000000..205854f27b
--- /dev/null
+++ b/src/main/java/seedu/duke/model/event/EventParameter.java
@@ -0,0 +1,45 @@
+package seedu.duke.model.event;
+
+import java.util.ArrayList;
+
+//@@author AndreWongZH
+/**
+ * Represents the data that is returned when EventStorageManager loads data.
+ */
+public class EventParameter {
+ private final ArrayList ccas;
+ private final ArrayList tests;
+ private final ArrayList classes;
+ private final ArrayList tuitions;
+
+ public EventParameter() {
+ ccas = new ArrayList<>();
+ tests = new ArrayList<>();
+ classes = new ArrayList<>();
+ tuitions = new ArrayList<>();
+ }
+
+ public EventParameter(ArrayList ccas, ArrayList tests,
+ ArrayList classes, ArrayList tuitions) {
+ this.ccas = ccas;
+ this.tests = tests;
+ this.classes = classes;
+ this.tuitions = tuitions;
+ }
+
+ public ArrayList getCcas() {
+ return ccas;
+ }
+
+ public ArrayList getTests() {
+ return tests;
+ }
+
+ public ArrayList getClasses() {
+ return classes;
+ }
+
+ public ArrayList getTuitions() {
+ return tuitions;
+ }
+}
diff --git a/src/main/java/seedu/duke/model/event/FindSchedule.java b/src/main/java/seedu/duke/model/event/FindSchedule.java
new file mode 100644
index 0000000000..e1fc52d99b
--- /dev/null
+++ b/src/main/java/seedu/duke/model/event/FindSchedule.java
@@ -0,0 +1,74 @@
+package seedu.duke.model.event;
+
+import seedu.duke.common.LogManager;
+
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+//@@author AndreWongZH
+/**
+ * Represents a process dedicated to filter out events based on given keywords.
+ */
+public class FindSchedule {
+ public static final String INPUT_SPACE = " ";
+ private final ArrayList classes;
+ private final ArrayList ccas;
+ private final ArrayList tests;
+ private final ArrayList tuitions;
+
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+ private final String userInput;
+ private final ArrayList filteredEvents;
+
+ public FindSchedule(String userInput, ArrayList classes, ArrayList ccas,
+ ArrayList tests, ArrayList tuitions) {
+ assert userInput.length() != 0 : "user input should not be missing";
+ this.userInput = userInput;
+ this.classes = classes;
+ this.ccas = ccas;
+ this.tests = tests;
+ this.tuitions = tuitions;
+ filteredEvents = new ArrayList<>();
+ }
+
+ /**
+ * Returns all the events whose description matches with the provided keywords.
+ * Merges all the event types into one single arraylist.
+ *
+ * @return An Arraylist of type String to be printed out.
+ */
+ public ArrayList getFilteredEvents() {
+ logger.log(Level.INFO, "Combining all arraylist into one main arraylist");
+ ArrayList events = new ArrayList<>();
+ events.addAll(classes);
+ events.addAll(ccas);
+ events.addAll(tests);
+ events.addAll(tuitions);
+
+ filterEvents(events);
+
+ return filteredEvents;
+ }
+
+ /**
+ * Loops through event list to checks if the event's description matches with the keywords.
+ * Both strings are first converted to lowercase before comparison.
+ * If description matches, add the event into the filteredEvents arraylist.
+ *
+ * @param events An ArrayList of type Event to be checked against
+ */
+ private void filterEvents(ArrayList events) {
+ logger.log(Level.INFO, "loop through all the keywords to check if in event description");
+ String[] separatedInputs = userInput.split(INPUT_SPACE);
+
+ for (Event event : events) {
+ for (String keyword : separatedInputs) {
+ if (event.getDescription().toLowerCase().contains(keyword.toLowerCase())) {
+ filteredEvents.add(event.toString());
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/model/event/ListSchedule.java b/src/main/java/seedu/duke/model/event/ListSchedule.java
new file mode 100644
index 0000000000..cbb1a2ef8e
--- /dev/null
+++ b/src/main/java/seedu/duke/model/event/ListSchedule.java
@@ -0,0 +1,176 @@
+package seedu.duke.model.event;
+
+import seedu.duke.common.LogManager;
+import seedu.duke.exception.EmptyListException;
+import seedu.duke.controller.parser.DateTimeParser;
+import seedu.duke.model.ConfigParameter;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+//@@author AndreWongZH
+/**
+ * Represents the process of filtering events by date and getting all the required events for listing.
+ */
+public class ListSchedule {
+ public static final int DURATION_MINUTES = 60;
+ public static final int EMPTY_LIST_SIZE = 0;
+ private final ArrayList classes;
+ private final ArrayList ccas;
+ private final ArrayList tests;
+ private final ArrayList tuitions;
+
+ private static final String CATEGORY_TUITIONS = "Tuitions: ";
+ private static final String CATEGORY_CLASSES = "Classes: ";
+ private static final String CATEGORY_TESTS = "Tests: ";
+ private static final String CATEGORY_CCAS = "CCAs: ";
+
+ private final DateTimeParser dateTimeParser = new DateTimeParser();
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+
+ private final String userInput;
+ private Calendar inputCalendar;
+ private long totalDuration = 0;
+ private final ConfigParameter configParameter;
+
+ public ListSchedule(String userInput, ArrayList classes, ArrayList ccas,
+ ArrayList tests, ArrayList tuitions, ConfigParameter configParameter) {
+ this.userInput = userInput;
+ this.classes = classes;
+ this.ccas = ccas;
+ this.tests = tests;
+ this.tuitions = tuitions;
+ this.configParameter = configParameter;
+ }
+
+ /**
+ * Returns an ArrayList of type string to be printed to user.
+ * If user input is null then get all events to be printed.
+ * If user input is not null then convert it into a calendar object before filtering by date.
+ *
+ * @return An ArrayList of string to be printed out.
+ * @throws EmptyListException If the array list to be returned is empty.
+ * @throws ParseException If user input cannot be able to covert into calendar object.
+ */
+ public ArrayList getPrintableEvents() throws EmptyListException, ParseException {
+ logger.log(Level.INFO, "starting to convert events instance to strings");
+ ArrayList printedEvents = new ArrayList<>();
+ ArrayList printedClasses;
+ ArrayList printedTests;
+ ArrayList printedCcas;
+ ArrayList printedTuitions;
+
+ inputCalendar = checkAndConvertToday();
+
+ if (haveClasses()) {
+ logger.log(Level.INFO, "converting class events");
+ printedClasses = parseEventWithNumberPad(classes);
+ addToMainList(printedClasses, printedEvents, CATEGORY_CLASSES);
+ }
+ if (haveCcas()) {
+ logger.log(Level.INFO, "converting CCA events");
+ printedCcas = parseEventWithNumberPad(ccas);
+ addToMainList(printedCcas, printedEvents, CATEGORY_CCAS);
+ }
+ if (haveTests()) {
+ logger.log(Level.INFO, "converting test events");
+ printedTests = parseEventWithNumberPad(tests);
+ addToMainList(printedTests, printedEvents, CATEGORY_TESTS);
+ }
+ if (haveTuitions()) {
+ logger.log(Level.INFO, "converting tuition events");
+ printedTuitions = parseEventWithNumberPad(tuitions);
+ addToMainList(printedTuitions, printedEvents, CATEGORY_TUITIONS);
+ }
+
+ if (hasNoSchedule() || printedEvents.size() == EMPTY_LIST_SIZE) {
+ logger.log(Level.WARNING, "schedule is empty");
+ if (userInput == null) {
+ throw new EmptyListException("empty");
+ }
+ throw new EmptyListException("not found");
+ }
+
+ if (inputCalendar != null) {
+ long noOfMinutesLeft = (configParameter.getRecommendedHours() * DURATION_MINUTES) - totalDuration;
+ int hoursLeft = Math.toIntExact(noOfMinutesLeft / DURATION_MINUTES);
+ int minsLeft = Math.toIntExact(noOfMinutesLeft - (hoursLeft * DURATION_MINUTES));
+ printedEvents.add("Time left for this day: " + hoursLeft + "hr " + minsLeft + "mins");
+ }
+ return printedEvents;
+ }
+
+ private boolean haveClasses() {
+ return classes.size() != EMPTY_LIST_SIZE;
+ }
+
+ private boolean haveCcas() {
+ return ccas.size() != EMPTY_LIST_SIZE;
+ }
+
+ private boolean haveTests() {
+ return tests.size() != EMPTY_LIST_SIZE;
+ }
+
+ private boolean haveTuitions() {
+ return tuitions.size() != EMPTY_LIST_SIZE;
+ }
+
+ private boolean hasNoSchedule() {
+ return (!haveClasses() && !haveCcas() && !haveTests() && !haveTuitions());
+ }
+
+ //@@author Aliciaho
+ /**
+ * If the user input contains today/week, get the date for today.
+ * Else if user input a date, convert the string date to calendar form
+ *
+ * @return resultCalender Calendar containing the resulting output
+ */
+ private Calendar checkAndConvertToday() throws ParseException {
+ Calendar resultCalendar = null;
+ if (userInput != null && (userInput.contentEquals("today") || userInput.contentEquals("week"))) {
+ resultCalendar = Calendar.getInstance();
+ } else if (userInput != null) {
+ resultCalendar = dateTimeParser.convertStringToCalendarByDate(userInput);
+ }
+ return resultCalendar;
+ }
+
+ //@@author Aliciaho
+ /**
+ * Converts event instances into strings representation padded with numbers.
+ * PrintedEvents cannot be null.
+ * EventArr cannot be empty or null.
+ *
+ * @param eventArr Array list of event instances to be converted.
+ * @return printedEvents Array list containing the relevant events in correct output format
+ */
+ private ArrayList parseEventWithNumberPad(ArrayList eventArr) {
+ assert eventArr != null;
+ assert eventArr.size() != 0;
+ ArrayList printedEvents = new ArrayList<>();
+
+ for (int i = 0; i < eventArr.size(); i++) {
+ Calendar listDate = eventArr.get(i).getStart();
+ if (userInput == null) {
+ printedEvents.add(i + 1 + ". " + eventArr.get(i));
+ } else if (dateTimeParser.isDateEqual(listDate, inputCalendar)) {
+ totalDuration += dateTimeParser.getDuration(listDate, eventArr.get(i).getEnd());
+ printedEvents.add(i + 1 + ". " + eventArr.get(i));
+ }
+ }
+
+ return printedEvents;
+ }
+
+ private void addToMainList(ArrayList subEvents, ArrayList printedEvents, String categoryName) {
+ if (subEvents.size() > 0) {
+ printedEvents.add(categoryName);
+ printedEvents.addAll(subEvents);
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/model/event/ListWeekCommand.java b/src/main/java/seedu/duke/model/event/ListWeekCommand.java
new file mode 100644
index 0000000000..dd208029e5
--- /dev/null
+++ b/src/main/java/seedu/duke/model/event/ListWeekCommand.java
@@ -0,0 +1,6 @@
+package seedu.duke.model.event;
+
+//@@author durianpancakes
+public enum ListWeekCommand {
+ CURRENT_WEEK, NEXT_WEEK
+}
diff --git a/src/main/java/seedu/duke/model/event/cca/EventCca.java b/src/main/java/seedu/duke/model/event/cca/EventCca.java
new file mode 100644
index 0000000000..04d09dbec0
--- /dev/null
+++ b/src/main/java/seedu/duke/model/event/cca/EventCca.java
@@ -0,0 +1,47 @@
+package seedu.duke.model.event.cca;
+
+import seedu.duke.model.event.Event;
+
+import seedu.duke.controller.parser.DateTimeParser;
+
+import java.util.Calendar;
+
+//@@author untitle4
+/**
+ * A class for event cca.
+ */
+public class EventCca extends Event {
+ public static final String CCA_ICON = "[CCA]";
+
+ public EventCca(String description, Calendar start, Calendar end) {
+ super(description, start, end);
+ }
+
+ public EventCca(String description, boolean isDone, Calendar start, Calendar end) {
+ super(description, start, end, isDone);
+ }
+
+ @Override
+ public String getIcon() {
+ return CCA_ICON;
+ }
+
+ @Override
+ public String toString() {
+ String result;
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ result = CCA_ICON + " " + super.toString() + " from "
+ + dateTimeParser.obtainFormattedDateTimeString(this.getStart())
+ + " to " + dateTimeParser.obtainFormattedDateTimeString(this.getEnd());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ EventCca otherEventCca = (EventCca) obj;
+
+ return super.equals(obj)
+ && this.getStart().equals(otherEventCca.getStart())
+ && this.getEnd().equals(otherEventCca.getEnd());
+ }
+}
diff --git a/src/main/java/seedu/duke/model/event/cca/EventCcaManager.java b/src/main/java/seedu/duke/model/event/cca/EventCcaManager.java
new file mode 100644
index 0000000000..c6140e3cbf
--- /dev/null
+++ b/src/main/java/seedu/duke/model/event/cca/EventCcaManager.java
@@ -0,0 +1,187 @@
+package seedu.duke.model.event.cca;
+
+import seedu.duke.controller.parser.DateTimeParser;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.InvalidDateException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.model.event.Event;
+import seedu.duke.common.LogManager;
+import seedu.duke.common.Messages;
+import seedu.duke.model.event.EventDataManager;
+import seedu.duke.model.event.EventManager;
+import seedu.duke.ui.UserInterface;
+
+import java.text.ParseException;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+//@@author untitle4
+/**
+ * A manager of cca that executes all the commands related to cca.
+ */
+public class EventCcaManager extends EventDataManager {
+ private final ArrayList ccas;
+ private final EventManager eventManager;
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+ private final UserInterface userInterface;
+
+ public EventCcaManager(ArrayList inputList, EventManager eventManager) {
+ ccas = inputList;
+ this.eventManager = eventManager;
+ userInterface = UserInterface.getInstance();
+ }
+
+ public ArrayList getCcas() {
+ return ccas;
+ }
+
+ public int getCcaListSize() {
+ assert ccas != null;
+ return ccas.size();
+ }
+
+ /**
+ * Add a cca from the user input.
+ * Convert the day-time format to system-recognized.
+ * @param userInput The input entered by the user.
+ * @throws MissingParameterException if symbols of params are missing.
+ * @throws EmptyParameterException if no parameters are provided.
+ */
+ @Override
+ public void add(String userInput) throws MissingParameterException, EmptyParameterException,
+ SwappedParameterException {
+ logger.log(Level.INFO, "initialising adding of a cca");
+
+ if ((!userInput.contains("/n")) || (!userInput.contains("/s"))
+ || (!userInput.contains("/e"))) {
+ logger.log(Level.WARNING, "no param is entered");
+ throw new MissingParameterException("'/n', '/s' and '/e'");
+ }
+
+ final String[] ccaDetails = userInput.trim().split("/");
+
+ validateSwappedParameters(ccaDetails);
+
+ logger.log(Level.INFO, "splitting user input into description, start date and end date");
+ String ccaDescription = ccaDetails[1].substring(1).trim();
+ String ccaStartDate = ccaDetails[2].substring(1).trim();
+ String ccaEndDate = ccaDetails[3].substring(1).trim();
+
+ if (ccaDescription.equals("") || ccaStartDate.equals("")
+ || ccaEndDate.equals("")) {
+ logger.log(Level.WARNING, "description/start date/end date is empty");
+ throw new EmptyParameterException();
+ }
+
+ try {
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ Calendar startCalendar = dateTimeParser.convertStringToCalendar(ccaStartDate);
+ Calendar endCalendar = dateTimeParser.convertStringToCalendar(ccaEndDate);
+ EventCca cca = new EventCca(ccaDescription, startCalendar, endCalendar);
+
+ eventManager.checkValidTimeGiven(cca);
+
+ // Checking if there are any events that clashes
+ ArrayList clashedEvents = eventManager.checkEventClash(cca);
+
+ //If no events clash and the recommended time did not exceed, add cca
+ if (clashedEvents.size() == 0 && !eventManager.didTimeExceed(cca)) {
+ ccas.add(cca);
+ logger.log(Level.INFO, "added cca to ArrayList");
+
+ userInterface.showToUser(Messages.MESSAGE_CCA_ADD_SUCCESS,
+ ccas.get(getCcaListSize() - 1).toString());
+ getCcaStatement(cca);
+
+ sortList();
+ logger.log(Level.INFO, "sorted CCA ArrayList");
+
+ //If events clashed, show the corresponding error message
+ } else if (clashedEvents.size() > 0) {
+ userInterface.showToUser("The cca you were trying to add",
+ cca.toString(),
+ "clashes with the following events in your list:");
+ for (Event clashedEvent : clashedEvents) {
+ userInterface.showToUser(clashedEvent.toString());
+ }
+ userInterface.showToUser(Messages.MESSAGE_PROMPT_CHECK_START_END_INPUTS);
+
+ //If the recommended time exceeded, show the corresponding error message
+ } else if (eventManager.didTimeExceed(cca)) {
+ userInterface.showToUser(Messages.MESSAGE_RECOMMENDED_TIME_EXCEEDED + " CCA is not added!");
+ }
+ } catch (DateTimeParseException | ParseException e) {
+ userInterface.showToUser(Messages.MESSAGE_INVALID_DATE);
+ } catch (InvalidDateException e) {
+ eventManager.processInvalidDateException(e.getErrorType());
+ }
+ }
+
+ /**
+ * Deletes a cca with the input index in the event list.
+ * @param userInputs The input entered by the user.
+ * @throws IndexOutOfBoundsException if the index is out of bound of event list.
+ */
+ @Override
+ public void delete(String[] userInputs) throws IndexOutOfBoundsException {
+ int ccaIndex;
+
+ try {
+ ccaIndex = Integer.parseInt(userInputs[2]);
+ userInterface.showToUser(Messages.MESSAGE_CCA_DELETE_SUCCESS,
+ ccas.get(ccaIndex - 1).toString());
+ Event eventCca = ccas.get(ccaIndex - 1);
+ ccas.remove(ccaIndex - 1);
+ getCcaStatement(eventCca);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ userInterface.showToUser(Messages.MESSAGE_CCA_DELETE_ERROR_NO_NUMBER_GIVEN);
+ logger.log(Level.WARNING, "absence of class index for deletion");
+ } catch (NumberFormatException e) {
+ userInterface.showToUser(Messages.MESSAGE_CCA_DELETE_ERROR_NON_NUMBER);
+ } catch (IndexOutOfBoundsException e) {
+ userInterface.showToUser(Messages.MESSAGE_INVALID_CCA_INDEX);
+ }
+ }
+
+ @Override
+ public void setDone(String[] userInputs) {
+ int ccaIndex;
+ logger.log(Level.INFO, "initialising setting cca as done");
+
+ try {
+ ccaIndex = Integer.parseInt(userInputs[2]);
+ } catch (NumberFormatException e) {
+ userInterface.showToUser(Messages.MESSAGE_CCA_DONE_ERROR_NON_NUMBER);
+ return;
+ } catch (IndexOutOfBoundsException e) {
+ userInterface.showToUser(Messages.MESSAGE_CCA_DONE_ERROR_NO_NUMBER_GIVEN);
+ return;
+ }
+
+ if ((ccaIndex <= 0) || (ccaIndex > getCcaListSize())) {
+ logger.log(Level.WARNING, "index entered is out of bounds");
+ throw new IndexOutOfBoundsException();
+ }
+
+ ccas.get(ccaIndex - 1).setDone();
+ logger.log(Level.INFO, "set cca as done from Arraylist");
+
+ userInterface.showToUser(Messages.MESSAGE_CCA_DONE_SUCCESS,
+ ccas.get(ccaIndex - 1).toString());
+ }
+
+ private void getCcaStatement(Event event) {
+ String ccaStatement = getCcaListSize() <= 1 ? " cca" : " ccas";
+ userInterface.showToUser("Now you have " + getCcaListSize() + ccaStatement + " in the list.");
+ userInterface.showToUser(Messages.MESSAGE_TIME_LEFT_HEADER + eventManager.getTimeLeft(event));
+ }
+
+ private void sortList() {
+ Collections.sort(ccas);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/duke/model/event/classlesson/EventClass.java b/src/main/java/seedu/duke/model/event/classlesson/EventClass.java
new file mode 100644
index 0000000000..1a862f4141
--- /dev/null
+++ b/src/main/java/seedu/duke/model/event/classlesson/EventClass.java
@@ -0,0 +1,51 @@
+package seedu.duke.model.event.classlesson;
+
+import seedu.duke.model.event.Event;
+
+import seedu.duke.controller.parser.DateTimeParser;
+
+import java.util.Calendar;
+
+//@@author elizabethcwt
+/**
+ * Class class
+ * Contains constructors, getters, toString and equals methods.
+ *
+ * @see EventClass#toString()
+ * @see EventClass#equals(Object)
+ */
+public class EventClass extends Event {
+ public static final String CLASS_ICON = "[CLASS]";
+
+ public EventClass(String description, Calendar start, Calendar end) {
+ super(description, start, end);
+ }
+
+ public EventClass(String description, boolean isDone, Calendar start, Calendar end) {
+ super(description, start, end, isDone);
+ }
+
+ @Override
+ public String getIcon() {
+ return CLASS_ICON;
+ }
+
+ @Override
+ public String toString() {
+ String result;
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ result = CLASS_ICON + " " + super.toString() + " from "
+ + dateTimeParser.obtainFormattedDateTimeString(this.getStart())
+ + " to " + dateTimeParser.obtainFormattedDateTimeString(this.getEnd());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ EventClass otherEventClass = (EventClass) obj;
+
+ return super.equals(obj)
+ && this.getStart().equals(otherEventClass.getStart())
+ && this.getEnd().equals(otherEventClass.getEnd());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/duke/model/event/classlesson/EventClassManager.java b/src/main/java/seedu/duke/model/event/classlesson/EventClassManager.java
new file mode 100644
index 0000000000..0860a98195
--- /dev/null
+++ b/src/main/java/seedu/duke/model/event/classlesson/EventClassManager.java
@@ -0,0 +1,243 @@
+package seedu.duke.model.event.classlesson;
+
+import java.text.ParseException;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import seedu.duke.controller.parser.DateTimeParser;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.InvalidDateException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.model.event.Event;
+import seedu.duke.common.LogManager;
+import seedu.duke.common.Messages;
+import seedu.duke.model.event.EventDataManager;
+import seedu.duke.model.event.EventManager;
+import seedu.duke.ui.UserInterface;
+
+//@@author elizabethcwt
+
+/**
+ * ClassManager class
+ * Stores user's classes in an ArrayList of Event class, named classes.
+ *
+ * Contains methods which allow:
+ *
+ * - Attaining class list size
+ * - Adding new classes into ArrayList
+ * - Deleting classes from ArrayList
+ *
+ *
+ * @see EventClassManager#getClassListSize()
+ * @see EventClassManager#add(String)
+ * @see EventClassManager#delete(String[])
+ */
+public class EventClassManager extends EventDataManager {
+
+ private final ArrayList classes;
+ private final UserInterface userInterface;
+ private final EventManager eventManager;
+
+ // Initialising Logger with name "Class"
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+
+ public EventClassManager(ArrayList classes, EventManager eventManager) {
+ this.classes = classes;
+ this.eventManager = eventManager;
+ userInterface = UserInterface.getInstance();
+ }
+
+ public ArrayList getClasses() {
+ return classes;
+ }
+
+ /**
+ * getClassListSize()
+ * Attains the size of the user's classes ArrayList.
+ *
+ * @return int - Classes ArrayList size
+ */
+ public int getClassListSize() {
+ // Assertion to test assumption that ArrayList is not null
+ assert classes != null : "classes ArrayList should not be null";
+ return classes.size();
+ }
+
+ /**
+ * addClass()
+ * Adds new class to classes ArrayList.
+ *
+ * @param userInput To take in the String consisting of the class name, start date-time and end date-time.
+ * @throws MissingParameterException if user input does not meet the requirements.
+ */
+ @Override
+ public void add(String userInput) throws MissingParameterException, EmptyParameterException,
+ SwappedParameterException {
+ logger.log(Level.INFO, "initialising adding of a class");
+
+ // Checks if user input contains the 3 required parameters (/n, /s and /e)
+ if ((!userInput.contains("/n")) || (!userInput.contains("/s")) || (!userInput.contains("/e"))) {
+ logger.log(Level.WARNING, "either class description, start date-time or end date-time parameter is"
+ + " missing");
+ throw new MissingParameterException("'/n', '/s' and '/e'");
+ }
+
+ // Splitting /n, /s and /e info. via a String array called classDetails
+ final String[] classDetails = userInput.trim().split("/");
+
+ validateSwappedParameters(classDetails);
+
+ logger.log(Level.INFO, "splitting the user input into class description, start date-time and end "
+ + "date-time");
+ String classDescription = classDetails[1].substring(1).trim().replaceAll("\\s+", " ");
+ String classStartDate = classDetails[2].substring(1).trim();
+ String classEndDate = classDetails[3].substring(1).trim();
+
+ // Checking if any of the 3 required parameters are empty
+ if (classDescription.equals("") || classStartDate.equals("") || classEndDate.equals("")) {
+ logger.log(Level.WARNING, "either class description, start date-time or end date-time is"
+ + " missing");
+ throw new EmptyParameterException();
+ }
+
+ try {
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ Calendar startCalendar = dateTimeParser.convertStringToCalendar(classStartDate);
+ Calendar endCalendar = dateTimeParser.convertStringToCalendar(classEndDate);
+ EventClass eventClass = new EventClass(classDescription, startCalendar, endCalendar);
+
+ eventManager.checkValidTimeGiven(eventClass);
+
+ // Checking if there are any events that clashes
+ ArrayList clashedEvents = eventManager.checkEventClash(eventClass);
+
+ //If no events clash and the recommended time did not exceed, add class
+ if (clashedEvents.size() == 0 && !eventManager.didTimeExceed(eventClass)) {
+ classes.add(eventClass);
+ logger.log(Level.INFO, "added class to ArrayList");
+
+ userInterface.showToUser(Messages.MESSAGE_CLASS_ADD_SUCCESS,
+ classes.get(getClassListSize() - 1).toString());
+ getClassStatement(eventClass);
+
+ sortList();
+ logger.log(Level.INFO, "sorted classes ArrayList");
+
+ //If events clashed, show the corresponding error message
+ } else if (clashedEvents.size() > 0) {
+ userInterface.showToUser("The class you were trying to add",
+ eventClass.toString(),
+ "clashes with the following events in your list:");
+ for (Event clashedEvent : clashedEvents) {
+ userInterface.showToUser(clashedEvent.toString());
+ }
+ userInterface.showToUser(Messages.MESSAGE_PROMPT_CHECK_START_END_INPUTS);
+
+ //If the recommended time exceeded, show the corresponding error message
+ } else if (eventManager.didTimeExceed(eventClass)) {
+ userInterface.showToUser(Messages.MESSAGE_RECOMMENDED_TIME_EXCEEDED + " Class is not added!");
+ }
+ } catch (DateTimeParseException | ParseException e) {
+ userInterface.showToUser(Messages.MESSAGE_INVALID_DATE);
+ } catch (InvalidDateException e) {
+ eventManager.processInvalidDateException(e.getErrorType());
+ }
+ }
+
+ /**
+ * deleteClass()
+ * Deletes a class from the classes ArrayList.
+ *
+ * @param userInputs To take in the class index of the classes to be deleted.
+ */
+ @Override
+ public void delete(String[] userInputs) {
+ try {
+ // Tries to convert classIndex user input into an integer
+ int classIndex = Integer.parseInt(userInputs[2]);
+
+ // Just to test if class index is valid - for exception use only
+ classes.get(classIndex - 1);
+
+ userInterface.showToUser(Messages.MESSAGE_CLASS_DELETE_SUCCESS,
+ classes.get(classIndex - 1).toString());
+
+ // Deletes class from classes ArrayList
+ Event eventClass = classes.get(classIndex - 1);
+ classes.remove(classIndex - 1);
+ logger.log(Level.INFO, "deletion of class from ArrayList");
+ getClassStatement(eventClass);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ userInterface.showToUser(Messages.MESSAGE_CLASS_DELETE_ERROR_NO_NUMBER_GIVEN);
+ logger.log(Level.WARNING, "absence of class index for deletion");
+ } catch (IndexOutOfBoundsException e) {
+ userInterface.showToUser(Messages.MESSAGE_INVALID_CLASS_INDEX);
+ logger.log(Level.WARNING, "invalid class index entered for deletion");
+ } catch (NumberFormatException e) {
+ userInterface.showToUser(Messages.MESSAGE_CLASS_DELETE_ERROR_NON_NUMBER);
+ logger.log(Level.WARNING, "non-integer class index entered for deletion");
+ }
+ }
+
+ /**
+ * getClassStatement()
+ * Prints statement to update the user once class has been added or deleted.
+ */
+ private void getClassStatement(Event event) {
+ String classStatement = getClassListSize() == 1 ? " class" : " classes";
+ userInterface.showToUser("Now you have " + getClassListSize() + classStatement + " in the list.");
+ userInterface.showToUser(Messages.MESSAGE_TIME_LEFT_HEADER + eventManager.getTimeLeft(event));
+ }
+
+ /**
+ * setClassDone()
+ * Sets class as done.
+ *
+ * @param userInputs To take in the class index of the class to be set as done.
+ * @throws IndexOutOfBoundsException when user input is an invalid class index integer.
+ * @Deprecated This method should not be used, as the done feature has been removed from our application.
+ */
+ @Override
+ public void setDone(String[] userInputs) throws IndexOutOfBoundsException {
+ int classNumber;
+ logger.log(Level.INFO, "initialising setting class as done");
+
+ try {
+ // Trying to convert user's input into an integer
+ classNumber = Integer.parseInt(userInputs[2]);
+ } catch (NumberFormatException e) {
+ logger.log(Level.WARNING, "wrong number format entered");
+ userInterface.showToUser(Messages.MESSAGE_CLASS_DONE_ERROR_NON_NUMBER);
+ return;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ userInterface.showToUser(Messages.MESSAGE_CLASS_DONE_ERROR_NO_NUMBER_GIVEN);
+ return;
+ }
+
+ // Checking if user's input is a valid class index integer
+ if ((classNumber <= 0) || (classNumber > getClassListSize())) {
+ logger.log(Level.WARNING, "index entered is out of bounds");
+ throw new IndexOutOfBoundsException();
+ }
+
+ // Sets class as done
+ final Event eventClass = classes.get(classNumber - 1);
+ classes.get(classNumber - 1).setDone();
+ logger.log(Level.INFO, "set class as done from ArrayList");
+
+ userInterface.showToUser(Messages.MESSAGE_CLASS_DONE_SUCCESS,
+ " " + classes.get(classNumber - 1));
+
+ getClassStatement(eventClass);
+ }
+
+ private void sortList() {
+ Collections.sort(classes);
+ }
+}
+
diff --git a/src/main/java/seedu/duke/model/event/test/EventTest.java b/src/main/java/seedu/duke/model/event/test/EventTest.java
new file mode 100644
index 0000000000..1f8c2e4d28
--- /dev/null
+++ b/src/main/java/seedu/duke/model/event/test/EventTest.java
@@ -0,0 +1,55 @@
+package seedu.duke.model.event.test;
+
+import seedu.duke.model.event.Event;
+import seedu.duke.controller.parser.DateTimeParser;
+
+import java.util.Calendar;
+
+//@@author Aliciaho
+/**
+ * Class EventTest
+ * Contains constructors, getters, toString and equals methods.
+ *
+ * @see EventTest#toString()
+ * @see EventTest#equals(Object)
+ */
+public class EventTest extends Event {
+ public static final String TEST_ICON = "[TEST]";
+
+ public EventTest(String description, Calendar start, Calendar end) {
+ super(description, start, end);
+ }
+
+ public EventTest(String description, boolean isDone, Calendar start, Calendar end) {
+ super(description, start, end, isDone);
+ }
+
+ @Override
+ public String getIcon() {
+ return TEST_ICON;
+ }
+
+ /**
+ * Convert event to readable format for user in form of icon + description + converted date time.
+ *
+ * @return converted string
+ */
+ @Override
+ public String toString() {
+ String result;
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ result = TEST_ICON + " " + super.toString() + " from "
+ + dateTimeParser.obtainFormattedDateTimeString(this.getStart())
+ + " to " + dateTimeParser.obtainFormattedDateTimeString(this.getEnd());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ EventTest otherEventTest = (EventTest) obj;
+
+ return super.equals(obj)
+ && this.getStart().equals(otherEventTest.getStart())
+ && this.getEnd().equals(otherEventTest.getEnd());
+ }
+}
diff --git a/src/main/java/seedu/duke/model/event/test/EventTestManager.java b/src/main/java/seedu/duke/model/event/test/EventTestManager.java
new file mode 100644
index 0000000000..c47fd57ab8
--- /dev/null
+++ b/src/main/java/seedu/duke/model/event/test/EventTestManager.java
@@ -0,0 +1,238 @@
+package seedu.duke.model.event.test;
+
+import seedu.duke.controller.parser.DateTimeParser;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.InvalidDateException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.model.event.Event;
+import seedu.duke.common.LogManager;
+import seedu.duke.common.Messages;
+import seedu.duke.model.event.EventDataManager;
+import seedu.duke.model.event.EventManager;
+import seedu.duke.ui.UserInterface;
+
+import java.text.ParseException;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+//@@author Aliciaho
+/**
+ * TestManager test
+ * Stores user's tests in an ArrayList of Event Test, named tests.
+ *
+ * Contains methods which allow:
+ *
+ * - Attaining test list size
+ * - Adding new tests into ArrayList
+ * - Deleting tests from ArrayList
+ * - Setting tests as {@code DONE}
+ *
+ *
+ * @see EventTestManager#getTestListSize()
+ * @see EventTestManager#add(String)
+ * @see EventTestManager#delete(String[])
+ * @see EventTestManager#setDone(String[])
+ */
+public class EventTestManager extends EventDataManager {
+ private final ArrayList tests;
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+ private final UserInterface userInterface;
+ private final EventManager eventManager;
+
+ public EventTestManager(ArrayList inputList, EventManager eventManager) {
+ tests = inputList;
+ this.eventManager = eventManager;
+ userInterface = UserInterface.getInstance();
+ }
+
+ public ArrayList getTests() {
+ return tests;
+ }
+
+ /**
+ * getTestListSize()
+ * Attains the size of the user's tests ArrayList.
+ * @return int - tests ArrayList size
+ */
+ public int getTestListSize() {
+ assert tests != null;
+ return tests.size();
+ }
+
+ /**
+ * addTest()
+ * Adds new test to tests ArrayList.
+ * @param userInput To take in the String consisting of the test name, start date-time and end date-time.
+ * @exception MissingParameterException exception thrown when parameter not entered
+ * @exception EmptyParameterException exception thrown when description is empty
+ */
+ @Override
+ public void add(String userInput) throws EmptyParameterException, MissingParameterException,
+ SwappedParameterException {
+ logger.log(Level.INFO, "initialising adding of a test");
+
+ if ((!userInput.contains("/n")) || (!userInput.contains("/s"))
+ || (!userInput.contains("/e"))) {
+ logger.log(Level.WARNING, "no param is entered");
+ throw new MissingParameterException("'/n', '/s' and '/e'");
+ }
+
+ userInput.replaceAll("\\s+","");
+ final String[] testDetails = userInput.trim().split("/");
+
+ validateSwappedParameters(testDetails);
+
+ logger.log(Level.INFO, "splitting user input into description, start date and end date");
+ String testDescription = testDetails[1].substring(1).trim().replaceAll("\\s+"," ");
+ String testStartDate = testDetails[2].substring(1).trim();
+ String testEndDate = testDetails[3].substring(1).trim();
+
+ if (testDescription.equals("") || testStartDate.equals("")
+ || testEndDate.equals("")) {
+ logger.log(Level.WARNING, "description/start date/end date is empty");
+ throw new EmptyParameterException();
+ }
+
+ try {
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ Calendar startCalendar = dateTimeParser.convertStringToCalendar(testStartDate);
+ Calendar endCalendar = dateTimeParser.convertStringToCalendar(testEndDate);
+
+ EventTest eventTest = new EventTest(testDescription, startCalendar, endCalendar);
+
+ eventManager.checkValidTimeGiven(eventTest);
+
+ // Checking if there are any events that clashes
+ ArrayList clashedEvents = eventManager.checkEventClash(eventTest);
+
+ //If no events clash and the recommended time did not exceed, add test
+ if (clashedEvents.size() == 0 && !eventManager.didTimeExceed(eventTest)) {
+ tests.add(eventTest);
+ logger.log(Level.INFO, "added test to ArrayList");
+
+ userInterface.showToUser(Messages.MESSAGE_TEST_ADD_SUCCESS,
+ tests.get(getTestListSize() - 1).toString());
+ getTestStatement(eventTest);
+
+ sortList();
+ logger.log(Level.INFO, "sorted Test ArrayList");
+
+ //If events clashed, show the corresponding error message
+ } else if (clashedEvents.size() > 0) {
+ userInterface.showToUser("The test you were trying to add",
+ eventTest.toString(),
+ "clashes with the following events in your list:");
+ for (Event clashedEvent : clashedEvents) {
+ userInterface.showToUser(clashedEvent.toString());
+ }
+ userInterface.showToUser(Messages.MESSAGE_PROMPT_CHECK_START_END_INPUTS);
+
+ //If the recommended time exceeded, show the corresponding error message
+ } else if (eventManager.didTimeExceed(eventTest)) {
+ userInterface.showToUser(Messages.MESSAGE_RECOMMENDED_TIME_EXCEEDED + " Test is not added!");
+ }
+ } catch (DateTimeParseException | ParseException e) {
+ logger.log(Level.WARNING, "invalid date time inputted");
+ userInterface.showToUser(Messages.MESSAGE_INVALID_DATE);
+ } catch (InvalidDateException e) {
+ eventManager.processInvalidDateException(e.getErrorType());
+ }
+ }
+
+ /**
+ * deleteTest()
+ * Deletes a test from the tests ArrayList.
+ * @param userInputs To take in the test index of the test to be deleted.
+ * @exception IndexOutOfBoundsException exception thrown for invalid index
+ * @exception NumberFormatException exception thrown for wrong number format
+ * @exception ArrayIndexOutOfBoundsException exception thrown for empty description
+ */
+ @Override
+ public void delete(String[] userInputs) throws IndexOutOfBoundsException {
+ int testNumber;
+ logger.log(Level.INFO, "initialising deleting of a test");
+
+ try {
+ testNumber = Integer.parseInt(userInputs[2]);
+
+ userInterface.showToUser(Messages.MESSAGE_TEST_DELETE_SUCCESS,
+ tests.get(testNumber - 1).toString());
+ Event eventTest = tests.get(testNumber - 1);
+ tests.remove(testNumber - 1);
+ logger.log(Level.INFO, "deleted test from ArrayList");
+
+ getTestStatement(eventTest);
+ } catch (NumberFormatException e) {
+ logger.log(Level.WARNING, "wrong number format entered");
+ userInterface.showToUser(Messages.MESSAGE_TEST_DELETE_ERROR_NON_NUMBER);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ logger.log(Level.WARNING, "no number was given");
+ userInterface.showToUser(Messages.MESSAGE_TEST_DELETE_ERROR_NO_NUMBER_GIVEN);
+ } catch (IndexOutOfBoundsException e) {
+ logger.log(Level.WARNING, "wrong index inputted");
+ userInterface.showToUser(Messages.MESSAGE_INVALID_TEST_INDEX);
+ }
+ }
+
+ /**
+ * getTaskStatement()
+ * Prints statement to update the user once test has been added or deleted.
+ */
+ private void getTestStatement(Event event) {
+ if ((getTestListSize() - 1 == 0) || (getTestListSize() == 0)) {
+ userInterface.showToUser("Now you have " + getTestListSize() + " test in the list.");
+ } else {
+ userInterface.showToUser("Now you have " + getTestListSize() + " tests in the list.");
+ }
+ userInterface.showToUser(Messages.MESSAGE_TIME_LEFT_HEADER + eventManager.getTimeLeft(event));
+ }
+
+ /**
+ * setTestDone()
+ * Sets test as done.
+ * @param userInputs To take in the test index of the test to be set as done.
+ * @exception IndexOutOfBoundsException when user input is an invalid test index integer.
+ * @exception NumberFormatException exception thrown for wrong number format
+ * @exception ArrayIndexOutOfBoundsException exception thrown for empty description
+ */
+ @Override
+ public void setDone(String[] userInputs) throws IndexOutOfBoundsException {
+ int testNumber;
+ logger.log(Level.INFO, "initialising setting test as done");
+
+ try {
+ testNumber = Integer.parseInt(userInputs[2]);
+ } catch (NumberFormatException e) {
+ logger.log(Level.WARNING, "wrong number format entered");
+ userInterface.showToUser(Messages.MESSAGE_TEST_DONE_ERROR_NON_NUMBER);
+ return;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ logger.log(Level.WARNING, "no number was given");
+ userInterface.showToUser(Messages.MESSAGE_TEST_DONE_ERROR_NO_NUMBER_GIVEN);
+ return;
+ }
+
+ if ((testNumber <= 0) || (testNumber > getTestListSize())) {
+ logger.log(Level.WARNING, "index entered is out of bounds");
+ throw new IndexOutOfBoundsException();
+ }
+
+ final Event eventTest = tests.get(testNumber - 1);
+ tests.get(testNumber - 1).setDone();
+ logger.log(Level.INFO, "set test as done from ArrayList");
+
+ userInterface.showToUser(Messages.MESSAGE_TEST_DONE_SUCCESS,
+ " " + tests.get(testNumber - 1));
+
+ getTestStatement(eventTest);
+ }
+
+ private void sortList() {
+ Collections.sort(tests);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/duke/model/event/tuition/EventTuition.java b/src/main/java/seedu/duke/model/event/tuition/EventTuition.java
new file mode 100644
index 0000000000..8542b504b6
--- /dev/null
+++ b/src/main/java/seedu/duke/model/event/tuition/EventTuition.java
@@ -0,0 +1,74 @@
+package seedu.duke.model.event.tuition;
+
+import seedu.duke.model.event.Event;
+
+import seedu.duke.controller.parser.DateTimeParser;
+
+import java.util.Calendar;
+
+//@@author durianpancakes
+/**
+ * Class EventTuition.
+ * Contains constructors, getters, toString and equals methods.
+ */
+public class EventTuition extends Event {
+ public static final String TUITION_ICON = "[TUITION]";
+ private final String location;
+
+ /**
+ * Constructs a EventTuition with a default NOT DONE status.
+ *
+ * @param description String containing description of the EventTuition.
+ * @param start Calendar containing the start time of the EventTuition.
+ * @param end Calendar containing the end time of the EventTuition.
+ * @param location String containing the location of the EventTuition.
+ */
+ public EventTuition(String description, Calendar start, Calendar end, String location) {
+ super(description, start, end);
+ this.location = location;
+ }
+
+ /**
+ * Constructs a EventTuition with a given NOT DONE/DONE status.
+ *
+ * @param description String containing description of the EventTuition.
+ * @param isDone boolean containing the done status of the EventTuition.
+ * @param start Calendar containing the start time of the EventTuition.
+ * @param end Calendar containing the end time of the EventTuition.
+ * @param location String containing the location of the EventTuition.
+ */
+ public EventTuition(String description, boolean isDone, Calendar start, Calendar end, String location) {
+ super(description, start, end, isDone);
+ this.location = location;
+ }
+
+ @Override
+ public String getIcon() {
+ return TUITION_ICON;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+
+ @Override
+ public String toString() {
+ String result;
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ result = TUITION_ICON + " " + super.toString() + " from "
+ + dateTimeParser.obtainFormattedDateTimeString(this.getStart())
+ + " to " + dateTimeParser.obtainFormattedDateTimeString(this.getEnd())
+ + " at " + this.location;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ EventTuition otherEventTuition = (EventTuition) obj;
+
+ return super.equals(obj)
+ && this.getStart().equals(otherEventTuition.getStart())
+ && this.getEnd().equals(otherEventTuition.getEnd())
+ && this.location.equals(otherEventTuition.getLocation());
+ }
+}
diff --git a/src/main/java/seedu/duke/model/event/tuition/EventTuitionManager.java b/src/main/java/seedu/duke/model/event/tuition/EventTuitionManager.java
new file mode 100644
index 0000000000..9ecad0aaf9
--- /dev/null
+++ b/src/main/java/seedu/duke/model/event/tuition/EventTuitionManager.java
@@ -0,0 +1,260 @@
+package seedu.duke.model.event.tuition;
+
+import seedu.duke.controller.parser.DateTimeParser;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.InvalidDateException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.model.event.Event;
+import seedu.duke.common.LogManager;
+import seedu.duke.common.Messages;
+import seedu.duke.model.event.EventDataManager;
+import seedu.duke.model.event.EventManager;
+import seedu.duke.ui.UserInterface;
+
+import java.text.ParseException;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+//@@author durianpancakes
+/**
+ * Manager class to manage EventTuition.
+ *
+ *
+ * Contain methods which allow:
+ *
+ * - Obtaining EventTuition list size
+ * - Adding new EventTuition into ArrayList
+ * - Deleting EventTuition from ArrayList
+ *
+ *
+ */
+public class EventTuitionManager extends EventDataManager {
+ private final ArrayList tuitions;
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+ private final UserInterface userInterface;
+ private final EventManager eventManager;
+ public static final int INDEX_OFFSET = 1;
+ public static final int NO_EVENTS = 0;
+ public static final int ONE_EVENT = 1;
+ public static final int DELETE_INDEX = 1;
+
+ /**
+ * Constructs an EventTuitionManager.
+ *
+ * @param tuitions Contains a list of Event that are classified as EventTuition.
+ * @param eventManager EventManager instance.
+ */
+ public EventTuitionManager(ArrayList tuitions, EventManager eventManager) {
+ this.tuitions = tuitions;
+ this.eventManager = eventManager;
+ userInterface = UserInterface.getInstance();
+ }
+
+ /**
+ * Obtain the EventTuition list.
+ *
+ * @return the list of EventTuition.
+ */
+ public ArrayList getTuitions() {
+ return tuitions;
+ }
+
+ /**
+ * Obtain the number of tuition in the list of EventTuition.
+ *
+ * @return the number of EventTuition in the list.
+ */
+ public int getTuitionListSize() {
+ return tuitions.size();
+ }
+
+ /**
+ * Method to add an EventTuition into the list of EventTuition.
+ *
+ * @param userInput The input entered by the user.
+ * @throws MissingParameterException when any of the following prefixes are missing in the userInput:
+ * '/n', '/s', '/e', '/l'.
+ * @throws EmptyParameterException when any of the following parameters are empty: '/n', '/s', '/e', '/l'.
+ */
+ @Override
+ public void add(String userInput) throws MissingParameterException, EmptyParameterException,
+ SwappedParameterException {
+ logger.log(Level.INFO, "Initializing adding of a tuition");
+ final String descriptionPrefix = "/n";
+ final String startPrefix = "/s";
+ final String endPrefix = "/e";
+ final String locationPrefix = "/l";
+
+ if ((!userInput.contains(descriptionPrefix)) || (!userInput.contains(startPrefix))
+ || (!userInput.contains(endPrefix)) || (!userInput.contains(locationPrefix))) {
+ logger.log(Level.WARNING, "either class description, start date-time or end date-time parameter is"
+ + " missing");
+ throw new MissingParameterException("'/n', '/s', '/e' and '/l'");
+ }
+
+ validateSwappedParameters(userInput.split("/"));
+
+ final int indexOfDescriptionPrefix = userInput.indexOf(descriptionPrefix);
+ final int indexOfStartPrefix = userInput.indexOf(startPrefix);
+ final int indexOfEndPrefix = userInput.indexOf(endPrefix);
+ final int indexOfLocationPrefix = userInput.indexOf(locationPrefix);
+
+ String description = userInput.substring(indexOfDescriptionPrefix, indexOfStartPrefix)
+ .replace(descriptionPrefix, "").trim();
+ String start = userInput.substring(indexOfStartPrefix, indexOfEndPrefix)
+ .replace(startPrefix, "").trim();
+ String end = userInput.substring(indexOfEndPrefix, indexOfLocationPrefix)
+ .replace(endPrefix, "").trim();
+ String location = userInput.substring(indexOfLocationPrefix)
+ .replace(locationPrefix, "").trim();
+
+ if (isEmptyString(description) || isEmptyString(start) || isEmptyString(end) || isEmptyString(location)) {
+ throw new EmptyParameterException();
+ }
+
+ try {
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ Calendar startCalendar = dateTimeParser.convertStringToCalendar(start);
+ Calendar endCalendar = dateTimeParser.convertStringToCalendar(end);
+ EventTuition eventTuition = new EventTuition(description, startCalendar,
+ endCalendar, location);
+
+ eventManager.checkValidTimeGiven(eventTuition);
+
+ // Checking if there are any events that clashes
+ ArrayList clashedEvents = eventManager.checkEventClash(eventTuition);
+
+ //If no events clash and the recommended time did not exceed, add tuition
+ if (clashedEvents.size() == NO_EVENTS && !eventManager.didTimeExceed(eventTuition)) {
+ tuitions.add(eventTuition);
+ logger.log(Level.INFO, "Tuition added successfully");
+
+ userInterface.showToUser(Messages.MESSAGE_TUITION_ADD_SUCCESS,
+ eventTuition.toString(),
+ getTuitionStatement(),
+ "Time left for this day: " + eventManager.getTimeLeft(eventTuition));
+
+ sortList();
+ logger.log(Level.INFO, "sorted Tuition ArrayList");
+
+ //If events clashed, show the corresponding error message
+ } else if (clashedEvents.size() > NO_EVENTS) {
+ userInterface.showToUser("The tuition you were trying to add",
+ eventTuition.toString(),
+ "clashes with the following events in your list:");
+ for (Event clashedEvent : clashedEvents) {
+ userInterface.showToUser(clashedEvent.toString());
+ }
+ userInterface.showToUser(Messages.MESSAGE_PROMPT_CHECK_START_END_INPUTS);
+
+ //If the recommended time exceeded, show the corresponding error message
+ } else if (eventManager.didTimeExceed(eventTuition)) {
+ userInterface.showToUser(Messages.MESSAGE_RECOMMENDED_TIME_EXCEEDED + " Tuition is not added!");
+ }
+ } catch (DateTimeParseException | ParseException e) {
+ userInterface.showToUser(Messages.MESSAGE_INVALID_DATE);
+ } catch (InvalidDateException e) {
+ eventManager.processInvalidDateException(e.getErrorType());
+ }
+ }
+
+ /**
+ * Method to delete an EventTuition from the list of EventTuition based on the index given by the user.
+ *
+ * @param userInputs The input entered by the user.
+ */
+ @Override
+ public void delete(String[] userInputs) {
+ try {
+ // Tries to convert classIndex user input into an integer
+ int tuitionIndex = Integer.parseInt(userInputs[DELETE_INDEX]);
+
+ // Just to test if class index is valid - for exception use only
+ tuitions.get(tuitionIndex - INDEX_OFFSET);
+
+ userInterface.showToUser(Messages.MESSAGE_TUITION_DELETE_SUCCESS,
+ tuitions.get(tuitionIndex - INDEX_OFFSET).toString());
+
+ // Deletes class from classes ArrayList
+ Event eventTuition = tuitions.get(tuitionIndex - INDEX_OFFSET);
+ tuitions.remove(tuitionIndex - INDEX_OFFSET);
+ logger.log(Level.INFO, "Deletion of tuition class from ArrayList");
+ userInterface.showToUser(getTuitionStatement(),
+ Messages.MESSAGE_TIME_LEFT_HEADER + eventManager.getTimeLeft(eventTuition));
+ } catch (ArrayIndexOutOfBoundsException e) {
+ userInterface.showToUser(Messages.MESSAGE_TUITION_DELETE_ERROR_NO_NUMBER_GIVEN);
+ } catch (IndexOutOfBoundsException e) {
+ userInterface.showToUser(Messages.MESSAGE_INVALID_TUITION_INDEX);
+ } catch (NumberFormatException e) {
+ userInterface.showToUser(Messages.MESSAGE_TUITION_DELETE_ERROR_NON_NUMBER);
+ }
+ }
+
+ /**
+ * Method to set an EventTuition to be DONE.
+ *
+ * @param userInputs The input entered by the user.
+ * @throws IndexOutOfBoundsException when user gives an index that is smaller than or equals to 0, or
+ * when index is greater than the number of EventTuition in the list.
+ */
+ @Deprecated
+ @Override
+ public void setDone(String[] userInputs) throws IndexOutOfBoundsException {
+ int tuitionNumber;
+ logger.log(Level.INFO, "Initialising setting tuition as done");
+
+ try {
+ // Trying to convert user's input into an integer
+ tuitionNumber = Integer.parseInt(userInputs[2]);
+ } catch (NumberFormatException e) {
+ logger.log(Level.WARNING, "Wrong number format entered");
+ userInterface.showToUser(Messages.MESSAGE_TUITION_DONE_ERROR_NON_NUMBER);
+ return;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ userInterface.showToUser(Messages.MESSAGE_TUITION_DONE_ERROR_NO_NUMBER_GIVEN);
+ return;
+ }
+
+ // Checking if user's input is a valid class index integer
+ if ((tuitionNumber <= 0) || (tuitionNumber > getTuitionListSize())) {
+ logger.log(Level.WARNING, "index entered is out of bounds");
+ throw new IndexOutOfBoundsException();
+ }
+
+ // Sets class as done
+ tuitions.get(tuitionNumber - INDEX_OFFSET).setDone();
+ logger.log(Level.INFO, "set class as done from ArrayList");
+
+ userInterface.showToUser(Messages.MESSAGE_TUITION_DONE_SUCCESS,
+ " " + tuitions.get(tuitionNumber - INDEX_OFFSET),
+ getTuitionStatement());
+ }
+
+ private String getTuitionStatement() {
+ String tuitionStatement = getTuitionListSize() == ONE_EVENT ? " class" : " classes";
+
+ return "Now you have " + getTuitionListSize() + tuitionStatement + " in the list";
+ }
+
+ /**
+ * Helper function to check if a string is empty.
+ *
+ * @param string String to be checked.
+ * @return boolean, true if empty, false if not empty.
+ */
+ private boolean isEmptyString(String string) {
+ return string.equals("");
+ }
+
+ /**
+ * Sorts the list of EventTuition based on the start time of the Event.
+ */
+ private void sortList() {
+ Collections.sort(tuitions);
+ }
+}
diff --git a/src/main/java/seedu/duke/model/quiz/FindQuiz.java b/src/main/java/seedu/duke/model/quiz/FindQuiz.java
new file mode 100644
index 0000000000..31dbd98a23
--- /dev/null
+++ b/src/main/java/seedu/duke/model/quiz/FindQuiz.java
@@ -0,0 +1,42 @@
+package seedu.duke.model.quiz;
+
+import seedu.duke.common.LogManager;
+
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+//@@author untitle4
+/**
+ * Provide a function to find quiz with certain keyword(s) input by the user.
+ */
+public class FindQuiz {
+ private final ArrayList quizzes;
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+ private final String userInput;
+ private final ArrayList filteredQuizzes;
+
+ public FindQuiz(String userInput, ArrayList quizzes) {
+ assert userInput.length() != 0;
+ this.quizzes = quizzes;
+ this.userInput = userInput;
+ filteredQuizzes = new ArrayList<>();
+ }
+
+ public ArrayList filterQuizzes() {
+ logger.log(Level.INFO, "loop through all the keywords to check if in quiz description");
+ String[] separatedInputs = userInput.split(" ");
+
+ for (int i = 0; i < quizzes.size(); i++) {
+ for (String keyword: separatedInputs) {
+ if (quizzes.get(i).getQuestion().toLowerCase().contains(keyword.toLowerCase())) {
+
+ filteredQuizzes.add("Question " + (i + 1) + ":\n" + quizzes.get(i).toString());
+ break;
+ }
+ }
+ }
+ return filteredQuizzes;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/duke/model/quiz/Quiz.java b/src/main/java/seedu/duke/model/quiz/Quiz.java
new file mode 100644
index 0000000000..ee85183d4e
--- /dev/null
+++ b/src/main/java/seedu/duke/model/quiz/Quiz.java
@@ -0,0 +1,157 @@
+package seedu.duke.model.quiz;
+
+import java.time.LocalDate;
+
+//@@author untitle4
+/**
+ * A class for quiz.
+ */
+public class Quiz {
+ private final String question;
+ private final String option1;
+ private final String option2;
+ private final String option3;
+ private final String option4;
+ private final int answer;
+ private final String explanation;
+ private LocalDate lastAccessed;
+
+ public Quiz(String question, String option1, String option2, String option3, String option4,
+ int answer, String explanation) {
+ this.question = question;
+ this.option1 = option1;
+ this.option2 = option2;
+ this.option3 = option3;
+ this.option4 = option4;
+ this.answer = answer;
+ this.explanation = explanation;
+ lastAccessed = LocalDate.now();
+ }
+
+ public Quiz(String question, String option1, String option2, String option3, String option4,
+ int answer, String explanation, LocalDate lastAccessed) {
+ this.question = question;
+ this.option1 = option1;
+ this.option2 = option2;
+ this.option3 = option3;
+ this.option4 = option4;
+ this.answer = answer;
+ this.explanation = explanation;
+ this.lastAccessed = lastAccessed;
+ }
+
+ public String getQuestion() {
+ return question;
+ }
+
+ public int getAnswer() {
+ return answer;
+ }
+
+ public String getExplanation() {
+ return explanation;
+ }
+
+ public LocalDate getLastAccessed() {
+ return lastAccessed;
+ }
+
+ @Override
+ public String toString() {
+ if (explanation.equals("")) {
+ return question + "\n\n"
+ + "(1) " + option1 + "\n"
+ + "(2) " + option2 + "\n"
+ + "(3) " + option3 + "\n"
+ + "(4) " + option4 + "\n\n";
+ } else {
+ return question + "\n\n"
+ + "(1) " + option1 + "\n"
+ + "(2) " + option2 + "\n"
+ + "(3) " + option3 + "\n"
+ + "(4) " + option4 + "\n\n"
+ + "Explanation: " + explanation + "\n\n";
+ }
+ }
+
+ //@@author elizabethcwt
+ /**
+ * printQuizQuestion() Method
+ * Prints the quiz question for the user to answer.
+ *
+ * @return String - quiz question in given format.
+ */
+ public String printQuizQuestion() {
+ return question + "\n\n"
+ + "(1) " + option1 + "\n"
+ + "(2) " + option2 + "\n"
+ + "(3) " + option3 + "\n"
+ + "(4) " + option4 + "\n\n";
+ }
+
+ /**
+ * printPostQuizQuestion() Method
+ * This method first checks if the relevant quiz question has an explanation in the quiz list text file.
+ *
+ * Explanation absent - Prints:
+ *
+ * - Quiz question
+ * - Correctness logo of the question (correct or wrong)
+ * - Question options
+ * - User's answer (correct or wrong)
+ * - Correct answer
+ *
+ *
+ * Explanation present - Prints:
+ *
+ * - Quiz question
+ * - Correctness logo of the question (correct or wrong)
+ * - Question options
+ * - User's answer (correct or wrong)
+ * - Correct answer
+ * - Explanation for the question
+ *
+ * @param l - A variable to count the quiz question number the user is at, at that point of time.
+ * @param correctnessLogo - To attach the icon representing the correctness of the user's answer for the relevant
+ * question.
+ * @return String - Components as stated above, and in given format.
+ */
+ public String printPostQuizQuestion(int l, String correctnessLogo) {
+ if (explanation.equals("")) {
+ return question + correctnessLogo + "\n\n"
+ + "(1) " + option1 + "\n"
+ + "(2) " + option2 + "\n"
+ + "(3) " + option3 + "\n"
+ + "(4) " + option4 + "\n\n"
+ + "Your answer: (" + l + ")\n"
+ + "Correct answer: (" + answer + ")\n\n";
+ } else {
+ return question + correctnessLogo + "\n\n"
+ + "(1) " + option1 + "\n"
+ + "(2) " + option2 + "\n"
+ + "(3) " + option3 + "\n"
+ + "(4) " + option4 + "\n\n"
+ + "Your answer: (" + l + ")\n"
+ + "Correct answer: (" + answer + ")\n"
+ + "Explanation: " + explanation + "\n";
+ }
+ }
+
+ //@@author AndreWongZH
+ /**
+ * Updates the quiz last accessed date to the current date.
+ */
+ public void updateLastAccessed() {
+ lastAccessed = LocalDate.now();
+ }
+
+ /**
+ * Converts quiz instance into storage readable string form.
+ *
+ * @return A string representation of the quiz.
+ */
+ public String convertToData() {
+ return question + "|" + option1 + "|" + option2 + "|" + option3 + "|" + option4
+ + "|" + answer + "|" + explanation + "|" + lastAccessed;
+ }
+}
diff --git a/src/main/java/seedu/duke/model/quiz/QuizInteractable.java b/src/main/java/seedu/duke/model/quiz/QuizInteractable.java
new file mode 100644
index 0000000000..e341aef1ab
--- /dev/null
+++ b/src/main/java/seedu/duke/model/quiz/QuizInteractable.java
@@ -0,0 +1,23 @@
+package seedu.duke.model.quiz;
+
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.model.Interactable;
+
+//@@author AndreWongZH
+/**
+ * Represents the public api methods for QuizManager that the controller can call.
+ */
+public interface QuizInteractable extends Interactable {
+ /**
+ * List all quizzes in QuizManager.
+ */
+ void list();
+
+ /**
+ * Find a list of quiz that matches with the keyword.
+ *
+ * @param userInput The input entered by the user.
+ * @throws MissingParameterException If keyword is missing from the command.
+ */
+ void find(String userInput) throws MissingParameterException;
+}
diff --git a/src/main/java/seedu/duke/model/quiz/QuizManager.java b/src/main/java/seedu/duke/model/quiz/QuizManager.java
new file mode 100644
index 0000000000..3001684281
--- /dev/null
+++ b/src/main/java/seedu/duke/model/quiz/QuizManager.java
@@ -0,0 +1,587 @@
+package seedu.duke.model.quiz;
+
+import seedu.duke.common.LogManager;
+import seedu.duke.common.Messages;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.model.ModelManager;
+import seedu.duke.ui.UserInterface;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+import java.util.logging.Level;
+import java.util.Collections;
+import java.util.logging.Logger;
+
+public class QuizManager extends ModelManager implements QuizInteractable {
+ public static final int BEGIN_INDEX = 0;
+ public static final int END_INDEX = 1;
+ public static final int END_DOUBLE_INDEX = 2;
+ public static final int END_TRIPLE_INDEX = 3;
+ public static final int EMPTY_SIZE = 0;
+ public static final int USER_INPUT_OFFSET = 9;
+
+ public static final int INDEX_QUESTION = 1;
+ public static final int INDEX_OP1 = 2;
+ public static final int INDEX_OP2 = 3;
+ public static final int INDEX_OP3 = 4;
+ public static final int INDEX_OP4 = 5;
+ public static final int INDEX_ANS = 6;
+ public static final int INDEX_EXP = 7;
+
+ public static final int OFFSET_QUESTION = 1;
+ public static final int OFFSET_OPTION = 2;
+ public static final int OFFSET_ANS = 1;
+ public static final int OFFSET_EXP = 3;
+
+ public static final int ANS_MIN = 1;
+ public static final int ANS_MAX = 4;
+
+ public static final String EMPTY_STRING = "";
+ public static final int INDEX_OFFSET = 1;
+ public static final int MAX_INPUT_LENGTH_NO_EXP = 7;
+ public static final int MAX_INPUT_LENGTH_WITH_EXP = 8;
+
+ public static final String QUESTION_PREFIX = " /q ";
+ public static final String ANSWER_PREFIX = " /a ";
+ public static final String OPTION_ONE_PREFIX = " /o1 ";
+ public static final String OPTION_TWO_PREFIX = " /o2 ";
+ public static final String OPTION_THREE_PREFIX = " /o3 ";
+ public static final String OPTION_FOUR_PREFIX = " /o4 ";
+ public static final String EXPLANATION_PREFIX = " /exp ";
+
+ public static final int SLASH_INDEX = 2;
+ public static final int OFFSET_PREFIX_1 = 3;
+ public static final int OFFSET_PREFIX_2 = 4;
+ public static final int OFFSET_PREFIX_3 = 5;
+
+ public static int QUIZ_ATTEMPTS = 0;
+ private final ArrayList quizzes;
+ private final ArrayList lastIncorrectQuizzes = new ArrayList<>();
+ private final ArrayList lastIncorrectAnswers = new ArrayList<>();
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+ public static int noOfQues;
+ private final UserInterface userInterface;
+ public static String correctnessLogo;
+ public static ArrayList quizIndexes = new ArrayList<>();
+ UserAnswerManager userAnswerManager = new UserAnswerManager();
+
+ public QuizManager(ArrayList quizzes) {
+ this.quizzes = quizzes;
+ this.userInterface = UserInterface.getInstance();
+ }
+
+ public ArrayList getQuizList() {
+ return quizzes;
+ }
+
+ public int getQuizListSize() {
+ assert quizzes != null;
+ return quizzes.size();
+ }
+
+ //@@author elizabethcwt
+ /**
+ * checkQuizSizeValidity() Method
+ * Checks if the number of questions in the quiz list is a valid integer (at least 1).
+ *
+ * If quiz size is 0, displays message stating quiz list is empty.
+ *
+ * If the quiz size is a valid integer above 0, the takeQuiz() method is called.
+ *
+ * Catches ArrayIndexOutOfBoundsException when number of questions user wants to take in the quiz is too little
+ * (less than 1) or too much (more than quiz list size).
+ *
+ * @param separatedInputs To take in the number of questions the user would like to take for the quiz.
+ * @see QuizManager#takeQuiz(String[])
+ */
+ public void checkQuizSizeValidity(String[] separatedInputs) {
+ try {
+ noOfQues = Integer.parseInt(separatedInputs[1]);
+ assert noOfQues != 0 : "noOfQues should not be 0";
+
+ if (getQuizListSize() == 0) {
+
+ // If user attempts to take a quiz, but the quiz list is empty
+ userInterface.showToUser(Messages.MESSAGE_EMPTY_QUIZ_LIST);
+ } else {
+
+ // If user attempts to take a quiz, and the quiz list has at least 1 quiz question
+ takeQuiz(separatedInputs);
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // To check the validity of user input before taking a quiz.
+ userInterface.showToUser(Messages.MESSAGE_INVALID_QUIZ_COMMAND);
+ }
+ }
+
+ /**
+ * takeQuiz() Method
+ * First checks if the user's input of the number of questions they want to take is valid.
+ *
+ * If this value is invalid, calls the handleInvalidNumOfQuestions() method.
+ *
+ * If this value is valid, calls the handleValidNumOfQuestions() method, then increments the QUIZ_ATTEMPTS variable
+ * by 1.
+ *
+ * @param separatedInputs To take in the number of questions the user would like to take for the quiz.
+ * @see QuizManager#handleInvalidNumOfQuestions()
+ * @see QuizManager#handleValidNumOfQuestions()
+ */
+ private void takeQuiz(String[] separatedInputs) {
+
+ if (!((noOfQues > 0) && (noOfQues <= getQuizListSize()))) {
+
+ // If user inputs an invalid number of questions to be attempted (NOT within range of 1 to quiz size)
+ handleInvalidNumOfQuestions();
+ } else {
+
+ // If user inputs a valid number of quiz questions (within range of 1 to quiz size)
+ handleValidNumOfQuestions();
+ QUIZ_ATTEMPTS++;
+ }
+ }
+
+ /**
+ * handleValidNumOfQuestions() Method
+ * This method first calls the initialisingShufflingOfQuestions() method, which shuffles ALL the questions in the\
+ * current quiz list.
+ *
+ * Initialises a counter for counting the number of questions to be taken (questionCounter, which acts as a
+ * variable).
+ *
+ * Calls the testForValidInput() method until questionCounter is incremented to the value of the number of questions
+ * the user wants to take for the quiz.
+ *
+ * Initialises a counter to count the number of questions answered correctly by the user in the quiz.
+ *
+ * Clears or empties the ArrayLists: lastIncorrectQuizzes and lastIncorrectAnswers to refresh the arrayList for a
+ * new quiz attempt.
+ *
+ * Calls the storeCorrectnessOfQuizAnswer) method.
+ *
+ * Prints out the review of the questions, which consists of:
+ *
+ * - Questions in the same order as the user has taken them in
+ * - Correctness logo next to the start of each question (correct or wrong)
+ * - User's answers (correct or wrong)
+ * - Correct answer
+ * - (Optional) Explanation to the question
+ * - The total score the user has attained at the bottom of all the questions
+ *
+ *
+ * Lastly, this method empties the userAnswers and correctness ArrayLists
+ *
+ * @see QuizManager#initialisingShufflingOfQuestions()
+ * @see QuizManager#testForValidInput(int)
+ * @see QuizManager#storeCorrectnessOfQuizAnswer(int)
+ */
+ private void handleValidNumOfQuestions() {
+
+ // Assert that noOfQues is within a valid range
+ assert ((noOfQues > 0) && (noOfQues <= getQuizListSize())) : "noOfQues should be of a valid value, but"
+ + "it is invalid";
+
+ initialisingShufflingOfQuestions();
+
+ int questionCounter = 0;
+
+ // Assert that questionCounter is less than noOfQues
+ assert (questionCounter < noOfQues) : "questionCounter should not be more than noOfQues";
+
+ while (questionCounter < noOfQues) {
+
+ questionCounter = testForValidInput(questionCounter);
+ }
+
+ // Initialising counter for correctly answered questions
+ int correctCounter = 0;
+
+ // Clear arraylist to store incorrect quizzes
+ lastIncorrectQuizzes.clear();
+ lastIncorrectAnswers.clear();
+
+ // Compare and note if students' answers are correct
+ correctCounter = storeCorrectnessOfQuizAnswer(correctCounter);
+
+ for (int l = 0; l < noOfQues; l++) {
+
+ // Assigning the correctness logo to be printed with questions post-quiz
+ assignCorrectnessLogo(l);
+
+ // Print out all quiz questions, user's answers, correctness, correct answers and explanations
+ userInterface.showToUser("Question " + (l + INDEX_OFFSET) + ": ",
+ quizzes.get(quizIndexes.get(l)).printPostQuizQuestion(userAnswerManager
+ .getUserAnswers().get(l), correctnessLogo));
+ }
+
+ // Print out quiz score
+ userInterface.showToUser(Messages.print_quiz_score(correctCounter, noOfQues));
+
+ // Empty userAnswers ArrayList and correctness ArrayList
+ userAnswerManager.getUserAnswers().clear();
+ userAnswerManager.getCorrectness().clear();
+ }
+
+ /**
+ * assignCorrectnessLogo() Method
+ * Checks if user's answers for the quiz question matches the correct quiz answer for the same question stored in
+ * the quiz list text file.
+ *
+ * @param l A variable representing the index of relevant the quiz question at that point of time.
+ */
+ private void assignCorrectnessLogo(int l) {
+
+ // Assert that the correctness of the user's input is true in this if loop
+ assert ((userAnswerManager.getCorrectness().get(l).equals(true))
+ || (userAnswerManager.getCorrectness().get(l).equals(false))) : "User's answer should either be correct"
+ + "or false for this question";
+
+ if (userAnswerManager.getCorrectness().get(l).equals(true)) {
+
+ correctnessLogo = " [CORRECT ☺︎]";
+ } else {
+
+ // Assert that the correctness of the user's input is false in this else loop
+ assert (userAnswerManager.getCorrectness().get(l).equals(false)) : "User's answer should be"
+ + " incorrect for this question";
+
+ correctnessLogo = " [WRONG ☹︎]";
+ }
+ }
+
+ /**
+ * storeCorrectnessOfQuizAnswer() Method
+ * Stores whether the user's answers are correct or wrong for each question, in the getCorrectness ArrayList.
+ *
+ * If the user's answer is correct, increment the correctCounter variable by 1.
+ *
+ * @param correctCounter The counter responsible for counting the number of correct answers by the user.
+ * @return correctCounter - For the purpose of calculating the user's quiz score.
+ */
+ private int storeCorrectnessOfQuizAnswer(int correctCounter) {
+ for (int k = 0; k < noOfQues; k++) {
+ if (userAnswerManager.getUserAnswers().get(k).equals(quizzes.get(quizIndexes
+ .get(k)).getAnswer())) {
+ userAnswerManager.getCorrectness().add(true);
+ correctCounter++;
+ } else {
+ userAnswerManager.getCorrectness().add(false);
+ lastIncorrectQuizzes.add(quizzes.get(quizIndexes.get(k)));
+ lastIncorrectAnswers.add(userAnswerManager.getUserAnswers().get(k));
+ }
+ quizzes.get(quizIndexes.get(k)).updateLastAccessed();
+ }
+ return correctCounter;
+ }
+
+ /**
+ * initialisingShufflingOfQuestions() Method
+ * Shuffles ALL the quiz questions in the quiz list.
+ *
+ * @see Collections#shuffle(List)
+ */
+ private void initialisingShufflingOfQuestions() {
+ // Create a new list of the question indexes
+ quizIndexes = new ArrayList<>();
+ for (int i = 0; i < quizzes.size(); i++) {
+ quizIndexes.add(i);
+ }
+
+ // Shuffle the question indexes
+ Collections.shuffle(quizIndexes);
+ }
+
+ /**
+ * handleInvalidNumOfQuestions() Method
+ * Displays message corresponding to an invalid number of questions to take in a quiz.
+ */
+ private void handleInvalidNumOfQuestions() {
+
+ // Assert that noOfQues is NOT an acceptable value
+ assert (!((noOfQues > 0) && (noOfQues <= getQuizListSize()))) : "noOfQues should not be of a valid"
+ + " value, but it is";
+
+ // If user inputs an invalid number of quiz questions (not within range of 1 to quiz size)
+ userInterface.showToUser(Messages.invalid_number_of_quiz_questions_message(quizzes.size()));
+ }
+
+ /**
+ * testForValidInput() Method
+ * This method first prints the first question to the user.
+ *
+ * It then scans the user's answer.
+ *
+ * If the user's answer is:
+ *
+ * - Empty - Displays message corresponding to a missing quiz question answer
+ * -
+ * Not empty but invalid (such as a non-integer, or an integer that is not 1, 2, 3 or 4) - Displays message
+ * corresponding to an invalid quiz question answer
+ *
+ * - Valid - Adds the user's answer to the getUserAnswers ArrayList, then increments the question counter
+ * variable by 1, to print the next question in the quiz
+ *
+ *
+ * @param questionCounter - To inform the program which question to print for the current iteration.
+ * @return questionCounter - To update the program of which question to print next.
+ */
+ public int testForValidInput(int questionCounter) {
+ // Print out each question
+ userInterface.showToUser("",
+ "Question " + (questionCounter + INDEX_OFFSET) + ": ",
+ quizzes.get(quizIndexes.get(questionCounter)).printQuizQuestion());
+
+ // Create a Scanner object
+ Scanner in = new Scanner(System.in);
+ String userInput = in.nextLine();
+ userInput.replaceAll("\\s+", "");
+
+ if (userInput.equals("")) {
+
+ userInterface.showToUser(Messages.MESSAGE_QUIZ_MISSING_ANSWER);
+ } else {
+
+ boolean b = userInput.equals("1") || userInput.equals("2") || userInput.equals("3")
+ || userInput.equals("4");
+
+ if (b) {
+
+ // Store user's quiz answers into ArrayList
+ userAnswerManager.getUserAnswers().add(Integer.parseInt(userInput));
+
+ questionCounter++;
+ return questionCounter;
+ } else {
+
+ // Assert that the user's input is NOT one of the valid options
+ assert (!b) : "User's input should not be one of the valid options (1, 2, 3 or 4)";
+
+ userInterface.showToUser(Messages.MESSAGE_QUIZ_INVALID_ANS_PROVIDED);
+ }
+ }
+
+ return questionCounter;
+ }
+
+ //@@author untitle4
+ /**
+ * Delete a quiz in the Arraylist of quizzes.
+ * Extract the index of the quiz that the user want to delete.
+ *
+ * @param userInputs The input entered by the user.
+ * @throws IndexOutOfBoundsException if the index is out of bounds.
+ */
+ @Override
+ public void delete(String[] userInputs) throws IndexOutOfBoundsException {
+ int quizIndex;
+
+ try {
+ quizIndex = Integer.parseInt(userInputs[2]);
+ if ((quizIndex <= 0) || (quizIndex > getQuizListSize())) {
+ throw new IndexOutOfBoundsException();
+ }
+ } catch (NumberFormatException e) {
+ userInterface.showToUser(Messages.MESSAGE_QUIZ_DELETE_ERROR_NON_NUMBER);
+ return;
+ } catch (IndexOutOfBoundsException e) {
+ userInterface.showToUser(Messages.MESSAGE_QUIZ_INDEX_OUT_OF_BOUND);
+ return;
+ }
+
+ userInterface.showToUser("Noted. I've removed this quiz question:",
+ quizzes.get(quizIndex - INDEX_OFFSET).toString());
+
+ quizzes.remove(quizIndex - INDEX_OFFSET);
+ getQuizStatement();
+ }
+
+ //@@author AndreWongZH
+ /**
+ * Adds a quiz to the ArrayList of quizzes.
+ * Extracts the question, options, explanations if any before adding it as a quiz.
+ * If user command contains extra parameters, inform the user and return.
+ *
+ * @param userInput The input entered by the user.
+ * @throws EmptyParameterException If there are missing parameters after the prefix.
+ */
+ @Override
+ public void add(String userInput) throws EmptyParameterException, SwappedParameterException {
+ if (!userInput.contains(QUESTION_PREFIX)) {
+ userInterface.showToUser(Messages.MESSAGE_QUIZ_QUESTION_NOT_FOUND);
+ return;
+ }
+
+ if (!userInput.contains(ANSWER_PREFIX)) {
+ userInterface.showToUser(Messages.MESSAGE_QUIZ_ANSWER_NOT_FOUND);
+ return;
+ }
+
+ if (!userInput.contains(OPTION_ONE_PREFIX) || !userInput.contains(OPTION_TWO_PREFIX)
+ || !userInput.contains(OPTION_THREE_PREFIX) || !userInput.contains(OPTION_FOUR_PREFIX)) {
+ userInterface.showToUser(Messages.MESSAGE_QUIZ_OPTIONS_NOT_FOUND);
+ return;
+ }
+ String[] separatedInputs = userInput.trim().split("/");
+
+ if ((separatedInputs.length > MAX_INPUT_LENGTH_NO_EXP && !userInput.contains(EXPLANATION_PREFIX))
+ || (separatedInputs.length > MAX_INPUT_LENGTH_WITH_EXP)) {
+ userInterface.showToUser(Messages.MESSAGE_INVALID_EXTRA_PARAM);
+ return;
+ }
+
+ validateSwappedParameters(separatedInputs);
+
+ try {
+ quizzes.add(parseQuizQuestion(separatedInputs));
+ userInterface.showToUser(Messages.MESSAGE_QUIZ_ADD_SUCCESSFUL);
+ } catch (NumberFormatException e) {
+ userInterface.showToUser(Messages.MESSAGE_QUIZ_INVALID_ANS_PROVIDED);
+ }
+ }
+
+ //@@author AndreWongZH
+ //Refactored by durianpancakes
+ /**
+ * Extracts out the question, options, answers and explanation from user input.
+ * If there is no explanation given, explanation will be empty string by default.
+ *
+ * @param separatedInputs An array string of user's input.
+ * @return A Quiz object.
+ * @throws EmptyParameterException If parameters are empty spaces.
+ * @throws NumberFormatException If answer index is not between 1 and 4.
+ */
+ private Quiz parseQuizQuestion(String[] separatedInputs) throws EmptyParameterException, NumberFormatException {
+ String question = separatedInputs[INDEX_QUESTION].substring(OFFSET_QUESTION).trim();
+ String option1 = separatedInputs[INDEX_OP1].substring(OFFSET_OPTION).trim();
+ String option2 = separatedInputs[INDEX_OP2].substring(OFFSET_OPTION).trim();
+ String option3 = separatedInputs[INDEX_OP3].substring(OFFSET_OPTION).trim();
+ String option4 = separatedInputs[INDEX_OP4].substring(OFFSET_OPTION).trim();
+ String answer = separatedInputs[INDEX_ANS].substring(OFFSET_ANS).trim();
+
+ String explanation = "";
+
+ if (separatedInputs.length > INDEX_EXP) {
+ explanation = separatedInputs[INDEX_EXP].substring(OFFSET_EXP).trim();
+ }
+
+ if (question.equals(EMPTY_STRING) || option1.equals(EMPTY_STRING) || option2.equals(EMPTY_STRING)
+ || option3.equals(EMPTY_STRING) || option4.equals(EMPTY_STRING) || answer.equals(EMPTY_STRING)) {
+ logger.log(Level.WARNING, "question or options or answer is empty");
+ throw new EmptyParameterException();
+ }
+
+ int answerInInt = Integer.parseInt(answer);
+
+ if (answerInInt > ANS_MAX || answerInInt < ANS_MIN) {
+ throw new NumberFormatException();
+ }
+
+ return new Quiz(question, option1, option2, option3, option4, answerInInt, explanation);
+ }
+
+ //@@author AndreWongZH
+ /**
+ * Validates if the parameters are swapped.
+ *
+ * @param userInputs An arraylist of type string of the user input.
+ * @throws SwappedParameterException If letter does not match up with the required prefix.
+ */
+ private void validateSwappedParameters(String[] userInputs) throws SwappedParameterException {
+ boolean hasQ = userInputs[INDEX_QUESTION].substring(BEGIN_INDEX, END_INDEX)
+ .contentEquals(QUESTION_PREFIX.substring(SLASH_INDEX, OFFSET_PREFIX_1));
+ boolean hasO1 = userInputs[INDEX_OP1].substring(BEGIN_INDEX, END_DOUBLE_INDEX)
+ .contentEquals(OPTION_ONE_PREFIX.substring(SLASH_INDEX, OFFSET_PREFIX_2));
+ boolean hasO2 = userInputs[INDEX_OP2].substring(BEGIN_INDEX, END_DOUBLE_INDEX)
+ .contentEquals(OPTION_TWO_PREFIX.substring(SLASH_INDEX, OFFSET_PREFIX_2));
+ boolean hasO3 = userInputs[INDEX_OP3].substring(BEGIN_INDEX, END_DOUBLE_INDEX)
+ .contentEquals(OPTION_THREE_PREFIX.substring(SLASH_INDEX, OFFSET_PREFIX_2));
+ boolean hasO4 = userInputs[INDEX_OP4].substring(BEGIN_INDEX, END_DOUBLE_INDEX)
+ .contentEquals(OPTION_FOUR_PREFIX.substring(SLASH_INDEX, OFFSET_PREFIX_2));
+ boolean hasA = userInputs[INDEX_ANS].substring(BEGIN_INDEX, END_INDEX)
+ .contentEquals(ANSWER_PREFIX.substring(SLASH_INDEX, OFFSET_PREFIX_1));
+ boolean hasE = true;
+
+ if (userInputs.length == MAX_INPUT_LENGTH_WITH_EXP) {
+ hasE = userInputs[INDEX_EXP].substring(BEGIN_INDEX, END_TRIPLE_INDEX)
+ .contentEquals(EXPLANATION_PREFIX.substring(SLASH_INDEX, OFFSET_PREFIX_3));
+ }
+
+ if (!hasQ || !hasO1 || !hasO2 || !hasO3 || !hasO4 || !hasA || !hasE) {
+ throw new SwappedParameterException();
+ }
+ }
+
+ //@@author untitle4
+ /**
+ * To find the quiz with certain keyword(s) in the Arraylist of quizzes.
+ *
+ * @param userInput The input entered by the user.
+ * @throws MissingParameterException if there is no keyword(s) provided.
+ */
+ @Override
+ public void find(String userInput) throws MissingParameterException {
+ String param = userInput.substring(USER_INPUT_OFFSET).trim();
+
+ if (param.length() == EMPTY_SIZE) {
+ throw new MissingParameterException("keywords as");
+ }
+
+ FindQuiz findQuiz = new FindQuiz(param, quizzes);
+ ArrayList filteredQuizzes = findQuiz.filterQuizzes();
+
+ if (filteredQuizzes.size() == EMPTY_SIZE) {
+ userInterface.showToUser(Messages.MESSAGE_NO_QUIZZES_FOUND);
+ return;
+ }
+
+ userInterface.printArray(filteredQuizzes);
+ }
+
+ //@@author untitle4
+ /**
+ * Show the incorrect quizzes in the user's last attempt.
+ */
+ public void recordedQuizzes() {
+ if (lastIncorrectQuizzes.size() == 0 && QUIZ_ATTEMPTS != 0) {
+ userInterface.showToUser(Messages.MESSAGE_QUIZ_FULL_MARKS);
+ } else if (QUIZ_ATTEMPTS == 0) {
+ userInterface.showToUser(Messages.MESSAGE_NO_QUIZ_ATTEMPTS);
+ } else {
+ userInterface.showToUser(Messages.MESSAGE_QUIZ_WRONG_QUESTIONS_HEADER);
+ for (int i = 0; i < lastIncorrectQuizzes.size(); i++) {
+ userInterface.showToUser(lastIncorrectQuizzes.get(i).printQuizQuestion());
+ userInterface.showToUser("Your answer: (" + lastIncorrectAnswers.get(i) + ")",
+ "Correct answer: (" + lastIncorrectQuizzes.get(i).getAnswer() + ")");
+ if (!lastIncorrectQuizzes.get(i).getExplanation().equals("")) {
+ userInterface.showToUser("Explanation: " + lastIncorrectQuizzes.get(i).getExplanation());
+ }
+ }
+ }
+
+ }
+
+ //@@author untitle4
+ /**
+ * List the Arraylist of quiz.
+ */
+ @Override
+ public void list() {
+ if (quizzes.size() == EMPTY_SIZE) {
+ userInterface.showToUser(Messages.MESSAGE_EMPTY_QUIZ_LIST);
+ } else {
+ userInterface.showToUser(Messages.MESSAGE_QUIZ_LIST_HEADER);
+ for (int i = 0; i < quizzes.size(); i++) {
+ userInterface.showToUser("Question " + (i + INDEX_OFFSET) + ":",
+ quizzes.get(i).toString());
+ }
+ }
+ }
+
+ private void getQuizStatement() {
+ String quizStatement = getQuizListSize() == 1 ? " quiz" : " quizzes";
+ userInterface.showToUser("Now you have " + getQuizListSize() + quizStatement + " in the quiz list.");
+ }
+}
diff --git a/src/main/java/seedu/duke/model/quiz/UserAnswerManager.java b/src/main/java/seedu/duke/model/quiz/UserAnswerManager.java
new file mode 100644
index 0000000000..e0c56c578f
--- /dev/null
+++ b/src/main/java/seedu/duke/model/quiz/UserAnswerManager.java
@@ -0,0 +1,29 @@
+package seedu.duke.model.quiz;
+
+import java.util.ArrayList;
+
+//@@author elizabethcwt
+/**
+ * UserAnswerManager Class
+ * Contains userAnswers, which is an ArrayList of the user's integer answers.
+ *
+ * Also contains correctness, another ArrayList of the boolean values of the user's answers for each question.
+ */
+public class UserAnswerManager {
+ private final ArrayList userAnswers;
+ private final ArrayList correctness;
+
+ public UserAnswerManager() {
+ this.userAnswers = new ArrayList<>();
+ this.correctness = new ArrayList<>();
+ }
+
+ // Getters
+ public ArrayList getUserAnswers() {
+ return userAnswers;
+ }
+
+ public ArrayList getCorrectness() {
+ return correctness;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/duke/storage/StorageExceptionHandler.java b/src/main/java/seedu/duke/storage/StorageExceptionHandler.java
new file mode 100644
index 0000000000..e53ead09bc
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/StorageExceptionHandler.java
@@ -0,0 +1,82 @@
+package seedu.duke.storage;
+
+import seedu.duke.common.Messages;
+import seedu.duke.model.ConfigParameter;
+import seedu.duke.storage.config.ConfigStorageManager;
+import seedu.duke.storage.contact.ContactStorageManager;
+import seedu.duke.storage.event.EventStorageManager;
+import seedu.duke.storage.quiz.QuizStorageManager;
+import seedu.duke.ui.UserInterface;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import static seedu.duke.storage.StorageManager.CONFIG_FILE_NAME;
+import static seedu.duke.storage.StorageManager.CONTACT_FILE_NAME;
+import static seedu.duke.storage.StorageManager.EVENT_FILE_NAME;
+import static seedu.duke.storage.StorageManager.QUIZ_FILE_NAME;
+
+//@@author durianpancakes
+/**
+ * Class to handle StorageCorruptedException.
+ */
+public class StorageExceptionHandler {
+ private final UserInterface userInterface;
+
+ public StorageExceptionHandler() {
+ userInterface = UserInterface.getInstance();
+ }
+
+ public boolean handleCorruptedStorage() {
+ // Valid input is defined to be y or n
+ boolean validInput = false;
+ boolean resetSuccessful = false;
+
+ userInterface.showToUser(Messages.MESSAGE_FACTORY_RESET_PROMPT,
+ Messages.MESSAGE_MANUAL_TROUBLESHOOT_PROMPT);
+
+ while (!validInput) {
+ String userInput = userInterface.getUserCommand();
+
+ switch (userInput) {
+ case "y":
+ resetSuccessful = factoryReset();
+ validInput = true;
+ break;
+ case "n":
+ userInterface.showToUser();
+ return false;
+ default:
+ userInterface.showToUser(Messages.MESSAGE_FACTORY_RESET_INVALID_INPUT_PROMPT);
+ }
+ }
+
+ if (resetSuccessful) {
+ userInterface.showToUser(Messages.MESSAGE_FACTORY_RESET_SUCCESSFUL);
+ } else {
+ userInterface.showToUser(Messages.MESSAGE_FACTORY_RESET_FAILED_OR_CANCELLED);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean factoryReset() {
+ QuizStorageManager quizStorageManager = new QuizStorageManager(QUIZ_FILE_NAME);
+ EventStorageManager eventStorageManager = new EventStorageManager(EVENT_FILE_NAME);
+ ContactStorageManager contactStorageManager = new ContactStorageManager(CONTACT_FILE_NAME);
+ ConfigStorageManager configStorageManager = new ConfigStorageManager(CONFIG_FILE_NAME);
+
+ try {
+ quizStorageManager.saveData(new ArrayList<>(), QUIZ_FILE_NAME);
+ eventStorageManager.saveData(new ArrayList<>());
+ contactStorageManager.saveData(new ArrayList<>(), CONTACT_FILE_NAME);
+ configStorageManager.saveData(new ConfigParameter());
+
+ return true;
+ } catch (IOException e) {
+ userInterface.showToUser(Messages.MESSAGE_STORAGE_INITIALIZATION_ERROR);
+
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/StorageManager.java b/src/main/java/seedu/duke/storage/StorageManager.java
new file mode 100644
index 0000000000..a760a8e2c8
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/StorageManager.java
@@ -0,0 +1,63 @@
+package seedu.duke.storage;
+
+import seedu.duke.common.LogManager;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Abstract StorageManager class containing the basic methods to create a data file.
+ */
+public abstract class StorageManager {
+ //@@author durianpancakes
+ public static final String EMPTY_FILE_NAME = "";
+ public static final String EVENT_FILE_NAME = "/events.txt";
+ public static final String QUIZ_FILE_NAME = "/quiz.txt";
+ public static final String CONTACT_FILE_NAME = "/contact.txt";
+ public static final String CONFIG_FILE_NAME = "/config.txt";
+ protected static String DIRECTORY_FOLDER_PATH = new File("data").getAbsolutePath();
+ protected String fileName;
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+
+ //@@author AndreWongZH
+ public StorageManager(String fileName) {
+ assert !fileName.equals(EMPTY_FILE_NAME);
+ this.fileName = fileName;
+ }
+
+ //@@author durianpancakes
+ /**
+ * Constructor for Test files.
+ *
+ * @param directory String containing the directory to be accessed.
+ * @param fileName String containing the file to be accessed.
+ */
+ public StorageManager(String directory, String fileName) {
+ assert !directory.equals(EMPTY_FILE_NAME);
+ DIRECTORY_FOLDER_PATH = new File(directory).getAbsolutePath();
+ assert !fileName.equals(EMPTY_FILE_NAME);
+ this.fileName = fileName;
+ }
+
+ /**
+ * Creates the 'data' directory if it does not exist. Creates the data file .txt if it does not exist.
+ *
+ * @return boolean. true if data file is created, false if data file already exists
+ * @throws IOException if there was a problem in creating a new file
+ */
+ protected boolean createDataFile() throws IOException {
+ File file = new File(DIRECTORY_FOLDER_PATH);
+ boolean isDirectoryCreated = file.mkdir();
+ file = new File(DIRECTORY_FOLDER_PATH + fileName);
+
+ if (isDirectoryCreated) {
+ logger.log(Level.INFO, "Directory not found, creating...");
+ } else {
+ logger.log(Level.INFO, "Directory found...");
+ }
+
+ return file.createNewFile();
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/config/ConfigDecoder.java b/src/main/java/seedu/duke/storage/config/ConfigDecoder.java
new file mode 100644
index 0000000000..048a5ba946
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/config/ConfigDecoder.java
@@ -0,0 +1,19 @@
+package seedu.duke.storage.config;
+
+import seedu.duke.exception.StorageCorruptedException;
+import seedu.duke.model.ConfigParameter;
+
+public class ConfigDecoder {
+ public ConfigParameter decodeConfig(String encodedConfig) throws StorageCorruptedException {
+ String[] splitEncodedConfig = encodedConfig.split("\\|", 3);
+ String userName = splitEncodedConfig[0];
+ int recommendedHours = Integer.parseInt(splitEncodedConfig[1]);
+ boolean hasProgramRan = Boolean.parseBoolean(splitEncodedConfig[2]);
+
+ if (recommendedHours <= 0 || recommendedHours > 12) {
+ throw new StorageCorruptedException();
+ }
+
+ return new ConfigParameter(userName, recommendedHours, hasProgramRan);
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/config/ConfigEncoder.java b/src/main/java/seedu/duke/storage/config/ConfigEncoder.java
new file mode 100644
index 0000000000..1d233f5028
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/config/ConfigEncoder.java
@@ -0,0 +1,13 @@
+package seedu.duke.storage.config;
+
+import seedu.duke.model.ConfigParameter;
+
+public class ConfigEncoder {
+ public String encodeConfig(ConfigParameter configParameter) {
+ String userName = configParameter.getName();
+ String recommendedHours = String.valueOf(configParameter.getRecommendedHours());
+ String hasProgramRan = String.valueOf(configParameter.getHasProgramRan());
+
+ return userName + "|" + recommendedHours + "|" + hasProgramRan;
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/config/ConfigStorageManager.java b/src/main/java/seedu/duke/storage/config/ConfigStorageManager.java
new file mode 100644
index 0000000000..c8de8b8db5
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/config/ConfigStorageManager.java
@@ -0,0 +1,63 @@
+package seedu.duke.storage.config;
+
+import seedu.duke.common.LogManager;
+import seedu.duke.common.Messages;
+import seedu.duke.exception.StorageCorruptedException;
+import seedu.duke.model.ConfigParameter;
+import seedu.duke.storage.StorageManager;
+import seedu.duke.ui.UserInterface;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.Scanner;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class ConfigStorageManager extends StorageManager {
+ private final ConfigEncoder configEncoder;
+ private final ConfigDecoder configDecoder;
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+ private final UserInterface userInterface;
+
+ public ConfigStorageManager(String fileName) {
+ super(fileName);
+ this.configEncoder = new ConfigEncoder();
+ this.configDecoder = new ConfigDecoder();
+ userInterface = UserInterface.getInstance();
+ }
+
+ public void saveData(ConfigParameter configParameter) throws IOException {
+ String encodedConfig = configEncoder.encodeConfig(configParameter);
+ Files.write(Path.of(DIRECTORY_FOLDER_PATH + fileName), Collections.singleton(encodedConfig));
+ }
+
+ public ConfigParameter loadData() throws StorageCorruptedException {
+ File eventFile = new File(DIRECTORY_FOLDER_PATH + fileName);
+ logger.log(Level.INFO, "Loading storage...");
+
+ try {
+ boolean fileCreated = createDataFile();
+ if (!fileCreated) {
+ logger.log(Level.INFO, "Data file found, reading data file...");
+ Scanner sc = new Scanner(eventFile);
+ String dataString = sc.nextLine();
+ ConfigParameter configParameter = configDecoder.decodeConfig(dataString);
+ logger.log(Level.INFO, "Load successful");
+
+ return configParameter;
+ } else {
+ logger.log(Level.INFO, "Data file not found, initializing data file...");
+ }
+ } catch (IOException e) {
+ userInterface.showToUser(Messages.MESSAGE_STORAGE_READ_ERROR);
+ logger.log(Level.SEVERE, "Initialization failed");
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "Config Storage corrupted");
+ throw new StorageCorruptedException();
+ }
+ return new ConfigParameter();
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/contact/ContactListDecoder.java b/src/main/java/seedu/duke/storage/contact/ContactListDecoder.java
new file mode 100644
index 0000000000..6f6246abd6
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/contact/ContactListDecoder.java
@@ -0,0 +1,56 @@
+package seedu.duke.storage.contact;
+
+import seedu.duke.exception.StorageSeparatorException;
+import seedu.duke.model.contact.Contact;
+
+import java.util.ArrayList;
+
+//@@author AndreWongZH
+/**
+ * Decodes the content in the contact text file and add them in an Arraylist as contacts
+ * to initialize the contact list.
+ */
+public class ContactListDecoder {
+ public static final int INDEX_SUBJECT = 0;
+ public static final int INDEX_NAME = 1;
+ public static final int INDEX_PHONE_NUM = 2;
+ public static final int INDEX_EMAIL = 3;
+ public static final int NUMBER_OF_PARAMETERS_REQUIRED = 4;
+
+ /**
+ * Reads and extracts out the information from quiz storage.
+ * Checks is any quizzes that have not been attempted since the last 2 days, notify user if so.
+ *
+ * @param encodedContactList An array list of contacts in string.
+ * @return An arraylist of type contact stored in the text file.
+ */
+ public ArrayList decodeContactList(ArrayList encodedContactList) throws StorageSeparatorException {
+ final ArrayList decodedContacts = new ArrayList<>();
+ for (String encodedContact : encodedContactList) {
+ decodedContacts.add(decodeContactFromString(encodedContact));
+ }
+
+ return decodedContacts;
+ }
+
+ /**
+ * Extracts the relevant info from the storage string.
+ *
+ * @param encodedContact A string of input from storage.
+ * @return A contact instance.
+ */
+ private Contact decodeContactFromString(String encodedContact) throws StorageSeparatorException {
+ final String[] data = encodedContact.trim().split("\\|");
+
+ if (data.length != NUMBER_OF_PARAMETERS_REQUIRED) {
+ throw new StorageSeparatorException();
+ }
+
+ String subject = data[INDEX_SUBJECT];
+ String name = data[INDEX_NAME];
+ String phoneNumber = data[INDEX_PHONE_NUM];
+ String email = data[INDEX_EMAIL];
+
+ return new Contact(subject, name, phoneNumber, email);
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/contact/ContactListEncoder.java b/src/main/java/seedu/duke/storage/contact/ContactListEncoder.java
new file mode 100644
index 0000000000..a29972296c
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/contact/ContactListEncoder.java
@@ -0,0 +1,27 @@
+package seedu.duke.storage.contact;
+
+import seedu.duke.model.contact.Contact;
+
+import java.util.ArrayList;
+
+//@@author AndreWongZH
+/**
+ * Encodes the String form of contacts in the Arraylist and store them in a text file.
+ */
+public class ContactListEncoder {
+ /**
+ * Converts contact type into its data storage representation for all contacts.
+ *
+ * @param contactList An array list of type contact to be encoded.
+ * @return An array list of string of contacts to be written to text file storage.
+ */
+ public ArrayList encodeContactList(ArrayList contactList) {
+ ArrayList encodedContacts = new ArrayList<>();
+
+ for (Contact contact: contactList) {
+ encodedContacts.add(contact.convertToData());
+ }
+
+ return encodedContacts;
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/contact/ContactStorageManager.java b/src/main/java/seedu/duke/storage/contact/ContactStorageManager.java
new file mode 100644
index 0000000000..a2bba0325f
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/contact/ContactStorageManager.java
@@ -0,0 +1,98 @@
+package seedu.duke.storage.contact;
+
+import seedu.duke.common.LogManager;
+import seedu.duke.common.Messages;
+import seedu.duke.exception.StorageCorruptedException;
+import seedu.duke.exception.StorageSeparatorException;
+import seedu.duke.model.contact.Contact;
+import seedu.duke.storage.StorageManager;
+import seedu.duke.ui.UserInterface;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Scanner;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+//@@author AndreWongZH
+/**
+ * Helps to manage the storage of contacts into a text file and load contacts from the file
+ * when the program is executed.
+ */
+public class ContactStorageManager extends StorageManager {
+ private final ContactListEncoder contactListEncoder;
+ private final ContactListDecoder contactListDecoder;
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+ private final UserInterface userInterface;
+
+ public ContactStorageManager(String fileName) {
+ super(fileName);
+ this.contactListEncoder = new ContactListEncoder();
+ this.contactListDecoder = new ContactListDecoder();
+ this.userInterface = UserInterface.getInstance();
+ }
+
+ /**
+ * Loads the content of the contact text file.
+ *
+ * @return An array list of type Contact.
+ */
+ public ArrayList loadData() throws StorageCorruptedException {
+ ArrayList data;
+ logger.log(Level.INFO, "Loading storage...");
+
+ try {
+ boolean fileCreated = createDataFile();
+ if (!fileCreated) {
+ logger.log(Level.INFO, "Data file found, reading data file...");
+ data = getDataFromFile();
+ logger.log(Level.INFO, "Load successful");
+ return contactListDecoder.decodeContactList(data);
+ }
+ } catch (IOException e) {
+ userInterface.showToUser(Messages.MESSAGE_STORAGE_READ_ERROR);
+ logger.log(Level.SEVERE, "Initialization failed");
+ } catch (StorageSeparatorException e) {
+ logger.log(Level.SEVERE, "Contact Storage corrupted");
+ throw new StorageCorruptedException();
+ }
+ logger.log(Level.INFO, "Data file not found, initializing data file...");
+ return new ArrayList<>();
+ }
+
+ /**
+ * Reads data from contact.txt line by line and saves to an array list.
+ * Returns the arrayList.
+ *
+ * @return An array list of contents in the contact.txt file.
+ * @throws FileNotFoundException If file is not found.
+ */
+ private ArrayList getDataFromFile() throws FileNotFoundException {
+ ArrayList data = new ArrayList<>();
+ File contactFile = new File(DIRECTORY_FOLDER_PATH + fileName);
+ Scanner sc = new Scanner(contactFile);
+
+ while (sc.hasNext()) {
+ String dataString = sc.nextLine();
+ data.add(dataString);
+ }
+
+ return data;
+ }
+
+ /**
+ * Saves the array list of contacts to a text file after every command.
+ *
+ * @param contactList Arraylist of type contact to be saved to txt file.
+ * @param filePath Name of the txt file.
+ * @throws IOException If file writer fails to write to txt file.
+ */
+ public void saveData(ArrayList contactList, String filePath) throws IOException {
+ ArrayList encodedContactList = contactListEncoder.encodeContactList(contactList);
+ Files.write(Path.of(DIRECTORY_FOLDER_PATH + filePath), encodedContactList);
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/event/EventListDecoder.java b/src/main/java/seedu/duke/storage/event/EventListDecoder.java
new file mode 100644
index 0000000000..c9dda043bd
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/event/EventListDecoder.java
@@ -0,0 +1,209 @@
+package seedu.duke.storage.event;
+
+import seedu.duke.controller.parser.DateTimeParser;
+import seedu.duke.exception.StorageCorruptedException;
+import seedu.duke.exception.StorageSeparatorException;
+import seedu.duke.model.event.cca.EventCca;
+import seedu.duke.model.event.classlesson.EventClass;
+import seedu.duke.model.event.Event;
+import seedu.duke.model.event.test.EventTest;
+import seedu.duke.model.event.tuition.EventTuition;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Calendar;
+
+//@@author durianpancakes
+/**
+ * Class responsible for the decoding of events read from the data file.
+ */
+public class EventListDecoder {
+ public EventListDecoder() {
+ }
+
+ /**
+ * Decodes an ArrayList of Strings read from the data file to an ArrayList of Events.
+ *
+ * @param encodedEventList ArrayList of encoded String read from the data file.
+ * @return ArrayList of decoded String.
+ * @throws StorageSeparatorException when any separator other than "|" is used.
+ * @throws StorageCorruptedException when the storage file is determined to be unreadable.
+ */
+ public ArrayList decodeEventList(ArrayList encodedEventList) throws StorageSeparatorException,
+ StorageCorruptedException {
+ final ArrayList decodedEvents = new ArrayList<>();
+ for (String encodedEvent : encodedEventList) {
+ decodedEvents.add(decodeEventFromString(encodedEvent));
+ }
+ return decodedEvents;
+ }
+
+ /**
+ * Decode a String to an Event.
+ *
+ * @param encodedEvent String containing the encoded Event.
+ * @return Event decoded from the String.
+ * @throws StorageSeparatorException when any separator other than "|" is used.
+ * @throws StorageCorruptedException when the storage file is determined to be unreadable.
+ */
+ private Event decodeEventFromString(String encodedEvent) throws StorageSeparatorException,
+ StorageCorruptedException {
+ final String[] data = encodedEvent.trim().split("\\|", 3);
+
+ assert data.length > 1;
+
+ switch (data[0]) {
+ case EventCca.CCA_ICON:
+ return parseCca(data);
+ case EventClass.CLASS_ICON:
+ return parseClass(data);
+ case EventTest.TEST_ICON:
+ return parseTest(data);
+ case EventTuition.TUITION_ICON:
+ return parseTuition(data);
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Parses a separated String input into an EventCca.
+ *
+ * @param data Array of Strings containing the separated parameters.
+ * @return EventCca decoded from the String
+ * @throws StorageSeparatorException when any separator other than "|" is used.
+ * @throws StorageCorruptedException when the storage file is determined to be unreadable.
+ */
+ private EventCca parseCca(String[] data) throws StorageSeparatorException, StorageCorruptedException {
+ boolean isDone;
+ String description;
+ isDone = Boolean.parseBoolean(data[1]);
+ String[] eventInfo = data[2].trim().split("\\|", 3);
+
+ if (eventInfo.length != 3) {
+ throw new StorageSeparatorException();
+ }
+
+ description = eventInfo[0];
+ String start = eventInfo[1];
+ String end = eventInfo[2];
+
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ Calendar startCalendar;
+ Calendar endCalendar;
+ try {
+ startCalendar = dateTimeParser.convertStringToCalendar(start);
+ endCalendar = dateTimeParser.convertStringToCalendar(end);
+ } catch (ParseException e) {
+ throw new StorageCorruptedException();
+ }
+
+ return new EventCca(description, isDone, startCalendar, endCalendar);
+ }
+
+ /**
+ * Parses a separated String input into an EventClass.
+ *
+ * @param data Array of Strings containing the separated parameters.
+ * @return EventClass decoded from the String
+ * @throws StorageSeparatorException when any separator other than "|" is used.
+ * @throws StorageCorruptedException when the storage file is determined to be unreadable.
+ */
+ private EventClass parseClass(String[] data) throws StorageSeparatorException, StorageCorruptedException {
+ boolean isDone;
+ String description;
+ isDone = Boolean.parseBoolean(data[1]);
+ String[] eventInfo = data[2].trim().split("\\|", 3);
+
+ if (eventInfo.length != 3) {
+ throw new StorageSeparatorException();
+ }
+
+ description = eventInfo[0];
+ String start = eventInfo[1];
+ String end = eventInfo[2];
+
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ Calendar startCalendar;
+ Calendar endCalendar;
+ try {
+ startCalendar = dateTimeParser.convertStringToCalendar(start);
+ endCalendar = dateTimeParser.convertStringToCalendar(end);
+ } catch (ParseException e) {
+ throw new StorageCorruptedException();
+ }
+
+ return new EventClass(description, isDone, startCalendar, endCalendar);
+ }
+
+ /**
+ * Parses a separated String input into an EventTest.
+ *
+ * @param data Array of Strings containing the separated parameters.
+ * @return EventTest decoded from the String
+ * @throws StorageSeparatorException when any separator other than "|" is used.
+ * @throws StorageCorruptedException when the storage file is determined to be unreadable.
+ */
+ private EventTest parseTest(String[] data) throws StorageSeparatorException, StorageCorruptedException {
+ boolean isDone;
+ String description;
+ isDone = Boolean.parseBoolean(data[1]);
+ String[] eventInfo = data[2].trim().split("\\|", 3);
+
+ if (eventInfo.length != 3) {
+ throw new StorageSeparatorException();
+ }
+
+ description = eventInfo[0];
+ String start = eventInfo[1];
+ String end = eventInfo[2];
+
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ Calendar startCalendar;
+ Calendar endCalendar;
+ try {
+ startCalendar = dateTimeParser.convertStringToCalendar(start);
+ endCalendar = dateTimeParser.convertStringToCalendar(end);
+ } catch (ParseException e) {
+ throw new StorageCorruptedException();
+ }
+
+ return new EventTest(description, isDone, startCalendar, endCalendar);
+ }
+
+ /**
+ * Parses a separated String input into an EventTuition.
+ *
+ * @param data Array of Strings containing the separated parameters.
+ * @return EventTuition decoded from the String
+ * @throws StorageSeparatorException when any separator other than "|" is used.
+ * @throws StorageCorruptedException when the storage file is determined to be unreadable.
+ */
+ private EventTuition parseTuition(String[] data) throws StorageSeparatorException, StorageCorruptedException {
+ boolean isDone;
+ String description;
+ isDone = Boolean.parseBoolean(data[1]);
+ String[] eventInfo = data[2].trim().split("\\|", 4);
+
+ if (eventInfo.length != 4) {
+ throw new StorageSeparatorException();
+ }
+
+ description = eventInfo[0];
+ String start = eventInfo[1];
+ String end = eventInfo[2];
+ String location = eventInfo[3];
+
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ Calendar startCalendar;
+ Calendar endCalendar;
+ try {
+ startCalendar = dateTimeParser.convertStringToCalendar(start);
+ endCalendar = dateTimeParser.convertStringToCalendar(end);
+ } catch (ParseException e) {
+ throw new StorageCorruptedException();
+ }
+
+ return new EventTuition(description, isDone, startCalendar, endCalendar, location);
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/event/EventListEncoder.java b/src/main/java/seedu/duke/storage/event/EventListEncoder.java
new file mode 100644
index 0000000000..8994707380
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/event/EventListEncoder.java
@@ -0,0 +1,82 @@
+package seedu.duke.storage.event;
+
+import seedu.duke.controller.parser.DateTimeParser;
+import seedu.duke.model.event.cca.EventCca;
+import seedu.duke.model.event.classlesson.EventClass;
+import seedu.duke.model.event.Event;
+import seedu.duke.model.event.test.EventTest;
+import seedu.duke.model.event.tuition.EventTuition;
+
+import java.util.ArrayList;
+
+//@@author durianpancakes
+/**
+ * Class responsible for the encoding of Events to String for writing to the data file.
+ */
+public class EventListEncoder {
+ public EventListEncoder() {
+ }
+
+ /**
+ * Encodes an ArrayList of Events into an ArrayList of Strings.
+ *
+ * @param eventList ArrayList of Events to be encoded.
+ * @return ArrayList of encoded Strings to be written to data file.
+ */
+ public ArrayList encodeEventList(ArrayList eventList) {
+ ArrayList encodedEvents = new ArrayList<>();
+
+ for (Event event : eventList) {
+ encodedEvents.add(encodeEventToString(event));
+ }
+
+ return encodedEvents;
+ }
+
+ /**
+ * Encode an Event to String.
+ *
+ * @param event Event to be encoded.
+ * @return String containing the encoded Event.
+ */
+ private String encodeEventToString(Event event) {
+ String result = "";
+ DateTimeParser dateTimeParser = new DateTimeParser();
+
+ String start = dateTimeParser.convertCalendarToString(event.getStart());
+ String end = dateTimeParser.convertCalendarToString(event.getEnd());
+
+ if (event instanceof EventCca) {
+ EventCca eventCca = (EventCca) event;
+ result = EventCca.CCA_ICON + "|"
+ + eventCca.isDone() + "|"
+ + eventCca.getDescription() + "|"
+ + start + "|"
+ + end;
+ } else if (event instanceof EventTest) {
+ EventTest eventTest = (EventTest) event;
+ result = EventTest.TEST_ICON + "|"
+ + eventTest.isDone() + "|"
+ + eventTest.getDescription() + "|"
+ + start + "|"
+ + end;
+ } else if (event instanceof EventTuition) {
+ EventTuition newEventTuition = (EventTuition) event;
+ result = EventTuition.TUITION_ICON + "|"
+ + newEventTuition.isDone() + "|"
+ + newEventTuition.getDescription() + "|"
+ + start + "|"
+ + end + "|"
+ + newEventTuition.getLocation();
+ } else if (event instanceof EventClass) {
+ EventClass newEventClass = (EventClass) event;
+ result = EventClass.CLASS_ICON + "|"
+ + newEventClass.isDone() + "|"
+ + newEventClass.getDescription() + "|"
+ + start + "|"
+ + end;
+ }
+
+ return result;
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/event/EventStorageManager.java b/src/main/java/seedu/duke/storage/event/EventStorageManager.java
new file mode 100644
index 0000000000..8ff819e8ee
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/event/EventStorageManager.java
@@ -0,0 +1,132 @@
+package seedu.duke.storage.event;
+
+import seedu.duke.exception.StorageCorruptedException;
+import seedu.duke.exception.StorageSeparatorException;
+import seedu.duke.model.event.cca.EventCca;
+import seedu.duke.model.event.classlesson.EventClass;
+import seedu.duke.model.event.Event;
+import seedu.duke.common.LogManager;
+import seedu.duke.common.Messages;
+import seedu.duke.model.event.test.EventTest;
+import seedu.duke.model.event.tuition.EventTuition;
+import seedu.duke.storage.StorageManager;
+import seedu.duke.ui.UserInterface;
+import seedu.duke.model.event.EventParameter;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Scanner;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+//@@author durianpancakes
+/**
+ * Manages the reading and writing operations of Event Storage.
+ */
+public class EventStorageManager extends StorageManager {
+ private final EventListEncoder eventListEncoder;
+ private final EventListDecoder eventListDecoder;
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+ private final UserInterface userInterface;
+
+ /**
+ * Constructs an EventStorageManager with the given directory and fileName.
+ *
+ * @param directory String containing the directory to be accessed
+ * @param fileName String containing the fileName to be created/read from.
+ */
+ public EventStorageManager(String directory, String fileName) {
+ super(directory, fileName);
+ this.eventListEncoder = new EventListEncoder();
+ this.eventListDecoder = new EventListDecoder();
+ userInterface = UserInterface.getInstance();
+ }
+
+ /**
+ * Constructs an EventStorageManager with the given fileName.
+ *
+ * @param fileName String containing the fileName to be created/read from.
+ */
+ public EventStorageManager(String fileName) {
+ super(fileName);
+ this.eventListEncoder = new EventListEncoder();
+ this.eventListDecoder = new EventListDecoder();
+ userInterface = UserInterface.getInstance();
+ }
+
+ /**
+ * Saves Events to the data file.
+ *
+ * @param eventList ArrayList of Events to be saved.
+ * @throws IOException when there is an error writing to the data file.
+ */
+ public void saveData(ArrayList eventList) throws IOException {
+ ArrayList encodedEventList = eventListEncoder.encodeEventList(eventList);
+ Files.write(Path.of(DIRECTORY_FOLDER_PATH + fileName), encodedEventList);
+ }
+
+ /**
+ * Loads Events from the data file.
+ *
+ * @return ArrayList of Events loaded from the data file.
+ * @throws StorageCorruptedException when the contents of the data file cannot be read by EventListDecoder.
+ */
+ public EventParameter loadData() throws StorageCorruptedException {
+ File eventFile = new File(DIRECTORY_FOLDER_PATH + fileName);
+ ArrayList data = new ArrayList<>();
+ logger.log(Level.INFO, "Loading storage...");
+
+ try {
+ boolean fileCreated = createDataFile();
+ if (!fileCreated) {
+ logger.log(Level.INFO, "Data file found, reading data file...");
+ Scanner sc = new Scanner(eventFile);
+ while (sc.hasNext()) {
+ String dataString = sc.nextLine();
+ data.add(dataString);
+ }
+ ArrayList eventList = eventListDecoder.decodeEventList(data);
+ logger.log(Level.INFO, "Load successful");
+ return separateEventsIntoList(eventList);
+ } else {
+ logger.log(Level.INFO, "Data file not found, initializing data file...");
+ }
+ } catch (IOException e) {
+ userInterface.showToUser(Messages.MESSAGE_STORAGE_READ_ERROR);
+ logger.log(Level.SEVERE, "Initialization failed");
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "Event Storage corrupted");
+ throw new StorageCorruptedException();
+ }
+ return new EventParameter();
+ }
+
+ /**
+ * Separates the master list of events into separate Event subclasses (i.e EventCca, EventClass, EventTest,
+ * EventTuition).
+ *
+ * @param events ArrayList of Events containing all events read from the storage.
+ * @return EventParameter containing ArrayLists of Event subclasses.
+ */
+ private EventParameter separateEventsIntoList(ArrayList events) {
+ ArrayList ccas = new ArrayList<>();
+ ArrayList classes = new ArrayList<>();
+ ArrayList tests = new ArrayList<>();
+ ArrayList tuitions = new ArrayList<>();
+ for (Event event : events) {
+ if (event instanceof EventCca) {
+ ccas.add(event);
+ } else if (event instanceof EventTuition) {
+ tuitions.add(event);
+ } else if (event instanceof EventClass) {
+ classes.add(event);
+ } else if (event instanceof EventTest) {
+ tests.add(event);
+ }
+ }
+ return new EventParameter(ccas, tests, classes, tuitions);
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/quiz/QuizListDecoder.java b/src/main/java/seedu/duke/storage/quiz/QuizListDecoder.java
new file mode 100644
index 0000000000..a14346cc3c
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/quiz/QuizListDecoder.java
@@ -0,0 +1,54 @@
+package seedu.duke.storage.quiz;
+
+import seedu.duke.exception.StorageCorruptedException;
+import seedu.duke.model.quiz.Quiz;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+
+import static java.time.temporal.ChronoUnit.DAYS;
+
+//@@author untitle4
+/**
+ * To decode the content in the quizzes text file and add them in an Arraylist as quizzes
+ * to initialize the quiz list.
+ */
+public class QuizListDecoder {
+ public static final int NUM_OF_DAYS = -2;
+
+ //@@author AndreWongZH
+ /**
+ * Reads and extracts out the information from quiz storage.
+ *
+ * @param encodedQuizList An array list of quizzes in string.
+ * @return An arraylist of type quiz stored in the text file.
+ * @throws StorageCorruptedException If data parsed is not what is expected.
+ */
+ public ArrayList decodeQuizList(ArrayList encodedQuizList) throws StorageCorruptedException {
+ final ArrayList decodedQuizzes = new ArrayList<>();
+ for (String encodedQuiz : encodedQuizList) {
+ decodedQuizzes.add(decodeQuizFromString(encodedQuiz));
+ }
+
+ return decodedQuizzes;
+ }
+
+ //@@author untitle4
+ private Quiz decodeQuizFromString(String encodedQuiz) throws StorageCorruptedException {
+ final String[] data = encodedQuiz.trim().split("\\|");
+
+ try {
+ String question = data[0];
+ String option1 = data[1];
+ String option2 = data[2];
+ String option3 = data[3];
+ String option4 = data[4];
+ int answer = Integer.parseInt(data[5]);
+ String explanation = data[6];
+ LocalDate lastAccessed = LocalDate.parse(data[7]);
+ return new Quiz(question, option1, option2, option3, option4, answer, explanation, lastAccessed);
+ } catch (Exception e) {
+ throw new StorageCorruptedException();
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/quiz/QuizListEncoder.java b/src/main/java/seedu/duke/storage/quiz/QuizListEncoder.java
new file mode 100644
index 0000000000..a86063ed35
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/quiz/QuizListEncoder.java
@@ -0,0 +1,22 @@
+package seedu.duke.storage.quiz;
+
+import seedu.duke.model.quiz.Quiz;
+
+import java.util.ArrayList;
+
+//@@author untitle4
+/**
+ * To encode the String form of quizzes in the Arraylist and store them in a text file.
+ */
+public class QuizListEncoder {
+
+ public ArrayList encodeQuizList(ArrayList quizList) {
+ ArrayList encodedQuizzes = new ArrayList<>();
+
+ for (Quiz quiz: quizList) {
+ encodedQuizzes.add(quiz.convertToData());
+ }
+
+ return encodedQuizzes;
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/quiz/QuizStorageManager.java b/src/main/java/seedu/duke/storage/quiz/QuizStorageManager.java
new file mode 100644
index 0000000000..d54365064d
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/quiz/QuizStorageManager.java
@@ -0,0 +1,74 @@
+package seedu.duke.storage.quiz;
+
+import seedu.duke.common.LogManager;
+import seedu.duke.common.Messages;
+import seedu.duke.exception.StorageCorruptedException;
+import seedu.duke.storage.StorageManager;
+import seedu.duke.ui.UserInterface;
+import seedu.duke.model.quiz.Quiz;
+
+import java.io.IOException;
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Scanner;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+//@@author untitle4
+/**
+ * Help manage the storage of quizzes into a text file and load quizzes from the file
+ * when the program is executed.
+ */
+public class QuizStorageManager extends StorageManager {
+ private final QuizListEncoder quizListEncoder;
+ private final QuizListDecoder quizListDecoder;
+ private static final Logger logger = LogManager.getLogManagerInstance().getLogger();
+ private final UserInterface userInterface;
+
+ public QuizStorageManager(String fileName) {
+ super(fileName);
+ this.quizListEncoder = new QuizListEncoder();
+ this.quizListDecoder = new QuizListDecoder();
+ this.userInterface = UserInterface.getInstance();
+ }
+
+ /**
+ * Load the content of the quiz text file.
+ * @return An ArrayList of type quiz
+ */
+ public ArrayList loadData() throws StorageCorruptedException {
+ File quizFile = new File(DIRECTORY_FOLDER_PATH + fileName);
+ ArrayList data = new ArrayList<>();
+ logger.log(Level.INFO, "Loading storage...");
+
+ try {
+ boolean fileCreated = createDataFile();
+ if (!fileCreated) {
+ logger.log(Level.INFO, "Data file found, reading data file...");
+ Scanner sc = new Scanner(quizFile);
+ while (sc.hasNext()) {
+ String dataString = sc.nextLine();
+ data.add(dataString);
+ }
+ logger.log(Level.INFO, "Load successful");
+ return quizListDecoder.decodeQuizList(data);
+ } else {
+ logger.log(Level.INFO, "Data file not found, initializing data file...");
+ }
+ } catch (IOException e) {
+ userInterface.showToUser(Messages.MESSAGE_STORAGE_READ_ERROR);
+ logger.log(Level.SEVERE, "Initialization failed");
+ } catch (StorageCorruptedException e) {
+ logger.log(Level.SEVERE, "Quiz Storage corrupted");
+ throw new StorageCorruptedException();
+ }
+ return new ArrayList<>();
+ }
+
+ public void saveData(ArrayList quizList, String filePath) throws IOException {
+ ArrayList encodedQuizList = quizListEncoder.encodeQuizList(quizList);
+ Files.write(Path.of(DIRECTORY_FOLDER_PATH + filePath), encodedQuizList);
+ }
+}
diff --git a/src/main/java/seedu/duke/ui/CalendarWeekRenderer.java b/src/main/java/seedu/duke/ui/CalendarWeekRenderer.java
new file mode 100644
index 0000000000..2adf1f8a52
--- /dev/null
+++ b/src/main/java/seedu/duke/ui/CalendarWeekRenderer.java
@@ -0,0 +1,134 @@
+package seedu.duke.ui;
+
+import seedu.duke.controller.parser.DateTimeParser;
+import seedu.duke.model.event.Event;
+import seedu.duke.model.event.EventManager;
+import seedu.duke.model.event.ListWeekCommand;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+
+import static seedu.duke.ui.CalendarWeekRendererUtils.DATE_LABEL_SPACES_OFFSET;
+import static seedu.duke.ui.CalendarWeekRendererUtils.DAY_LABEL_SPACES_OFFSET;
+
+//@@author durianpancakes
+/**
+ * Driver class for rendering the Week View of the `list event week` command.
+ */
+public class CalendarWeekRenderer {
+ private final EventManager eventManager;
+ private final UserInterface userInterface;
+ private static final int DAYS_IN_WEEK = 7;
+ public static final String EMPTY_STRING = "";
+ private CalendarWeekRendererUtils utils;
+
+ /**
+ * Constructs a CalendarWeekRenderer instance.
+ * Automatically calls renderWeekSchedule() which produces the Week View.
+ *
+ * @param eventManager EventManager instance for the usage of certain helper methods in the class.
+ * @param listWeekCommand ListWeekCommand to identify if the current or next week is to be listed.
+ */
+ public CalendarWeekRenderer(EventManager eventManager, ListWeekCommand listWeekCommand) {
+ this.eventManager = eventManager;
+ this.userInterface = UserInterface.getInstance();
+ renderWeekSchedule(listWeekCommand);
+ }
+
+ /**
+ * Driver method to produce the Week View.
+ *
+ * @param listWeekCommand ListWeekCommand to identify if the current or next week is to be listed.
+ */
+ private void renderWeekSchedule(ListWeekCommand listWeekCommand) {
+ Calendar calendar = Calendar.getInstance();
+ ArrayList> weekMasterList = new ArrayList<>();
+ ArrayList daysOfWeek = null;
+ DateTimeParser dateTimeParser = new DateTimeParser();
+
+ if (listWeekCommand == ListWeekCommand.CURRENT_WEEK) {
+ weekMasterList = eventManager.getCurrentWeekEventMasterList();
+ daysOfWeek = dateTimeParser.getDaysOfWeek(calendar);
+ } else if (listWeekCommand == ListWeekCommand.NEXT_WEEK) {
+ calendar.add(Calendar.DAY_OF_MONTH, DAYS_IN_WEEK);
+ weekMasterList = eventManager.getNextWeekEventMasterList();
+ daysOfWeek = dateTimeParser.getDaysOfWeek(calendar);
+ }
+
+ utils = new CalendarWeekRendererUtils(weekMasterList);
+
+ // Sort master list before proceeding
+ for (int i = 0; i < DAYS_IN_WEEK; i++) {
+ Collections.sort(weekMasterList.get(i));
+ }
+
+ // 17 spaces per day
+
+ // Printing DAY headers
+ // 6 spaces before each DAY_LABEL
+ StringBuilder dayLabelString = new StringBuilder();
+ for (int i = 0; i < DAYS_IN_WEEK; i++) {
+ dayLabelString.append(utils.getSpaces(DAY_LABEL_SPACES_OFFSET));
+ dayLabelString.append(utils.getDayLabel(i));
+ dayLabelString.append(utils.getSpaces(DAY_LABEL_SPACES_OFFSET));
+ }
+
+ userInterface.showToUser(dayLabelString.toString());
+
+ StringBuilder todayLabelString = new StringBuilder();
+ for (int i = 0; i < DAYS_IN_WEEK; i++) {
+ assert daysOfWeek != null;
+ todayLabelString.append(utils.getIsToday(daysOfWeek.get(i)));
+ }
+ userInterface.showToUser(todayLabelString.toString());
+
+ // Printing DATE headers
+ StringBuilder dateHeaderString = new StringBuilder();
+ for (int i = 0; i < DAYS_IN_WEEK; i++) {
+ dateHeaderString.append(utils.getSpaces(DATE_LABEL_SPACES_OFFSET));
+ dateHeaderString.append(utils.getDateLabel(daysOfWeek, i));
+ dateHeaderString.append(utils.getSpaces(DATE_LABEL_SPACES_OFFSET));
+ }
+ userInterface.showToUser(dateHeaderString.toString());
+
+ while (!utils.isThereNothingLeftToPrint()) {
+ // Print in order of ICON -> DESCRIPTION -> START-END TIMES
+ // NOTE: DOES NOT SUPPORT TUITION LOCATION YET
+ StringBuilder eventIconString = new StringBuilder();
+ for (int i = 0; i < DAYS_IN_WEEK; i++) {
+ // Count from Monday to Sunday
+ // One Event takes up 5 columns max, with one space between each column
+ eventIconString.append(utils.getEventIcons(weekMasterList.get(i), i));
+ }
+
+ StringBuilder eventDescriptionString = new StringBuilder();
+ for (int i = 0; i < DAYS_IN_WEEK; i++) {
+ // Count from Monday to Sunday
+ // One Event takes up 5 columns max, with one space between each column
+ eventDescriptionString.append(utils.getDescriptions(weekMasterList.get(i), i));
+ }
+
+ StringBuilder eventStartEndString = new StringBuilder();
+ for (int i = 0; i < DAYS_IN_WEEK; i++) {
+ // Count from Monday to Sunday
+ // One Event takes up 5 columns max, with one space between each column
+ eventStartEndString.append(utils.getStartEndTime(weekMasterList.get(i), i));
+ }
+
+ StringBuilder breakTimeString = new StringBuilder();
+ for (int i = 0; i < DAYS_IN_WEEK; i++) {
+ // Count from Monday to Sunday
+ // One Event takes up 5 columns max, with one space between each column
+ breakTimeString.append(utils.getBreakTimeString(weekMasterList.get(i), i));
+ }
+
+ userInterface.showToUser(eventIconString.toString(),
+ eventDescriptionString.toString(),
+ eventStartEndString.toString(),
+ EMPTY_STRING,
+ breakTimeString.toString(),
+ EMPTY_STRING);
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/ui/CalendarWeekRendererUtils.java b/src/main/java/seedu/duke/ui/CalendarWeekRendererUtils.java
new file mode 100644
index 0000000000..9691dca5f3
--- /dev/null
+++ b/src/main/java/seedu/duke/ui/CalendarWeekRendererUtils.java
@@ -0,0 +1,272 @@
+package seedu.duke.ui;
+
+import seedu.duke.common.Messages;
+import seedu.duke.controller.parser.DateTimeParser;
+import seedu.duke.model.event.Event;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+
+//@@author durianpancakes
+/**
+ * Utility class containing methods to assist CalendarWeekRenderer to produce the Week View.
+ */
+public class CalendarWeekRendererUtils {
+ private final int[] eventNumberCounter;
+ private final int [] eventIndexCounters = {1, 1, 1, 1, 1, 1, 1};
+ public static final String TODAY_LABEL = "[TODAY]";
+ public static final int COLUMN_SPACES = 17;
+ public static final int DESCRIPTION_THRESHOLD = 14;
+ public static final int DAY_LABEL_SPACES_OFFSET = 6;
+ public static final int DATE_LABEL_SPACES_OFFSET = 5;
+ public static final int TODAY_LABEL_SPACES_OFFSET = 5;
+ public static final int NO_EVENTS = 0;
+ public static final int ONE_EVENT = 1;
+ public static final int INDEX_OFFSET = 1;
+ public static final int MONDAY = 0;
+ public static final int TUESDAY = 1;
+ public static final int WEDNESDAY = 2;
+ public static final int THURSDAY = 3;
+ public static final int FRIDAY = 4;
+ public static final int SATURDAY = 5;
+ public static final int SUNDAY = 6;
+
+ /**
+ * Constructs a CalendarWeekRendererUtils instance.
+ *
+ * @param weekMasterList ArrayList of ArrayList of Events containing all events in the week.
+ */
+ public CalendarWeekRendererUtils(ArrayList> weekMasterList) {
+ eventNumberCounter = new int[7];
+ this.eventNumberCounter[MONDAY] = weekMasterList.get(MONDAY).size();
+ this.eventNumberCounter[TUESDAY] = weekMasterList.get(TUESDAY).size();
+ this.eventNumberCounter[WEDNESDAY] = weekMasterList.get(WEDNESDAY).size();
+ this.eventNumberCounter[THURSDAY] = weekMasterList.get(THURSDAY).size();
+ this.eventNumberCounter[FRIDAY] = weekMasterList.get(FRIDAY).size();
+ this.eventNumberCounter[SATURDAY] = weekMasterList.get(SATURDAY).size();
+ this.eventNumberCounter[SUNDAY] = weekMasterList.get(SUNDAY).size();
+ }
+
+ /**
+ * Reduces the count of a particular eventNumberCounter by 1.
+ *
+ * @param counterIndex int containing the counter to be reduced.
+ */
+ private void reduceCounter(int counterIndex) {
+ eventNumberCounter[counterIndex]--;
+ }
+
+ private int getCounter(int counterIndex) {
+ return eventNumberCounter[counterIndex];
+ }
+
+ /**
+ * Helper function to check if there are any Events left to be printed.
+ *
+ * @return boolean, true if there is something left to print; false if there is nothing left to print.
+ */
+ public boolean isThereNothingLeftToPrint() {
+ return eventNumberCounter[MONDAY] == NO_EVENTS && eventNumberCounter[TUESDAY] == NO_EVENTS
+ && eventNumberCounter[WEDNESDAY] == NO_EVENTS && eventNumberCounter[THURSDAY] == NO_EVENTS
+ && eventNumberCounter[FRIDAY] == NO_EVENTS && eventNumberCounter[SATURDAY] == NO_EVENTS
+ && eventNumberCounter[SUNDAY] == NO_EVENTS;
+ }
+
+ /**
+ * Obtains the day label of a given day.
+ *
+ * @param num int containing the day number based on the Calendar API.
+ * @return String containing the day label based on the day number provided.
+ */
+ public String getDayLabel(int num) {
+ switch (num) {
+ case 0:
+ return Messages.MESSAGE_MONDAY_LABEL;
+ case 1:
+ return Messages.MESSAGE_TUESDAY_LABEL;
+ case 2:
+ return Messages.MESSAGE_WEDNESDAY_LABEL;
+ case 3:
+ return Messages.MESSAGE_THURSDAY_LABEL;
+ case 4:
+ return Messages.MESSAGE_FRIDAY_LABEL;
+ case 5:
+ return Messages.MESSAGE_SATURDAY_LABEL;
+ case 6:
+ return Messages.MESSAGE_SUNDAY_LABEL;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Obtains the Event's duration in NUM "h" NUM "m" format.
+ *
+ * @param start Calendar containing the start time of the Event.
+ * @param end Calendar containing the end time of the Event.
+ * @return String containing the Event's duration formatted in " h m".
+ */
+ public String getEventDuration(Calendar start, Calendar end) {
+ int hours = end.get(Calendar.HOUR_OF_DAY) - start.get(Calendar.HOUR_OF_DAY);
+ int minutes = end.get(Calendar.MINUTE) - end.get(Calendar.MINUTE);
+
+ return hours + "h" + minutes + "m";
+ }
+
+ /**
+ * Checks if the date given is today and obtains the Today label if needed.
+ *
+ * @param compareCalendar Calendar containing the Date of the day to be checked.
+ * @return String containing the TODAY_LABEL if the given Calendar has the same day, month and year, else, return
+ * a string containing COLUMN_GAP number of whitespaces.
+ */
+ public String getIsToday(Calendar compareCalendar) {
+ Calendar today = Calendar.getInstance();
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ if (dateTimeParser.isDateEqual(compareCalendar, today)) {
+ return getSpaces(TODAY_LABEL_SPACES_OFFSET) + TODAY_LABEL + getSpaces(TODAY_LABEL_SPACES_OFFSET);
+ } else {
+ return getSpaces(COLUMN_SPACES);
+ }
+ }
+
+ /**
+ * Helper method to obtain a String containing a given number of whitespaces.
+ *
+ * @param num int containing the number of spaces in the String to be returned.
+ * @return String containing the number of spaces required.
+ */
+ public String getSpaces(int num) {
+ return " ".repeat(Math.max(0, num));
+ }
+
+ /**
+ * Obtains the Date label of a given day in the week.
+ *
+ * @param daysOfWeek ArrayList containing the Calendars of a week.
+ * @param num int containing the Day number (0 for Monday, 6 for Sunday).
+ * @return String containing the Day label formatted in [DD-MM].
+ */
+ public String getDateLabel(ArrayList daysOfWeek, int num) {
+ Calendar calendar = daysOfWeek.get(num);
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ return "[" + dateTimeParser.obtainFormattedDayAndMonthString(calendar) + "]";
+ }
+
+ /**
+ * Obtains the Event Icon of Event to be rendered.
+ *
+ * @param events ArrayList containing Events of the week.
+ * @param counterIndex int containing the day's event to be printed.
+ * @return String containing the Event's Icon, if any. Else, returns a string containing COLUMN_GAP number of
+ * whitespaces.
+ */
+ public String getEventIcons(ArrayList events, int counterIndex) {
+ StringBuilder eventIconsString = new StringBuilder();
+
+ if (getCounter(counterIndex) != NO_EVENTS) {
+ String indexString = eventIndexCounters[counterIndex] + ".";
+ String iconString = events.get(eventIndexCounters[counterIndex] - INDEX_OFFSET).getIcon();
+ eventIconsString.append(indexString);
+ eventIconsString.append(iconString);
+ eventIconsString.append(getSpaces(COLUMN_SPACES - iconString.length() - indexString.length()));
+ } else {
+ eventIconsString.append(getSpaces(COLUMN_SPACES));
+ }
+
+ return eventIconsString.toString();
+ }
+
+ /**
+ * Obtains the Event's description to be rendered.
+ *
+ * @param events ArrayList containing Events of the week.
+ * @param counterIndex int containing the day's event to be printed, if any.
+ * @return String containing the Event's description, if any. Else, returns a string containing COLUMN_GAP number of
+ * whitespaces.
+ */
+ public String getDescriptions(ArrayList events, int counterIndex) {
+ StringBuilder descriptionsString = new StringBuilder();
+
+ if (getCounter(counterIndex) != NO_EVENTS) {
+ String indexString = eventIndexCounters[counterIndex] + ".";
+ int indexStringLength = indexString.length();
+ descriptionsString.append(getSpaces(indexStringLength));
+ String descriptionString = events.get(eventIndexCounters[counterIndex] - INDEX_OFFSET).getDescription();
+ if (descriptionString.length() > DESCRIPTION_THRESHOLD) {
+ // Truncate the string as the description is too long
+ descriptionString = descriptionString.substring(0, 11) + "...";
+ }
+ descriptionsString.append(descriptionString);
+ descriptionsString.append(getSpaces(COLUMN_SPACES - descriptionString.length() - indexStringLength));
+ } else {
+ descriptionsString.append(getSpaces(COLUMN_SPACES));
+ }
+
+ return descriptionsString.toString();
+ }
+
+ /**
+ * Obtains the Event's start end time to be rendered.
+ *
+ * @param events ArrayList containing Events of the week.
+ * @param counterIndex int containing the day's event to be printed, if any.
+ * @return String containing the Event's start end time, if any. Else, returns a string containing COLUMN_GAP
+ * number of whitespaces.
+ */
+ public String getStartEndTime(ArrayList events, int counterIndex) {
+ StringBuilder startEndTimesString = new StringBuilder();
+
+ if (getCounter(counterIndex) != NO_EVENTS) {
+ String indexString = eventIndexCounters[counterIndex] + ".";
+ int indexStringLength = indexString.length();
+ startEndTimesString.append(getSpaces(indexStringLength));
+ Calendar startCalendar = events.get(eventIndexCounters[counterIndex] - INDEX_OFFSET).getStart();
+ Calendar endCalendar = events.get(eventIndexCounters[counterIndex] - INDEX_OFFSET).getEnd();
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ String startString = dateTimeParser.parseTime(startCalendar);
+ String endString = dateTimeParser.parseTime(endCalendar);
+ String startEndString = startString + "-" + endString;
+ startEndTimesString.append(startEndString);
+ startEndTimesString.append(getSpaces(COLUMN_SPACES - startEndString.length() - indexStringLength));
+ } else {
+ startEndTimesString.append(getSpaces(COLUMN_SPACES));
+ }
+
+ return startEndTimesString.toString();
+ }
+
+ /**
+ * Obtain the break time between each events to be rendered.
+ *
+ * @param events ArrayList containing Events of the week.
+ * @param counterIndex int containing the day's event to be printed, if any.
+ * @return String containing the Event's break time before another event, if any. Else, returns a string
+ * containing COLUMN_GAP number of whitespaces.
+ */
+ public String getBreakTimeString(ArrayList events, int counterIndex) {
+ StringBuilder breakTimeString = new StringBuilder();
+ if (getCounter(counterIndex) != NO_EVENTS) {
+ if (getCounter(counterIndex) > ONE_EVENT) {
+ String indexString = eventIndexCounters[counterIndex] + ".";
+ int indexStringLength = indexString.length();
+ breakTimeString.append(getSpaces(indexStringLength));
+ Calendar thisEventEnd = events.get(eventIndexCounters[counterIndex] - INDEX_OFFSET).getEnd();
+ Calendar nextEventStart = events.get(eventIndexCounters[counterIndex]).getStart();
+ int hours = nextEventStart.get(Calendar.HOUR_OF_DAY) - thisEventEnd.get(Calendar.HOUR_OF_DAY);
+ int minutes = nextEventStart.get(Calendar.MINUTE) - thisEventEnd.get(Calendar.MINUTE);
+ String breakString = hours + "h" + minutes + "m break";
+ breakTimeString.append(breakString);
+ breakTimeString.append(getSpaces(COLUMN_SPACES - breakString.length() - indexStringLength));
+ } else {
+ breakTimeString.append(getSpaces(COLUMN_SPACES));
+ }
+ reduceCounter(counterIndex);
+ eventIndexCounters[counterIndex]++;
+ } else {
+ breakTimeString.append(getSpaces(COLUMN_SPACES));
+ }
+
+ return breakTimeString.toString();
+ }
+}
diff --git a/src/main/java/seedu/duke/ui/UserInterface.java b/src/main/java/seedu/duke/ui/UserInterface.java
new file mode 100644
index 0000000000..bc1f7cd99a
--- /dev/null
+++ b/src/main/java/seedu/duke/ui/UserInterface.java
@@ -0,0 +1,158 @@
+package seedu.duke.ui;
+
+import seedu.duke.common.Messages;
+import seedu.duke.controller.ControlManager;
+import seedu.duke.controller.parser.CommandType;
+import seedu.duke.model.ConfigParameter;
+import seedu.duke.model.Model;
+import seedu.duke.model.event.EventManager;
+import seedu.duke.model.event.ListWeekCommand;
+import seedu.duke.storage.contact.ContactStorageManager;
+import seedu.duke.storage.event.EventStorageManager;
+import seedu.duke.storage.quiz.QuizStorageManager;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Scanner;
+
+//@@author durianpancakes
+/**
+ * Singleton UserInterface class containing methods to obtain input from user, and prints output to use.
+ */
+public class UserInterface {
+ private static UserInterface userInterface = null;
+ private final Scanner in;
+ private final PrintStream out;
+ public static final String CURSOR = ">> ";
+
+ private UserInterface() {
+ this.in = new Scanner(System.in);
+ this.out = System.out;
+ }
+
+ /**
+ * Method to obtain the singleton instance of UserInterface.
+ *
+ * @return UserInterface instance.
+ */
+ public static UserInterface getInstance() {
+ if (userInterface == null) {
+ userInterface = new UserInterface();
+ }
+
+ return userInterface;
+ }
+
+ /**
+ * Displays the Welcome message to the user.
+ *
+ * @param configParameter ConfigParameter containing the recommended hours to be printed.
+ */
+ public void showWelcomeMessage(ConfigParameter configParameter) {
+ showToUser(Messages.MESSAGE_HELLO + configParameter.getName(),
+ Messages.MESSAGE_SHOW_HOURS + configParameter.getRecommendedHours());
+ out.print(Messages.MESSAGE_PROMPT_COMMAND);
+ }
+
+ /**
+ * Obtains the user's command.
+ *
+ * @return String containing the user's command.
+ */
+ public String getUserCommand() {
+ // Adds an additional row space for better readability
+ showEmptyLine();
+ // Indicator for user input:
+ out.print(CURSOR);
+
+ return in.nextLine();
+ }
+
+ private void showEmptyLine() {
+ showToUser("");
+ }
+
+ /**
+ * Method to display multiple Strings.
+ *
+ * @param message String containing message to be printed. Can be separated by "," to be printed on another
+ * line
+ */
+ public void showToUser(String... message) {
+ for (String m : message) {
+ out.println(m);
+ }
+ }
+
+ //@@author AndreWongZH
+ /**
+ * Prints to user through an array list of strings.
+ * This is invoked from either list or find command.
+ *
+ * @param stringArrayList ArrayList of type string to be printed.
+ */
+ public void printArray(ArrayList stringArrayList) {
+ assert stringArrayList != null;
+ for (String line : stringArrayList) {
+ showToUser(line);
+ }
+ }
+
+ //@@author AndreWongZH
+ /**
+ * Compares if command type is equal to BYE and returns true if it does.
+ *
+ * @param commandType A CommandType of one of the commands.
+ * @return A boolean to indicate if program loop should end.
+ */
+ public boolean checkIfProgramEnds(CommandType commandType) {
+ return commandType != CommandType.BYE;
+ }
+
+ //@@author AndreWongZH
+ /**
+ * Passes user input into the control manager for it to run its logic.
+ *
+ * @param model Data stored in the program.
+ * @param eventStorageManager Updates the storage data in event.txt after command is ran.
+ * @param quizStorageManager Updates the storage data in quiz.txt after command is ran.
+ * @return A boolean to tell the program to quit or not.
+ */
+ public boolean runUi(Model model,
+ EventStorageManager eventStorageManager,
+ QuizStorageManager quizStorageManager,
+ ContactStorageManager contactStorageManager) {
+ CommandType commandType = null;
+ String line = getUserCommand();
+
+ if (hasInputs(line)) {
+ ControlManager controlManager = new ControlManager(line, model,
+ eventStorageManager, quizStorageManager, contactStorageManager);
+ commandType = controlManager.runLogic();
+ }
+
+ return checkIfProgramEnds(commandType);
+ }
+
+ //@@author AndreWongZH
+ /**
+ * Returns true if input is not empty.
+ *
+ * @param userInput String the user has entered.
+ * @return A boolean to check if user input is not empty.
+ */
+ private boolean hasInputs(String userInput) {
+ return !userInput.trim().isEmpty();
+ }
+
+ //@@author durianpancakes
+ /**
+ * Method to print the week schedule of the user.
+ * @param eventManager EventManager instance for CalendarWeekRenderer to access helper methods.
+ * @param listWeekCommand ListWeekCommand to identify the week to be printed. CURRENT_WEEK for this week,
+ * NEXT_WEEK for the next week.
+ */
+ public void printWeekSchedule(EventManager eventManager, ListWeekCommand listWeekCommand) {
+ new CalendarWeekRenderer(eventManager, listWeekCommand);
+ }
+}
diff --git a/src/main/java/seedu/duke/unused/DoneCommand.java b/src/main/java/seedu/duke/unused/DoneCommand.java
new file mode 100644
index 0000000000..545887844f
--- /dev/null
+++ b/src/main/java/seedu/duke/unused/DoneCommand.java
@@ -0,0 +1,27 @@
+package seedu.duke.unused;
+
+import seedu.duke.controller.command.Command;
+import seedu.duke.model.ModelMain;
+import seedu.duke.model.event.EventDataManager;
+
+//@@author AndreWongZH-unused
+/*
+ * We decided to remove the done feature because it does not tie in well with our application.
+ */
+/**
+ * Represents the command for setting class, cca, test and tuition to be done.
+ */
+public class DoneCommand extends Command {
+
+ public static final String INPUT_SPACE = " ";
+
+ public DoneCommand(String userInput) {
+ super(userInput);
+ }
+
+ @Override
+ public void execute(ModelMain modelMain) {
+ EventDataManager eventDataModel = (EventDataManager) modelMain;
+ eventDataModel.setDone(userInput.split(INPUT_SPACE));
+ }
+}
diff --git a/src/main/java/seedu/duke/unused/Event.java b/src/main/java/seedu/duke/unused/Event.java
new file mode 100644
index 0000000000..db4f69bfb1
--- /dev/null
+++ b/src/main/java/seedu/duke/unused/Event.java
@@ -0,0 +1,21 @@
+package seedu.duke.unused;
+
+public class Event implements Comparable {
+ public static final String DONE_STATUS = "[DONE]";
+ public static final String NOT_DONE_STATUS = "[NOT DONE]";
+
+ protected boolean isDone;
+
+ public boolean isDone() {
+ return isDone;
+ }
+
+ public String getStatus() {
+ return (isDone ? DONE_STATUS : NOT_DONE_STATUS);
+ }
+
+ @Override
+ public int compareTo(Event o) {
+ return 0;
+ }
+}
diff --git a/src/main/java/seedu/duke/unused/QuizListDecoder.java b/src/main/java/seedu/duke/unused/QuizListDecoder.java
new file mode 100644
index 0000000000..3c0e64e8e2
--- /dev/null
+++ b/src/main/java/seedu/duke/unused/QuizListDecoder.java
@@ -0,0 +1,34 @@
+package seedu.duke.unused;
+
+//@@author AndreWongZH-unused
+
+/*
+ We decided to scrap the functionality that notifies user if quiz has not been taken since 2 days ago.
+ This is because the taking of quiz is randomized and thus there is no way in our current version to
+ manually update the date for quiz taken.
+ */
+
+/*
+ public class QuizListDecoder {
+ public static final int NUM_OF_DAYS = -2;
+
+ //@@author AndreWongZH
+ public ArrayList decodeQuizList(ArrayList encodedQuizList) throws StorageCorruptedException {
+ final ArrayList decodedQuizzes = new ArrayList<>();
+
+ for (Quiz quiz : decodedQuizzes) {
+ long numDays = DAYS.between(LocalDate.now(), quiz.getLastAccessed());
+ if (numDays <= NUM_OF_DAYS) {
+ Notify.create()
+ .title("Plan&score Notification")
+ .text("you have outdated quizzes! Attempt them now!")
+ .hideAfter(10000)
+ .showInformation();
+ break;
+ }
+ }
+
+ return decodedQuizzes;
+ }
+ }
+*/
diff --git a/src/main/java/seedu/duke/unused/QuizManager b/src/main/java/seedu/duke/unused/QuizManager
new file mode 100644
index 0000000000..3c88270dfd
--- /dev/null
+++ b/src/main/java/seedu/duke/unused/QuizManager
@@ -0,0 +1,43 @@
+//@@author elizabethcwt-unused
+
+// This part of the loop was unused as our team decided to change the valid number of quiz questions to attempt, from
+// just either 10, 20 or 30, to any number from 1 to the total number of quiz questions in the quiz list
+
+//import seedu.duke.model.ModelManager;
+//import seedu.duke.model.quiz.QuizInteractable;
+//
+//public class QuizManager extends ModelManager implements QuizInteractable {
+// if ((noOfQues > getQuizListSize()) && (getQuizListSize() < 10))
+// {
+//
+// // Assert that noOfQues is more than the quiz size, and quiz size is less than 10
+// assert ((noOfQues > getQuizListSize()) && (getQuizListSize() < 10));
+//
+// // If user wants to try more questions than he/she has in the current quiz, and has less than 10
+// // questions
+// userInterface.showToUser(String.format(Messages.MESSAGE_INSUFFICIENT_QUES_LESS_THAN_10,
+// getQuizListSize()));
+// } else if (noOfQues > getQuizListSize() && (getQuizListSize() >=10))
+//
+// {
+//
+// // Assert that noOfQues is more than the quiz size, and quiz size is at least 10
+// assert ((noOfQues > getQuizListSize()) && (getQuizListSize() >= 10));
+//
+// // If user wants to try more questions than he/she has in the current quiz, but has at least 10
+// // questions
+// userInterface.showToUser(String.format(Messages.MESSAGE_INSUFFICIENT_QUES_MORE_THAN_10,
+// getQuizListSize()));
+// }
+//}
+//
+//@@author elizabethcwt-unused
+//
+// public static final String MESSAGE_INSUFFICIENT_QUES_LESS_THAN_10 = "OOPS! You wanted to take a quiz with "
+// + QuizManager.noOfQues + " questions, but your current quiz only has %s question(s).\nPlease add more"
+// + " questions to your quiz via the 'add quiz' command!\n";
+//
+// //@@author elizabethcwt-unused
+// public static final String MESSAGE_INSUFFICIENT_QUES_MORE_THAN_10 = "OOPS! You wanted to take a quiz with "
+// + QuizManager.noOfQues + " questions, but your current quiz only has %s question(s).\nPlease either add"
+// + "more questions to your quiz via the 'add quiz' command, or try a quiz with less questions!\n";
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/controller/ControlManagerTest.java b/src/test/java/seedu/duke/controller/ControlManagerTest.java
new file mode 100644
index 0000000000..7a7d371e1b
--- /dev/null
+++ b/src/test/java/seedu/duke/controller/ControlManagerTest.java
@@ -0,0 +1,52 @@
+package seedu.duke.controller;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.controller.parser.CommandType;
+import seedu.duke.model.ConfigParameter;
+import seedu.duke.model.Model;
+import seedu.duke.model.contact.ContactManager;
+import seedu.duke.model.event.EventManager;
+import seedu.duke.model.event.EventParameter;
+import seedu.duke.model.quiz.QuizManager;
+import seedu.duke.storage.contact.ContactStorageManager;
+import seedu.duke.storage.event.EventStorageManager;
+import seedu.duke.storage.quiz.QuizStorageManager;
+import seedu.duke.model.config.ConfigManager;
+
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+//@@author AndreWongZH
+class ControlManagerTest {
+ @Test
+ void runLogic_listEventInput_expectListCommand() {
+ ControlManager controlManager = initializeControlManager("list event");
+ CommandType actualCommandType = controlManager.runLogic();
+ assertEquals(CommandType.LIST, actualCommandType);
+ }
+
+ @Test
+ void runLogic_byeInput_expectByeCommand() {
+ ControlManager controlManager = initializeControlManager("bye");
+ CommandType actualCommandType = controlManager.runLogic();
+ assertEquals(CommandType.BYE, actualCommandType);
+ }
+
+ @Test
+ void runLogic_invalidInput_expectNull() {
+ ControlManager controlManager = initializeControlManager("invalid command");
+ CommandType actualCommandType = controlManager.runLogic();
+ assertNull(actualCommandType);
+ }
+
+ private ControlManager initializeControlManager(String userInput) {
+ Model model = new Model(new EventManager(new EventParameter(), new ConfigParameter()),
+ new ContactManager(new ArrayList<>()), new QuizManager(new ArrayList<>()), ConfigManager.getInstance());
+ EventStorageManager eventStorageManager = new EventStorageManager("events.txt");
+ QuizStorageManager quizStorageManager = new QuizStorageManager("quiz.txt");
+ ContactStorageManager contactStorageManager = new ContactStorageManager("contact.txt");
+ return new ControlManager(userInput, model, eventStorageManager, quizStorageManager, contactStorageManager);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/controller/EventManagerTest.java b/src/test/java/seedu/duke/controller/EventManagerTest.java
new file mode 100644
index 0000000000..352d01be27
--- /dev/null
+++ b/src/test/java/seedu/duke/controller/EventManagerTest.java
@@ -0,0 +1,29 @@
+package seedu.duke.controller;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.model.ConfigParameter;
+import seedu.duke.model.event.EventManager;
+import seedu.duke.model.event.EventParameter;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class EventManagerTest {
+
+ @Test
+ void addTestToList_exceedTime() throws MissingParameterException, EmptyParameterException,
+ SwappedParameterException {
+ EventManager eventManager = new EventManager(new EventParameter(), new ConfigParameter("me",
+ 10, true));
+ eventManager.getTestManager().add("add test /n Math test "
+ + "/s 2020-09-26 1000 /e 2020-09-26 1500");
+ eventManager.getTestManager().add("add test /n Eng test "
+ + "/s 2020-09-26 1600 /e 2020-09-26 2300");
+ int actualOutputs = eventManager.getTestManager().getTestListSize();
+ int expectedOutputs = 1;
+ assertEquals(expectedOutputs, actualOutputs);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/controller/ModelExtractorTest.java b/src/test/java/seedu/duke/controller/ModelExtractorTest.java
new file mode 100644
index 0000000000..9406557db0
--- /dev/null
+++ b/src/test/java/seedu/duke/controller/ModelExtractorTest.java
@@ -0,0 +1,44 @@
+package seedu.duke.controller;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.controller.parser.ModelExtractor;
+import seedu.duke.exception.InvalidModelException;
+import seedu.duke.model.ConfigParameter;
+import seedu.duke.model.Model;
+import seedu.duke.controller.parser.ModelType;
+import seedu.duke.model.ModelMain;
+import seedu.duke.model.contact.ContactManager;
+import seedu.duke.model.event.EventManager;
+import seedu.duke.model.event.EventParameter;
+import seedu.duke.model.event.classlesson.EventClassManager;
+import seedu.duke.model.quiz.QuizManager;
+import seedu.duke.model.config.ConfigManager;
+
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+//@@author AndreWongZH
+class ModelExtractorTest {
+ @Test
+ void retrieveModel_typeClass_eventClassManager() throws InvalidModelException {
+ Model model = initializeModel();
+ ModelExtractor modelExtractor = new ModelExtractor(model, ModelType.CLASS);
+ ModelMain actualModel = modelExtractor.retrieveModel();
+ assertEquals(EventClassManager.class, actualModel.getClass());
+ }
+
+ @Test
+ void retrieveModel_typeNull_null() throws InvalidModelException {
+ Model model = initializeModel();
+ ModelExtractor modelExtractor = new ModelExtractor(model, null);
+ ModelMain actualModel = modelExtractor.retrieveModel();
+ assertNull(actualModel);
+ }
+
+ private Model initializeModel() {
+ return new Model(new EventManager(new EventParameter(), new ConfigParameter()),
+ new ContactManager(new ArrayList<>()), new QuizManager(new ArrayList<>()), ConfigManager.getInstance());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/controller/command/CommandFactoryTest.java b/src/test/java/seedu/duke/controller/command/CommandFactoryTest.java
new file mode 100644
index 0000000000..05846fe721
--- /dev/null
+++ b/src/test/java/seedu/duke/controller/command/CommandFactoryTest.java
@@ -0,0 +1,22 @@
+package seedu.duke.controller.command;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.controller.parser.CommandType;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+//@@author AndreWongZH
+class CommandFactoryTest {
+ @Test
+ void generateActionableCommand_addType_addCommandClass() {
+ Command actualCommand = new CommandFactory(CommandType.ADD, "").generateActionableCommand();
+ assertEquals(AddCommand.class, actualCommand.getClass());
+ }
+
+ @Test
+ void generateActionableCommand_nullType_addCommandClass() {
+ Command actualCommand = new CommandFactory(CommandType.BYE, "").generateActionableCommand();
+ assertNull(actualCommand);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/controller/command/ListCommandTest.java b/src/test/java/seedu/duke/controller/command/ListCommandTest.java
new file mode 100644
index 0000000000..b5c23600a2
--- /dev/null
+++ b/src/test/java/seedu/duke/controller/command/ListCommandTest.java
@@ -0,0 +1,14 @@
+package seedu.duke.controller.command;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.exception.IncompleteListCommandException;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+//@@author AndreWongZH
+class ListCommandTest {
+ @Test
+ void execute_nullModel_expectException() {
+ ListCommand listCommand = new ListCommand("");
+ assertThrows(IncompleteListCommandException.class, () -> listCommand.execute(null));
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/controller/parser/CommandParserTest.java b/src/test/java/seedu/duke/controller/parser/CommandParserTest.java
new file mode 100644
index 0000000000..5cc7d1f36d
--- /dev/null
+++ b/src/test/java/seedu/duke/controller/parser/CommandParserTest.java
@@ -0,0 +1,31 @@
+package seedu.duke.controller.parser;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.exception.ExtraParameterException;
+import seedu.duke.exception.InvalidCommandException;
+import seedu.duke.exception.MissingModelException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+//@@author AndreWongZH
+class CommandParserTest {
+ @Test
+ void extractCommand_deleteString_deleteCommandType() throws InvalidCommandException,
+ MissingModelException, ExtraParameterException {
+ CommandType actualType = new CommandParser("delete 22").extractCommand();
+ assertEquals(CommandType.DELETE, actualType);
+ }
+
+ @Test
+ void extractCommand_singleDoneString_expectException() {
+ CommandParser commandParser = new CommandParser("done");
+ assertThrows(MissingModelException.class, commandParser::extractCommand);
+ }
+
+ @Test
+ void extractCommand_randomString_expectException() {
+ CommandParser commandParser = new CommandParser("random string");
+ assertThrows(InvalidCommandException.class, commandParser::extractCommand);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/controller/parser/ModelParserTest.java b/src/test/java/seedu/duke/controller/parser/ModelParserTest.java
new file mode 100644
index 0000000000..0fd5c25629
--- /dev/null
+++ b/src/test/java/seedu/duke/controller/parser/ModelParserTest.java
@@ -0,0 +1,29 @@
+package seedu.duke.controller.parser;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.exception.InvalidModelException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+//@@author AndreWongZH
+class ModelParserTest {
+ @Test
+ void extractModel_singleString_expectNull() throws InvalidModelException {
+ ModelType actualType = new ModelParser("single").extractModel();
+ assertNull(actualType);
+ }
+
+ @Test
+ void extractModel_getTuitionString_expectException() throws InvalidModelException {
+ ModelType actualType = new ModelParser("add tuition").extractModel();
+ assertEquals(ModelType.TUITION, actualType);
+ }
+
+ @Test
+ void extractModel_invalidString_expectException() {
+ ModelParser modelParser = new ModelParser("random string");
+ assertThrows(InvalidModelException.class, modelParser::extractModel);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/model/contact/ContactTest.java b/src/test/java/seedu/duke/model/contact/ContactTest.java
new file mode 100644
index 0000000000..a596ca8399
--- /dev/null
+++ b/src/test/java/seedu/duke/model/contact/ContactTest.java
@@ -0,0 +1,13 @@
+package seedu.duke.model.contact;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class ContactTest {
+ @Test
+ public void sampleTest() {
+ assertTrue(true);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/model/event/FindScheduleTest.java b/src/test/java/seedu/duke/model/event/FindScheduleTest.java
new file mode 100644
index 0000000000..f78f87b6a5
--- /dev/null
+++ b/src/test/java/seedu/duke/model/event/FindScheduleTest.java
@@ -0,0 +1,52 @@
+package seedu.duke.model.event;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.model.event.classlesson.EventClass;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+//@@author AndreWongZH
+class FindScheduleTest {
+ private final ArrayList classes = new ArrayList<>();
+ private final ArrayList ccas = new ArrayList<>();
+ private final ArrayList tests = new ArrayList<>();
+ private final ArrayList tuitions = new ArrayList<>();
+
+ public FindScheduleTest() {
+ Calendar cal = Calendar.getInstance();
+ cal.set(2020, Calendar.MARCH, 10, 23,10);
+ classes.add(new EventClass("Math exam", cal, cal));
+ classes.add(new EventClass("English exam", cal, cal));
+ }
+
+ @Test
+ void getFilteredEvents_matchingKeyword_oneSearchResult() {
+ FindSchedule findSchedule = new FindSchedule("math", classes, ccas, tests, tuitions);
+ ArrayList actualEvents = findSchedule.getFilteredEvents();
+ ArrayList expectedEvents = new ArrayList<>(
+ List.of("[CLASS] Math exam from 10th Mar 2020, 11:10PM to 10th Mar 2020, 11:10PM"));
+ assertEquals(expectedEvents, actualEvents);
+ }
+
+ @Test
+ void getFilteredEvents_nonMatchingKeyword_noSearchResult() {
+ FindSchedule findSchedule = new FindSchedule("science", classes, ccas, tests, tuitions);
+ ArrayList actualEvents = findSchedule.getFilteredEvents();
+ ArrayList expectedEvents = new ArrayList<>();
+ assertEquals(expectedEvents, actualEvents);
+ }
+
+ @Test
+ void getFilteredEvents_matchingMultipleKeyword_twoSearchResult() {
+ FindSchedule findSchedule = new FindSchedule("math english", classes, ccas, tests, tuitions);
+ ArrayList actualEvents = findSchedule.getFilteredEvents();
+ ArrayList expectedEvents = new ArrayList<>(
+ List.of("[CLASS] Math exam from 10th Mar 2020, 11:10PM to 10th Mar 2020, 11:10PM",
+ "[CLASS] English exam from 10th Mar 2020, 11:10PM to 10th Mar 2020, 11:10PM"));
+ assertEquals(expectedEvents, actualEvents);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/model/event/ListScheduleTest.java b/src/test/java/seedu/duke/model/event/ListScheduleTest.java
new file mode 100644
index 0000000000..3c5eb991ec
--- /dev/null
+++ b/src/test/java/seedu/duke/model/event/ListScheduleTest.java
@@ -0,0 +1,107 @@
+package seedu.duke.model.event;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.controller.parser.DateTimeParser;
+import seedu.duke.exception.EmptyListException;
+import seedu.duke.model.ConfigParameter;
+import seedu.duke.model.event.cca.EventCca;
+import seedu.duke.model.event.classlesson.EventClass;
+import seedu.duke.model.event.test.EventTest;
+import seedu.duke.model.event.tuition.EventTuition;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+//@@author AndreWongZH
+class ListScheduleTest {
+ ConfigParameter configParameter = new ConfigParameter("me", 10, true);
+
+ @Test
+ void getPrintableEvents_emptySchedule_expectException() {
+ ListSchedule listSchedule = new ListSchedule(null, new ArrayList<>(), new ArrayList<>(),
+ new ArrayList<>(), new ArrayList<>(), configParameter);
+ assertThrows(EmptyListException.class, listSchedule::getPrintableEvents);
+ }
+
+ @Test
+ void getPrintableEvents_classSchedule_allClasses() throws EmptyListException, ParseException {
+ ArrayList classes = new ArrayList<>();
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ Calendar startCalendar = dateTimeParser.convertStringToCalendar("2019-02-26 1400");
+ Calendar endCalendar = dateTimeParser.convertStringToCalendar("2019-02-27 1500");
+ EventClass eventClass = new EventClass("Math", startCalendar, endCalendar);
+ classes.add(eventClass);
+ ListSchedule listSchedule = new ListSchedule(null, classes, new ArrayList<>(),
+ new ArrayList<>(), new ArrayList<>(), configParameter);
+ ArrayList actualOutputs = listSchedule.getPrintableEvents();
+ ArrayList expectedOutputs = new ArrayList<>(
+ List.of("Classes: ", "1. [CLASS] Math from 26th Feb 2019, "
+ + "02:00PM to 27th Feb 2019, 03:00PM"));
+ assertEquals(expectedOutputs, actualOutputs);
+ }
+
+ @Test
+ void getPrintableEvents_allSchedule_allEvents() throws EmptyListException, ParseException {
+ ArrayList classes = new ArrayList<>();
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ classes.add(new EventClass("Math", dateTimeParser.convertStringToCalendar("2019-02-26 1400"),
+ dateTimeParser.convertStringToCalendar("2019-02-27 1500")));
+ ArrayList ccas = new ArrayList<>();
+ ccas.add(new EventCca("Basketball", dateTimeParser.convertStringToCalendar("2019-02-26 1400"),
+ dateTimeParser.convertStringToCalendar("2019-02-27 1500")));
+ ArrayList tests = new ArrayList<>();
+ tests.add(new EventTest("Science", dateTimeParser.convertStringToCalendar("2019-02-26 1400"),
+ dateTimeParser.convertStringToCalendar("2019-02-27 1500")));
+ ArrayList tuitions = new ArrayList<>();
+ tuitions.add(new EventTuition("English", dateTimeParser.convertStringToCalendar("2019-02-26 1400"),
+ dateTimeParser.convertStringToCalendar("2019-02-27 1500"),
+ "Choa Chu Kang Avenue 5 Block 433"));
+
+ ListSchedule listSchedule = new ListSchedule(null, classes, ccas, tests, tuitions, configParameter);
+ ArrayList actualOutputs = listSchedule.getPrintableEvents();
+ ArrayList expectedOutputs = new ArrayList<>(
+ List.of("Classes: ",
+ "1. [CLASS] Math from 26th Feb 2019, 02:00PM to 27th Feb 2019, 03:00PM",
+ "CCAs: ",
+ "1. [CCA] Basketball from 26th Feb 2019, 02:00PM to 27th Feb 2019, 03:00PM",
+ "Tests: ",
+ "1. [TEST] Science from 26th Feb 2019, 02:00PM to 27th Feb 2019, 03:00PM",
+ "Tuitions: ",
+ "1. [TUITION] English from 26th Feb 2019, 02:00PM to 27th Feb 2019, 03:00PM"
+ + " at Choa Chu Kang Avenue 5 Block 433"));
+ assertEquals(expectedOutputs, actualOutputs);
+ }
+
+ @Test
+ void getPrintableEvents_classScheduleToday_oneClass() throws EmptyListException, ParseException {
+ ArrayList classes = new ArrayList<>();
+ DateTimeParser dateTimeParser = new DateTimeParser();
+ Calendar calendar = Calendar.getInstance();
+ calendar.set(Calendar.HOUR_OF_DAY, 14);
+ calendar.set(Calendar.MINUTE, 0);
+ classes.add(new EventClass("Math", calendar,
+ dateTimeParser.convertStringToCalendar("2019-02-27 1500")));
+ ListSchedule listSchedule = new ListSchedule("today", classes, new ArrayList<>(),
+ new ArrayList<>(), new ArrayList<>(), configParameter);
+ ArrayList actualOutputs = listSchedule.getPrintableEvents();
+ actualOutputs.remove(2);
+
+ String formattedDate = dateTimeParser.obtainFormattedDateTimeString(calendar);
+
+ ArrayList expectedOutputs = new ArrayList<>(
+ List.of("Classes: ", "1. [CLASS] Math from " + formattedDate
+ + " to 27th Feb 2019, 03:00PM"));
+
+ assertEquals(expectedOutputs, actualOutputs);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/model/event/cca/AddEventCcaJUnitTest.java b/src/test/java/seedu/duke/model/event/cca/AddEventCcaJUnitTest.java
new file mode 100644
index 0000000000..d5aa445a2d
--- /dev/null
+++ b/src/test/java/seedu/duke/model/event/cca/AddEventCcaJUnitTest.java
@@ -0,0 +1,28 @@
+package seedu.duke.model.event.cca;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.storage.TestUtils;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.model.event.Event;
+
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class AddEventCcaJUnitTest {
+ TestUtils testUtils = new TestUtils();
+
+ @Test
+ void addCcaToList() throws EmptyParameterException, MissingParameterException, SwappedParameterException {
+ ArrayList cca = new ArrayList<>();
+ EventCcaManager eventCcaManager = new EventCcaManager(cca,
+ testUtils.getEmptyEventManager());
+ eventCcaManager.add("add cca /n basketball camp "
+ + "/s 2020-10-13 1500 /e 2020-10-13 1600");
+ String actualOutputs = cca.get(0).getDescription();
+ String expectedOutputs = "basketball camp";
+ assertEquals(actualOutputs, expectedOutputs);
+ }
+}
diff --git a/src/test/java/seedu/duke/model/event/cca/DeleteEventCcaJUnitTest.java b/src/test/java/seedu/duke/model/event/cca/DeleteEventCcaJUnitTest.java
new file mode 100644
index 0000000000..83dcb9eb81
--- /dev/null
+++ b/src/test/java/seedu/duke/model/event/cca/DeleteEventCcaJUnitTest.java
@@ -0,0 +1,39 @@
+package seedu.duke.model.event.cca;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.storage.TestUtils;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.model.event.Event;
+
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class DeleteEventCcaJUnitTest {
+ TestUtils testUtils = new TestUtils();
+
+ @Test
+ void deleteCcaInList() throws EmptyParameterException, MissingParameterException, SwappedParameterException {
+ ArrayList cca = new ArrayList<>();
+ EventCcaManager eventCcaManager = new EventCcaManager(cca,
+ testUtils.getEmptyEventManager());
+
+ eventCcaManager.add("add cca /n basketball "
+ + "/s 2020-10-13 1500 /e 2020-10-13 1700");
+
+ String[] userInput = "delete cca 1".trim().split(" ");
+
+ try {
+ eventCcaManager.delete(userInput);
+ } catch (IndexOutOfBoundsException e) {
+ e.printStackTrace();
+ }
+
+ int actualOutput = eventCcaManager.getCcaListSize();
+ int expectedOutput = 0;
+
+ assertEquals(actualOutput, expectedOutput);
+ }
+}
diff --git a/src/test/java/seedu/duke/model/event/cca/DoneEventCcaJUnitTest.java b/src/test/java/seedu/duke/model/event/cca/DoneEventCcaJUnitTest.java
new file mode 100644
index 0000000000..d269258d28
--- /dev/null
+++ b/src/test/java/seedu/duke/model/event/cca/DoneEventCcaJUnitTest.java
@@ -0,0 +1,39 @@
+package seedu.duke.model.event.cca;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.storage.TestUtils;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.model.event.Event;
+
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class DoneEventCcaJUnitTest {
+ TestUtils testUtils = new TestUtils();
+
+ @Test
+ void setCcaDone() throws EmptyParameterException, MissingParameterException, SwappedParameterException {
+ ArrayList cca = new ArrayList<>();
+ EventCcaManager eventCcaManager = new EventCcaManager(cca,
+ testUtils.getEmptyEventManager());
+
+ eventCcaManager.add("add cca /n basketball "
+ + "/s 2020-10-13 1500 /e 2020-10-13 1700");
+
+ String[] userInput = "done cca 1".trim().split(" ");
+
+ try {
+ eventCcaManager.setDone(userInput);
+ } catch (IndexOutOfBoundsException e) {
+ e.printStackTrace();
+ }
+
+ boolean actualOutputs = cca.get(0).isDone();
+ boolean expectedOutputs = true;
+
+ assertEquals(actualOutputs, expectedOutputs);
+ }
+}
diff --git a/src/test/java/seedu/duke/model/event/classlesson/AddEventClassJUnitTest.java b/src/test/java/seedu/duke/model/event/classlesson/AddEventClassJUnitTest.java
new file mode 100644
index 0000000000..b868500f78
--- /dev/null
+++ b/src/test/java/seedu/duke/model/event/classlesson/AddEventClassJUnitTest.java
@@ -0,0 +1,28 @@
+package seedu.duke.model.event.classlesson;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.storage.TestUtils;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.model.event.Event;
+
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class AddEventClassJUnitTest {
+ TestUtils testUtils = new TestUtils();
+
+ @Test
+ void addClassToList() throws EmptyParameterException, MissingParameterException, SwappedParameterException {
+ ArrayList classes = new ArrayList<>();
+ EventClassManager eventClassManager = new EventClassManager(classes,
+ testUtils.getEmptyEventManager());
+ eventClassManager.add("add class /n Math /s 2020-08-19 1300 /e 2020-08-19 1400");
+ int actualOutputs = eventClassManager.getClassListSize();
+ int expectedOutputs = 1;
+ assertEquals(actualOutputs, expectedOutputs);
+ }
+}
+
diff --git a/src/test/java/seedu/duke/model/event/classlesson/DeleteEventClassJUnitTest.java b/src/test/java/seedu/duke/model/event/classlesson/DeleteEventClassJUnitTest.java
new file mode 100644
index 0000000000..19f353659b
--- /dev/null
+++ b/src/test/java/seedu/duke/model/event/classlesson/DeleteEventClassJUnitTest.java
@@ -0,0 +1,41 @@
+package seedu.duke.model.event.classlesson;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.storage.TestUtils;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.model.event.Event;
+
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class DeleteEventClassJUnitTest {
+ TestUtils testUtils = new TestUtils();
+
+ @Test
+ void addClassToList() throws EmptyParameterException, MissingParameterException, SwappedParameterException {
+ ArrayList classes = new ArrayList<>();
+ EventClassManager eventClassManager = new EventClassManager(classes,
+ testUtils.getEmptyEventManager());
+
+ eventClassManager.add("add class /n English lesson "
+ + "/s 2020-09-26 1400 /e 2020-09-26 1500");
+ eventClassManager.add("add class /n Science lesson "
+ + "/s 2020-09-23 1600 /e 2020-09-23 1800");
+
+ String [] userInput = "delete class 1".trim().split(" ");
+
+ try {
+ eventClassManager.delete(userInput);
+ } catch (IndexOutOfBoundsException e) {
+ e.printStackTrace();
+ }
+
+ int actualOutputs = eventClassManager.getClassListSize();
+ int expectedOutputs = 1;
+
+ assertEquals(actualOutputs, expectedOutputs);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/model/event/test/AddTestJUnitTest.java b/src/test/java/seedu/duke/model/event/test/AddTestJUnitTest.java
new file mode 100644
index 0000000000..38204a15f6
--- /dev/null
+++ b/src/test/java/seedu/duke/model/event/test/AddTestJUnitTest.java
@@ -0,0 +1,28 @@
+package seedu.duke.model.event.test;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.storage.TestUtils;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.model.event.Event;
+
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class AddTestJUnitTest {
+ TestUtils testUtils = new TestUtils();
+
+ @Test
+ void addTestToList() throws MissingParameterException, EmptyParameterException, SwappedParameterException {
+ ArrayList test = new ArrayList<>();
+ EventTestManager eventTestManager = new EventTestManager(test,
+ testUtils.getEmptyEventManager());
+ eventTestManager.add("add test /n Math test "
+ + "/s 2020-09-26 1400 /e 2020-09-26 1500");
+ int actualOutputs = eventTestManager.getTestListSize();
+ int expectedOutputs = 1;
+ assertEquals(actualOutputs, expectedOutputs);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/model/event/test/DeleteTestJUnitTest.java b/src/test/java/seedu/duke/model/event/test/DeleteTestJUnitTest.java
new file mode 100644
index 0000000000..72c05a6663
--- /dev/null
+++ b/src/test/java/seedu/duke/model/event/test/DeleteTestJUnitTest.java
@@ -0,0 +1,41 @@
+package seedu.duke.model.event.test;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.storage.TestUtils;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.model.event.Event;
+
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class DeleteTestJUnitTest {
+ TestUtils testUtils = new TestUtils();
+
+ @Test
+ void deleteTestFromList() throws MissingParameterException, EmptyParameterException, SwappedParameterException {
+ ArrayList test = new ArrayList<>();
+ EventTestManager eventTestManager = new EventTestManager(test,
+ testUtils.getEmptyEventManager());
+
+ eventTestManager.add("add test /n Math test "
+ + "/s 2020-09-26 1400 /e 2020-09-26 1500");
+ eventTestManager.add("add test /n Science test "
+ + "/s 2020-09-23 1600 /e 2020-09-23 1800");
+
+ String [] userInput = "delete test 1".trim().split(" ");
+
+ try {
+ eventTestManager.delete(userInput);
+ } catch (IndexOutOfBoundsException e) {
+ e.printStackTrace();
+ }
+
+ int actualOutputs = eventTestManager.getTestListSize();
+ int expectedOutputs = 1;
+
+ assertEquals(actualOutputs, expectedOutputs);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/model/event/test/DoneTestJUnitTest.java b/src/test/java/seedu/duke/model/event/test/DoneTestJUnitTest.java
new file mode 100644
index 0000000000..5baac6b3ce
--- /dev/null
+++ b/src/test/java/seedu/duke/model/event/test/DoneTestJUnitTest.java
@@ -0,0 +1,41 @@
+package seedu.duke.model.event.test;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.storage.TestUtils;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.model.event.Event;
+
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class DoneTestJUnitTest {
+ TestUtils testUtils = new TestUtils();
+
+ @Test
+ void setTestDoneFromList() throws MissingParameterException, EmptyParameterException, SwappedParameterException {
+ ArrayList test = new ArrayList<>();
+ EventTestManager eventTestManager = new EventTestManager(test, testUtils.getEmptyEventManager());
+
+ eventTestManager.add("add test /n Math test "
+ + "/s 2020-09-26 1400 /e 2020-09-26 1500");
+ eventTestManager.add("add test /n Science test "
+ + "/s 2020-09-23 1600 /e 2020-09-23 1800");
+
+ String [] userInput = "done test 1".trim().split(" ");
+
+ try {
+ eventTestManager.setDone(userInput);
+ } catch (IndexOutOfBoundsException e) {
+ e.printStackTrace();
+ }
+
+ boolean actualOutputs = test.get(0).isDone();
+ boolean expectedOutputs = true;
+
+ assertEquals(actualOutputs, expectedOutputs);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/model/event/tuition/AddEventTuitionJUnitTest.java b/src/test/java/seedu/duke/model/event/tuition/AddEventTuitionJUnitTest.java
new file mode 100644
index 0000000000..3f3edb383c
--- /dev/null
+++ b/src/test/java/seedu/duke/model/event/tuition/AddEventTuitionJUnitTest.java
@@ -0,0 +1,49 @@
+package seedu.duke.model.event.tuition;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.exception.SwappedParameterException;
+import seedu.duke.storage.TestUtils;
+import seedu.duke.controller.parser.DateTimeParser;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.MissingParameterException;
+import seedu.duke.model.event.Event;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class AddEventTuitionJUnitTest {
+ TestUtils testUtils = new TestUtils();
+
+ @Test
+ void addEvent_validTuition_noException() throws EmptyParameterException,
+ MissingParameterException, ParseException, SwappedParameterException {
+ ArrayList tuitions = new ArrayList<>();
+ EventTuitionManager eventTuitionManager = new EventTuitionManager(tuitions,
+ testUtils.getEmptyEventManager());
+ eventTuitionManager.add("add tuition /n math tuition "
+ + "/s 2020-10-13 1500 /e 2020-10-13 1700 "
+ + "/l home");
+
+ DateTimeParser dateTimeParser = new DateTimeParser();
+
+ EventTuition eventTuition = new EventTuition("math tuition",
+ dateTimeParser.convertStringToCalendar("2020-10-13 1500"),
+ dateTimeParser.convertStringToCalendar("2020-10-13 1700"),
+ "home");
+ assertEquals(tuitions.get(0), eventTuition);
+ }
+
+ @Test
+ void addEvent_invalidTuition_exceptionThrown() {
+ ArrayList tuitions = new ArrayList<>();
+ EventTuitionManager eventTuitionManager = new EventTuitionManager(tuitions,
+ testUtils.getEmptyEventManager());
+
+ assertThrows(EmptyParameterException.class, () -> eventTuitionManager.add("/n /s /e /l"));
+ assertThrows(MissingParameterException.class, () -> eventTuitionManager.add("/n math tuition "
+ + "/s 2020-09-22 1700 /e 2020-09-22 1800"));
+ }
+}
diff --git a/src/test/java/seedu/duke/model/event/tuition/DoneEventTuitionJUnitTest.java b/src/test/java/seedu/duke/model/event/tuition/DoneEventTuitionJUnitTest.java
new file mode 100644
index 0000000000..677b0ad45d
--- /dev/null
+++ b/src/test/java/seedu/duke/model/event/tuition/DoneEventTuitionJUnitTest.java
@@ -0,0 +1,44 @@
+package seedu.duke.model.event.tuition;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.storage.TestUtils;
+
+import java.text.ParseException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class DoneEventTuitionJUnitTest {
+ TestUtils testUtils = new TestUtils();
+
+ @Test
+ void setValidTuitionDone() throws ParseException {
+ EventTuitionManager eventTuitionManager = new EventTuitionManager(testUtils.getTuitionList(),
+ testUtils.getEmptyEventManager());
+
+ String [] userInput = "done tuition 1".trim().split(" ");
+
+ try {
+ eventTuitionManager.setDone(userInput);
+ } catch (IndexOutOfBoundsException e) {
+ e.printStackTrace();
+ }
+
+ boolean actualOutputs = eventTuitionManager.getTuitions().get(0).isDone();
+ boolean expectedOutputs = true;
+
+ assertEquals(actualOutputs, expectedOutputs);
+ }
+
+ @Test
+ void setTuitionDone_invalidIndex_exceptionThrown() throws ParseException {
+ EventTuitionManager eventTuitionManager = new EventTuitionManager(testUtils.getTuitionList(),
+ testUtils.getEmptyEventManager());
+
+ String [] userInputIndexTooLarge = "done tuition 5".trim().split(" ");
+ String [] userInputIndexNegative = "done tuition -1".trim().split(" ");
+
+ assertThrows(IndexOutOfBoundsException.class, () -> eventTuitionManager.setDone(userInputIndexTooLarge));
+ assertThrows(IndexOutOfBoundsException.class, () -> eventTuitionManager.setDone(userInputIndexNegative));
+ }
+}
diff --git a/src/test/java/seedu/duke/model/quiz/AddQuizJUnitTest.java b/src/test/java/seedu/duke/model/quiz/AddQuizJUnitTest.java
new file mode 100644
index 0000000000..3d574519f7
--- /dev/null
+++ b/src/test/java/seedu/duke/model/quiz/AddQuizJUnitTest.java
@@ -0,0 +1,38 @@
+package seedu.duke.model.quiz;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.SwappedParameterException;
+
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class AddQuizJUnitTest {
+ @Test
+ void addQuizToList() throws EmptyParameterException, SwappedParameterException {
+ ArrayList quizzes = new ArrayList<>();
+ QuizManager quizManager = new QuizManager(quizzes);
+ quizManager.add("add quiz /q What is 1+1? /o1 1 /o2 2 "
+ + "/o3 3 /o4 4 /a 2 /exp 1 plus 1 must be equal to 2!");
+
+ int actualQuizNumber = quizManager.getQuizListSize();
+ int expectedQuizNumber = 1;
+ assertEquals(actualQuizNumber, expectedQuizNumber);
+
+ String actualQuizExplanation = "1 plus 1 must be equal to 2!";
+ String expectedQuizExplanation = quizManager.getQuizList().get(0).getExplanation();
+ assertEquals(actualQuizExplanation, expectedQuizExplanation);
+ }
+
+ //@@author durianpancakes
+ @Test
+ void parseQuiz_emptyOptions_exceptionThrown() {
+ ArrayList quizzes = new ArrayList<>();
+ QuizManager quizManager = new QuizManager(quizzes);
+
+ assertThrows(EmptyParameterException.class, () -> quizManager.add("add quiz /q What is 1+1? /o1 1 /o2 2 "
+ + "/o3 /o4 4 /a 3"));
+ }
+}
diff --git a/src/test/java/seedu/duke/model/quiz/DeleteQuizJUnitTest.java b/src/test/java/seedu/duke/model/quiz/DeleteQuizJUnitTest.java
new file mode 100644
index 0000000000..aab4ef17f0
--- /dev/null
+++ b/src/test/java/seedu/duke/model/quiz/DeleteQuizJUnitTest.java
@@ -0,0 +1,27 @@
+package seedu.duke.model.quiz;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.SwappedParameterException;
+
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class DeleteQuizJUnitTest {
+ @Test
+ void deleteQuizToList() throws EmptyParameterException, SwappedParameterException {
+ ArrayList quizzes = new ArrayList<>();
+ QuizManager quizManager = new QuizManager(quizzes);
+ String userInput = "delete quiz 1";
+ String[] separatedInputs = userInput.split(" ");
+ quizManager.add("add quiz /q What is 1+1? /o1 1 /o2 2 "
+ + "/o3 3 /o4 4 /a 2 /exp 1 plus 1 must be equal to 2!");
+ quizManager.delete(separatedInputs);
+
+ int actualQuizNumber = quizManager.getQuizListSize();
+ int expectedQuizNumber = 0;
+ assertEquals(actualQuizNumber, expectedQuizNumber);
+
+ }
+}
diff --git a/src/test/java/seedu/duke/model/quiz/ListQuizJUnitTest.java b/src/test/java/seedu/duke/model/quiz/ListQuizJUnitTest.java
new file mode 100644
index 0000000000..958805076d
--- /dev/null
+++ b/src/test/java/seedu/duke/model/quiz/ListQuizJUnitTest.java
@@ -0,0 +1,31 @@
+package seedu.duke.model.quiz;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.exception.EmptyParameterException;
+import seedu.duke.exception.SwappedParameterException;
+
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ListQuizJUnitTest {
+ @Test
+ void listQuizTest() throws EmptyParameterException, SwappedParameterException {
+ ArrayList quizzes = new ArrayList<>();
+ QuizManager quizManager = new QuizManager(quizzes);
+ quizManager.add("add quiz /q What is 1+1? /o1 1 /o2 2 "
+ + "/o3 3 /o4 4 /a 2 /exp 1 plus 1 must be equal to 2!");
+
+ String actualOutput = "Question " + (0 + 1) + ":\n" + quizzes.get(0);
+ String expectedOutput = "Question 1:\n"
+ + "What is 1+1?\n"
+ + "\n"
+ + "(1) 1\n"
+ + "(2) 2\n"
+ + "(3) 3\n"
+ + "(4) 4\n"
+ + "\n"
+ + "Explanation: 1 plus 1 must be equal to 2!\n\n";
+ assertEquals(actualOutput, expectedOutput);
+ }
+}
diff --git a/src/test/java/seedu/duke/model/quiz/QuizTest.java b/src/test/java/seedu/duke/model/quiz/QuizTest.java
new file mode 100644
index 0000000000..25fd02a5e0
--- /dev/null
+++ b/src/test/java/seedu/duke/model/quiz/QuizTest.java
@@ -0,0 +1,12 @@
+package seedu.duke.model.quiz;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class QuizTest {
+ @Test
+ public void sampleTest() {
+ assertTrue(true);
+ }
+}
\ No newline at end of file
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..1f76000482
--- /dev/null
+++ b/src/test/java/seedu/duke/storage/StorageTest.java
@@ -0,0 +1,23 @@
+package seedu.duke.storage;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.exception.StorageCorruptedException;
+import seedu.duke.storage.event.EventStorageManager;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+//@@author durianpancakes
+public class StorageTest {
+ EventStorageManager validEventStorage = new EventStorageManager("testdata", "/validdata.txt");
+ EventStorageManager invalidEventStorage = new EventStorageManager("testdata", "/invaliddata.txt");
+
+ @Test
+ public void loadData_validData_noException() throws StorageCorruptedException {
+ validEventStorage.loadData();
+ }
+
+ @Test
+ public void loadData_invalidData_exceptionThrown() {
+ assertThrows(StorageCorruptedException.class, () -> invalidEventStorage.loadData());
+ }
+}
diff --git a/src/test/java/seedu/duke/storage/TestUtils.java b/src/test/java/seedu/duke/storage/TestUtils.java
new file mode 100644
index 0000000000..cce6c051ba
--- /dev/null
+++ b/src/test/java/seedu/duke/storage/TestUtils.java
@@ -0,0 +1,54 @@
+package seedu.duke.storage;
+
+import seedu.duke.controller.parser.DateTimeParser;
+import seedu.duke.model.ConfigParameter;
+import seedu.duke.model.event.Event;
+import seedu.duke.model.event.EventManager;
+import seedu.duke.model.event.EventParameter;
+import seedu.duke.model.event.cca.EventCca;
+import seedu.duke.model.event.classlesson.EventClass;
+import seedu.duke.model.event.test.EventTest;
+import seedu.duke.model.event.tuition.EventTuition;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+
+public class TestUtils {
+ public EventManager getEmptyEventManager() {
+ return new EventManager(new EventParameter(),
+ new ConfigParameter("me", 10, true));
+ }
+
+ public ArrayList getEventList() throws ParseException {
+ ArrayList events = new ArrayList<>();
+ DateTimeParser dateTimeParser = new DateTimeParser();
+
+ events.add(new EventCca("Basketball training",
+ dateTimeParser.convertStringToCalendar("2020-09-21 1800"),
+ dateTimeParser.convertStringToCalendar("2020-09-21 1900")));
+ events.add(new EventClass("Math tuition",
+ dateTimeParser.convertStringToCalendar("2020-09-22 1400"),
+ dateTimeParser.convertStringToCalendar("2020-09-22 1600")));
+ events.add(new EventTest("CS2113T Finals",
+ dateTimeParser.convertStringToCalendar("2020-12-04 1500"),
+ dateTimeParser.convertStringToCalendar("2020-12-04 1600")));
+ events.add(new EventTuition("English",
+ dateTimeParser.convertStringToCalendar("2020-12-05 1600"),
+ dateTimeParser.convertStringToCalendar("2020-12-05 1800"),
+ "home"));
+
+ return events;
+ }
+
+ public ArrayList getTuitionList() throws ParseException {
+ ArrayList tuitions = new ArrayList<>();
+ DateTimeParser dateTimeParser = new DateTimeParser();
+
+ tuitions.add(new EventTuition("math", dateTimeParser.convertStringToCalendar("2020-09-26 1400"),
+ dateTimeParser.convertStringToCalendar("2020-09-26 1500"), "home"));
+ tuitions.add(new EventTuition("english", dateTimeParser.convertStringToCalendar("2020-09-27 1400"),
+ dateTimeParser.convertStringToCalendar("2020-09-27 1500"), "tuition centre"));
+
+ return tuitions;
+ }
+}
diff --git a/testdata/invaliddata.txt b/testdata/invaliddata.txt
new file mode 100644
index 0000000000..f424604960
--- /dev/null
+++ b/testdata/invaliddata.txt
@@ -0,0 +1,11 @@
+[CCA]|false|basketball|2020-10-28 1500|2020-10-28 1700
+[CCA]|false|basketball|2020-10-28 1800|2020-10-28 1900
+[CCA]|false|basketball|2020-10-31 0900|2020-10-31 1100
+[CCA]|false|basketball|2020-11-03 1500+2020-11-03 1700
+[CCA]|false|basketball|2020-11-05 1800|2020-11-05 1900
+[CCA]|false|basketball|2020-11-06 0900/2020-11-06 1100
+[TEST]|false|math|2020-10-26 0900|2020-10-31 1100
+[TEST]|false|science|2020-10-29 0900|2020-10-31 1100
+[TEST]|false|math|2020-11-03 0900|2020-11-03 1100
+[TEST]|false|science|2020-11-04 0900|2020-11-04 1100
+[CLASS]|false|math|2020-09-21 1500|2020-09-21 1600
\ No newline at end of file
diff --git a/testdata/validdata.txt b/testdata/validdata.txt
new file mode 100644
index 0000000000..9a40e51419
--- /dev/null
+++ b/testdata/validdata.txt
@@ -0,0 +1,12 @@
+[CCA]|false|basketball|2020-10-28 1500|2020-10-28 1700
+[CCA]|false|basketball|2020-10-28 1800|2020-10-28 1900
+[CCA]|false|basketball|2020-10-31 0900|2020-10-31 1100
+[CCA]|false|basketball|2020-11-03 1500|2020-11-03 1700
+[CCA]|false|basketball|2020-11-05 1800|2020-11-05 1900
+[CCA]|false|basketball|2020-11-06 0900|2020-11-06 1100
+[TEST]|false|math|2020-10-26 0900|2020-10-31 1100
+[TEST]|false|science|2020-10-29 0900|2020-10-31 1100
+[TEST]|false|math|2020-11-03 0900|2020-11-03 1100
+[TEST]|false|science|2020-11-04 0900|2020-11-04 1100
+[CLASS]|false|math|2020-09-21 1500|2020-09-21 1600
+[CLASS]|false|math|2020-09-21 1800|2020-09-21 1900
\ No newline at end of file
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 892cb6cae7..8f836a2f3a 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
+
+Key in the number of hours you would like to study each day (not more than 12)?
+
+Hello James Arthur
+This is your recommended hours per day: 10
+What can we do for you? (Enter 'help' for the list of available commands!)
+
+BYE BYE! SEE YOU NEXT TIME! :3
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index f6ec2e9f95..f165bb4b03 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -1 +1,3 @@
-James Gosling
\ No newline at end of file
+James Arthur
+10
+bye
\ No newline at end of file