diff --git a/.gitignore b/.gitignore
index f69985ef1f..b9d68580fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,5 +13,8 @@ src/main/resources/docs/
*.iml
bin/
+# Data files
+data/*
+
/text-ui-test/ACTUAL.txt
text-ui-test/EXPECTED-UNIX.TXT
diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..3dc7aeff56
--- /dev/null
+++ b/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: command.NoCap
+
diff --git a/README.md b/README.md
index 4eef34421f..b3dc277757 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# Duke project template
+# NoCap
-This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it.
+This project is based on Duke, a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it.
## Setting up in Intellij
@@ -9,21 +9,16 @@ Prerequisites: JDK 11 (use the exact version), update Intellij to the most recen
1. **Ensure Intellij JDK 11 is defined as an SDK**, as described [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk) -- this step is not needed if you have used JDK 11 in a previous Intellij project.
* In the same dialog, you _may_ have to set the Project language level field to the SDK default option.
1. **Import the project _as a Gradle project_**, as described [here](https://se-education.org/guides/tutorials/intellijImportGradleProject.html).
-1. **Verify the set up**: After the importing is complete, locate the `src/main/java/seedu/duke/Duke.java` file, right-click it, and choose `Run Duke.main()`. If the setup is correct, you should see something like the below:
+1. **Verify the set up**: After the importing is complete, locate the `src/main/java/seedu/nocap/NoCap.java` file, right-click it, and choose `Run NoCap.main()`. If the setup is correct, you should see something like the below:
```
> Task :compileJava
> Task :processResources NO-SOURCE
> Task :classes
-
- > Task :Duke.main()
- Hello from
- ____ _
- | _ \ _ _| | _____
- | | | | | | | |/ / _ \
- | |_| | |_| | < __/
- |____/ \__,_|_|\_\___|
-
- What is your name?
+
+ > Task :NoCap.main()
+ Data loaded successfully
+ Welcome to NoCap
+ You are now accessing semester: Y1S1
```
Type some word and press enter to let the execution proceed to the end.
@@ -40,7 +35,7 @@ Prerequisites: JDK 11 (use the exact version), update Intellij to the most recen
### JUnit tests
-* A skeleton JUnit test (`src/test/java/seedu/duke/DukeTest.java`) is provided with this project template.
+* A skeleton JUnit test (`src/test/java/seedu/duke/NoCapTest.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
@@ -54,12 +49,7 @@ The project uses [GitHub actions](https://github.com/features/actions) for CI. W
## Documentation
-`/docs` folder contains a skeleton version of the project documentation.
-
-Steps for publishing documentation to the public:
-1. If you are using this project template for an individual project, go your fork on GitHub.
- If you are using this project template for a team project, go to the team fork on GitHub.
-1. Click on the `settings` tab.
-1. Scroll down to the `GitHub Pages` section.
-1. Set the `source` as `master branch /docs folder`.
-1. Optionally, use the `choose a theme` button to choose a theme for your documentation.
+`/docs` folder contains the following documentation for `NoCap`:
+* [About Us](/docs/AboutUs.md)
+* [Developer Guide](/docs/DeveloperGuide.md)
+* [User Guide](/docs/UserGuide.md)
diff --git a/build.gradle b/build.gradle
index b0c5528fb5..31b16c207a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -10,8 +10,14 @@ 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'
+ testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.8.1'
+ testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.8.1'
+ implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.13.0'
+ implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.13.0'
+ // https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations
+ implementation group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.13.0'
+
+
}
test {
@@ -29,11 +35,11 @@ test {
}
application {
- mainClassName = "seedu.duke.Duke"
+ mainClassName = "command.NoCap"
}
shadowJar {
- archiveBaseName = "duke"
+ archiveBaseName = "NoCap"
archiveClassifier = null
}
@@ -43,4 +49,5 @@ checkstyle {
run{
standardInput = System.in
+ enableAssertions = true
}
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 0f072953ea..110a7fab8e 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -2,8 +2,8 @@
Display | Name | Github Profile | Portfolio
--------|:----:|:--------------:|:---------:
- | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
+ | Yeat Nai Jie | [Github](https://github.com/naijie2108) | [Portfolio](team/naijie2108.md)
+ | Yap Joon Siong | [Github](https://github.com/yapjoonsiong) | [Portfolio](team/yapjoonsiong.md)
+ | Yam Jin Ee Dmitri | [Github](https://github.com/DmitriYam) | [Portfolio](team/dmitriyam.md)
+ | Yeo Zi Hao Edwin | [Github](https://github.com/yzhedwin) | [Portfolio](team/yzhedwin.md)
+ | Zeng Jiexiong | [Github](https://github.com/jiexiong-zeng) | [Portfolio](team/jiexiong-zeng.md)
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 64e1f0ed2b..603f11f651 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,38 +1,821 @@
# Developer Guide
+## Navigation
+
+* [Acknowledgements](#acknowledgements)
+* [Design and Implementation](#design--implementation)
+ * [Parser](#parser)
+ * [Storage](#storage)
+ * [Semester](#semester)
+ * [Module](#module)
+ * [Schedule List](#schedulelist)
+ * [Task list](#tasklist)
+ * [Task](#task)
+ * [Overall Task List](#overalltasklist)
+ * [Overall Task](#overalltask)
+* [Product Scope](#appendix-a-product-scope)
+* [User Stories](#appendix-b-user-stories)
+* [Non-Functional Requirements](#appendix-c-non-functional-requirements)
+* [Glossary](#appendix-d-glossary)
+* [Instructions for Manual Testing](#appendix-e-instructions-for-manual-testing)
+
## Acknowledgements
-{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+Third party libraries:
+
+- [Jackson Databind](https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind)
+- [Jackson Datatype-jsr310](https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jsr310)
+- [Jackson Annotations](https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations)
+
+# Design & Implementation
+
+## Parser
+
+**API** : command.parser
+
+The Parser classes is responsible for receiving user input and converting it into commands which are directly passed to
+respective classes.
+
+The simplified class diagram below is an overview of relationship between Parser classes and other classes.
+
+
+
+How the parsing works:
+
+* `NoCap` passes the user input to `Parser` which separates the input into useful information such as taskType,
+ taskDescription, Module, etc.
+* When commands include **listing tasks**, the taskDescription is passed to `ListParser` which determines the method of
+ sorting and creates filtered `OverallTaskList` and `TaskList` accordingly.
+* Otherwise, the taskDescription is passed to `Command` which calls the corresponding commands in `SemesterList`,
+ `Semester` ,`ModuleList`, `Module` , `Task`, `Gradable Task`. For clarity purposes, associations are shown but
+ dependencies are not.
+* `ParserChecks` is a class that handles various error checking and string searching methods such as
+ `ParserSearch#getTaskFromIndex()` and `ParserSearch#getTaskFromKeyword()`. `Command` utilizes these methods to verify
+ the Strings before passing them to other classes.
+ *In NoCap, Parser verifies the validity of input (Whether it exists in the right format). Input content is verified by
+ individual classes for correctness.*
+* `DateParser` handles parsing String into LocalDateTime format and displaying LocalDateTime as String. It is utilized
+ by `Task`. Additional date formats can be added in `DateParser#inputFormatter()`
+
+Below is a step by step example of how the parser receives and decipher a user input. In this example, the user enters
+`list task sortbydate`.
+
+The Sequence Diagram below illustrates the process
+
+**Note**: The alternate paths are omitted from the diagram for clarity.
+
+Step 1: The User launches the application. `NoCap` creates a new `Parser` instance through the constructor and `Parser`
+creates `ListParser`.
+
+Step 2: The application waits for User input. User enters `list task sortbydate`. `NoCap` passes the input to `Parser`
+through `Parser#chooseTask()`.
+
+Step 3: `splitInput` is called for the first time and splits the user input into `list` and `task sortbydate`.
+> **TaskType** is set to `list`, and **TaskDescription** is set to `task sortbydate`.
+
+**TaskType** matches a possible command String.
+
+
+Step 4: `splitInput` is called for the second time and splits the user input into `task` and `sortbydate`.
+> **TaskType** is set to `task`, and **TaskDescription** is set to `sortbydate`.
+
+**TaskType** and **TaskDescription** are passed to `ListParser` through `ListParser#overallListParser`.
+
+
+Step 5: `overallListParser` creates an `OverallTaskList`. Through nested switch cases, **TaskType** and **TaskDescription** are matched, and the corresponding method `OverallTaskList#sortByDateAndPrint()` is called. As the
+name implies, this method sorts all tasks by date and prints them.
+> If **TaskType** does not match, then an error message is displayed.
+> If **TaskDescription** does not match, all tasks are printed by default.
+
+
+
+Step 6: The full command is carried out and the application returns to NoCap and waits for new User Input.
+
+The diagram below illustrates the `splitString` process:
+
+
+
+
+## Storage
+
+**API** : `command.storage`
+
+The Storage component saves data of NoCap into JSON format, and reads them back into corresponding objects when needed
+using a 3rd party library Jackson Databind.
+
+It consists of 2 utility classes StorageDecoder and StorageEncoder. StorageEncoder is used to encode the parent object
+`SemesterList` into a JSON file. StorageDecoder decodes a JSON file into a `SemesterList `object
+
+How the `StorageEncoder` class works:
+
+
+
+1. The static method `encodeAndSaveSemesterListToJson()` from `StorageEncoder` is called when NoCap data needs to be
+ saved
+2. If the save file directory has not been created yet, it is first created in order to store the save file
+3. Similarly, an empty file is created to store the data if it has not been created yet
+4. Then, the object `SemesterList` which holds all NoCap information is converted into a JSON file with an `ObjectMapper`
+ object from the `jackson-databind` library
+5. Upon completion, the data file is saved in a default data directory.
+
+How the `StorageDecoder` class works:
+
+
+
+1. The static method `decodeJsonToSemesterList()` from `StorageDecoder` is called when NoCap data needs to be loaded
+ from the save file
+2. If there is no save file available in the default data directory, a new `SemesterList `object is created and returned
+ to the caller
+3. Otherwise, an `ObjectMapper` object from the `jackson-databind` library is used to deserialize the JSON save file
+ into a `SemesterList` object to be returned to the caller
+
+
+## Semester
+
+**API** : `semester`
+
+
+
+The `Semester` component stores all NoCap data i.e., all `Semester` objects and their components and cumulative average
+point (CAP) (which are contained in a SemesterList object)
+
+* It consists of 2 classes `SemesterList` and `Semester`
+* `SemesterList` is used to compute and store the cumulative CAP of all semesters and also stores 10 `Semester` objects
+* Each `Semester` object stores and computes the individual CAP for the semester, while also storing a `ModuleList` of
+ the `Module` objects taken during the semester
+* The computation of the CAP for both `SemesterList` and `Semester` is automatically done when a grade/credit as added
+ to a `Module` object within any semester
+
+This is how CAP is computed:
+
+
+
+* When `commandAddGrade()` or `commandAddCredit()` is called in Parser, `addGrade(description)`
+ or `addCredit(description)` respectively are called in `Module`, setting the module’s `grade`, `points` and `credits`
+ to their corresponding values.
+* Then, `updateCap()` is called in `Semester` with the newly set `grade`/`credits` values in `Module`, followed
+ by `updateCap()` in `SemesterList` with the newly set `points`/`credits` values in `Semester`.
+
+
+## Module
+
+**API** : `module`
+
+All data related to module is stored in the module class. An Arraylist of Module is used to store and manage the
+modules. ModuleList is also responsible for constructing and printing out the Timetable.
+
+
+
+The modules are stored in an ArrayList and ModuleList uses the Module.get(int index) method to access the target Module.
+
+- ModuleList is responsible for printing the Time Table.
+- ModuleList contains the getter method find(String input) which returns a module by the same name as the input.
+- Module contains getter and setter methods to change or access its contents.
+- When Module is constructed, an empty gradableTaskList, taskList and scheduleList wll be instantiated and stored in
+ Module.
+
+Data stored in Module includes:
+
+1. moduleName
+2. letterGrade
+3. credits
+4. points
+5. TaskList
+6. GradableTaskList
+7. ScheduleList
+
+The modules are stored in an ArrayList and ModuleList uses the Module.get(int index) method to access the target Module.
+
+- ModuleList is responsible for printing the Time Table. It accesses different schedules of different mods before
+ constructing a Time Table.
+- ModuleList contains getter method find(String input) which returns a module by the same name as the input.
+
+# 
+
+How printing a timetable works:
+
+1. When Timetable is called, ModuleList goes into a loop to print out the timetable. ModuleList iterates through the 207
+ character long length and the 33 lines which makes up the entire timetable.
+2. Each iteration of the loop can result in 1 of 3 cases:
+1. It is at a border. When this happens a "#" character is printed to the console which denotes a border.
+2. It is empty. When this happens a " "(blank) character is printed to the console.
+3. It contains module information. When this happens, getMoudleName() , getModuleLocation() and getModuleComment() is
+ called. The information is then printed onto the console.
+
+
+## ScheduleList
+
+**API** : `schedule`
+
+ScheduleList consists of all data for the schedule for the module.
+
+This includes:
+
+1. `day`
+2. `location`
+3. `startTime`
+4. `comments`
+
+How ScheduleList works:
+
+1. An empty `ScheduleList` is created when a module is constructed.
+2. When `addClass` is called in `module` , `ScheduleList` parses the input from the user and splits the information into
+ the relevant information. The information is then used to generate a new instance of `Schedule` which is then added
+ to the list.
+
+Notes about ScheduleList
+
+- ScheduleList checks that the input for the day of the week is only from the list of possible days: `MON`, `TUE`, `WED`
+ , `THU`,
+ `FRI`, `SAT` ,`SUN`. All other inputs will result in an exception being thrown.
+- When a new `Schedule` class is called, `ScheduleList` ensures that the length of venue and comments are less than 16
+ characters in length. This is to ensure that it fits within its time slot within the Timetable when printed.
+
+
+
+Adding Schedule to scheduleList
+
+- When addclass() is called, schedule first checks if there is a duplicate schedule currently in the list. This is done
+ by going through the whole list and checking if a schedule has the same time slot. If there exists a schedule in the
+ same time slot, an error message is printed.
+- If it is an empty timeslot, schedule list parses the input and checks for formatting errors within the input.
+- A new instance of Schedule is generated and added to the schedule list.
+
+
+## TaskList
+
+**API** : `task.tasklist`
+
+
+
+How the `TaskList` component works:
+
+
+
+1. `TaskList` stores all tasks in an `ArrayList`.
+2. When the `addTask()` method is called, the method `getDate()` will return the `date` string from the user input
+3. The method `removeDate()` will return the `description` string from the user input by removing the date component in
+ the user input.
+4. Then store it as a local variable of a `String` type.
+5. The `String` variables will then be passed to instantialize a new `Task` object.
+6. This `Task` object will then be stored in the `ArrayList` in the `TaskList` object.
+7. The methods `weeklyTaskList()`, `monthlyTaskList()` and `yearlyTaskList()` returns an `ArrayList` which contains
+ the `Task` objects of deadline within a week, a month and a year respectively.
+8. The methods `sortTaskListByDate()` and `sortTaskListByStatus()` will sort the current `TaskList` object by ascending
+ order of `Deadline` and completion status respectively
+9. The `ArrayList` returned by the above methods can then be passed to `printTasks()` which will call `toString()` in
+ each `Task` object and print to the `Output Stream`.
+
+
+## Task
+
+**API** : `task.task`
+
+`Task` object stores the following for each task:
-## Design & implementation
+1. `description`
+2. `date`
+3. `isDone`
+4. `isLate`
+5. `deadline`
-{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.}
+How the `Task` component works:
+
-## Product scope
-### Target user profile
+1. Whenever the `Task` object is instantiated, the `attributes` listed above will be initialized by the `setter`
+ methods: `setDescription()`, `setDate()`, `setDone()`, `setLate()` and `setDeadline()`.
+2. When calling `printAllTask()`, `printWeeklyTask()`, `printMonthlyTask()` in `OverallTaskList` the
+ method `updateOverdue()`will be called which checks for the truth value of the `boolean` attribute `isDone` and also
+ whether the current date and time of the system clock is after the `deadline` of the `Task` object.
+3. If `isDone` is `FALSE` and the `deadline` is later than the current date and time, `updateOverdue()` will set the
+ attribute `isLate` of the current `Task` object to `TRUE`.
+4. Calling the toString converts the task information in the Task object to printable String.
-{Describe the target user profile}
+Note:
-### Value proposition
+* The printTask() call in the sequence diagram is a generalised method from: `OverallTaskList#addAllNormalTasks()`
+ , `OverallTaskList#addAllGradableTasks()` and `TaskList#printTasks()`
+* Any call from the methods above will result in the following sequence in the sequence diagram.
+
-{Describe the value proposition: what problem does it solve?}
+## OverallTaskList
-## User Stories
+**API** : `task.OverallTasklist`
+
+
+
+_Class diagram for OverallTask and OverallTaskList_
+
+**Note**: Some methods are omitted from the class diagram to improve clarity
+
+The `OverallTaskList` class is instantiated from `ListParser` only when the end user needs to list available tasks in
+a `Semester`.
+
+How the `OverallTaskList` class works:
+
+1. `OverallTask` objects (explained further under `OverallTask`) are stored in an ArrayList `overallTaskList.`
+2. Both `Task` and `GradableTask` objects are converted to `OverallTask` objects first before being inserted into
+ `OverallTaskList`.
+3. When the `OverallTaskList` object is instantiated, a `ModuleList` object from a semester is passed to its
+ constructor.
+
+
+ 
+4. The constructor calls the method `addAllModuleListTasks(module list)` which converts and adds all the tasks in the
+ module list into `OverallTaskList`.
+5. Once the object is instantiated, the following methods can be called to sort and print the tasks in the
+ ArrayList `overallTaskList`. All sorting and filtering is done via `Java Streams`, and method details are omitted.
+
+ * `sortByDateAndPrint() - Print all tasks sorted by deadline`
+ * `sortByStatusAndPrint() - Print all tasks sorted by status(done)`
+ * `printWeeklyTasks() - Print tasks due in a week`
+ * `printMonthlyTasks() - Print tasks due in a month`
+ * `printYearlyTasks() - Print tasks due in a year`
+ * `printAllTasks() - Print all tasks without sorting`
+ * `printGradableTasks() - Print all gradable tasks`
+ * `printNormalTasks() - Print all non-gradable tasks`
+
+Notes about `OverallTaskList`
+
+* Once `ListParser` is done using the object, it is deleted and the task list is not stored anywhere. The reason for
+ this is to reduce coupling between objects and remove the need to update separate task lists whenever tasks are added
+ to `Modules`.
+
+
+## OverallTask
+
+**API** : `task.OverallTask`
+
+`OverallTask` objects are stored in a `OverallTaskList` object when the end user needs to list available tasks in
+a `Semester`. It stores information from `GradableTask/Task `objects together with their module name.
+
+`OverallTask` object stores the following for each task:
+
+1. `description`
+2. `date`
+3. `isDone`
+4. `isLate`
+5. `deadline`
+6. `isGradable`
+7. `weightage`
+8. `moduleName`
+
+How the `OverallTask` component works:
+
+1. It inherits from `Task`, with additional attributes `isGradable`, `weightage` and `moduleName`.
+2. The attributes `isGradable`, `weightage` are added to provide more information for gradable tasks, while `moduleName`
+ is added to display module information.
+3. It can be instantiated with 2 different constructors:
+ * `OverallTask(task: Task, moduleName: String)` - Instantiates using a `Task` object
+ 
+ * `OverallTask(gradableTask:GradableTask, moduleName: String)` - Instantiates using a `GradableTask `object
+ 
+
+4. During instantiation, information from `Task/GradableTask` objects are added to the `OverallTask` object together
+ with their `moduleName` .
+5. Calling the `toString()` method generates a string containing task information together with its `moduleName`.
+
+
+# Appendix A: Product Scope
+
+**Target User Profile:**
+
+* NUS student
+* is reasonably comfortable using CLI apps
+* can type fast
+* prefers typing to mouse interactions
+* prefer desktop apps over other types
+
+**Value Proposition:**
+
+A centralized platform which allows NUS Students to carry out their learning management without needing to frequently
+switch between multiple tools or applications such as NUSMods, Luminus, Sticky Notes etc.
+
+
+# Appendix B: 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|Forgetful student|have an app to automatically list out my deadlines for each week|prioritise my work.|
+|v1.0|Busy student|be reminded of my tasks|remember all my tasks.|
+|v1.0|Student|see which assignments are completed and which are not|know my progress in this module.|
+|v1.0|User|visualize my timetable|reference it easily.|
+|v1.0|User of a to-do app|see the deadline of each task|prioritise my work.|
+|v2.0|Student|be able to update module details|update outdated information.|
+|v2.0|Student|easily track my CAP progression| gauge how well I am doing.|
+|v2.0|Student|have quick access to upcoming gradable assignments|be sure everything is prepared for.|
+|v2.0|Student|be able to know what classes i have up next| prepare for them in time.|
+|v2.1|University student| see the weightage of the modules| place emphasis/focus on certain work when there is a lack of time.|
+
+
+
+# Appendix C: Non Functional Requirements
+
+1. Should work on any mainstream OS as long as it has Java 11 or above installed.
+2. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be
+ able to accomplish most of the tasks faster using commands than using the mouse.
+
+
+# Appendix D: Glossary
+
+* **Command Line Interface(CLI)** - A command-line interface (CLI) processes commands to a computer program in the form
+ of lines of text(From [Wikipedia](https://en.wikipedia.org/wiki/Command-line_interface)).
+* **Mainstream Operating Systems(OS)** - Windows, Linux, Unix, OS-X
+* **Visualise** - Snapshot of the graded components which the module is made up of and their individual percentages.
+
+
+# Appendix E: Instructions for Manual Testing
+
+Given below are instructions to test the app manually.
+
+**Note:** These instructions only provide a starting point for testers to work on, testers are expected to do more
+exploratory testing.
+
+## Launch
+
+1. Initial launch
+ 1. Download the JAR file and copy it into an empty folder
+ 2. Open up your terminal, and navigate to the folder containing the JAR file
+ 3. Type the following command:
+ ```
+ java -jar NoCap.jar
+ ```
+ **Note**: It is important that you navigate to the directory containing the JAR file first before running the
+ application, as it may affect the location of the save file.
+
+## Saving/Loading data
+
+1. Automatic saving
+ 1. Carry out any command that adds/modifies data in the application, e.g `add CS2102`, or simply exit the
+ application using the command `bye`.
+ 2. Expected: A JSON file is created/updated automatically in the data folder located in folder containing the JAR
+ file, provided the instructions in `Launch` is followed correctly.
+2. No save file exists
+ 1. Prerequisites: Make sure the data folder does not exist/is deleted from the folder containing the JAR file.
+ 2. Run the application as stated in `Launch`
+ 3. Expected: Application starts with an empty template and shows the following message:
+ ```
+ No save file found, starting with an empty template
+ Welcome to NoCap
+ ```
+3. Save file exists
+ 1. Prerequisites: Make sure that a save file already exists in the data folder that is located in the folder
+ containing the JAR file. If not, simply carry out any command that adds/modifies data in the application,
+ e.g `add CS2102`(see 1. Automatic Saving), and the save file will be created automatically.
+ 2. Run the application as stated in `Launch`
+ 3. Expected: Application loads the save file when starting the application and shows the following message:
+ ```
+ Data loaded successfully
+ Welcome to NoCap
+ ```
+4. Corrupted save file
+ 1. Prerequisites: Make sure that a save file already exists in the data folder that is located in the folder
+ containing the JAR file If not, simply carry out any command that adds/modifies data in the application,
+ e.g `add CS2102`(see 1. Automatic Saving), and the save file will be created automatically.
+ 2. To simulate data corruption, remove lines to cause syntax errors in the JSON file, such as lines containing `{` and `}`.
+ 3. Run the application
+ 4. Expected: An error message is shown and application starts with an empty template, showing the message below:
+ ```
+ Error reading save file, creating new template
+ Welcome to NoCap
+ ```
+
+## Listing semesters
+
+1. Run the command `list semesters`
+2. Expected: 10 pre-loaded semesters from Y1S1 to Y5S2 presented in the following message:
+ ```
+ 1 : Y1S1
+ 2 : Y1S2
+ 3 : Y2S1
+ 4 : Y2S2
+ 5 : Y3S1
+ 6 : Y3S2
+ 7 : Y4S1
+ 8 : Y4S2
+ 9 : Y5S1
+ 10 : Y5S2
+ ```
+
+## Switching semesters
+
+1. On first use, NoCap defaults to Y1S1 semester
+2. On subsequent launches, NoCap defaults to the last accessed semester
+3. Switching between semesters
+ 1. Choose a semester index from 1-10 with reference to `list semesters`
+ 2. Run the command `switch ` to switch to the new semester
+ 3. For example, if '3' is chosen, running `switch 3` gives the following expected message:
+ ```
+ Semester successfully switched
+ You are now accessing semester: Y2S1
+ ```
+
+## Adding a module to a semester
+
+1. Prerequisite: Module should already exist.
+2. Adding a module that does not exist.
+ 1. Run the command: `add CS2040C`
+ 2. Expected:
+ ```
+ Module successfully added:
+ 1
+ Module name: CS2040C
+ CREDITS: 0
+ ---------------------------
+ SCHEDULE:
+ ---------------------------
+ GRADE: NIL
+ TASKS: []
+ BREAKDOWN:
+ ```
+3. Adding a module that already exists.
+ 1. Prerequisite: module with the same name exists in current semester list. e.g. CS2040C
+ 2. Run the command `add CS2040C`
+ 3. Expected:`This module already exists!`
+
+4. Adding a module that is longer than 16 Characters.
+
+ Expected: `Module name must be less than 17 characters`
+
+## Deleting a module from a semester
+
+1. Prerequisite: Module index should already exist in the ModuleList
+2. Deleting a module with a valid index.
+ 1. Run the command: `delete 1`
+ 2. Expected:
+ ```
+ CS2040C has been successfully deleted
+ Remaining Modules are:
+ ```
+3. Deleting a module with an invalid index
+ 1. Run the command: `delete 999`
+ 2. Expected
+ ```
+ Invalid number value
+ ```
+
+## Listing all modules in a semester
+
+1. Prerequisite: Semester should exist.
+2. Run the command: `list module`
+3. Expected:
+ ```
+ 1
+ Module name: CS2113T
+ CREDITS: 4
+ ---------------------------
+ SCHEDULE:
+ ---------------------------
+ GRADE: B+
+ TASKS: []
+ BREAKDOWN:
+
+ 2
+ Module name: CS1010
+ CREDITS: 0
+ ---------------------------
+ SCHEDULE:
+ ---------------------------
+ GRADE: NIL
+ TASKS: []
+ BREAKDOWN:
+ ```
+
+## List tasks in a semester
+
+1. List tasks when there are no available tasks
+ 1. Prerequisites: There should be no tasks added to modules in the current semester yet.
+ 2. Run the command `list task`
+ 3. Expected: No tasks are shown and the following message is shown:
+ ```
+ All tasks:
+ You have no tasks
+ ```
+2. List tasks with optional arguments
+ 1. Prerequisites: There should be tasks added to modules in the semester beforehand. Can be checked by
+ running `list task`. If there are no tasks in the semester, add in tasks first(including both gradable and
+ non-gradable tasks)
+ 2. Run list task command with optional arguments, as specified in the user guide, e.g. `list task gradable`
+ 3. Expected: Tasks are shown accordingly, depending on the optional argument
+
+## Adding a task to a module
+
+1. Prerequisite: Module CS1010 already exists.
+2. Adding a valid Task to CS1010.
+ 1. Run the command: `/m cs1010 addtask as01 /by 11/12/2021 2359`
+ 2. Expected:
+ ```
+ Added new task to CS1010
+ [ ] as01 by: 11 Dec 2021 11:59 PM
+ ```
+3. Adding a Task with an invalid syntax.
+ 1. Run the command : `/m cs1010 addtask as01 /by 11-12-21 2359`
+ 2. Expected:
+ ```
+ Wrong date format input!
+ Format: dd/MM/yyyy hhmm
+ ```
+ 3. Run the command: `/m cs1010 addtask /by 11/12/2021 2359`
+ 4. Expected:
+ ```
+ You are missing a description!
+ ```
+
+## Adding a GradableTask to module
+
+1. Prerequisite: Module CS2040C should already exist.
+2. Adding a valid GradableTask to module.
+ 1. Run the command: `/m cs2040c addgradable finals /by 11/11/2021 1000 /w 50`
+ 2. Expected:
+
+ ```
+ Added new task to CS2040C
+ finals by: 11 Nov 2021 10:00 AM Weightage 50% [ ]
+ BREAKDOWN:
+ <======================50%=======================>
+ ##################################################
+ |-----------------------1------------------------|
+ 1: finals
+
+ 1 finals by: 11 Nov 2021 10:00 AM Weightage 50% [ ]
+ ```
+
+3. Adding a GradableTask with an invalid syntax.
+ 1. Run the command : `/m cs2040c addgradable finals /by 00/00/00 /w 50`
+ 2. Expected:
+ ```
+ Wrong date format input!
+ Format: dd/MM/yyyy hhmm
+ BREAKDOWN:
+ ```
+
+## Listing all gradable tasks
+
+1. Prerequisite: module CS2040C should already exist.
+2. Run the command: `/m cs2040c addgradable finals /by 11/11/2021 1000 /w 50`
+3. Run the command: `/m cs2040c addgradable midterms /by 11/09/2021 1300 /w 50`
+4. Run the command: `/m CS2040C list gradable`
+5. Expected:
+ ```
+ BREAKDOWN:
+ <======================50%=======================><======================50%=======================>
+ ##################################################@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ |-----------------------1------------------------||-----------------------2------------------------|
+ 1: finals
+ 2: midterms
+
+ 1 finals by: 11 Nov 2021 10:00 AM Weightage 50% [ ]
+ 2 midterms by: 11 Sep 2021 01:00 PM Weightage 50% [ ]
+
+ ```
+
+## Editing a task
+
+1. Prerequisites: module CS2040C exists without any existing task.
+2. Add a new task: `/m cs2040c addtask testcase 1 /by 11/11/2021 0000`.
+3. Editing description:
+ 1. Run the command: `/m cs2040c editdesc 1 testcase 2`.
+ 2. Run the command: `/m cs2040c list`.
+ 3. Expected:
+ ```
+ Task List for CS2040C:
+ There are 1 tasks
+ 1.[ ] testcase 2 by: 11 Nov 2021 12:00 AM
+ ```
+4. Editing deadline:
+ 1. Run the command: `/m cs2040c editdate 1 12/12/2022 1212`.
+ 2. Run the command: `/m cs2040c list`.
+ 3. Expected:
+ ```
+ Task List for CS2040C:
+ There are 1 tasks
+ 1.[ ] testcase 2 by: 12 Dec 2022 12:12 PM
+ ```
+
+## Deleting a task
+
+1. Prerequisites: module CS2040C exists without any existing task.
+2. Add a new task: `/m cs2040c addtask testcase 1 /by 11/11/2021 0000`.
+3. Deleting a task without matching keyword:
+ 1. Run the command: `/m cs2040c deletetask apple`.
+ 2. Expected: `Task with the specified keyword not found!`
+4. Deleting a task with the matching keyword:
+ 1. Run the command: `/m cs2040c deletetask test`.
+ 2. Expected:
+ ```
+ The following task(s) are found:
+ 1: testcase 2
+ Please choose a task to perform the action, or press x to cancel:
+ ```
+ 3. Enter input: `1`.
+ 4. Expected: `testcase 1 has been deleted`.
+
+## Marking a task as complete
+
+1. Prerequisites: module CS2040C exists without any existing task.
+2. Add a new task: `/m cs2040c addtask testcase 1 /by 11/11/2021 0000`.
+3. Run the command: `/m cs2040c done 1`.
+4. Expected:
+ ```
+ Task is completed:
+ [X] testcase 1 by: 11 Nov 2021 12:00 AM
+ ```
+
+## Listing all tasks in a module
+
+1. Prerequisites: module CS2040C exists without any existing task.
+2. Add a new task: `/m cs2040c addtask testcase 1 /by 11/11/2021 0000`.
+3. Add a new task: `/m cs2040c addtask testcase 2 /by 12/12/2022 1212`.
+4. Run the command: `/m cs2040c list`.
+5. Expected:
+ ```
+ Task List for CS2040C:
+ There are 2 tasks
+ 1.[ ] testcase 1 by: 11 Nov 2021 12:00 AM
+ 2.[ ] testcase 2 by: 12 Dec 2022 12:12 PM
+ ```
+
+## Adding a grade to a module
+
+1. Prerequisites: module CS2040C exists without any task/class/credit.
+2. Run the command: `/m cs2040c addgrade A`
+3. Expected:
+ ```
+ Module grade successfully added:
+ Module name: CS2040C
+ CREDITS: 0
+ ---------------------------
+ SCHEDULE:
+ ---------------------------
+ GRADE: A
+ TASKS: []
+ BREAKDOWN:
+ ```
+
+## Deleting a grade from a module
+
+1. Prerequisites: module CS2040C exists and has an assigned grade.
+2. Run the command: `/m cs2040c deletegrade`.
+3. Expected: `Module grade has been successfully deleted`.
+
+## Adding a credit to module
+
+1. Prerequisites: module CS2040C exists without any task/class/grade.
+2. Run the command: `/m cs2040c addcredit 4`
+3. Expected:
+ ```
+ Module credits successfully added:
+ Module name: CS2040C
+ CREDITS: 4
+ ---------------------------
+ SCHEDULE:
+ ---------------------------
+ GRADE: NIL
+ TASKS: []
+ BREAKDOWN:
+ ```
+
+## Adding a class to module
+
+1. Prerequisite: module CS2040C exists.
+2. Adding a class with valid syntax.
+ 1. Run the command: `/m CS2040C addclass MON/1000/ZOOM/LECT`
+ 2. Expected:
+ ```
+ Module Class successfully added:
+ 1.
+ Day: MON
+ Start Time: 1000
+ Location: ZOOM
+ Comments: LECT
+ ```
+5. Adding a class that occupies the same timeslot.
+ 1. Run the command: `/m CS2040C addclass MON/1000/ZOOM/TUT`
+ 2. Expected: `A class already exists in this timeslot!`
+
+## Delete class from a module
+
+1. Select an existing class index with reference to the command `/m info`.
+2. Run the command `/m deleteclass `.
+3. Expected: Corresponding class is deleted from module schedule.
-## Non-Functional Requirements
+## Show timetable
-{Give non-functional requirements}
+1. Run the command `timetable`
+2. Expected: Prints out a timetable of all classes from 0800 to 1700 periods
-## Glossary
+## Show semester cap
-* *glossary item* - Definition
+1. Run the command `cap`
+2. If no credit is added for any module in the semester, expected:
+ ```
+ Unable to calculate cap as no credit assigned to any existing module
+ ```
+3. Else, the cap is calculated accordingly and printed.
-## Instructions for manual testing
+## Show all cap
-{Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing}
+1. Run the command `allcap`
+2. Expected: prints out the aggregate cumulative CAP and respective CAP of each semester
+3. Semesters with no credit assigned for any module will default to 0.00 CAP
diff --git a/docs/README.md b/docs/README.md
index bbcc99c1e7..d38bbbe686 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,8 +1,25 @@
-# Duke
+# NoCap
-{Give product intro here}
+## Introduction
+NoCap (NC) is a **desktop app for managing modules taken in NUS, optimized for use via a Command Line Interface** (CLI).
+If you can type fast, NC can get your module management tasks done faster than traditional GUI apps. It is the perfect
+app for NUS students!
-Useful links:
-* [User Guide](UserGuide.md)
-* [Developer Guide](DeveloperGuide.md)
+## Useful links
* [About Us](AboutUs.md)
+* [User Guide](UserGuide.md)
+* [DeveloperGuide](DeveloperGuide.md)
+
+## Downloads
+Download the latest version of **NoCap** [here](https://github.com/AY2122S1-CS2113T-F11-1/tp/releases)
+
+## Acknowledgements
+Third party libraries used:
+
+- [Jackson Databind](https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind)
+- [Jackson Datatype-jsr310](https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jsr310)
+- [Jackson Annotations](https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations)
+
+
+
+
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index abd9fbe891..d638df1211 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -2,41 +2,630 @@
## Introduction
-{Give a product intro}
+NoCap (NC) is a **desktop app for managing modules taken in NUS, optimized for use via a Command Line Interface** (CLI).
+If you can type fast, NC can get your module management tasks done faster than traditional GUI apps. It is the perfect
+app for NUS students!
## Quick Start
-{Give steps to get started quickly}
-
1. Ensure that you have Java 11 or above installed.
-1. Down the latest version of `Duke` from [here](http://link.to/duke).
+2. Download the latest version of `NoCap` from [here](https://github.com/AY2122S1-CS2113T-F11-1/tp/releases).
+3. Move the downloaded NoCap.jar to an empty directory.
+4. Navigate to this directory on the Command Prompt.
+5. Run NoCap.jar using the command `java -jar NoCap.jar`.
+
+## Features
+
+### **Command format:**
+
+* Words in `<>` are the parameters to be supplied by the user. \
+ e.g. `add ` `` is the name of the module supplied by the user.
+* Parameters must be in the exact order as seen in the user guide. \
+ e.g. if the command specifies `/m CG1111 addclass MON/0800/E1-03/tutorial`
+ , `/m CG1111 addclass 0800/E1-03/tutorial/MON/`
+ is not acceptable.
+* Commands that require <> parameters must be supplied, if not an error will be thrown. e.g. if the command
+ specifies `/m addclass ` , `/m CG1111 addclass MON/0800/E1-03/` is not acceptable.
+* Commands that require () parameters are optional. e.g. for `/m list (optional argument)`, `/m cs1010 list` is acceptable.
+* All specifier commands are case-sensitive. e.g. `HELP`, `/M CG1111 ADDCLASS MON/0800/E1-03/tutorial` and `ADD CS2113T`
+ are invalid commands.
+
+### Viewing help : `help`
+
+Shows a message explaining how to use NoCap.
+
+### Listing semesters : `list semesters`
+
+Lists all preloaded semesters and their corresponding indexes.
+
+Example output:
+
+```
+ 1 : Y1S1
+ 2 : Y1S2
+ 3 : Y2S1
+ 4 : Y2S2
+ 5 : Y3S1
+ 6 : Y3S2
+ 7 : Y4S1
+ 8 : Y4S2
+ 9 : Y5S1
+ 10 : Y5S2
+```
+
+### Switching semesters : `switch `
+
+Switches the currently accessed semester to the corresponding input index. (refer to `list semesters` for the index).
+The currently accessed semester on start up is your last accessed semester from any previous use.
+
+Examples:
+
+* `Switch 1`
+* `Switch 2`
+
+### Adding module: `add `
+
+Adds a module
+
+Examples:
+
+* `add CS2113T`
+* `add MA1508`
+
+Note:
+
+* Module code can only contain up to 16 characters
+
+### Delete module: `delete `
+
+Deletes a module corresponding to the input index. (refer to `list module` for indexes )
+
+Examples:
+
+* `delete 1`
+* `delete 2`
+
+Example output:
+
+```
+CS2113T has been successfully deleted
+Remaining Modules are:
+1
+Module name: CS2040C
+CREDITS: 4
+---------------------------
+SCHEDULE:
+---------------------------
+GRADE: A
+TASKS: []
+BREAKDOWN:
+```
+
+### List module : `list module`
+
+Lists all added modules and their corresponding indexes.
+
+### Add task to module : `/m addtask /by (time)`
+
+* The `date` is in the format of dd/MM/yyyy, dd MM yyyy.
+* The `time` is in the format of hhmm.
+* The `description` can contain white spaces.
+
+Note:
+
+* If time is omitted, time will default to 0000 hrs.
+* If duplicate task exist and has a different deadline, the existing task's deadline will be updated with the new
+ deadline.
+* If duplicate task has same deadline, new task will be rejected by the program.
+* The task description cannot contain any `/by` blocks.
+
+Example of usage:
+
+* `/m cs1010 addtask Remember to S/U /by 20/11/2020`
+
+Example of expected output:
+
+
+
+**Warning** :
+
+* For every month, the program will take in 01 - 31 as an input for the day of the month.
+* In the case when the month does not have 31 days, the program will treat any input after the last day of the month
+ until 31 as the last day.
+
+Example:
+
+* Date input as `31/02/2021` will be parsed as `28/02/2021` because there are only 28 days in the month of February.
+* Date input as `31/04/2021` will be parsed as `30/04/2021` because there are only 30 days in the month of April
+
+### Add gradable task to module : `/m addgradable /by (time) /w `
+
+Adds a Gradable Task to the module.
+
+* Refer to Add Task for deadline format.
+
+Note:
+
+* The weightage needs to be an Integer between 5 - 100.
+* The total value of all gradable tasks within the module needs to be less than or equals to 100, else a error message
+ will be shown.
+* The GradableTask description cannot contain any `/by` or `/w` blocks.
+
+Examples:
+
+* `/m CS2113 addgradable Finals /by 10/10/2010 1000 /w 30`
+* `/m CS2113 addgradable assignments /by 10/10/2010 1000 /w 40`
+
+Expected output:
+
+
+
+Note:
+
+* The suffix of the Gradable Task `[]` shows if the task is completed.
+* If it is completed, the suffix will show `[X]`.
+
+### Edit description of task : `/m editdesc `
+
+Edit the description of a task at the specified index in the given module.
+
+Note:
+
+* An error message will be shown if the user input a duplicate description of an existing task.
+
+### Edit deadline of task : `/m editdate `
+
+Edit the deadline of a task at the specified index in the given module.
+
+### Delete task from module : `/m deletetask `
+
+Finds and list down tasks with the substring. Type corresponding index to delete the task.
+
+### Marks module task as done : `/m done `
+
+Mark task as done.
+
+Other similar commands include:
+Mark task as not done: `/m notdone `
+Mark gradable task as done:`/m gradabledone `
+Mark gradable task as not done:`/m gradablenotdone `
+
+### Listing module tasks : `/m list (optional argument)`
+
+Shows a list of task of specified module.
+
+By default, all tasks in the module specified in the current semester are listed, but this can be customised by adding
+optional arguments.
+
+(optional argument) includes:
+
+* gradable - Shows a list of gradable tasks in the module.
+* sortbydate - Sort tasks by due date, the closest deadline have the higher priority in the list. does not print the
+ task list.
+* sortbystatus - Sort tasks by status, finished tasks of lower priority. Does not print task list.
+* w - list tasks due within the next week.
+* m - list tasks due within the next month.
+* y - list tasks due within the next year.
+
+Task Prefixes:
+
+* There are 2 prefixes in each Task defined as `[ ]`
+* The first prefix is a `LATE` tag. If the task is overdue, the tag will show `[LATE]`
+* The second prefix is a `DONE` tag. If the task is marked completed, the tag will show `[X]`
+* Format will be as follows `[LATE][DONE] `
+
+Examples with expected output:
+
+Assuming tasks have been added to modules beforehand:
+
+* `/m cs1010 list`
+
+ ```
+ Task List for CS1010:
+ 1.[LATE][X] Remember to S/U by: 20 Nov 2020 12:00 AM
+ 2.[LATE][ ] Remember to drop out by: 12 Dec 2020 11:59 PM
+ 3.[ ] retake cs1010 by: 12 Dec 2021 11:59 PM
+ 4.[ ] do assignment by: 30 Oct 2021 04:00 PM
+ ```
+
+* `/m cs1010 list gradable`
+
+ ```
+ BREAKDOWN:
+ <=================40%==================>
+ ########################################
+ |------------------1-------------------|
+ 1: assignments
+
+ 1 assignments by: 10 Oct 2010 10:00 AM Weightage 40% [ ]
+ ```
+* `/m cs1010 list sortbydate`
+
+ ```
+ CS1010 successfully sorted by date
+ ```
+
+ ```
+ /m cs1010 list
+ ```
+
+ ```
+ Task List for CS1010:
+ 1.[LATE][X] Remember to S/U by: 20 Nov 2020 12:00 AM
+ 2.[LATE][ ] Remember to drop out by: 12 Dec 2020 11:59 PM
+ 3.[ ] do assignment by: 30 Oct 2021 04:00 PM
+ 4.[ ] retake cs1010 by: 12 Dec 2021 11:59 PM
+
+ ```
+
+
+* `/m cs1010 list w`
+
+ ```
+ Task List for CS1010:
+ There are 3 tasks due within 7 days
+ 1.[LATE][X] Remember to S/U by: 20 Nov 2020 12:00 AM
+ 2.[LATE][ ] Remember to drop out by: 12 Dec 2020 11:59 PM
+ 3.[ ] do assignment by: 30 Oct 2021 04:00 PM
+ ```
+
+NOTE:
+
+* For optional arguments `w`, `m` and `y`, overdue tasks are listed together with the weekly/monthly/yearly tasks
+ regardless of due date as a reminder that the user has forgotten to do the task.
+* `/m list` does not show gradable tasks.
+* To show gradable task in module, have to input optional argument as shown above.
-## Features
+### Add class to module : `/m addclass `
-{Give detailed description of each feature}
+Adds a class to a module
-### Adding a todo: `todo`
-Adds a new item to the list of todo items.
+Examples:
-Format: `todo n/TODO_NAME d/DEADLINE`
+* `/m CG1111 addclass MON/0800/E1-03/tutorial`
+* `/m MA1508 addclass WED/1000/zoom/lecture`
-* The `DEADLINE` can be in a natural language format.
-* The `TODO_NAME` cannot contain punctuation.
+Note:
-Example of usage:
+* DAY can only take on the following inputs in both uppercase and lowercase: {MON, TUE, WED, THU, FRI, SAT, SUN}
+* PERIOD is a 1hr block in 24hr format
+* Examples:
-`todo n/Write the rest of the User Guide d/next week`
+
+
+
+Valid
+
+ Invalid
+
+
+
+ 0800
+
+ 0830
+
+
+
+ 1000
+
+ 2500
+
+
+
+ 0000
+
+ 11111
+
+
+
-`todo n/Refactor the User Guide to remove passive voice d/13/04/2020`
+* LOCATION and COMMENTS can only take on a maximum of 16 characters and cannot be empty
+* Only one class can be added in any period
+
+### Delete classes from module: `/m deleteclass `
+
+Deletes a module corresponding to the input index.(refer to `/m info` or `list module` for indexes)
+
+Examples:
+
+* `/m CS2040C deleteclass 1`
+* `/m CG1112 deleteclass 2`
+
+Example output:
+
+```
+Class:
+Day: TUE
+Start Time: 1000
+Location: zoom
+Comments: lect
+has been successfully deleted
+
+Remaining Classes are:
+1.
+Day: MON
+Start Time: 1000
+Location: zoom
+Comments: lect
+```
+
+### Add credit to module: `/m addcredit `
+
+Add credits to a module.
+
+Examples:
+
+* `/m CS2113 addcredit 4`
+* `/m MA1511 addcredit 2`
+
+Example output:
+
+```
+Module credits successfully added:
+Module name: CS2040C
+CREDITS: 4
+---------------------------
+SCHEDULE:
+---------------------------
+GRADE: A
+TASKS: []
+BREAKDOWN:
+```
+
+Note:
+
+* Credit values must be positive integers
+
+### Add grade to module: `/m addgrade `
+
+Adds a grade to a module
+
+Examples:
+
+* `/m CS2113 addgrade A`
+* `/m MA1508 addgrade B-`
+
+Example output:
+
+ /m cs2040c addgrade A
+ Module grade successfully added:
+ Module name: CS2040C
+ CREDITS: 4
+ ---------------------------
+ SCHEDULE:
+ ---------------------------
+ GRADE: A
+ TASKS: []
+ BREAKDOWN:
+
+Note:
+
+* A grade can be added to a module only after credit is assigned to the module.
+
+Note:
+
+* The input for `` is case-sensitive.
+* The input for `` must correspond to one of the following values:
+ 1. A+
+ 2. A
+ 3. A-
+ 4. B+
+ 5. B
+ 6. B-
+ 7. C+
+ 8. C
+ 9. D+
+ 10. D
+ 11. F
+
+### Delete grade from module: `/m deletegrade`
+
+Deletes the grade, if any.
+
+Examples:
+
+* `/m CS2113 deletegrade`
+* `/m MA1511 deletegrade`
+
+Example output:
+
+ Module grade has been successfully deleted
+ Module name: CS1010
+ CREDITS: 0
+ ---------------------------
+ SCHEDULE:
+ ---------------------------
+ GRADE: null
+ TASKS: []
+ BREAKDOWN:
+
+### View Timetable : `timetable `
+
+Shows the timetable for the currently accessed semester.
+
+Example output:
+
+
+Note:
+
+* Timetable can only display classes from 0800 to 1700 periods
+* If timetable shows artifacts (i.e. columns not aligned/information displayed incorrectly):
+ * Restart NoCap in a maximized command prompt window
+ * Reduce text size in your computer settings
+
+### Listing all tasks : `list task (optional argument)`
+
+Shows a list of all tasks within the current semester
+
+By default, all tasks in the current semester are listed, but this can be customised by adding optional arguments.
+
+(optional argument) includes:
+
+* sortbydate - Sort tasks by due date.
+* sortbystatus - Sort tasks by status, displaying unfinished tasks first.
+* gradable - list gradable tasks only.
+* normal - list non-gradable tasks only.
+* w - list tasks due within the next week.
+* m - list tasks due within the next month.
+* y - list tasks due within the next year.
+
+Tasks are listed in the format:
+
+`[Module Code][Gradable][Lateness][Done] by: [Weightage]`
+
+* [Module Name] - Name of the module
+* [Gradable] - Shows `[G]` if the task is gradable, and `[ ]` if the task is non-gradable.
+* [Lateness] - Shows `[LATE]` if the task is overdue. Only shows up for overdue tasks
+* [Done] - Shows `[X]` if the task is done, and `[ ]` if the task isn’t done yet.
+* <description> - Description of the task
+* <deadline> - Deadline of the task
+* [Weightage] - Weightage of the task, if it is gradable. Only shows up for gradable tasks.
+
+Example tasks:
+
+1. `[CS2132][G][ ] Assignment by: 16 Dec 2021 12:00 AM [Weightage: 50%]`
+
+ * Belongs to the module CS2132
+ * Gradable
+ * Not done yet
+ * Due on 16 Dec 2021 12:00 AM
+ * Has a weightage of 50%.
+
+2. `[CS2132][ ][X] Quiz by: 16 Nov 2021 12:00 AM`
+
+ * Belongs to the module CS2132
+ * Non-Gradable
+ * Done
+ * Due on 16 Nov 2021 12:00 AM
+
+Example commands with expected output:
+
+Assuming tasks have been added to modules beforehand:
+
+* `list task`
+
+ ```
+ All tasks:
+ 1. [CS2132][G][ ] Assignment by: 16 Dec 2021 12:00 AM [Weightage: 50%]
+ 2. [CS2132][G][LATE][ ] asdf by: 10 Dec 2000 12:00 AM [Weightage: 50%]
+ ```
+
+
+* `list task sortbydate`
+
+ ```
+ Tasks sorted by date:
+ 1. [CS2132][G][LATE][ ] asdf by: 10 Dec 2000 12:00 AM [Weightage: 50%]
+ 2. [CS2132][G][ ] Assignment by: 16 Dec 2021 12:00 AM [Weightage: 50%]
+ ```
+
+
+* `list task w`
+
+ ```
+ Weekly tasks:
+ 1. [CS2132][G][LATE][ ] asdf by: 10 Dec 2000 12:00 AM [Weightage: 50%]
+ ```
+
+NOTE:
+
+* For optional arguments `w`, `m` and `y`, overdue tasks are listed together with the weekly/monthly/yearly tasks
+ regardless of due date as a reminder that the user has forgotten to do the task.
+
+### View CAP : `cap`
+
+Shows the CAP for the currently accessed semester.
+
+Example output:
+
+* `This semester's CAP: 4.25`
+
+NOTE:
+
+* Modules with a credit value of 0 will not be calculated towards overall CAP.
+
+### View all CAP : `allcap`
+
+Shows the CAP for all semesters and aggregated CAP.
+
+Format: `allcap`
+
+Example output:
+
+ Cumulative CAP: 4.25
+ Y1S1: 4.25
+ Y1S2: 0.0
+ Y2S1: 0.0
+ Y2S2: 0.0
+ Y3S1: 0.0
+ Y3S2: 0.0
+ Y4S1: 0.0
+ Y4S2: 0.0
+ Y5S1: 0.0
+ Y5S2: 0.0
+
+### Exiting the program : `bye`
+
+Exits the program.
+
+### Saving data
+
+Program data is saved whenever data is added or modified. The process is done automatically, so no user input is needed
+for this.
+
+### Loading data
+
+Program data is loaded from the data folder during startup of the program. If the program can detect the data file
+successfully, the data is loaded and the following message should appear:
+
+```
+Data loaded successfully
+Welcome to NoCap
+```
+
+On the other hand, if no data file can be found, the program starts with an empty template, and the following message
+should appear:
+
+```
+No save file found, starting with an empty template
+Welcome to NoCap
+```
## FAQ
-**Q**: How do I transfer my data to another computer?
+**Q**: How do I transfer my data to another computer?
-**A**: {your answer here}
+**A**: Simply transfer your data.json file to the data subfolder of another NoCap application.
-## Command Summary
+> **WARNING**: Replacing data.json file in another NoCap folder results in the existing data being erased!
-{Give a 'cheat sheet' of commands here}
+## Command Summary
-* Add todo `todo n/TODO_NAME d/DEADLINE`
+|Action|Format|
+|--------|----------|
+|show help| help |
+|Show all semesters| list semesters|
+|Change semester| switch \|
+|Add module| add \|
+|Delete module| delete \|
+|List all module| list module|
+|Add task| /m \ addtask \ /by \ (time)|
+|Add gradable task | /m \ addgradable \ /by \ (time) /w \|
+|Edit description| /m \ editdesc \ \|
+|Edit deadline| /m \ editdate \ \|
+|Delete task| /m \ deletetask \|
+|Mark task as complete| /m \ done \|
+|Mark task as incomplete| /m \ notdone \|
+|Mark gradable task as complete| /m \ gradabledone \|
+|Mark gradable task as incomplete| /m \ gradablenotdone \|
+|List module tasks| /m \ list (optional argument)|
+|Add class | /m \ addclass \|
+|Delete class| /m \ deleteclass \|
+|Add grade| /m \ addgrade \|
+|Delete grade| /m \ deletegrade|
+|Add credit| /m \ addcredit |
+|Show timetable| timetable|
+|List all tasks| list task (optional argument)|
+|Show semester cap| cap|
+|Show overall cap| allcap|
+|Exit NoCap|bye|
diff --git a/docs/_config.yml b/docs/_config.yml
new file mode 100644
index 0000000000..c4192631f2
--- /dev/null
+++ b/docs/_config.yml
@@ -0,0 +1 @@
+theme: jekyll-theme-cayman
\ No newline at end of file
diff --git a/docs/media/AddTaskOutput.jpg b/docs/media/AddTaskOutput.jpg
new file mode 100644
index 0000000000..4a2a2c9f2b
Binary files /dev/null and b/docs/media/AddTaskOutput.jpg differ
diff --git a/docs/media/DmitriYamphoto.jpg b/docs/media/DmitriYamphoto.jpg
new file mode 100644
index 0000000000..49554646b9
Binary files /dev/null and b/docs/media/DmitriYamphoto.jpg differ
diff --git a/docs/media/GradableTaskOutput.jpg b/docs/media/GradableTaskOutput.jpg
new file mode 100644
index 0000000000..f90475dd5f
Binary files /dev/null and b/docs/media/GradableTaskOutput.jpg differ
diff --git a/docs/media/ModuleListseq.png b/docs/media/ModuleListseq.png
new file mode 100644
index 0000000000..66dbf5ad23
Binary files /dev/null and b/docs/media/ModuleListseq.png differ
diff --git a/docs/media/OverallTaskClassDiagram.drawio b/docs/media/OverallTaskClassDiagram.drawio
new file mode 100644
index 0000000000..a460699425
--- /dev/null
+++ b/docs/media/OverallTaskClassDiagram.drawio
@@ -0,0 +1 @@
+7Vptc+I2EP41zKSdoeMXTMhHDAnXO+5KLumlX4UtjIpsObYIIb++K1t+wwIcApfJlU+glbSS9nm0Wq3VMgf+8yhC4fwrczFtGZr73DKHLcPQNaMDP0KyTiXWlZEKvIi4slEhuCMvOOsppUvi4rjSkDNGOQmrQocFAXZ4RYaiiK2qzWaMVkcNkYdrgjsH0br0gbh8nkp7xmUh/4SJN89G1rtXaY2PssZyJfEcuWxVEpnXLXMQMcbTf/7zAFNhvMwuD3+uH+h40R19vo0f0d/2l/tvP9qpspvXdMmXEOGAH6z6ZTG7+fSj8+8/4eTmdqWN+tPPbdlFe0J0Ke311xOOEKX3KF6MSczl0vk6s2e8Ij5FAZTsGQv4nawBa9iIEi+A/w5ME0cgAFWcABR9WcFZCFJnTqg7Rmu2FIuJOXIWWcmes4i8gFpEoUoHAVRHXLLK6FZa3ImeINZAGuEY2kwyC+m5aIxiLts4jFIUxmSaTFg08VHkkcBmnDM/U8SWgYtdWcohTwo8YoucRKJ/Q1wkfsIa+LnESonTCDMf82gNTbLansRF7jm9I8urgsGGJmXzMnstuTmR3DVerjsf7jvsMhR4YIR8PMPcP57eVYwHiFSGQxSADxDHtjBjXOYj/CkttRAlLH0FYzs1xtYoSklCzzn3CxZtQpdxleIZVzDVJ64r1NlxiBwSePeCucO2XkjGScehWUi+S7uYCY044ihlmuAORVNMJywmnDChP0rb2iEjAU+MZ9kta5hIIj5gAcwYkYRVGBi8woLFCr7t3NT7+bauwriPXr3t5KrAvANTfEseB0bviZqPj7PbwJ08aF/aVwpMYbHahi+68Jm7pDhxS2a/JZwwTNzUhYG/5lW/1fgAVuA5CQaMMvBNw4ClPoxQuiHaQ43UiVVZ0KmzQIgY9J3RxIHMgVA4UDCjygAb1jTQ/rAEF4wBlPWi3JAeNdibMmYrPcxOM3p0uifih17jR1sM47p9SgvgBU3iixInNulS5kgieGIw7Jktx2VLr+FZdTK2mFu8SRiBDfqpO4kvzhQ4HQX0pgfKZvxwCAeU89MvdwW2TYNa7RzUHi+otbReNcjUFI5CNxQkudIOCWot6+MEtXpPecLBxdmJSJjEjBDtJA7rjoMb87a5rA/uivb6nXxfvym2VbLsVMdR/cKSQAt8yjEdM8BoCJJ74uNfFNpDTpkU7a3QZpfdd4PWUkJL4qGICzJwp4xRjIIzrE1hvey9M6xdJazpbeIb8gtof2lffAJkdU1xk/yp0NbDwnTHjiLkpsY8b9lDgDWbxnKnAlYdQJF4LM7ZM6gHgWr9xNBJOUFDdZU/J3vfGBHrvYZx0xHSvWpYVTn8jXwvlC64+E32bioZ1HJ7yWlcOoq35n4/+O4+BQuuGrpsa8ft+21Z3frHRyUNPHk23+d0KPFgVK4UDNH+X8w4gd83VF8bT8UMtYOoR+A1+HDg9sXzACixMLH1nrxWNQkG3W8IzY4M7Ho4S/phOmWr60JgJwKoyKjx6hxZzJaRg3esV9qRo8jDTXa5mO7OPS4JIahUToF15IQiTBEnT9UXEjuyaxPB2kKtufl5OnsSkqlIFyx7FVyoKbI6exSlFtmvqL2hyNzQw2azGFdUHCtzZ9RvFBnqBVeTrG3JC+zxOVOZu00DCRs5Cy8h7+aXCJGpTpPIutX8qMg2l3xwI6fVyp+5NM/ott9Ip7Wywwmxql8Sfm+MVRbEHQGs3nuAdZyt367m8Dtvhw6KxROqtHnxEM28/g8=
\ No newline at end of file
diff --git a/docs/media/OverallTaskClassDiagram.png b/docs/media/OverallTaskClassDiagram.png
new file mode 100644
index 0000000000..bad60e93f9
Binary files /dev/null and b/docs/media/OverallTaskClassDiagram.png differ
diff --git a/docs/media/OverallTaskConstructorGradableTaskSequenceDiagram.png b/docs/media/OverallTaskConstructorGradableTaskSequenceDiagram.png
new file mode 100644
index 0000000000..705c0dcb47
Binary files /dev/null and b/docs/media/OverallTaskConstructorGradableTaskSequenceDiagram.png differ
diff --git a/docs/media/OverallTaskConstructorGradableTaskSourceSequenceDiagram.txt b/docs/media/OverallTaskConstructorGradableTaskSourceSequenceDiagram.txt
new file mode 100644
index 0000000000..d95ff365d4
--- /dev/null
+++ b/docs/media/OverallTaskConstructorGradableTaskSourceSequenceDiagram.txt
@@ -0,0 +1,7 @@
+title OverallTask Constructor
+
+participant ":OverallTask" as OverallTask
+[->*OverallTask: new OverallTask(Gradable Task, Module Name)
+activate OverallTask
+OverallTask -->[:
+deactivate OverallTask
\ No newline at end of file
diff --git a/docs/media/OverallTaskConstructorTaskSequenceDiagram.png b/docs/media/OverallTaskConstructorTaskSequenceDiagram.png
new file mode 100644
index 0000000000..3ed10282d2
Binary files /dev/null and b/docs/media/OverallTaskConstructorTaskSequenceDiagram.png differ
diff --git a/docs/media/OverallTaskConstructorTaskSequenceDiagramSource.txt b/docs/media/OverallTaskConstructorTaskSequenceDiagramSource.txt
new file mode 100644
index 0000000000..74fcac4ecf
--- /dev/null
+++ b/docs/media/OverallTaskConstructorTaskSequenceDiagramSource.txt
@@ -0,0 +1,7 @@
+title OverallTask Constructor
+
+participant ":OverallTask" as OverallTask
+[->*OverallTask: new OverallTask(Task, Module Name)
+activate OverallTask
+OverallTask -->[:
+deactivate OverallTask
\ No newline at end of file
diff --git a/docs/media/OverallTaskListConstructorSequenceDiagram.png b/docs/media/OverallTaskListConstructorSequenceDiagram.png
new file mode 100644
index 0000000000..8888fadc1c
Binary files /dev/null and b/docs/media/OverallTaskListConstructorSequenceDiagram.png differ
diff --git a/docs/media/OverallTaskListConstructorSequenceDiagramSource.txt b/docs/media/OverallTaskListConstructorSequenceDiagramSource.txt
new file mode 100644
index 0000000000..0a409b354f
--- /dev/null
+++ b/docs/media/OverallTaskListConstructorSequenceDiagramSource.txt
@@ -0,0 +1,12 @@
+title Constructor
+
+participant ":OverallTaskList" as OverallTaskList
+
+[->*OverallTaskList: new OverallTaskList(module list)
+activate OverallTaskList
+OverallTaskList->OverallTaskList:addAllModuleListTasks(module list)
+activate OverallTaskList
+OverallTaskList-->OverallTaskList:
+deactivate OverallTaskList
+OverallTaskList -->[:
+deactivate OverallTaskList
diff --git a/docs/media/ParserClassDiagram.JPG b/docs/media/ParserClassDiagram.JPG
new file mode 100644
index 0000000000..89648aab5a
Binary files /dev/null and b/docs/media/ParserClassDiagram.JPG differ
diff --git a/docs/media/ParserClassDiagram.drawio.xml b/docs/media/ParserClassDiagram.drawio.xml
new file mode 100644
index 0000000000..99bd15dd81
--- /dev/null
+++ b/docs/media/ParserClassDiagram.drawio.xml
@@ -0,0 +1,2 @@
+
+7VrbctowEP0aHtPBNr7wmJCETHNtSSdNXjqqLbCKbFFZBMjXVw5yfJG4NBiLdvKEtVrb0jmro12ZltWL5n0KJuE1CSBume1g3rJOW6Zp2IbHf1LLYmnx2sIwoigQTrlhgF6gMLaFdYoCmJQcGSGYoUnZ6JM4hj4r2QClZFZ2GxJcfusEjKBkGPgAy9YHFLBQzMJu5/YLiEZh9majLXoikDkLQxKCgMwKJuusZfUoIWx5Fc17EKfgZbgs7ztf0fs2MApjts0N9CmkJ6Ob6y9g0R32neT3528PRxnMzwBPxYzFaNkig4CSaRzA9CntlnUyCxGDgwnw094ZJ53bQhZh3jL4pTwqMdBnSBmcF0xilH1IIsjogruIXssVgxIh0+mI9iwnwPKELSyAb2VQA0H66O3ZOS78QkDzNzBZEiow4HEimoSykIxIDPBZbj0p45b7XBEyEWj9gowtRNCDKSNlLOEcse/p7Z9s0Xos9JzOxZNfG4usEfP5Fm5Km4/Fvvy211Z230reEjKlPlyHjViVgI4gW+PXWfqluK2NAgoxYOi5vP7qZ1SK+xvS48GsO/g7Rjn4LUXwG6Yi+J19xb4lIcVVfsrx0Q2VWYGq4zUI1ctFuOg/3gUoGpzdh93+8dEROJK180Mlyqu/qBJKCA1dKnHZv/zx5NPx2LGHtxQ/xVcDopnQAp05uRsINUp05uyuJbSCdm0Mr9YTXbLfkcTsDtAEUu1iVtX9RsVMCZUtQXUKGDwQuOxuGS5HO1yGo1MrjPdoRfs9WvEeaXC2lIbuQaWIpt6kvwH1Tzgj7DitkrnBxyBJkJ+ZzxF+G1IcZE4xieHSIvqbCQxttYMyR7E3yF9t69j9BxayEqHDqvUcaSe7Qgk70J2s0Y1fmfS6hyN72y6Aiu65Daa9rrwklKhqrWOy5LLAaW0cmXviqBxSCaNkDHsEE8otARyCKWZ7Z1Lp5+nUMlfSsqWO9ULojxPtalYtY/Tn5Z4EWJ+CAPzE8B4kY+2AVQ+xbO2AdSXAbvl0AMYpXunGqR0z7+BqZUM+Ts7Si3YvzauhvDT5ZFkZmrLGiVR7yPPsiglgNIp50+cQ8ldYJyl0yAf4WHREKAhet2UVF2W26shgrM3faxwFG1YNbNzY+PLGv/0y6AZfn7x79+pi0FEseeP/Rb9b+VrWNZtDX5n7f3wqWwnNtp/KXGVWoyt57GgltI5c09xPPbATx01lkep6QBbEIqdC6Q6rqNtIYr0lwrbkai321g2ysP31SBSBONCeuFWrg0aT3XUHVQWsDjbLbRQsdVTr3Qlq2NqNDRJSOOnOj8Pzw245TdxVZVarxxZbiL2jyrzeyucLFgWHCUExSwpPvksNhYK1km+6XuVvZxV/x17rzy+WI8jD8m0qO0SqclnXsaSl9augeOtif5+nI7yZ/7dwiWv+D03r7A8= 7Zldb5swFIZ/TaTtohLYIQmX+WjXm0qVsqlSbyYXO+DV2Mg4TdJfP7uY8GHarB9hUptWiuAF2/L7nAMnJwM4T7c/JMqSK4EJGwAPbwdwMQAgDAP9aYRdIfgesEosKbZaJSzpIylvtOqaYpI3blRCMEWzphgJzkmkGhqSUmyat60Ea66aoZg4wjJCzFVvKFZJoU7AuNIvCY2TcmV/FBZXUlTebHeSJwiLTU2C5wM4l0Ko4ijdzgkz5pW+nJ3BJZj+SX6PxttFNl/ni834rJjs4jVD9luQhKs3T72YXt3eXU4fb6YL/vgruAsuprd2iPeA2Nr6ZfeqdqWBm4QqssxQZM43OkgGcJaolOkzXx+iPCuwreiW6KVmuZLinswFE1KrXHA9bmaXIVKRbYvLgU35e6d1iBKREiV3elw5ize2W7DxCaA931SwR0OrJTXQexHZAIv3k1cm6gPr4ys89R1PGc2ViXuU3xtvhVR3O4wUcbyWYs2xcXHhadMOOL8SXJU2Y7JCa6Yc+yt9RRlz1aNx8YMmFzh2ucBJF5djYQHPYPkyCIaBi8AHfSKADoKvlBIgPMwDdD6qjsVj6PD4EiCGoAkiKEuaemL4fYIInkmMT4ugnQv/H8GoE8HPXeZmgjZDNb3urHnqlg4AXAXm31RMjMZca5F2kOiLM+Mv1RXr1F5IKcZmrU7CzRioQ9ZrFH9HfalMWg+x0AU36uAGj8Vt3MltQfJI0kxRwU/4avjgMGjh66gJYJ/8Jqe8+ydwfhNc0FFP95p34SnvXlNyQO8gvrBPfOW79aVv/oTjqenAVGwwypMnE/0mSKNfI6W58CcFeHCPt+y6gBcptGsOgp22ztsI1AwOOgwuNUkYUvShuWaX63aFa0G5qr8XW4CH7fZCLtYyInZYvU3TnikYHZhJIRkT5cz0FAb7jb8jMtz+xSkyPjAyAvBRkeHMdOzIcFsoecaoWipJefztuxmf6w/9TGYapWnWStP2Sog5prJogtH0nd8t+20wOunod1RMvXZRfLeN8jKGAkBOIsHxZyDQVbN+EAF9WjX0i7ypfhaB538B
\ No newline at end of file
diff --git a/docs/media/ParserSequenceDiagram.png b/docs/media/ParserSequenceDiagram.png
new file mode 100644
index 0000000000..dd5af665a0
Binary files /dev/null and b/docs/media/ParserSequenceDiagram.png differ
diff --git a/docs/media/ParserSequenceDiagramSource.txt b/docs/media/ParserSequenceDiagramSource.txt
new file mode 100644
index 0000000000..3dc882b697
--- /dev/null
+++ b/docs/media/ParserSequenceDiagramSource.txt
@@ -0,0 +1,52 @@
+title Parser
+
+actor Actor
+participant NoCap
+participant parse Parser
+participant list ListParser
+participant allTaskList OverallTaskList
+
+activate NoCap
+NoCap->*parse Parser:Parser()
+activate parse Parser
+parse Parser->*list ListParser:ListParser()
+activate list ListParser
+parse Parser<--list ListParser:
+deactivate list ListParser
+parse Parser-->NoCap:
+deactivate parse Parser
+
+Actor->NoCap: Input
+loop !parse.isExit()
+NoCap->parse Parser:chooseTask()
+activate parse Parser
+
+parse Parser->parse Parser:splitInput()
+activate parse Parser
+parse Parser-->parse Parser:
+deactivate parse Parser
+
+parse Parser->parse Parser:splitInput()
+activate parse Parser
+parse Parser-->parse Parser:
+deactivate parse Parser
+
+parse Parser->list ListParser:overallListParser()
+activate list ListParser
+
+list ListParser->*allTaskList OverallTaskList:OverallTaskList()
+activate allTaskList OverallTaskList
+list ListParser<--allTaskList OverallTaskList:
+deactivate allTaskList OverallTaskList
+
+list ListParser->allTaskList OverallTaskList:sortByDateAndPrint()
+activate allTaskList OverallTaskList
+list ListParser<--allTaskList OverallTaskList:
+deactivate allTaskList OverallTaskList
+
+list ListParser-->parse Parser:
+deactivate list ListParser
+parse Parser-->NoCap:
+deactivate parse Parser
+
+end
\ No newline at end of file
diff --git a/docs/media/SemesterListDiagram.jpg b/docs/media/SemesterListDiagram.jpg
new file mode 100644
index 0000000000..da4e3a1381
Binary files /dev/null and b/docs/media/SemesterListDiagram.jpg differ
diff --git a/docs/media/StorageDecoderSequenceDiagram.png b/docs/media/StorageDecoderSequenceDiagram.png
new file mode 100644
index 0000000000..7b809c15fd
Binary files /dev/null and b/docs/media/StorageDecoderSequenceDiagram.png differ
diff --git a/docs/media/StorageDecoderSequenceDiagramSource.txt b/docs/media/StorageDecoderSequenceDiagramSource.txt
new file mode 100644
index 0000000000..3178e23ca0
--- /dev/null
+++ b/docs/media/StorageDecoderSequenceDiagramSource.txt
@@ -0,0 +1,45 @@
+title StorageDecoder
+
+
+participant "<> \n StorageDecoder" as StorageDecoder
+
+participant ":ObjectMapper" as ObjectMapper
+
+participant ":SemesterList" as SemesterList
+
+StorageDecoder<-[: decodeJsonToSemesterList()
+
+activate StorageDecoder
+
+alt No save file found
+StorageDecoder->*SemesterList: new SemesterList()
+activate SemesterList
+SemesterList-->StorageDecoder:
+deactivate SemesterList
+StorageDecoder-->[: new semester list
+
+else else
+StorageDecoder->*ObjectMapper:new ObjectMapper()
+activate ObjectMapper
+ObjectMapper-->StorageDecoder:
+deactivate ObjectMapper
+
+StorageDecoder->ObjectMapper:readValue()
+activate ObjectMapper
+ObjectMapper-->StorageDecoder:decoded semester list
+deactivate ObjectMapper
+destroyafter ObjectMapper
+StorageDecoder-->[: decoded semester list
+end
+deactivateafter StorageDecoder
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/media/StorageEncoderSequenceDiagram.png b/docs/media/StorageEncoderSequenceDiagram.png
new file mode 100644
index 0000000000..680a8d06f4
Binary files /dev/null and b/docs/media/StorageEncoderSequenceDiagram.png differ
diff --git a/docs/media/StorageEncoderSequenceDiagramSource.txt b/docs/media/StorageEncoderSequenceDiagramSource.txt
new file mode 100644
index 0000000000..072f055e21
--- /dev/null
+++ b/docs/media/StorageEncoderSequenceDiagramSource.txt
@@ -0,0 +1,45 @@
+title StorageEncoder
+
+
+participant "<> \n StorageEncoder" as StorageEncoder
+
+participant ":ObjectMapper" as ObjectMapper
+
+StorageEncoder<-[: encodeAndSaveSemesterListToJson(semesterList)
+
+activate StorageEncoder
+
+opt [No Data Directory]
+StorageEncoder->StorageEncoder:createDataDirectory()
+activate StorageEncoder
+StorageEncoder<<--StorageEncoder:
+deactivate StorageEncoder
+end
+
+opt [No Data File Found]
+StorageEncoder->StorageEncoder:createFile()
+activate StorageEncoder
+StorageEncoder<<--StorageEncoder:
+deactivate StorageEncoder
+end
+
+StorageEncoder->*ObjectMapper:new ObjectMapper()
+
+activate ObjectMapper
+ObjectMapper-->StorageEncoder:
+deactivate ObjectMapper
+
+StorageEncoder->ObjectMapper:writeValue(semesterList)
+activate ObjectMapper
+ObjectMapper-->StorageEncoder:
+deactivate ObjectMapper
+destroyafter ObjectMapper
+
+StorageEncoder-->[:
+deactivate StorageEncoder
+
+
+
+
+
+
diff --git a/docs/media/TaskClassDiagram.png b/docs/media/TaskClassDiagram.png
new file mode 100644
index 0000000000..72c5ba1cba
Binary files /dev/null and b/docs/media/TaskClassDiagram.png differ
diff --git a/docs/media/TaskListSequenceDiagram.png b/docs/media/TaskListSequenceDiagram.png
new file mode 100644
index 0000000000..7d0dbebb19
Binary files /dev/null and b/docs/media/TaskListSequenceDiagram.png differ
diff --git a/docs/media/TaskSequenceDiagram.png b/docs/media/TaskSequenceDiagram.png
new file mode 100644
index 0000000000..dc573f2742
Binary files /dev/null and b/docs/media/TaskSequenceDiagram.png differ
diff --git a/docs/media/UGgradabletasklist.png b/docs/media/UGgradabletasklist.png
new file mode 100644
index 0000000000..1b8c992682
Binary files /dev/null and b/docs/media/UGgradabletasklist.png differ
diff --git a/docs/media/capComputationSequenceDiagram.png b/docs/media/capComputationSequenceDiagram.png
new file mode 100644
index 0000000000..7dea522d93
Binary files /dev/null and b/docs/media/capComputationSequenceDiagram.png differ
diff --git a/docs/media/edwin-profile.jpg b/docs/media/edwin-profile.jpg
new file mode 100644
index 0000000000..0ad0d908c6
Binary files /dev/null and b/docs/media/edwin-profile.jpg differ
diff --git a/docs/media/moduleListClassDiagram.png b/docs/media/moduleListClassDiagram.png
new file mode 100644
index 0000000000..6be400820d
Binary files /dev/null and b/docs/media/moduleListClassDiagram.png differ
diff --git a/docs/media/scheduleseq.png b/docs/media/scheduleseq.png
new file mode 100644
index 0000000000..85f983d67f
Binary files /dev/null and b/docs/media/scheduleseq.png differ
diff --git a/docs/media/splitStringDiagram.JPG b/docs/media/splitStringDiagram.JPG
new file mode 100644
index 0000000000..303ecc28d0
Binary files /dev/null and b/docs/media/splitStringDiagram.JPG differ
diff --git a/docs/media/timetableExampleOutput.PNG b/docs/media/timetableExampleOutput.PNG
new file mode 100644
index 0000000000..b4820ea247
Binary files /dev/null and b/docs/media/timetableExampleOutput.PNG differ
diff --git a/docs/team/dmitriyam.md b/docs/team/dmitriyam.md
new file mode 100644
index 0000000000..abe836aefd
--- /dev/null
+++ b/docs/team/dmitriyam.md
@@ -0,0 +1,151 @@
+# Yam Jin Ee Dmitri - Project Portfolio Page
+
+## Overview
+NoCap (NC) is a **desktop app for managing modules taken in NUS, optimized for use via a Command Line Interface** (CLI).
+If you can type fast, NC can get your module management tasks done faster than traditional GUI apps. It is the perfect
+app for NUS students!
+
+### Summary of Contributions
+* **Code Contributed:** [RepoSense Link](https://nus-cs2113-ay2122s1.github.io/tp-dashboard/?search=Dmitri&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-25&tabOpen=true&tabType=authorship&tabAuthor=DmitriYam&tabRepo=AY2122S1-CS2113T-F11-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false)
+* **Enhancement Implemented:**
+
+ 1. module package.
+
+ *Created base class for module.
+ *Implemented visualisation of module to command prompt.
+
+ 2. schedule class
+ * Implemented visualisation of schedule and schedule list to command prompt.
+
+ 3. GradableTask GradableTaskList
+ * Extended Task class to produce a class that stores gradable components.
+ * Created constructors which checked for syntax errors or weightage overflow errors.
+
+ 4. VisualiseGradable
+ * Created a class to better visualise all gradable components by breaking them down into their individual componnents
+ and labelling the percentages.
+
+* **Contributions to UG:**
+
+ * Features: Add GradableTasks
+ * Features: List GradableTasks
+ * Features: Add class to module
+ * Features: Delete class from module
+ * Features: Add credit to module
+ * Features: Add grade to module
+ * Features: delete grade from module
+ *
+* **Contributions to DG:**
+
+ * Design and implementation of ModuleList and ScheduleList classes.
+ * Instruction for testing for features:
+ * Add module
+ * Add grade
+ * Add class
+ * Add credit
+ * Add gradable
+ * Visualise gradable
+
+* **Contributions to team-based tasks:**
+ * Helped keep track of weekly deadlines and deliverables.
+ * Contributed ideas to the team such as extending Tasks for module breakdown.
+
+* **Review/Mentoring Contributions**
+ * Helped teammates debug their code when they faced difficulties.
+ * Remind the group of internal deadlines.
+
+###Developer Guide Extract
+
+# Module
+
+**API** : `module`
+
+All data related to module is stored in the module class. An Arraylist of Module is used to store and manage the
+modules. ModuleList is also responsible for constructing and printing out the Timetable.
+
+
+
+The modules are stored in an ArrayList and ModuleList uses the Module.get(int index) method to access the target Module.
+
+- ModuleList is responsible for printing the Time Table.
+- ModuleList contains the getter method find(String input) which returns a module by the same name as the input.
+- Module contains getter and setter methods to change or access its contents.
+- When Module is constructed, an empty gradableTaskList, taskList and scheduleList wll be instantiated and stored in
+ Module.
+
+Data stored in Module includes:
+
+1. moduleName
+2. letterGrade
+3. credits
+4. points
+5. TaskList
+6. GradableTaskList
+7. ScheduleList
+
+The modules are stored in an ArrayList and ModuleList uses the Module.get(int index) method to access the
+target Module.
+
+- ModuleList is responsible for printing the Time Table. It accesses different schedules of different mods before
+ constructing a Time Table.
+- ModuleList contains getter method find(String input) which returns a module by the same name as the input.
+
+How printing a timetable works:
+
+1. ModuleList first extracts day of week and timeslot information from different schedules.
+2. It then prints out the Timetable one line at a time. At the same time it checks if the day of week and the timeslot
+ corresponds to the schedule.
+
+- If day of week and timeslot corresponds, venue and comments information is printed out
+- If day of week and timeslot does not correspond, and blank character " " is printed instead. =======
+
+# 
+
+How printing a timetable works:
+
+1. When Timetable is called, ModuleList goes into a loop to print out the timetable. ModuleList iterates through the 207
+ character long length and the 33 lines which makes up the entire timetable.
+2. Each iteration of the loop can result in 1 of 3 cases:
+1. It is at a border. When this happens a "#" character is printed to the console which denotes a border.
+2. It is empty. When this happens a " "(blank) character is printed to the console.
+3. It contains module information. When this happens, getMoudleName() , getModuleLocation() and getModuleComment() is
+ called. The information is then printed onto the console.
+
+# ScheduleList
+
+**API** : `schedule`
+
+ScheduleList consists of all data for the schedule for the module.
+
+This includes:
+
+1. `day`
+2. `location`
+3. `startTime`
+4. `comments`
+
+How ScheduleList works:
+
+1. An empty `ScheduleList` is created when a module is constructed.
+2. When `addClass` is called in `module` , `ScheduleList` parses the input from the user and splits the information into the
+ relevant information. The information is then used to generate a new instance of `Schedule` which is then added to the
+ list.
+3. `toString()` prints out all relevant schedule information in a list format. This is done by going through the list and
+ printing `Schedule` one after another.
+
+Notes about ScheduleList
+
+- ScheduleList checks that the input for the day of the week is only from the list of possible days: `MON`, `TUE`, `WED`, `THU`,
+ `FRI`, `SAT` ,`SUN`. All other inputs will result in an exception being thrown.
+- When a new `Schedule` class is called, `ScheduleList` ensures that the length of venue and comments are less than 16
+ characters in length. This is to ensure that it fits within its time slot within the Timetable when printed.
+
+
+
+Adding Schedule to scheduleList
+
+- When addclass() is called, schedule first checks if there is a duplicate schedule currently in the list. This is done
+ by going through the whole list and checking if a schedule has the same time slot. If there exists a schedule in the
+ same time slot, an error message is printed.
+- If it is an empty timeslot, schedule list parses the input and checks for formatting errors within the input.
+- A new instance of Schedule is generated and added to the schedule list.
diff --git a/docs/team/jiexiong-zeng.md b/docs/team/jiexiong-zeng.md
new file mode 100644
index 0000000000..5862d0dba4
--- /dev/null
+++ b/docs/team/jiexiong-zeng.md
@@ -0,0 +1,84 @@
+# Zeng Jiexiong - Project Portfolio Page
+
+## Overview
+NoCap (NC) is a **desktop app for managing modules taken in NUS, optimized for use via a Command Line Interface** (CLI).
+If you can type fast, NC can get your module management tasks done faster than traditional GUI apps. It is the perfect
+app for NUS students!
+
+### Summary of Contributions
+* **Code Contributed:** [RepoSense Link](https://nus-cs2113-ay2122s1.github.io/tp-dashboard/?search=f11-1&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-25&tabOpen=true&tabAuthor=jiexiong-zeng&tabRepo=AY2122S1-CS2113T-F11-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false&tabType=authorship)
+* **Enhancement Implemented:**
+ * Parser package consisting of: Parser, ListParser, DateParser, ParserChecks, Command
+ * Processes all user inputs and parses them for information, calling the corresponding methods from other classes. Checks for input validity errors (Whether the desired input exists, eg: missing date time string).
+* **Contributions to UG:**
+ * Command Summary
+ * Reorganise structure, clean up and uniform syntax
+* **Contributions to DG:**
+ * Design and Implementation Parser section
+ * Appendix B: User Stories
+ * Reorganise structure, clean up and uniform syntax
+* **Contributions to team-based tasks:**
+ * Setting up the GitHub team org/repo.
+ * Scribing to-dos in team meetings and creating corresponding issue trackers.
+ * Managing team deadlines and creating releases.
+
+### Developer Guide Extract
+
+**API** : command.parser
+
+The Parser classes is responsible for receiving user input and converting it into commands which are directly passed to
+respective classes.
+
+The class diagram below is an overview of relationship between Parser classes and other classes.
+
+
+
+How the parsing works:
+
+* `NoCap` passes the user input to `Parser` which separates the input into useful information such as taskType,
+ taskDescription, Module, etc.
+* When commands include **listing tasks**, the taskDescription is passed to `ListParser` which determines the method of
+ sorting and creates filtered `OverallTaskList` and `TaskList` accordingly.
+* Otherwise, the taskDescription is passed to `Command` which calls the corresponding commands in `SemesterList`,
+ `Semester` ,`ModuleList`, `Module` , `Task`, `Gradable Task`. For clarity purposes, associations are shown but
+ dependencies are not.
+* `ParserChecks` is a utility class that handles various error checking and string searching methods such as
+ `ParserSearch#getTaskFromIndex()` and `ParserSearch#getTaskFromKeyword()`. `Command` utilizes these methods to verify
+ the Strings before passing them to other classes.
+ *In NoCap, Parser verifies the validity of input (Whether it exists in the right format). Input content is verified by
+ individual classes for correctness.*
+* `DateParser` handles parsing String into LocalDateTime format and displaying LocalDateTime as String. It is utilized
+ by `Task`. Additional date formats can be added in `DateParser#inputFormatter()`
+
+Below is a step by step example of how the parser receives and decipher a user input. In this example, the user enters
+`list task sortbydate`.
+
+The Sequence Diagram below illustrates the process
+
+**Note**: The alternate paths are omitted from the diagram for clarity.
+
+Step 1: The User launches the application. `NoCap` creates a new `Parser` instance through the constructor and `Parser`
+creates `ListParser`.
+
+Step 2: The application waits for User input. User enters `list task sortbydate`. `NoCap` passes the input to `Parser`
+through `Parser#chooseTask()`.
+
+Step 3: `splitInput` is called for the first time and splits the user input into `list` and `task sortbydate`.
+> **TaskType** is set to `list`, and **TaskDescription** is set to `task sortbydate`.
+
+**TaskType** matches a possible command String.
+
+
+Step 4: `splitInput` is called for the second time and splits the user input into `task` and `sortbydate`.
+> **TaskType** is set to `task`, and **TaskDescription** is set to `sortbydate`.
+
+**TaskType** and **TaskDescription** are passed to `ListParser` through `ListParser#overallListParser`.
+
+
+Step 5: `overallListParser` creates an `OverallTaskList`. Through nested switch cases, **TaskType** and **TaskDescription** are matched, and the corresponding method `OverallTaskList#sortByDateAndPrint()` is called. As the
+name implies, this method sorts all tasks by date and prints them.
+> If **TaskType** does not match, then an error message is displayed.
+> If **TaskDescription** does not match, all tasks are printed by default.
+
+Step 6: The full command is carried out and the application returns to NoCap and waits for new User Input.
+
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/naijie2108.md b/docs/team/naijie2108.md
new file mode 100644
index 0000000000..0233cbb98c
--- /dev/null
+++ b/docs/team/naijie2108.md
@@ -0,0 +1,151 @@
+# Yeat Nai Jie - Project Portfolio Page
+
+## Overview
+NoCap (NC) is a **desktop app for managing modules taken in NUS, optimized for use via a Command Line Interface** (CLI).
+If you can type fast, NC can get your module management tasks done faster than traditional GUI apps. It is the perfect
+app for NUS students!
+
+### Summary of Contributions
+* **Code contributed:** [Reposense Link](https://nus-cs2113-ay2122s1.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-25&tabOpen=true&tabType=authorship&tabAuthor=naijie2108&tabRepo=AY2122S1-CS2113T-F11-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false)
+* **Enhancements Implemented** :
+ * `storage` package
+ * Consists of utility classes `StorageEncoder` and `StorageDecoder`, which deals with encoding and decoding the application data
+ into a JSON file.
+ * Implemented using a 3rd party library `Jackson Databind`
+ * Spent time learning how to use and integrate the 3rd party library with the application, such as adding appropriate getters and constructors to classes
+ in order to bypass permission issues.
+ * Integrated and modified the classes as the project went on, such as learning to deal with non-native
+ data types like `LocalDateTime`
+ * `OverallTask` and `OverallTaskList` classes
+ * Classes used for listing all tasks in a semester
+ * Includes methods which allows multiple ways of filtering and sorting of semester tasks.
+ * Integrated and modified classes as project went on to fit with other classes
+ * Added constructors which construct `OverallTask` from either `Task` or `GradableTask` objects.
+ * Added custom comparators for better sorting of tasks
+ * `exceptions` package
+ * Created a framework for custom exceptions to be used by other team members.
+ * Consist of two classes `NoCapExceptions` and `ExceptionMessages`.
+* **Contributions to the UG** :
+ * Features: Listing all tasks
+ * Features: Saving data section
+ * Features: Loading data section
+* **Contributions to the DG** :
+ * Acknowledgements
+ * Design and implementation: Storage
+ * Design and implementation: OverallTaskList and OverallTask
+ * Appendix A: Product Scope
+ * Appendix D: Glossary
+ * Appendix E: Launch
+ * Appendix E: Saving/Loading data
+ * Appendix E: List tasks in a semester
+* **Contributions to team-based tasks** :
+ * Managing gradle build file (`build.gradle`) in adding 3rd party libraries and assertions
+ * Setting up milestone `v2.1`
+ * Contacting teaching team to enquire on doubts about project
+ * Keep track of deadlines and milestones.
+* **Review/Mentoring Contributions**
+ * Help teammates when faced with technical issues by meeting up and helping to debug,
+ such as debugging failing IO redirection tests and failing JUNIT tests.
+
+
+
+## Developer Guide Extract
+
+## OverallTaskList
+
+**API** : `task.OverallTasklist`
+
+
+
+_Class diagram for OverallTask and OverallTaskList_
+
+**Note**: Some methods are omitted from the class diagram to improve clarity
+
+The `OverallTaskList` class is instantiated from `ListParser` only when the end user needs to list available tasks in
+a `Semester`.
+
+
+How the `OverallTaskList` class works:
+
+1. `OverallTask` objects (explained further under `OverallTask`) are stored in an ArrayList `overallTaskList.`
+2. Both `Task` and `GradableTask` objects are converted to `OverallTask` objects first before being inserted into
+ `OverallTaskList`.
+3. When the `OverallTaskList` object is instantiated, a `ModuleList` object from a semester is passed to its
+ constructor.
+
+4. The constructor calls the method `addAllModuleListTasks(module list)` which converts and adds all the tasks in the
+ module list into `OverallTaskList`.
+5. Once the object is instantiated, the following methods can be called to sort and print the tasks in the
+ ArrayList `overallTaskList`. All sorting and filtering is done via `Java Streams`, and method details are omitted.
+
+ * `sortByDateAndPrint() - Print all tasks sorted by deadline`
+ * `sortByStatusAndPrint() - Print all tasks sorted by status(done)`
+ * `printWeeklyTasks() - Print tasks due in a week`
+ * `printMonthlyTasks() - Print tasks due in a month`
+ * `printYearlyTasks() - Print tasks due in a year`
+ * `printAllTasks() - Print all tasks without sorting`
+ * `printGradableTasks() - Print all gradable tasks`
+ * `printNormalTasks() - Print all non-gradable tasks`
+
+Notes about `OverallTaskList`
+
+* Once `ListParser` is done using the object, it is deleted and the task list is not stored anywhere. The reason for
+ this is to reduce coupling between objects and remove the need to update separate task lists whenever tasks are added
+ to `Modules`.
+
+
+## User Guide Extract
+
+### Listing all tasks : `list task (optional argument)`
+
+Shows a list of all tasks within the current semester
+
+By default, all tasks in the current semester are listed, but this can be customised by adding optional arguments.
+
+(optional argument) includes:
+
+* sortbydate - Sort tasks by due date.
+* sortbystatus - Sort tasks by status, displaying unfinished tasks first.
+* gradable - list gradable tasks only.
+* normal - list non-gradable tasks only.
+* w - list tasks due within the next week.
+* m - list tasks due within the next month.
+* y - list tasks due within the next year.
+
+Tasks are listed in the format:
+
+`[Module Code][Gradable][Lateness][Done] by: [Weightage]`
+
+* [Module Name] - Name of the module
+* [Gradable] - Shows `[G]` if the task is gradable, and `[ ]` if the task is non-gradable.
+* [Lateness] - Shows `[LATE]` if the task is overdue. Only shows up for overdue tasks
+* [Done] - Shows `[X]` if the task is done, and `[ ]` if the task isn’t done yet.
+* <description> - Description of the task
+* <deadline> - Deadline of the task
+* [Weightage] - Weightage of the task, if it is gradable. Only shows up for gradable tasks.
+
+Example tasks:
+
+1. `[CS2132][G][ ] Assignment by: 16 Dec 2021 12:00 AM [Weightage: 50%]`
+
+ * Belongs to the module CS2132
+ * Gradable
+ * Not done yet
+ * Due on 16 Dec 2021 12:00 AM
+ * Has a weightage of 50%.
+
+Example commands with expected output:
+
+Assuming tasks have been added to modules beforehand:
+
+* `list task`
+
+ ```
+ All tasks:
+ 1. [CS2132][G][ ] Assignment by: 16 Dec 2021 12:00 AM [Weightage: 50%]
+ 2. [CS2132][G][LATE][ ] asdf by: 10 Dec 2000 12:00 AM [Weightage: 50%]
+ ```
+NOTE:
+
+* For optional arguments `w`, `m` and `y`, overdue tasks are listed together with the weekly/monthly/yearly tasks
+ regardless of due date as a reminder that the user has forgotten to do the task.
diff --git a/docs/team/yapjoonsiong.md b/docs/team/yapjoonsiong.md
new file mode 100644
index 0000000000..0e1e2f3a79
--- /dev/null
+++ b/docs/team/yapjoonsiong.md
@@ -0,0 +1,43 @@
+# Yap Joon Siong - Project Portfolio Page
+
+## Overview
+NoCap (NC) is a **desktop app for managing modules taken in NUS, optimized for use via a Command Line Interface** (CLI).
+If you can type fast, NC can get your module management tasks done faster than traditional GUI apps. It is the perfect
+app for NUS students!
+
+### Summary of Contributions
+* **Code contributed:** [Reposense Link](https://nus-cs2113-ay2122s1.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-25&tabOpen=true&tabType=authorship&tabAuthor=yapjoonsiong&tabRepo=AY2122S1-CS2113T-F11-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&zFR=false)
+* **Enhancements Implemented** :
+ * `semester` package
+ * Consists of utility classes `SemesterList` and `Semester`, where `SemesterList` computes and stores the
+ cumulative CAP of all semesters and also stores 10 `Semester` objects, and `Semester` stores and computes the
+ individual CAP for the semester, while also storing a ModuleList of the Module objects taken during the semester
+ * Integrated and modified dependencies (`ModuleList` and `Module`) as the project went on
+ * Implemented core functionalities such as `cap` and `allcap`
+ * `schedule` package
+ * Consists of utility classes `ScheduleList` and `Schedule`, where `ScheduleList` stores all `Schedule` objects,
+ and `Schedule` stores all information for any schedule added to `Module`
+ * Integrated and modified dependencies (namely `Module`) as the project went on
+ * `ModuleList` and `Module` classes
+ * Integrated classes and implemented core functionalities such as `timetable` and backend methods to calculate
+ `cap` and `allcap`
+ * Implemented core backend method to find modules by name
+* **Contributions to the UG** :
+ * Features: List Semesters
+ * Features: Switching Semesters
+ * Features: Adding a class
+ * Features: Deleting a class
+ * Features: Add credits to a module
+ * Features: View CAP
+ * Features: View all CAP
+* **Contributions to the DG** :
+ * Acknowledgements
+ * Design and implementation: semester
+ * Appendix C: Non Functional Requirements
+* **Contributions to team-based tasks** :
+ * Managing collaborative google drive folder
+ * Setting up conference group to facilitate meetings
+* **Review/Mentoring Contributions**
+ * Help teammates when faced with technical issues by meeting up and helping to debug functional problems.
+ * Integrated most core functional classes together at the start of the project to release a Minimum Viable Product
+
\ No newline at end of file
diff --git a/docs/team/yzhedwin.md b/docs/team/yzhedwin.md
new file mode 100644
index 0000000000..169c647f7f
--- /dev/null
+++ b/docs/team/yzhedwin.md
@@ -0,0 +1,172 @@
+# Yeo Zi Hao Edwin - Project Portfolio Page
+
+## Overview
+
+NoCap (NC) is a **desktop app for managing modules taken in NUS, optimized for use via a Command Line Interface** (CLI).
+If you can type fast, NC can get your module management tasks done faster than traditional GUI apps. It is the perfect
+app for NUS students!
+
+### Summary of Contributions
+
+* **Code
+ contributed:** [RepoSense Link](https://nus-cs2113-ay2122s1.github.io/tp-dashboard/?search=f11&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-25&tabOpen=true&tabType=authorship&tabAuthor=yzhedwin&tabRepo=AY2122S1-CS2113T-F11-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&zFR=false&until=2021-11-03FileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false)
+* **Enhancements Implemented** :
+ * `Task` class
+ * Class made to be used as a base class for `OverallTask` and `GradableTask`
+ * Stores the information needed for each task added.
+ * Implemented method to determine if task is overdue.
+ * Implemented method to check if the task added is valid
+ * Added a very readable toString() method.
+ * Continued maintenance of the class for dependencies throughout the project.
+ * `TaskList` class
+ * Allowed class to be inheritable by `OverallTaskList` and `GradableTaskList`
+ * Implemented custom comparators to sort the task list with special conditions.
+ * Manipulated the user input to get desired data such as `deadline` and `task description`.
+ * Added different ways to manipulate `LocalDateTime` data type such as filtering the list
+ * for weekly, monthly and yearly tasks.
+ * Added checks to see if there is duplication in the task list before adding or modifying tasks.
+ * Made the code more defensive by catching errors from erroneous inputs.
+ * Enhanced adding of task by automatically updating task deadline when user adds a duplicate task of different
+ deadline.
+ * Continued maintenance of the class for dependencies throughout the project.
+ * Testing
+ * Added a way to test output from print functions by using `ByteArrayOutputStream` and `PrintStream` and
+ * verifying the actual and expected outputs.
+* **Contributions to the UG** :
+ * Features: Listing module tasks
+ * Features: Adding tasks to module
+* **Contributions to the DG** :
+ * Design and implementation: Task
+ * Design and implementation: TaskList
+* **Contributions to team-based tasks** :
+ * Contributed ideas during brainstorming about enhancement and bug fixes.
+ * Maintained clean github environment.
+* **Review/Mentoring Contributions**
+ * Helped teammates debug code in a collaborative manner.
+
+### Developer Guide Extract
+
+## TaskList
+
+**API** : `task.tasklist`
+
+
+
+How the `TaskList` component works:
+
+
+
+1. `TaskList` stores all tasks in an `ArrayList`.
+2. When the `addTask()` method is called, the method `getDate()` will return the `date` string from the user input
+3. The method `removeDate()` will return the `description` string from the user input by removing the date component in
+ the user input.
+4. Then store it as a local variable of a `String` type.
+5. The `String` variables will then be passed to instantialize a new `Task` object.
+6. This `Task` object will then be stored in the `ArrayList` in the `TaskList` object.
+7. The methods `weeklyTaskList()`, `monthlyTaskList()` and `yearlyTaskList()` returns an `ArrayList` which contains
+ the `Task` objects of deadline within a week, a month and a year respectively.
+8. The methods `sortTaskListByDate()` and `sortTaskListByStatus()` will sort the current `TaskList` object by ascending
+ order of `Deadline` and completion status respectively
+9. The `ArrayList` returned by the above methods can then be passed to `printTasks()` which will call `toString()` in
+ each `Task` object and print to the `Output Stream`.
+
+
+## Task
+
+**API** : `task.task`
+
+`Task` object stores the following for each task:
+
+1. `description`
+2. `date`
+3. `isDone`
+4. `isLate`
+5. `deadline`
+
+How the `Task` component works:
+
+
+
+1. Whenever the `Task` object is instantiated, the `attributes` listed above will be initialized by the `setter`
+ methods: `setDescription()`, `setDate()`, `setDone()`, `setLate()` and `setDeadline()`.
+2. When calling `printAllTask()`, `printWeeklyTask()`, `printMonthlyTask()` in `OverallTaskList` the
+ method `updateOverdue()`will be called which checks for the truth value of the `boolean` attribute `isDone` and also
+ whether the current date and time of the system clock is after the `deadline` of the `Task` object.
+3. If `isDone` is `FALSE` and the `deadline` is later than the current date and time, `updateOverdue()` will set the
+ attribute `isLate` of the current `Task` object to `TRUE`.
+4. Calling the toString converts the task information in the Task object to printable String.
+
+Note:
+
+* The printTask() call in the sequence diagram is a generalised method from: `OverallTaskList#addAllNormalTasks()`
+ , `OverallTaskList#addAllGradableTasks()` and `TaskList#printTasks()`
+* Any call from the methods above will result in the following sequence in the sequence diagram.
+
+
+### User Guide Extract
+
+### Add task to module : `/m addtask /by (time)`
+
+* The `date` is in the format of dd/MM/yyyy, dd MM yyyy.
+* The `time` is in the format of hhmm.
+* The `description` can contain white spaces.
+
+Note:
+
+* If time is omitted, time will default to 0000 hrs.
+* If duplicate task exist and has a different deadline, the existing task's deadline will be updated with the new
+ deadline.
+* If duplicate task has same deadline, new task will be rejected by the program.
+
+Example of usage:
+
+* `/m cs1010 addtask Remember to S/U /by 20/11/2020`
+
+Example of expected output:
+
+
+
+**Warning** :
+
+* For every month, the program will take in 01 - 31 as an input for the day of the month.
+* In the case when the month does not have 31 days, the program will treat any input after the last day of the month
+ until 31 as the last day.
+
+Example:
+
+* Date input as `31/02/2021` will be parsed as `28/02/2021` because there are only 28 days in the month of February.
+* Date input as `31/04/2021` will be parsed as `30/04/2021` because there are only 30 days in the month of April
+
+### Listing module tasks : `/m list (optional argument)`
+
+Shows a list of task of specified module.
+
+By default, all tasks in the module specified in the current semester are listed, but this can be customised by adding
+optional arguments.
+
+(optional argument) includes:
+
+* gradable - Shows a list of gradable tasks in the module.
+* sortbydate - Sort tasks by due date, the closest deadline have the higher priority in the list. does not print the
+ task list.
+* sortbystatus - Sort tasks by status, finished tasks of lower priority. Does not print task list.
+* w - list tasks due within the next week.
+* m - list tasks due within the next month.
+* y - list tasks due within the next year.
+
+Task Prefixes:
+
+* There are 2 prefixes in each Task defined as `[ ]`
+* The first prefix is a `LATE` tag. If the task is overdue, the tag will show `[LATE]`
+* The second prefix is a `DONE` tag. If the task is marked completed, the tag will show `[X]`
+* Format will be as follows `[LATE][DONE] `
+
+Examples with expected output:
+* Removed to satisfy page requirement
+
+NOTE:
+
+* For optional arguments `w`, `m` and `y`, overdue tasks are listed together with the weekly/monthly/yearly tasks
+ regardless of due date as a reminder that the user has forgotten to do the task.
+* `/m list` does not show gradable tasks.
+* To show gradable task in module, have to input optional argument as shown above.
\ No newline at end of file
diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..3dc7aeff56
--- /dev/null
+++ b/src/main/java/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: command.NoCap
+
diff --git a/src/main/java/command/Logger.java b/src/main/java/command/Logger.java
new file mode 100644
index 0000000000..3e80c792fe
--- /dev/null
+++ b/src/main/java/command/Logger.java
@@ -0,0 +1,25 @@
+package command;
+
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+
+public final class Logger {
+ public static String GLOBAL_LOGGER_NAME = "LOGGER";
+ public static java.util.logging.Logger myLogger;
+ private static LogManager logManager;
+
+
+ public Logger() {
+ myLogger = java.util.logging.Logger.getLogger(GLOBAL_LOGGER_NAME);
+ logManager = LogManager.getLogManager();
+ logManager.getLogger(GLOBAL_LOGGER_NAME).setLevel(Level.OFF);
+ }
+
+ public static java.util.logging.Logger myLogger() {
+ myLogger = java.util.logging.Logger.getLogger(GLOBAL_LOGGER_NAME);
+ logManager = LogManager.getLogManager();
+ logManager.getLogger(GLOBAL_LOGGER_NAME).setLevel(Level.OFF);
+ return myLogger;
+ }
+
+}
diff --git a/src/main/java/command/NoCap.java b/src/main/java/command/NoCap.java
new file mode 100644
index 0000000000..2528989767
--- /dev/null
+++ b/src/main/java/command/NoCap.java
@@ -0,0 +1,41 @@
+package command;
+
+import command.parser.Parser;
+import command.storage.StorageDecoder;
+import module.ModuleList;
+import semester.SemesterList;
+import semester.Semester;
+
+import java.util.NoSuchElementException;
+import java.util.Scanner;
+
+import command.Logger;
+
+public class NoCap {
+
+ public static SemesterList semesterList = StorageDecoder.decodeJsonToSemesterList();
+ public static ModuleList moduleList;
+ public static Semester semester;
+
+ private void run() {
+ Ui.printStartMessage(semesterList.get(semesterList.getAccessedSemesterIndex()).getSemester());
+ Parser parse = new Parser();
+ Scanner in = new Scanner(System.in);
+ while (!parse.isExit()) {
+ System.out.println();
+ moduleList = semesterList.extractAccessedSemester().getModuleList();
+ semester = semesterList.extractAccessedSemester();
+ String input = in.nextLine();
+ System.out.println();
+ parse.chooseTask(input);
+ }
+ }
+
+ public static void main(String[] args) {
+ try {
+ new NoCap().run();
+ } catch (NoSuchElementException e) {
+ Ui.cheekyErrorMessage();
+ }
+ }
+}
diff --git a/src/main/java/command/Ui.java b/src/main/java/command/Ui.java
new file mode 100644
index 0000000000..cfc4f4c87f
--- /dev/null
+++ b/src/main/java/command/Ui.java
@@ -0,0 +1,475 @@
+package command;
+
+
+import module.Module;
+import module.ModuleList;
+import schedule.Schedule;
+import schedule.ScheduleList;
+import task.GradableTaskList;
+import task.OverallTask;
+import task.Task;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+public class Ui {
+ private static final String BORDER = "==================================================================\n";
+ private static final String LINE = "__________________________________________________________________\n";
+ private static final String CMD_LIST_SEMESTER = "[Commands for LIST/SWITCH SEMESTERS]\n";
+ private static final String CMD_LIST_MODULE = "[Commands for ADD/DELETE/LIST MODULES]\n";
+ private static final String CMD_LIST_ADD = "[ADD Commands]\n";
+ private static final String CMD_LIST_DELETE = "[DELETE Commands]\n";
+ private static final String CMD_LIST_LIST = "[LIST Commands]\n";
+ private static final String CMD_LIST_MARK = "[MARK Commands]\n";
+ private static final String CMD_LIST_EDIT = "[EDIT Commands]\n";
+ private static final String CMD_LIST_SHOW = "[SHOW Commands]\n";
+ private static final String CMD_LIST_EXIT = "[EXIT Commands]\n";
+
+ public static void printHelpMessage() {
+ System.out.println("[NoCap Command List]\n"
+ + BORDER
+ + "Show NoCap Command List: help \n"
+ + LINE
+ + CMD_LIST_SEMESTER
+ + "Show all semesters: list semesters\n"
+ + "Change semester: switch \n"
+ + LINE
+ + CMD_LIST_MODULE
+ + "Add module: add \n"
+ + "Delete module: delete \n"
+ + "List all module: list module\n"
+ + LINE
+ + CMD_LIST_ADD
+ + "Add task: /m addtask /by (time)\n"
+ + "Add gradable task: /m addgradable /by (time) /w \n"
+ + "Add class: /m addclass \n"
+ + "Add grade: /m addgrade \n"
+ + "Add credit: /m addcredit\n"
+ + LINE
+ + CMD_LIST_DELETE
+ + "Delete task: /m deletetask \n"
+ + "Delete class: /m deleteclass \n"
+ + "Delete grade: /m deletegrade\n"
+ + LINE
+ + CMD_LIST_LIST
+ + "List all tasks: list task (optional argument)\n"
+ + "List module tasks: /m list (optional argument)\n"
+ + LINE
+ + CMD_LIST_MARK
+ + "Mark task as complete: /m done \n"
+ + "Mark task as incomplete: /m notdone \n"
+ + "Mark gradable task as complete: /m gradabledone \n"
+ + "Mark gradable task as incomplete: /m gradablenotdone \n"
+ + LINE
+ + CMD_LIST_EDIT
+ + "Edit description: /m editdesc \n"
+ + "Edit deadline: /m editdate \n"
+ + LINE
+ + CMD_LIST_SHOW
+ + "Show timetable: timetable\n"
+ + "Show module cap: cap\n"
+ + "Show overall cap: allcap\n"
+ + LINE
+ + CMD_LIST_EXIT
+ + "Exit NoCap: bye\n"
+ + BORDER);
+
+ }
+
+ public static void missingDescription() {
+ System.out.println("You are missing a description!");
+ }
+
+ public static void missingDate() {
+ System.out.println("You are missing a date!");
+ }
+
+ public static void printStartMessage(String semester) {
+ System.out.println("Welcome to NoCap");
+ System.out.println("You are now accessing semester: " + semester);
+ }
+
+ public static void invalidDate() {
+ System.out.println("Missing /by");
+ }
+
+ public static void invalidWeightage() {
+ System.out.println("Missing /w or wrong syntax! please refer to the UG for proper syntax.");
+ }
+
+
+ public static void wrongDateTimeFormat() {
+ System.out.println("Wrong date format input!");
+ System.out.println("Format: dd/MM/yyyy hhmm");
+ }
+
+ public static void inputNotInteger() {
+ System.out.println("Input must be an integer!");
+ }
+
+ public static void switchSemesterMessage(String semester) {
+ System.out.println("Semester successfully switched");
+ System.out.println("You are now accessing semester: " + semester);
+ }
+
+ public static void addModuleNameMessage(ModuleList moduleList) {
+ System.out.println("Module successfully added: ");
+ moduleList.printModules();
+ }
+
+ public static void addModuleGradeMessage(Module module) {
+ System.out.println("Module grade successfully added: ");
+ System.out.println(module);
+ }
+
+ public static void addModuleCreditsMessage(Module module) {
+ System.out.println("Module credits successfully added: ");
+ System.out.println(module);
+ }
+
+ public static void addModuleClassMessage(Module module) {
+ System.out.println("Module Class successfully added: ");
+ System.out.println(module.getScheduleList());
+ }
+
+ public static void addTaskMessage(Task task, String moduleName) {
+ System.out.println("Added new task to " + moduleName);
+ System.out.println(task);
+ }
+
+ public static void printTaskList(String module, int taskCount) {
+ System.out.println("Task List for " + module.toUpperCase(Locale.ROOT) + ":");
+ System.out.println("There are " + taskCount + " tasks");
+ }
+
+ public static void printWeeklyTaskList(String module, int taskCount) {
+ System.out.println("Task List for " + module.toUpperCase(Locale.ROOT) + ":");
+ System.out.println("There are " + taskCount + " tasks due within 7 days");
+ }
+
+ public static void printMonthlyTaskList(String module, int taskCount) {
+ System.out.println("Task List for " + module.toUpperCase(Locale.ROOT) + ":");
+ System.out.println("There are " + taskCount + " tasks due within a month");
+ }
+
+ public static void printYearlyTaskList(String module, int taskCount) {
+ System.out.println("Task List for " + module.toUpperCase(Locale.ROOT) + ":");
+ System.out.println("There are " + taskCount + " tasks due within a year");
+ }
+
+ public static void printEmptyTaskList(String module) {
+ System.out.println("There are no tasks due in " + module.toUpperCase(Locale.ROOT));
+ }
+
+ public static void printSortListByDate(String module) {
+ System.out.println(module.toUpperCase(Locale.ROOT) + " successfully sorted by date");
+ }
+
+
+ public static void printSortListByStatus(String module) {
+ System.out.println(module.toUpperCase(Locale.ROOT) + " successfully sorted by status");
+ }
+
+ /**
+ * Method to print error when save file cannot be parsed.
+ */
+ public static void parseSaveFileError() {
+ System.out.println("Error parsing save file");
+ }
+
+ /**
+ * Method to print error when a save file could not be written.
+ */
+ public static void writeSaveFileError() {
+ System.out.println("Error writing to save file");
+ }
+
+ /**
+ * Method to print error if there are errors when trying to save the file.
+ */
+ public static void saveFileError() {
+ System.out.println("Error saving file");
+ }
+
+ /**
+ * Method to print message when data is loaded successfully from a save file.
+ */
+ public static void loadFileSuccessful() {
+ System.out.println("Data loaded successfully");
+ }
+
+ /**
+ * Method to print message when a save file cannot be found.
+ */
+ public static void printNoSaveFileMessage() {
+ System.out.println("No save file found, starting with an empty template");
+ }
+
+ /**
+ * Method to print message when a save file is corrupted.
+ */
+ public static void printCorruptFileMessage() {
+ System.out.println("Error reading save file, creating new template");
+ }
+
+ public static void printInvalidListFormat() {
+ System.out.println("The list format is wrong!");
+ System.out.println("Please refer to the user guide or help function for more details!");
+ }
+
+ public static void printInvalidIndex() {
+ System.out.println("Task with the specified index not found!");
+ }
+
+ public static void printInvalidKeyword() {
+ System.out.println("Task with the specified keyword not found!");
+ }
+
+ public static void printTaskFound() {
+ System.out.println("The following task(s) are found:");
+ }
+
+ public static void printSelectIndex() {
+ System.out.println("Please choose a task to perform the action, or press x to cancel:");
+ }
+
+ public static void printTaskDeleted(Task task) {
+ System.out.println(task.getDescription() + " has been deleted.");
+ }
+
+ public static void printMarkDoneMessage(Task task) {
+ System.out.println("Task is completed:" + System.lineSeparator() + task);
+ }
+
+ public static void printMarkNotDoneMessage(Task task) {
+ System.out.println("Task is marked not complete:" + System.lineSeparator() + task);
+ }
+
+ public static void printExitMessage() {
+ System.out.println("Thank you for using NoCap!");
+ System.out.println("Have a nice day!");
+ }
+
+ public static void duplicateModuleError() {
+ System.out.println("This module already exists!");
+ }
+
+ public static void printInvalidInputMessage() {
+ System.out.println("Invalid Input!");
+ }
+
+ public static void printInvalidModuleNameMessage() {
+ System.out.println("Invalid Module name!");
+ }
+
+ /**
+ * Method to print overall task list that has been sorted by date.
+ *
+ * @param newTaskList Overall task list to be printed.
+ */
+ public static void printOverallListOrderedByDate(List newTaskList) {
+ System.out.println("Tasks sorted by date: ");
+ if (newTaskList.isEmpty()) {
+ System.out.println("You have no tasks");
+ return;
+ }
+ for (int i = 0; i < newTaskList.size(); i++) {
+ System.out.println((i + 1) + ". " + newTaskList.get(i));
+ }
+ }
+
+ /**
+ * Method to print overall task list that has been sorted by status.
+ *
+ * @param newTaskList Overall task list to be printed.
+ */
+ public static void printOverallListOrderedByStatus(List newTaskList) {
+ System.out.println("Tasks sorted by status: ");
+ if (newTaskList.isEmpty()) {
+ System.out.println("You have no tasks");
+ return;
+ }
+ for (int i = 0; i < newTaskList.size(); i++) {
+ System.out.println((i + 1) + ". " + newTaskList.get(i));
+ }
+ }
+
+ /**
+ * Method to print overall task list containing weekly tasks.
+ *
+ * @param newTaskList Overall task list to be printed.
+ */
+ public static void printOverallWeeklyTasks(List newTaskList) {
+ System.out.println("Weekly tasks: ");
+ if (newTaskList.isEmpty()) {
+ System.out.println("You have no weekly tasks");
+ return;
+ }
+ for (int i = 0; i < newTaskList.size(); i++) {
+ System.out.println((i + 1) + ". " + newTaskList.get(i));
+ }
+ }
+
+ /**
+ * Method to print overall task list containing monthly tasks.
+ *
+ * @param newTaskList Overall task list to be printed.
+ */
+ public static void printOverallMonthlyTasks(List newTaskList) {
+ System.out.println("Monthly tasks: ");
+ if (newTaskList.isEmpty()) {
+ System.out.println("You have no monthly tasks");
+ return;
+ }
+ for (int i = 0; i < newTaskList.size(); i++) {
+ System.out.println((i + 1) + ". " + newTaskList.get(i));
+ }
+ }
+
+ /**
+ * Method to print overall task list containing yearly tasks.
+ *
+ * @param newTaskList Overall task list to be printed.
+ */
+ public static void printOverallYearlyTasks(List newTaskList) {
+ System.out.println("Yearly tasks: ");
+ if (newTaskList.isEmpty()) {
+ System.out.println("You have no yearly tasks");
+ return;
+ }
+ for (int i = 0; i < newTaskList.size(); i++) {
+ System.out.println((i + 1) + ". " + newTaskList.get(i));
+ }
+ }
+
+ /**
+ * Method to print all tasks in an overall task list.
+ *
+ * @param overallTaskList Overall task list to be printed.
+ */
+ public static void printAllOverallTasks(ArrayList overallTaskList) {
+ System.out.println("All tasks: ");
+ if (overallTaskList.isEmpty()) {
+ System.out.println("You have no tasks");
+ return;
+ }
+ for (int i = 0; i < overallTaskList.size(); i++) {
+ System.out.println((i + 1) + ". " + overallTaskList.get(i));
+ }
+ }
+
+ /**
+ * Method to print overall task list containing only gradable tasks.
+ *
+ * @param newTaskList Overall task list to be printed.
+ */
+ public static void printGradableTasks(List newTaskList) {
+ System.out.println("Gradable tasks: ");
+ if (newTaskList.isEmpty()) {
+ System.out.println("You have no gradable tasks");
+ return;
+ }
+ for (int i = 0; i < newTaskList.size(); i++) {
+ System.out.println((i + 1) + ". " + newTaskList.get(i));
+ }
+ }
+
+ /**
+ * Method to print overall task list containing only non-gradable tasks.
+ *
+ * @param newTaskList Overall task list to be printed.
+ */
+ public static void printNormalTasks(List newTaskList) {
+ System.out.println("Non-gradable tasks: ");
+ if (newTaskList.isEmpty()) {
+ System.out.println("You have no non-gradable tasks");
+ return;
+ }
+ for (int i = 0; i < newTaskList.size(); i++) {
+ System.out.println((i + 1) + ". " + newTaskList.get(i));
+ }
+ }
+
+ public static void visualiseGradableTask(GradableTaskList gl) {
+ VisualiseGradable v = new VisualiseGradable(gl);
+ v.print();
+ }
+
+ public static void wrongWeightage() {
+ System.out.println("Invalid Weightage, must be between 5 and 100 inclusive.");
+ }
+
+ public static void wrongWeightageSplits() {
+ System.out.println("Sum of all weightages cannot exceed 100%");
+ }
+
+ public static void moduleNameHasSpace() {
+ System.out.println("Module name cannot include space.");
+ }
+
+ public static void duplicateTaskError() {
+ System.out.println("Doing this will result in duplicate tasks!");
+ }
+
+ public static void taskUpdateMessage() {
+ System.out.println("The task you are trying to add already exists");
+ System.out.println("Deadline for this task has been updated");
+ }
+
+ public static void printUpdateTaskDescription(Task t) {
+ System.out.println("Task is updated with a new description."
+ + System.lineSeparator() + "This is your updated Task:"
+ + System.lineSeparator() + t);
+ }
+
+ public static void printUpdateTaskDeadline(Task t) {
+ System.out.println("Task is updated with a new deadline."
+ + System.lineSeparator() + "This is your updated Task:"
+ + System.lineSeparator() + t);
+ }
+
+ public static void deleteModuleMessage(Module m) {
+ System.out.print(m.getModuleName());
+ System.out.println(" has been successfully deleted");
+ }
+
+ public static void deleteScheduleMessage(Schedule s) {
+ System.out.println("Class: ");
+ System.out.println(s);
+ System.out.println("has been successfully deleted");
+ }
+
+ public static void printRemainingModules() {
+ System.out.println("Remaining Modules are: ");
+ }
+
+ public static void printRemainingSchedules(ScheduleList sl) {
+ System.out.println("\nRemaining Classes are: ");
+ System.out.println(sl);
+ }
+
+ public static void deleteGradeMesage(Module m) {
+ System.out.println("Module grade has been successfully deleted");
+ System.out.println(m);
+ }
+
+ public static void emptyModuleListMessage() {
+ System.out.println("You currently have no Modules.");
+ }
+
+ public static void invalidGrade() {
+ System.out.println("Invalid grade!");
+ }
+
+ public static void wrongListFormat() {
+ System.out.println("Incorrect list command format!");
+ System.out.println("Refer to the UG for the correct format");
+ }
+
+ public static void cheekyErrorMessage() {
+ System.out.println("Y U DO THIS");
+ System.out.println("Pls use 'bye' command, honey");
+ }
+
+}
diff --git a/src/main/java/command/VisualiseGradable.java b/src/main/java/command/VisualiseGradable.java
new file mode 100644
index 0000000000..1571ceefa1
--- /dev/null
+++ b/src/main/java/command/VisualiseGradable.java
@@ -0,0 +1,149 @@
+package command;
+
+import task.GradableTask;
+import task.GradableTaskList;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+public class VisualiseGradable {
+ String[] signs = new String[]{"#", "@", "&", "+"};
+
+ public GradableTaskList gradableTaskList;
+
+ /**
+ * Constructor for VisualiseGrdable.
+ *
+ * @param gl GradableTaskList to be visualised.
+ */
+ public VisualiseGradable(GradableTaskList gl) {
+ assert (gl != null);
+ this.gradableTaskList = gl;
+ }
+
+ private int getLength(GradableTask gradableTask) {
+ return gradableTask.getWeightage();
+ }
+
+ private String printMidBar() {
+ StringBuilder midLine = new StringBuilder();
+ int count = 0;
+ try {
+ while (count < this.gradableTaskList.size()) {
+ for (int i = 0; i < getLength(this.gradableTaskList.getGradableTask(count)); i++) {
+ midLine.append(signs[count % 4]);
+ }
+ count++;
+ }
+ } catch (IndexOutOfBoundsException e) {
+ System.out.println(e);
+ }
+ return midLine.toString();
+ }
+
+ private String printBottomBar() {
+ StringBuilder bottomLine = new StringBuilder();
+ StringBuilder legend = new StringBuilder();
+ int count = 0;
+ try {
+ while (count < this.gradableTaskList.size()) {
+ StringBuilder bottomLinePart = new StringBuilder();
+ int descriptionLength = this.gradableTaskList.getGradableTask(count).getDescription().length();
+ int weightageLength = getLength(this.gradableTaskList.getGradableTask(count));
+ int dashLength = (weightageLength - 3) / 2;
+ bottomLinePart.append("|");
+ bottomLinePart.append("-".repeat(Math.max(0, (dashLength))));
+ bottomLinePart.append(count + 1);
+ bottomLinePart.append("-".repeat(Math.max(0, (dashLength))));
+ while (bottomLinePart.toString().length() < weightageLength - 1) {
+ bottomLinePart.append("-");
+ }
+ bottomLinePart.append("|");
+ bottomLine.append(bottomLinePart);
+ legend.append(count + 1);
+ legend.append(": ");
+ legend.append(this.gradableTaskList.getGradableTask(count).getDescription());
+ legend.append(System.lineSeparator());
+ count++;
+ }
+ } catch (IndexOutOfBoundsException e) {
+ System.out.println(e);
+ }
+ bottomLine.append(System.lineSeparator());
+ bottomLine.append(legend);
+ return bottomLine.toString();
+ }
+
+ private String printTopBar() {
+ StringBuilder topLine = new StringBuilder();
+ int count = 0;
+ try {
+ while (count < this.gradableTaskList.size()) {
+ StringBuilder linePart = new StringBuilder();
+ int weightageLength = getLength(this.gradableTaskList.getGradableTask(count));
+ int dashLength = (weightageLength - 5) / 2;
+ linePart.append("<");
+ linePart.append("=".repeat(Math.max(0, (dashLength))));
+ linePart.append(this.gradableTaskList.getGradableTask(count).getWeightage());
+ linePart.append("%");
+ linePart.append("=".repeat(Math.max(0, (dashLength))));
+ while (linePart.toString().length() < weightageLength - 1) {
+ linePart.append("=");
+ }
+ linePart.append(">");
+ topLine.append(linePart);
+ count++;
+ }
+ } catch (IndexOutOfBoundsException e) {
+ System.out.println(e);
+ }
+ return topLine.toString();
+ }
+
+ private List sortByDone() {
+ List sorted = new ArrayList();
+ sorted.addAll(this.gradableTaskList.getGradableTaskList());
+ sorted.sort(new Comparator() {
+ @Override
+ public int compare(GradableTask o1, GradableTask o2) {
+ return Boolean.compare(o1.isDone(), o2.isDone());
+ }
+ });
+
+ return sorted;
+ }
+
+ private void showByDone() {
+ List sorted = sortByDone();
+ int count = 0;
+ for (GradableTask g : sorted) {
+ if (!g.isDone()) {
+ count++;
+ }
+ }
+ List undone = sorted.subList(0, count);
+ System.out.println("Uncompleted:");
+ for (GradableTask g : undone) {
+ System.out.println(g);
+ }
+ List done = sorted.subList(count, sorted.size());
+ System.out.println("Completed:");
+ for (GradableTask g : done) {
+ System.out.println(g);
+ }
+ }
+
+ /**
+ * Method to print the GradableTaskList to the terminal
+ * Individual methods to print out each part of the visualised GradableTaskList is called in order.
+ */
+ public void print() {
+ System.out.println("BREAKDOWN:");
+ System.out.println(printTopBar());
+ System.out.println(printMidBar());
+ System.out.println(printBottomBar());
+ System.out.print(this.gradableTaskList);
+ }
+
+}
diff --git a/src/main/java/command/parser/Command.java b/src/main/java/command/parser/Command.java
new file mode 100644
index 0000000000..ae550c1349
--- /dev/null
+++ b/src/main/java/command/parser/Command.java
@@ -0,0 +1,371 @@
+package command.parser;
+
+import command.NoCap;
+import command.Ui;
+import command.storage.StorageEncoder;
+import exceptions.NoCapExceptions;
+import module.Module;
+import task.GradableTask;
+import task.Task;
+import task.TaskList;
+
+import java.util.Locale;
+
+/**
+ * Class containing parser methods that calls commands to other classes.
+ */
+public class Command {
+
+ private final ParserChecks parserChecks = new ParserChecks();
+
+ public Command() {
+ }
+
+ /**
+ * Takes in an input string and parse it for semester index.
+ * Checks input is indeed an integer and not empty.
+ *
+ * @param taskDescription string index of Semester to switch to.
+ */
+ void commandSwitchSemester(String taskDescription) {
+ if (parserChecks.isEmptyDescription(taskDescription)
+ | parserChecks.isNotInteger(taskDescription)) {
+ return;
+ }
+ try {
+ NoCap.semesterList.setAccessedSemesterIndex(Integer.parseInt(taskDescription) - 1);
+ Ui.switchSemesterMessage(NoCap.semesterList
+ .get(Integer.parseInt(taskDescription) - 1).getSemester());
+ StorageEncoder.encodeAndSaveSemesterListToJson(NoCap.semesterList);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ void commandPrintCap() {
+ try {
+ NoCap.semester.printCap();
+ StorageEncoder.encodeAndSaveSemesterListToJson(NoCap.semesterList);
+ } catch (NoCapExceptions e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ void commandPrintAllCap() {
+ try {
+ NoCap.semesterList.printAllCap();
+ StorageEncoder.encodeAndSaveSemesterListToJson(NoCap.semesterList);
+ } catch (NoCapExceptions e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ void commandPrintTimeTable() {
+ NoCap.moduleList.printTimeTable();
+ StorageEncoder.encodeAndSaveSemesterListToJson(NoCap.semesterList);
+ }
+
+ void commandShowInfo(Module module) {
+ module.showInformation();
+ }
+
+ /**
+ * Takes in an input string and parse it for module name
+ * Checks input is not empty, module name does not already exists and does not include spaces.
+ *
+ * @param taskDescription module name to add.
+ */
+ void commandAddModule(String taskDescription) {
+ if (parserChecks.isEmptyDescription(taskDescription)
+ || parserChecks.isDuplicateModule(taskDescription)
+ || parserChecks.includeSpace(taskDescription)) {
+ return;
+ }
+ try {
+ NoCap.moduleList.add(taskDescription.toUpperCase(Locale.ROOT));
+ Ui.addModuleNameMessage(NoCap.moduleList);
+ StorageEncoder.encodeAndSaveSemesterListToJson(NoCap.semesterList);
+ } catch (NoCapExceptions e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ /**
+ * Takes in an input string and parse it for class information.
+ * Checks input is not empty.
+ *
+ * @param module module in which the class is to be added to.
+ * @param taskDescription class to add.
+ */
+ void commandAddClass(Module module, String taskDescription) {
+ if (parserChecks.isEmptyDescription(taskDescription)) {
+ return;
+ }
+ try {
+ module.addClass(taskDescription);
+ Ui.addModuleClassMessage(module);
+ } catch (NoCapExceptions e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ /**
+ * Takes in an input string and parse it for task information
+ * Checks input is not empty and contains /by.
+ *
+ * @param module module in which the task is to be added to.
+ * @param taskDescription module name to add.
+ */
+ void commandAddTask(Module module, String taskDescription) {
+ if (parserChecks.isEmptyDescription(taskDescription)
+ || !parserChecks.hasDateDescription(taskDescription)) {
+ return;
+ }
+ module.addTask(taskDescription);
+ }
+
+ /**
+ * Takes in an input string and parse it for task information
+ * Checks input is not empty and contains /by and /w.
+ *
+ * @param module module in which the gradable task is to be added to.
+ * @param taskDescription module name to add.
+ */
+ void commandAddGradable(Module module, String taskDescription) {
+ if (parserChecks.isEmptyDescription(taskDescription)
+ || !parserChecks.hasDateDescription(taskDescription)
+ || !parserChecks.hasWeightageDescription(taskDescription)) {
+ return;
+ }
+ module.addGradableTask(taskDescription);
+ Ui.visualiseGradableTask(module.getGradableTaskList());
+ }
+
+ /**
+ * Takes in an input string and parse it for grade information
+ * Checks input is not empty and contains a valid grade digit.
+ *
+ * @param module module in which the grade is to be added to.
+ * @param taskDescription grade to add
+ */
+ void commandAddGrade(Module module, String taskDescription) {
+ if (parserChecks.isEmptyDescription(taskDescription) || !parserChecks.validGrade(taskDescription)) {
+ return;
+ }
+ module.addGrade(taskDescription);
+ Ui.addModuleGradeMessage(module);
+ try {
+ NoCap.semester.updateCap();
+ NoCap.semesterList.updateCap();
+ } catch (NoCapExceptions e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ /**
+ * Takes in an input string and parse it for credit information
+ * Checks input is not empty and is an integer.
+ *
+ * @param module module in which the credit is to be assigned to.
+ * @param taskDescription credit value to add
+ */
+ void commandAddCredit(Module module, String taskDescription) {
+ if (parserChecks.isEmptyDescription(taskDescription)
+ || parserChecks.isNotInteger(taskDescription)) {
+ return;
+ }
+ try {
+ module.addCredits(Integer.parseInt(taskDescription));
+ Ui.addModuleCreditsMessage(module);
+ NoCap.semester.updateCap();
+ NoCap.semesterList.updateCap();
+ } catch (NoCapExceptions e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ /**
+ * Takes in a module index and deletes it from module list.
+ * Checks input is not empty and contains an index.
+ *
+ * @param taskDescription string index of module to delete.
+ */
+ void commandDeleteModule(String taskDescription) {
+ if (parserChecks.isEmptyDescription(taskDescription)
+ || parserChecks.isNotInteger(taskDescription)) {
+ return;
+ }
+ try {
+ NoCap.moduleList.delete(taskDescription);
+ Ui.printRemainingModules();
+ NoCap.moduleList.printModules();
+ StorageEncoder.encodeAndSaveSemesterListToJson(NoCap.semesterList);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.out.println("Invalid number value");
+ }
+ }
+
+ /**
+ * Deletes a class at the specified index from the module given.
+ * Checks input is not empty and contains an index.
+ *
+ * @param module module in which the class is to be deleted from.
+ * @param taskDescription string index of class to delete.
+ */
+ void commandDeleteClass(Module module, String taskDescription) {
+ if (parserChecks.isEmptyDescription(taskDescription)
+ || parserChecks.isNotInteger(taskDescription)) {
+ return;
+ }
+ try {
+ module.deleteClass(taskDescription);
+ Ui.printRemainingSchedules(module.getScheduleList());
+ StorageEncoder.encodeAndSaveSemesterListToJson(NoCap.semesterList);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.out.println("Invalid number value");
+ }
+ }
+
+ /**
+ * Deletes a task from a module by searching for keyword matches.
+ *
+ * @param module module in which the task is to be deleted from.
+ * @param taskDescription keyword to search for.
+ */
+ void commandDeleteTask(Module module, String taskDescription) {
+ if (parserChecks.isEmptyDescription(taskDescription)) {
+ return;
+ }
+ Task selectedTask = parserChecks.getTaskFromKeyword(taskDescription, module.taskList.getTaskList());
+ if (selectedTask != null) {
+ Ui.printTaskDeleted(selectedTask);
+ module.deleteTask(selectedTask);
+ }
+ }
+
+ /**
+ * Deletes an assigned grade from a module.
+ *
+ * @param module module in which the grade is to be deleted from.
+ */
+ void commandDeleteGrade(Module module) {
+ module.deleteGrade();
+ Ui.deleteGradeMesage(module);
+ try {
+ NoCap.semester.updateCap();
+ NoCap.semesterList.updateCap();
+ } catch (NoCapExceptions e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ /**
+ * Edits the description of a task.
+ * Perform additional checks to verify that a task of that index can be found,
+ * and the provided description is not the same as an existing task.
+ *
+ * @param module module of the task to be edited.
+ * @param taskType index of the task in the module.
+ * @param taskDescription new description to replace the old description.
+ */
+ void commandEditDescription(Module module, String taskType, String taskDescription) {
+ TaskList list = module.getTaskList();
+ Task selectedTask = parserChecks.getTaskFromIndex(taskType, module.taskList.getTaskList());
+ if (selectedTask != null && taskDescription.isBlank()) {
+ Ui.missingDescription();
+ return;
+ }
+ if (selectedTask != null && !list.hasDuplicateDescription(taskDescription)) {
+ selectedTask.setDescription(taskDescription);
+ Ui.printUpdateTaskDescription(selectedTask);
+ } else if (list.hasDuplicateDescription(taskDescription)) {
+ Ui.duplicateTaskError();
+ }
+ }
+
+ /**
+ * Edits the deadline of a task.
+ * Perform additional checks to verify that a task of that index can be found.
+ *
+ * @param module module of the task to be edited.
+ * @param taskType index of the task in the module.
+ * @param taskDescription new deadline to replace the old deadline.
+ */
+ void commandEditDeadline(Module module, String taskType, String taskDescription) {
+ Task selectedTask = parserChecks.getTaskFromIndex(taskType, module.taskList.getTaskList());
+ if (selectedTask != null) {
+ selectedTask.parseDeadline(taskDescription);
+ }
+ }
+
+ /**
+ * Edit the status of a task to be completed.
+ * Checks taskDescription is not empty
+ *
+ * @param module module of the task to be edited.
+ * @param taskDescription index of task in module.
+ */
+ void commandMarkDone(Module module, String taskDescription) {
+ if (parserChecks.isEmptyDescription(taskDescription)) {
+ return;
+ }
+ Task selectedTask = parserChecks.getTaskFromIndex(taskDescription, module.taskList.getTaskList());
+ if (selectedTask != null) {
+ selectedTask.markDone();
+ }
+ }
+
+ /**
+ * Edit the status of a task to be not completed.
+ * Checks taskDescription is not empty
+ *
+ * @param module module of the task to be edited.
+ * @param taskDescription index of task in module.
+ */
+ void commandMarkNotDone(Module module, String taskDescription) {
+ if (parserChecks.isEmptyDescription(taskDescription)) {
+ return;
+ }
+ Task selectedTask = parserChecks.getTaskFromIndex(taskDescription, module.taskList.getTaskList());
+ if (selectedTask != null) {
+ selectedTask.markNotDone();
+ }
+ }
+
+ /**
+ * Edit the status of a gradable task to be completed.
+ * Checks taskDescription is not empty
+ *
+ * @param module module of the task to be edited.
+ * @param taskDescription index of task in module.
+ */
+ void commandMarkGradableDone(Module module, String taskDescription) {
+ if (parserChecks.isEmptyDescription(taskDescription)) {
+ return;
+ }
+ GradableTask selectedTask = parserChecks.getGradableTaskFromIndex(taskDescription,
+ module.gradableTaskList.getGradableTaskList());
+ if (selectedTask != null) {
+ selectedTask.markDone();
+ }
+ }
+
+ /**
+ * Edit the status of a gradable task to be not completed.
+ * Checks taskDescription is not empty
+ *
+ * @param module module of the task to be edited.
+ * @param taskDescription index of task in module.
+ */
+ void commandMarkGradableNotDone(Module module, String taskDescription) {
+ if (parserChecks.isEmptyDescription(taskDescription)) {
+ return;
+ }
+ GradableTask selectedTask = parserChecks.getGradableTaskFromIndex(taskDescription,
+ module.gradableTaskList.getGradableTaskList());
+ if (selectedTask != null) {
+ selectedTask.markNotDone();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/command/parser/DateParser.java b/src/main/java/command/parser/DateParser.java
new file mode 100644
index 0000000000..7b453e0356
--- /dev/null
+++ b/src/main/java/command/parser/DateParser.java
@@ -0,0 +1,49 @@
+package command.parser;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.temporal.ChronoField;
+
+/**
+ * Class containing parser methods that specifically handles dateTime input.
+ * Additional dateTime Format can be added to DateTimeFormatter.
+ */
+public class DateParser {
+
+ private static DateTimeFormatter inputFormatter = new DateTimeFormatterBuilder()
+ .appendOptional(DateTimeFormatter.ISO_LOCAL_DATE)
+ .appendOptional(DateTimeFormatter.ofPattern("d M yyyy HH:mm"))
+ .appendOptional(DateTimeFormatter.ofPattern("d M yyyy HHmm"))
+ .appendOptional(DateTimeFormatter.ofPattern("d M yyyy"))
+ .appendOptional(DateTimeFormatter.ofPattern("d M yy"))
+ .appendOptional(DateTimeFormatter.ofPattern("d/M/yyyy HH:mm"))
+ .appendOptional(DateTimeFormatter.ofPattern("d/M/yyyy HHmm"))
+ .appendOptional(DateTimeFormatter.ofPattern("d/M/yyyy"))
+ .appendOptional(DateTimeFormatter.ofPattern("d/M/yy"))
+ .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
+ .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
+ .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
+ .toFormatter();
+
+ public static DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("d MMM yyyy hh:mm a");
+
+ /**
+ * Takes in an input string and parse it for LocalDateTime.
+ * Throws DateTimeException if invalid string.
+ *
+ * @param string input string to be parsed. Assumes not empty.
+ */
+ public static LocalDateTime parseDate(String string) {
+ return LocalDateTime.parse(string, inputFormatter);
+ }
+
+ /**
+ * Takes in a LocalDateTime variable and returns String based on outputFormatter.
+ *
+ * @param dateTime LocaleDateTime to be parsed into String
+ */
+ public static String dateStringOutput(LocalDateTime dateTime) {
+ return dateTime.format(outputFormatter);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/command/parser/ListParser.java b/src/main/java/command/parser/ListParser.java
new file mode 100644
index 0000000000..f05e8bada9
--- /dev/null
+++ b/src/main/java/command/parser/ListParser.java
@@ -0,0 +1,179 @@
+package command.parser;
+
+import command.NoCap;
+import command.Ui;
+import module.Module;
+import task.OverallTaskList;
+import task.TaskList;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Class containing parser methods that specifically handles list methods.
+ */
+public class ListParser {
+ public static final String SORT_BY_DATE = "sortbydate";
+ public static final String SORT_BY_STATUS = "sortbystatus";
+ public static final String SHOW_WEEK = "w";
+ public static final String SHOW_MONTH = "m";
+ public static final String SHOW_YEAR = "y";
+ public static final String SHOW_GRADABLE = "gradable";
+ public static final String SHOW_NORMAL = "normal";
+ private static final Logger logger = command.Logger.myLogger();
+
+ public ListParser() {
+ }
+
+ private boolean isEmpty(Module module) {
+ if (module.taskList.size() == 0) {
+ Ui.printEmptyTaskList(module.getModuleName());
+ return true;
+ }
+ return false;
+ }
+
+ private void sortByDate(Module module) {
+ TaskList list = module.getTaskList();
+ if (!isEmpty(module)) {
+ list.sortTaskListByDate(module.getModuleName());
+ }
+ }
+
+ private void sortByStatus(Module module) {
+ TaskList list = module.getTaskList();
+ if (!isEmpty(module)) {
+ list.sortTaskListByStatus(module.getModuleName());
+ }
+ }
+
+ private void listWeekly(Module module) {
+ TaskList list = module.getTaskList();
+ if (!isEmpty(module)) {
+ list.showAllWeekly(module.getModuleName());
+ }
+ }
+
+ private void listAll(Module module) {
+ TaskList list = module.getTaskList();
+ if (!isEmpty(module)) {
+ Ui.printTaskList(module.getModuleName(), list.getTaskCount());
+ list.printTasks(list.getTaskList());
+ }
+ }
+
+ private void listMonthly(Module module) {
+ TaskList list = module.getTaskList();
+ if (!isEmpty(module)) {
+ list.showAllMonthly(module.getModuleName());
+ }
+ }
+
+ private void listYearly(Module module) {
+ TaskList list = module.getTaskList();
+ if (!isEmpty(module)) {
+ list.showAllYearly(module.getModuleName());
+ }
+ }
+
+ /**
+ * Parser method for listing outside of modules.
+ * Check if taskType is MODULE, SEMESTER or TASK. If not, print error message.
+ * If taskType is Task, taskDescription is checked for optional sorting methods. If none matches,
+ * a default listing method is used.
+ *
+ * @param taskType string indicating type of listing.
+ * @param taskDescription string indicating sorting methods if taskType is task. Ignored otherwise.
+ */
+ public void overallListParser(String taskType, String taskDescription) {
+ switch (taskType) {
+ case Parser.MODULE:
+ if (NoCap.moduleList.size() == 0) {
+ Ui.emptyModuleListMessage();
+ } else {
+ NoCap.moduleList.printModules();
+ }
+ break;
+ case Parser.SEMESTERS:
+ NoCap.semesterList.printSemesters();
+ break;
+ case Parser.TASK:
+ OverallTaskList allTaskList = new OverallTaskList(NoCap.moduleList);
+ switch (taskDescription) {
+ case SORT_BY_DATE:
+ logger.log(Level.INFO, "Sort TaskList by date");
+ allTaskList.sortByDateAndPrint();
+ break;
+ case SORT_BY_STATUS:
+ logger.log(Level.INFO, "Sort TaskList by status");
+ allTaskList.sortByStatusAndPrint();
+ break;
+ case SHOW_WEEK:
+ logger.log(Level.INFO, "Print weekly TaskList");
+ allTaskList.printWeeklyTasks();
+ break;
+ case SHOW_MONTH:
+ logger.log(Level.INFO, "Print monthly TaskList");
+ allTaskList.printMonthlyTasks();
+ break;
+ case SHOW_YEAR:
+ allTaskList.printYearlyTasks();
+ logger.log(Level.INFO, "Print yearly TaskList");
+ break;
+ case SHOW_GRADABLE:
+ allTaskList.printGradableTasks();
+ logger.log(Level.INFO, "Print gradable TaskList");
+ break;
+ case SHOW_NORMAL:
+ allTaskList.printNormalTasks();
+ logger.log(Level.INFO, "Print normal TaskList");
+ break;
+ default:
+ allTaskList.printAllTasks();
+ break;
+ }
+ break;
+ default:
+ Ui.printInvalidListFormat();
+ break;
+ }
+ }
+
+ /**
+ * Parser method for listing tasks within a module.
+ * if input matches a specific sorting method, then a specified list is printed. If not,
+ * a default listing method is used.
+ *
+ * @param module the module in which the tasks are being accessed.
+ * @param input string indicating sorting methods. Default sorting is used if sorting method stated is invalid.
+ */
+ public void moduleListParser(Module module, String input) {
+ if (input.isBlank()) {
+ listAll(module);
+ return;
+ }
+ switch (input) {
+ case SORT_BY_DATE:
+ sortByDate(module);
+ break;
+ case SORT_BY_STATUS:
+ sortByStatus(module);
+ break;
+ case SHOW_WEEK:
+ listWeekly(module);
+ break;
+ case SHOW_MONTH:
+ listMonthly(module);
+ break;
+ case SHOW_YEAR:
+ listYearly(module);
+ break;
+ case SHOW_GRADABLE:
+ Ui.visualiseGradableTask(module.getGradableTaskList());
+ break;
+ default:
+ Ui.wrongListFormat();
+ break;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/command/parser/Parser.java b/src/main/java/command/parser/Parser.java
new file mode 100644
index 0000000000..540b4a7431
--- /dev/null
+++ b/src/main/java/command/parser/Parser.java
@@ -0,0 +1,213 @@
+package command.parser;
+
+import command.NoCap;
+import command.Ui;
+import command.storage.StorageEncoder;
+import module.Module;
+
+import java.util.Locale;
+
+/**
+ * Class containing initial commands to parse the user input.
+ */
+public class Parser {
+ public static final String EMPTY_STRING = "";
+ public static final String SPACE_STRING = " ";
+ public static final String SWITCHSEMESTER = "switch";
+ public static final String SEMESTERS = "semesters";
+ public static final String CAP = "cap";
+ public static final String ALLCAP = "allcap";
+ public static final String TASK = "task";
+ public static final String GRADABLE = "gradable";
+ public static final String MODULE = "module";
+ public static final String HELP = "help";
+ public static final String ADD = "add";
+ public static final String DELETE = "delete";
+ public static final String LIST = "list";
+ public static final String TIMETABLE = "timetable";
+ public static final String MODULETYPE = "/m";
+ public static final String EXIT = "bye";
+ public static final String ADDCLASS = "addclass";
+ public static final String ADDTASK = "addtask";
+ public static final String ADDGRADABLE = "addgradable";
+ public static final String ADDGRADE = "addgrade";
+ public static final String ADDCREDIT = "addcredit";
+ public static final String DELETECLASS = "deleteclass";
+ public static final String DELETETASK = "deletetask";
+ public static final String DELETEGRADE = "deletegrade";
+ public static final String EDITDESCRIPTION = "editdesc";
+ public static final String EDITDEADLINE = "editdate";
+ public static final String DONE = "done";
+ public static final String NOTDONE = "notdone";
+ public static final String GRADABLEDONE = "gradabledone";
+ public static final String GRADABLENOTDONE = "gradablenotdone";
+ public static final String INFO = "info";
+
+ private String taskType;
+ private String taskDescription;
+ private final Command command = new Command();
+ private final ListParser list = new ListParser();
+ protected boolean isExit;
+
+ public Parser() {
+ this.isExit = false;
+ }
+
+ /**
+ * Takes in the initial user input and splits it into taskType and taskDescription
+ * to be further processed.
+ *
+ * @param line User input
+ */
+ public void chooseTask(String line) {
+ splitInput(line);
+ switch (taskType) {
+ case HELP:
+ Ui.printHelpMessage();
+ break;
+ case SWITCHSEMESTER:
+ command.commandSwitchSemester(taskDescription);
+ break;
+ case CAP:
+ command.commandPrintCap();
+ break;
+ case ALLCAP:
+ command.commandPrintAllCap();
+ break;
+ case ADD:
+ command.commandAddModule(taskDescription);
+ break;
+ case DELETE:
+ command.commandDeleteModule(taskDescription);
+ break;
+ case TIMETABLE:
+ command.commandPrintTimeTable();
+ break;
+ case LIST:
+ splitInput(taskDescription);
+ list.overallListParser(taskType, taskDescription);
+ StorageEncoder.encodeAndSaveSemesterListToJson(NoCap.semesterList);
+ break;
+ case MODULETYPE:
+ moduleParser(taskDescription);
+ StorageEncoder.encodeAndSaveSemesterListToJson(NoCap.semesterList);
+ break;
+ case EXIT:
+ Ui.printExitMessage();
+ StorageEncoder.encodeAndSaveSemesterListToJson(NoCap.semesterList);
+ this.isExit = true;
+ break;
+ default:
+ Ui.printInvalidInputMessage();
+ break;
+ }
+ }
+
+ /**
+ * Parser group for user inputs starting with /m
+ * First separate the input int part is saved as module.
+ * The next part is split again to obtain the new taskType and taskDescription
+ *
+ * @param input string after removing /m
+ */
+ void moduleParser(String input) {
+ splitInput(input);
+ Module module;
+ try {
+ module = NoCap.moduleList.find(taskType.toUpperCase(Locale.ROOT));
+ } catch (ArrayIndexOutOfBoundsException e) {
+ Ui.printInvalidModuleNameMessage();
+ return;
+ }
+
+ splitInput(taskDescription);
+ switch (taskType) {
+ case LIST:
+ //splitInput(taskDescription);
+ list.moduleListParser(module, taskDescription);
+ break;
+ case ADDCLASS:
+ command.commandAddClass(module, taskDescription);
+ break;
+ case ADDTASK:
+ command.commandAddTask(module, taskDescription);
+ break;
+ case ADDGRADABLE:
+ command.commandAddGradable(module, taskDescription);
+ break;
+ case ADDGRADE:
+ command.commandAddGrade(module, taskDescription);
+ break;
+ case ADDCREDIT:
+ command.commandAddCredit(module, taskDescription);
+ break;
+ case DELETECLASS:
+ command.commandDeleteClass(module, taskDescription);
+ break;
+ case DELETETASK:
+ command.commandDeleteTask(module, taskDescription);
+ break;
+ case DELETEGRADE:
+ command.commandDeleteGrade(module);
+ break;
+ case EDITDESCRIPTION:
+ splitInput(taskDescription);
+ command.commandEditDescription(module,taskType,taskDescription);
+ break;
+ case EDITDEADLINE:
+ splitInput(taskDescription);
+ command.commandEditDeadline(module,taskType,taskDescription);
+ break;
+ case DONE:
+ command.commandMarkDone(module, taskDescription);
+ break;
+ case NOTDONE:
+ command.commandMarkNotDone(module, taskDescription);
+ break;
+ case GRADABLEDONE:
+ command.commandMarkGradableDone(module, taskDescription);
+ break;
+ case GRADABLENOTDONE:
+ command.commandMarkGradableNotDone(module, taskDescription);
+ break;
+ case INFO:
+ command.commandShowInfo(module);
+ break;
+ default:
+ Ui.printInvalidInputMessage();
+ break;
+ }
+ }
+
+ /**
+ * Splits inputs string and store them into taskType and taskDescription.
+ * If there are no SPACE_STRING, taskType is the input String and taskDescription is EMPTY_STRING.
+ * If the input string is empty, both variables are set to empty.
+ *
+ * @param input string to be split
+ */
+ void splitInput(String input) {
+ try {
+ int typePos = input.indexOf(SPACE_STRING);
+ taskType = input.substring(0, typePos);
+ taskDescription = input.substring(typePos).trim();
+ } catch (StringIndexOutOfBoundsException e) {
+ taskType = input.trim();
+ taskDescription = EMPTY_STRING;
+ assert (taskType.equals(input.trim()));
+ }
+ }
+
+ public boolean isExit() {
+ return this.isExit;
+ }
+
+ public String getTaskType() {
+ return taskType;
+ }
+
+ public String getTaskDescription() {
+ return taskDescription;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/command/parser/ParserChecks.java b/src/main/java/command/parser/ParserChecks.java
new file mode 100644
index 0000000000..f4d0e393ee
--- /dev/null
+++ b/src/main/java/command/parser/ParserChecks.java
@@ -0,0 +1,226 @@
+package command.parser;
+
+import command.NoCap;
+import command.Ui;
+import task.GradableTask;
+import task.Task;
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.Scanner;
+
+/**
+ * Class containing parser methods that specifically handles error checking.
+ * Also includes methods for getting Task from index or string.
+ */
+public class ParserChecks {
+ public static final String START_OF_DATE = "/by";
+ public static final String START_OF_WEIGHTAGE = "/w";
+
+ public ParserChecks() {
+ }
+
+ /**
+ * Returns a task at the specified index from a tasklist.
+ * If input is not an index, error message is printed.
+ * Returns null if task cannot be found.
+ *
+ * @param input index to be accessed.
+ * @param taskList arraylist with tasks.
+ */
+ public Task getTaskFromIndex(String input, ArrayList taskList) {
+ int index;
+ Task task = null;
+ try {
+ index = Integer.parseInt(input) - 1;
+ if (isValidIndex(index)) {
+ task = taskList.get(index);
+ }
+ } catch (IndexOutOfBoundsException | NumberFormatException e) {
+ Ui.printInvalidIndex();
+ }
+ return task;
+ }
+
+ /**
+ * Returns a gradable task at the specified index from a tasklist.
+ * If input is not an index, error message is printed.
+ * Returns null if task cannot be found.
+ *
+ * @param input index to be accessed.
+ * @param taskList arraylist with gradable tasks.
+ */
+ public GradableTask getGradableTaskFromIndex(String input, ArrayList taskList) {
+ int index;
+ GradableTask task = null;
+ try {
+ index = Integer.parseInt(input) - 1;
+ if (isValidIndex(index)) {
+ task = taskList.get(index);
+ }
+ } catch (IndexOutOfBoundsException | NumberFormatException e) {
+ Ui.printInvalidIndex();
+ }
+ return task;
+ }
+
+ /**
+ * Searches for tasks containing keyword from an arraylist of tasks and creates a filtered arraylist.
+ * Prints out this filtered list and request for user input to select an index.
+ * Calls getTaskFromIndex to get task from the filtered list.
+ *
+ * @param keyword keyword to search for.
+ * @param allTaskList arraylist with all tasks.
+ */
+ public Task getTaskFromKeyword(String keyword, ArrayList allTaskList) {
+ Task task = null;
+ ArrayList taskList = new ArrayList();
+ for (Task item : allTaskList) {
+ if (item.getDescription().contains(keyword)) {
+ taskList.add(item);
+ }
+ }
+
+ if (taskList.isEmpty()) {
+ Ui.printInvalidKeyword();
+ return task;
+ }
+ assert (!taskList.isEmpty());
+
+ Ui.printTaskFound();
+ int counter = 1;
+ for (Task item : taskList) {
+ System.out.println(counter + ": " + item.getDescription());
+ counter++;
+ }
+
+ String input = requestUserInput();
+
+ if (input.equals("x")) {
+ return task;
+ }
+ task = getTaskFromIndex(input, taskList);
+ return task;
+ }
+
+ private String requestUserInput() {
+ Ui.printSelectIndex();
+ Scanner in = new Scanner(System.in);
+ String input = in.nextLine();
+ return input;
+ }
+
+ boolean isValidIndex(int index) {
+ if (index < 0) {
+ Ui.printInvalidIndex();
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Used in add to verify module does not exist. Prevent duplicate module entries.
+ *
+ * @param input moduleName to be checked against.
+ * @return true if input is existing module.
+ */
+ boolean isDuplicateModule(String input) {
+ try {
+ NoCap.moduleList.find(input.toUpperCase(Locale.ROOT));
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return false;
+ }
+ Ui.duplicateModuleError();
+ return true;
+ }
+
+ boolean isEmptyDescription(String input) {
+ if (input.isEmpty()) {
+ Ui.missingDescription();
+ return true;
+ }
+ return false;
+ }
+
+ boolean isNotInteger(String input) {
+ if (input == null) {
+ Ui.inputNotInteger();
+ return true;
+ }
+ try {
+ int in = Integer.parseInt(input);
+ } catch (NumberFormatException e) {
+ Ui.inputNotInteger();
+ return true;
+ }
+ return false;
+ }
+
+ boolean hasDateDescription(String input) {
+ if (!input.contains(START_OF_DATE)) {
+ Ui.invalidDate();
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Checks if /w exists after /by.
+ *
+ * @param input string to be checked
+ * @return true if checks passes.
+ */
+ boolean hasWeightageDescription(String input) {
+ int typePos = input.indexOf(START_OF_WEIGHTAGE);
+ if (typePos == -1) {
+ Ui.invalidWeightage();
+ return false;
+ }
+ String secondPart = input.substring(typePos);
+ if (secondPart.contains(START_OF_DATE)) {
+ Ui.invalidWeightage();
+ return false;
+ }
+ typePos = input.indexOf(START_OF_DATE);
+ secondPart = input.substring(typePos);
+ if (secondPart.contains(START_OF_WEIGHTAGE)) {
+ return true;
+ }
+ Ui.invalidWeightage();
+ return false;
+ }
+
+ boolean includeSpace(String input) {
+ if (input.trim().contains(Parser.SPACE_STRING)) {
+ Ui.moduleNameHasSpace();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks the input string is a valid grade.
+ *
+ * @param input string to be checked.
+ * @return true if checks passes.
+ */
+ boolean validGrade(String input) {
+ String grade = input.trim();
+ switch (grade) {
+ case "A+":
+ case "A":
+ case "A-":
+ case "B+":
+ case "B":
+ case "B-":
+ case "C+":
+ case "C":
+ case "D+":
+ case "D":
+ case "F":
+ return true;
+ default:
+ Ui.invalidGrade();
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/command/storage/StorageDecoder.java b/src/main/java/command/storage/StorageDecoder.java
new file mode 100644
index 0000000000..b609336670
--- /dev/null
+++ b/src/main/java/command/storage/StorageDecoder.java
@@ -0,0 +1,54 @@
+package command.storage;
+
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import command.Ui;
+import semester.SemesterList;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class StorageDecoder {
+
+ //Constants
+ private static final String ROOT = System.getProperty("user.dir");
+ private static final Path FILE_PATH = Paths.get(ROOT, "data", "data.json");
+
+ //Logger object
+ private static final Logger logger = command.Logger.myLogger();
+
+ /**
+ * Decodes a json file located at a specified path into a SemesterList object.
+ * Is implemented using the jackson databind library.
+ *
+ * @return A SemesterList containing information from previous runs.
+ */
+ public static SemesterList decodeJsonToSemesterList() {
+ ObjectMapper objectMapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
+ objectMapper.registerModule(new JavaTimeModule());
+ SemesterList semesters = new SemesterList();
+ try {
+ if (!Files.exists(FILE_PATH)) {
+ Ui.printNoSaveFileMessage();
+ assert semesters.toString().equals(new SemesterList().toString());
+ return semesters;
+ }
+ semesters = objectMapper.readValue(new File(FILE_PATH.toString()), SemesterList.class);
+ Ui.loadFileSuccessful();
+ logger.log(Level.INFO, "Load file successful");
+ } catch (IOException e) {
+ Ui.printCorruptFileMessage();
+ // Create a new save file if save file is corrupted
+ assert semesters.toString().equals(new SemesterList().toString());
+ StorageEncoder.encodeAndSaveSemesterListToJson(semesters);
+ }
+ return semesters;
+ }
+}
diff --git a/src/main/java/command/storage/StorageEncoder.java b/src/main/java/command/storage/StorageEncoder.java
new file mode 100644
index 0000000000..7e8aca90b6
--- /dev/null
+++ b/src/main/java/command/storage/StorageEncoder.java
@@ -0,0 +1,82 @@
+package command.storage;
+
+import com.fasterxml.jackson.databind.DatabindException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import command.Ui;
+import exceptions.ExceptionMessages;
+import exceptions.NoCapExceptions;
+import semester.SemesterList;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class StorageEncoder {
+
+ //Constants
+ private static final String ROOT = System.getProperty("user.dir");
+ private static final Path FILE_PATH = Paths.get(ROOT, "data", "data.json");
+ private static final Path DIRECTORY_PATH = Paths.get(ROOT, "data");
+
+ //Logger object
+ private static final Logger logger = command.Logger.myLogger();
+
+ /**
+ * Takes in a SemesterList object and converts it into json format.
+ * Then, stores it in a specified file path. If the specified directory does
+ * not exist, this method will attempt to create the directory first before saving the
+ * SemesterList object. Is implemented using the jackson databind library.
+ *
+ * @param semesterList The SemesterList object to be converted into json format.
+ */
+ public static void encodeAndSaveSemesterListToJson(SemesterList semesterList) {
+ ObjectMapper objectMapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
+ objectMapper.registerModule(new JavaTimeModule());
+ try {
+ if (!Files.exists(DIRECTORY_PATH)) {
+ createDataDirectory();
+ }
+ if (!Files.exists(FILE_PATH)) {
+ createFile();
+ }
+ objectMapper.writeValue(new File(FILE_PATH.toString()), semesterList);
+ logger.log(Level.INFO, "File saved");
+ } catch (NoCapExceptions e) {
+ System.out.println(e.getMessage());
+ } catch (DatabindException e) {
+ Ui.parseSaveFileError();
+ } catch (IOException e) {
+ Ui.writeSaveFileError();
+ }
+ }
+
+ private static void createDataDirectory() throws NoCapExceptions {
+ File newDirectory = new File(DIRECTORY_PATH.toString());
+ boolean createSuccess = newDirectory.mkdir();
+ logger.log(Level.INFO, "New data directory being created");
+ assert Files.exists(DIRECTORY_PATH);
+ if (!createSuccess) {
+ throw new NoCapExceptions(ExceptionMessages.EXCEPTION_CREATE_DIRECTORY_FAIL);
+ }
+ }
+
+ private static void createFile() throws NoCapExceptions {
+ try {
+ File newFile = new File(FILE_PATH.toString());
+ logger.log(Level.INFO, "New data file being created");
+ boolean createSuccess = newFile.createNewFile();
+ if (!createSuccess) {
+ throw new NoCapExceptions(ExceptionMessages.EXCEPTION_CREATE_FILE_FAIL);
+ }
+ assert Files.exists(FILE_PATH);
+ } catch (IOException e) {
+ Ui.saveFileError();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/exceptions/ExceptionMessages.java b/src/main/java/exceptions/ExceptionMessages.java
new file mode 100644
index 0000000000..25e7fbda5c
--- /dev/null
+++ b/src/main/java/exceptions/ExceptionMessages.java
@@ -0,0 +1,9 @@
+package exceptions;
+
+/**
+ * Class used to store exception messages used in NoCapExceptions.
+ */
+public class ExceptionMessages {
+ public static final String EXCEPTION_CREATE_DIRECTORY_FAIL = "Error creating directory";
+ public static final String EXCEPTION_CREATE_FILE_FAIL = "Error creating save file";
+}
diff --git a/src/main/java/exceptions/NoCapExceptions.java b/src/main/java/exceptions/NoCapExceptions.java
new file mode 100644
index 0000000000..8c53360858
--- /dev/null
+++ b/src/main/java/exceptions/NoCapExceptions.java
@@ -0,0 +1,10 @@
+package exceptions;
+
+/**
+ * Custom exception class to handle NoCap specific exceptions.
+ */
+public class NoCapExceptions extends Exception {
+ public NoCapExceptions(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/module/Module.java b/src/main/java/module/Module.java
new file mode 100644
index 0000000000..7868af6f9f
--- /dev/null
+++ b/src/main/java/module/Module.java
@@ -0,0 +1,230 @@
+package module;
+
+
+import command.NoCap;
+
+import command.VisualiseGradable;
+
+import exceptions.NoCapExceptions;
+
+import schedule.Schedule;
+import schedule.ScheduleList;
+import task.GradableTaskList;
+import task.Task;
+import task.TaskList;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+public class Module {
+ protected String letterGrade;
+ protected double points;
+ protected String moduleName;
+ public TaskList taskList;
+ private ScheduleList scheduleList;
+ protected int credits;
+ public GradableTaskList gradableTaskList;
+ private static final Logger logger = command.Logger.myLogger();
+
+ /**
+ * Constructor to create an instance of Module.
+ * Empty SchdeuleList, TaskList and GradableTaskList is created
+ * Credits is automatically assigned 0
+ * LetterGrade automatically assigned to null
+ *
+ * @param moduleName Name of module to be created
+ */
+ public Module(String moduleName) throws NoCapExceptions {
+ if (moduleName.length() > 16) {
+ throw new NoCapExceptions("Module name must be less than 17 characters");
+ }
+ this.moduleName = moduleName;
+ this.letterGrade = "NIL";
+ this.scheduleList = new ScheduleList();
+ this.credits = 0;
+ this.points = 0;
+ this.taskList = new TaskList();
+ this.gradableTaskList = new GradableTaskList();
+ }
+
+ /**
+ * For deserialization from JSON file.
+ */
+ public Module() {
+ }
+
+ //Getters and Setters
+ public String getLetterGrade() {
+ return letterGrade;
+ }
+
+ public void setLetterGrade(String letterGrade) {
+ this.letterGrade = letterGrade;
+ }
+
+ public String getModuleName() {
+ return moduleName;
+ }
+
+ public void setModuleName(String moduleName) {
+ this.moduleName = moduleName;
+ }
+
+ public TaskList getTaskList() {
+ return taskList;
+ }
+
+ public void setTaskList(TaskList taskList) {
+ this.taskList = taskList;
+ }
+
+ public ScheduleList getScheduleList() {
+ return this.scheduleList;
+ }
+
+ public void setScheduleList(ScheduleList scheduleList) {
+ this.scheduleList = scheduleList;
+ }
+
+ public GradableTaskList getGradableTaskList() {
+ return this.gradableTaskList;
+ }
+
+ public void addGradableTask(String userInput) {
+ this.gradableTaskList.addGradableTask(this.moduleName, userInput);
+ }
+
+ public int getCredits() {
+ return credits;
+ }
+
+ public void setCredits(int credits) {
+ this.credits = credits;
+ }
+
+ //Methods
+ public int size() {
+ return this.scheduleList.size();
+ }
+
+ public Schedule get(int index) {
+ assert index >= 0;
+ return this.scheduleList.getSchedule(index);
+ }
+
+ /**
+ * +
+ * Add grade to the module.
+ * Points are automatically update when the grade is added.
+ *
+ * @param letterGrade grade of module
+ */
+
+ public void addGrade(String letterGrade) {
+ this.letterGrade = letterGrade;
+ updatePoints();
+ }
+
+ public void deleteGrade() {
+ this.letterGrade = "NIL";
+ updatePoints();
+ }
+
+ private void updatePoints() {
+ switch (letterGrade) {
+ case "A+":
+ case "A":
+ points = 5;
+ break;
+ case "A-":
+ points = 4.5;
+ break;
+ case "B+":
+ points = 4.0;
+ break;
+ case "B":
+ points = 3.5;
+ break;
+ case "B-":
+ points = 3.0;
+ break;
+ case "C+":
+ points = 2.5;
+ break;
+ case "C":
+ points = 2.0;
+ break;
+ case "D+":
+ points = 1.5;
+ break;
+ case "D":
+ points = 1.0;
+ break;
+ case "F":
+ default:
+ points = 0;
+ break;
+ }
+ }
+
+ public double getPoints() {
+ return points;
+ }
+
+ public void addClass(Schedule schedule) {
+ this.scheduleList.addClass(schedule);
+ }
+
+ //overloading to take in String input * Added by jiexiong to keep Parser clean
+
+ /**
+ * Overload addClass method to take in String input.
+ * Adds a Class object with corresponding details.
+ *
+ * @param input User input class details.
+ * @throws NoCapExceptions Throws exception when Class exists in a slot or when input is wrongly formatted.
+ */
+ public void addClass(String input) throws NoCapExceptions {
+ this.scheduleList.addClass(input);
+ }
+
+ public void deleteClass(String input) {
+ this.scheduleList.deleteClass(input);
+ }
+
+ public void addTask(String userInput) {
+ this.taskList.addTask(this.moduleName, userInput);
+ }
+
+ public void deleteTask(Task task) {
+ this.taskList.delete(task);
+ }
+
+ public void addCredits(int credits) throws NoCapExceptions {
+ if (credits < 0) {
+ throw new NoCapExceptions("credit must be an integer > 0");
+ }
+ this.credits = credits;
+ }
+
+ public void showInformation() {
+ System.out.println(toString());
+ }
+
+ /**
+ * Reformats Module for easier viewing.
+ */
+ @Override
+ public String toString() {
+
+ return "Module name: " + moduleName
+ + "\nCREDITS: " + credits
+ + "\n--------------------------- "
+ + "\nSCHEDULE: \n" + scheduleList
+ + "--------------------------- "
+ + "\nGRADE: " + letterGrade
+ + "\nTASKS: " + taskList
+ + "\nBREAKDOWN: \n" + gradableTaskList;
+ }
+}
diff --git a/src/main/java/module/ModuleList.java b/src/main/java/module/ModuleList.java
new file mode 100644
index 0000000000..07134eeeb5
--- /dev/null
+++ b/src/main/java/module/ModuleList.java
@@ -0,0 +1,297 @@
+package module;
+
+import command.Ui;
+import command.storage.StorageDecoder;
+import exceptions.NoCapExceptions;
+
+import java.util.ArrayList;
+import java.util.Objects;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class ModuleList {
+ private static final Logger logger = command.Logger.myLogger();
+
+ private static final int HEIGHT = 33;
+ private static final int LENGTH = 207;
+ private static final int BOXWIDTH = 20;
+ private static final int BOXHEIGHT = 5;
+ private ArrayList moduleList;
+
+ public ModuleList() {
+ this.moduleList = new ArrayList<>();
+ }
+
+ //Getters
+ public ArrayList getModuleList() {
+ return moduleList;
+ }
+
+ //Setters
+ public void setModuleList(ArrayList moduleList) {
+ this.moduleList = moduleList;
+ }
+
+ public void add(Module module) {
+ this.moduleList.add(module);
+ }
+
+ /**
+ * Overload add method to take in String input.
+ *
+ * @param input User input for Module name.
+ */
+ public void add(String input) throws NoCapExceptions {
+ Module module = new Module(input);
+ this.moduleList.add(module);
+ logger.log(Level.INFO, "Module added successfully");
+ }
+
+ public void delete(Module module) {
+ moduleList.remove(module);
+ }
+
+ /**
+ * Overload delete method to take in String input.
+ * input index validity is checked.
+ *
+ * @param input User input for Module name.
+ */
+ public void delete(String input) {
+ int moduleIndex = Integer.parseInt(input) - 1;
+ if (moduleIndex < 0 || moduleIndex >= moduleList.size()) {
+ throw new ArrayIndexOutOfBoundsException("Invalid number value");
+ }
+ Ui.deleteModuleMessage(moduleList.get(moduleIndex));
+ moduleList.remove(get(moduleIndex));
+ logger.log(Level.INFO, "Module deleted successfully");
+ }
+
+ public int size() {
+ return this.moduleList.size();
+ }
+
+ public Module get(int index) {
+ assert index >= 0;
+ return this.moduleList.get(index);
+ }
+
+ /**
+ * Method to find Module objects by their description.
+ * Iterates through every Module object in moduleList and returns Module with a matching description.
+ *
+ * @param input User input Module description.
+ * @return Module object.
+ */
+ public Module find(String input) {
+ String moduleName = input.trim();
+ int index = -1;
+ for (int i = 0; i < moduleList.size(); i++) {
+ if (Objects.equals(moduleList.get(i).moduleName, moduleName)) {
+ index = i;
+ }
+ }
+ if (index == -1) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ logger.log(Level.INFO, "Module found successfully");
+ return moduleList.get(index);
+ }
+
+ /**
+ * Method to print all existing Modules and their details in moduleList.
+ */
+ public void printModules() {
+ for (int i = 0; i < moduleList.size(); i++) {
+ System.out.println(i + 1);
+ System.out.println(moduleList.get(i));
+ }
+ }
+
+ private String formatTimeString(int time) {
+ String timeString;
+ assert (time < 24 && time >= 0);
+ if (time < 10) {
+ timeString = "0" + time + "00";
+ } else {
+ timeString = time + "00";
+ }
+ assert timeString.length() == 4;
+ return timeString;
+ }
+
+ private int moduleAtTime(String timeString, int r) {
+ int moduleIndex = -1;
+ String day;
+ if (r <= 6) {
+ day = "MON";
+ } else if (r <= 11) {
+ day = "TUE";
+ } else if (r <= 16) {
+ day = "WED";
+ } else if (r <= 21) {
+ day = "THU";
+ } else if (r <= 26) {
+ day = "FRI";
+ } else {
+ day = "SAT";
+ }
+ for (int i = 0; i < moduleList.size(); i++) {
+ for (int j = 0; j < moduleList.get(i).size(); j++) {
+ if (Objects.equals(moduleList.get(i).get(j).getDay(), day)
+ && Objects.equals(moduleList.get(i).get(j).getStartTime(), timeString)) {
+ moduleIndex = i;
+ break;
+ }
+ }
+ }
+ return moduleIndex;
+ }
+
+ private int classAtTime(String timeString, int r) {
+ int classIndex = 0;
+ String day;
+ day = dayOfRow(r);
+ for (Module module : moduleList) {
+ for (int j = 0; j < module.size(); j++) {
+ if (Objects.equals(module.get(j).getDay(), day)
+ && Objects.equals(module.get(j).getStartTime(), timeString)) {
+ classIndex = j;
+ break;
+ }
+ }
+ }
+ return classIndex;
+ }
+
+ private String dayOfRow(int r) {
+ String day;
+ if (r >= 3 && r <= 6) {
+ day = "MON";
+ } else if (r >= 8 && r <= 11) {
+ day = "TUE";
+ } else if (r >= 13 && r <= 16) {
+ day = "WED";
+ } else if (r >= 18 && r <= 21) {
+ day = "THU";
+ } else if (r >= 23 && r <= 26) {
+ day = "FRI";
+ } else {
+ day = "SAT";
+ }
+ return day;
+ }
+
+ /**
+ * Method to print timetable containing classes for every Module.
+ * Iterates through each Module and their Schedule to check for existing classes in a timeslot.
+ * Prints out class details in the allocated time slot if a class exists in the slot.
+ */
+ public void printTimeTable() {
+ for (int r = 0; r < HEIGHT; r++) {
+ for (int c = 0; c < LENGTH; c++) {
+ if ((c + 12) % BOXWIDTH == 0) {
+ int time = ((c + 12) / BOXWIDTH) + 7;
+ String timeString = formatTimeString(time);
+ c = printTimeColumns(r, c, timeString);
+ } else if ((r + 2) % BOXHEIGHT == 0 && c == 2) { //print day
+ c = printRowDay(r, c);
+ } else if (c == LENGTH - 1) {
+ System.out.println("#");
+ } else if ((r - 2) % BOXHEIGHT == 0 || r == 0 || r == HEIGHT - 1 || (c - 6) % BOXWIDTH == 0 || c == 0) {
+ System.out.print("#");
+ } else {
+ System.out.print(" ");
+ }
+ }
+ }
+ logger.log(Level.INFO, "Timetable printed successfully");
+ }
+
+ private int printTimeColumns(int r, int c, String timeString) {
+ if (r == 1) { // print time header
+ System.out.print(timeString);
+ c += 4;
+ } else if ((r + 2) % BOXHEIGHT == 0) { //print module name if class exists in timeslot
+ c = printSlotModuleName(r, c, timeString);
+ } else if ((r + 1) % BOXHEIGHT == 0) { //print class location
+ c = printSlotClassLocation(r, c, timeString);
+ } else if (r > 0 && r % BOXHEIGHT == 0) { //print class comment
+ c = printSlotClassComment(r, c, timeString);
+ }
+ return c;
+ }
+
+ private int printRowDay(int r, int c) {
+ int day = (r + 2) / BOXHEIGHT;
+ switch (day) {
+ case 1:
+ System.out.print("MON");
+ break;
+ case 2:
+ System.out.print("TUE");
+ break;
+ case 3:
+ System.out.print("WED");
+ break;
+ case 4:
+ System.out.print("THU");
+ break;
+ case 5:
+ System.out.print("FRI");
+ break;
+ case 6:
+ System.out.print("SAT");
+ break;
+ default:
+ System.out.print("INVALID");
+ }
+ c += 2;
+ return c;
+ }
+
+ private int printSlotClassComment(int r, int c, String timeString) {
+ int moduleIndex = moduleAtTime(timeString, r);
+ int classIndex = classAtTime(timeString, r);
+ if (moduleIndex >= 0) {
+ System.out.print(moduleList.get(moduleIndex).get(classIndex).getComment());
+ c += moduleList.get(moduleIndex).get(classIndex).getComment().length();
+ }
+ return c;
+ }
+
+ private int printSlotClassLocation(int r, int c, String timeString) {
+ int moduleIndex = moduleAtTime(timeString, r);
+ int classIndex = classAtTime(timeString, r);
+ if (moduleIndex >= 0) {
+ System.out.print(moduleList.get(moduleIndex).get(classIndex).getLocation());
+ c += moduleList.get(moduleIndex).get(classIndex).getLocation().length();
+ }
+ return c;
+ }
+
+ private int printSlotModuleName(int r, int c, String timeString) {
+ int moduleIndex = moduleAtTime(timeString, r);
+ if (moduleIndex >= 0) {
+ System.out.print(moduleList.get(moduleIndex).moduleName);
+ c += moduleList.get(moduleIndex).moduleName.length();
+ }
+ return c;
+ }
+
+ /**
+ * Overrides toString() to show a formatted ModuelList when printed.
+ *
+ * @return String of formatted ModuleList
+ */
+ @Override
+ public String toString() {
+ StringBuilder output = new StringBuilder();
+ for (int i = 0; i < moduleList.size(); i++) {
+ output.append(i + 1).append(System.lineSeparator())
+ .append(moduleList.get(i)).append(System.lineSeparator());
+ }
+ return output.toString();
+ }
+
+}
diff --git a/src/main/java/schedule/Schedule.java b/src/main/java/schedule/Schedule.java
new file mode 100644
index 0000000000..87eea05f9f
--- /dev/null
+++ b/src/main/java/schedule/Schedule.java
@@ -0,0 +1,103 @@
+package schedule;
+
+import exceptions.NoCapExceptions;
+
+import java.time.Duration;
+import java.time.LocalTime;
+import java.util.Objects;
+
+public class Schedule {
+ protected String startTime;
+ protected String location;
+ protected String day;
+ protected String comment;
+
+ /**
+ * Constructor for Schdeule
+ * During construction of Schedule, input format is checked.
+ *
+ * @param day Day of class
+ * @param startTime timeslot of class
+ * @param location location of the class
+ * @param comment additional comments for the class
+ * @throws NoCapExceptions Throws an exception when a input format error is triggered.
+ */
+
+ public Schedule(String day, String startTime, String location, String comment) throws NoCapExceptions {
+ if (location.length() > 16 || comment.length() > 16) {
+ throw new NoCapExceptions("location and comment must be less than 17 characters");
+ }
+ if (!isCorrectDayFormat(day)) {
+ throw new NoCapExceptions("Wrong day format. Please key in mon/tue/wed/thu/fri/sat in either LOWERCASE or "
+ + "UPPERCASE");
+ }
+ if (!isCorrectTimeFormat(startTime)) {
+ throw new NoCapExceptions("Wrong time format. Please key in 1 hour blocks of time (eg. 1000/1300)");
+ }
+ this.day = day;
+ this.startTime = startTime;
+ this.location = location;
+ this.comment = comment;
+ }
+
+ private boolean isCorrectTimeFormat(String time) {
+ return time.length() == 4 && time.substring(2).equals("00") && Integer.parseInt(time.substring(0, 2)) < 24;
+ }
+
+ private boolean isCorrectDayFormat(String day) {
+ return Objects.equals(day, "MON") || Objects.equals(day, "TUE") || Objects.equals(day, "WED")
+ || Objects.equals(day, "THU") || Objects.equals(day, "FRI") || Objects.equals(day, "SAT");
+ }
+
+ /**
+ * For deserialization from JSON file.
+ */
+ public Schedule() {
+ }
+
+ //Getters
+ public String getLocation() {
+ return this.location;
+ }
+
+ public String getComment() {
+ return this.comment;
+ }
+
+ public String getDay() {
+ return day;
+ }
+
+ public String getStartTime() {
+ return startTime;
+ }
+
+ //Setters
+ public void setStartTime(String startTime) {
+ this.startTime = startTime;
+ }
+
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ public void setDay(String day) {
+ this.day = day;
+ }
+
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ /**
+ * Reformats Schedule for simpler viewing.
+ */
+ @Override
+
+ public String toString() {
+ return "Day: " + day
+ + "\nStart Time: " + startTime
+ + "\nLocation: " + location
+ + "\nComments: " + comment;
+ }
+}
diff --git a/src/main/java/schedule/ScheduleList.java b/src/main/java/schedule/ScheduleList.java
new file mode 100644
index 0000000000..b814e3a687
--- /dev/null
+++ b/src/main/java/schedule/ScheduleList.java
@@ -0,0 +1,125 @@
+package schedule;
+
+import command.NoCap;
+import command.Ui;
+import exceptions.NoCapExceptions;
+import module.Module;
+
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class ScheduleList {
+ protected ArrayList scheduleList;
+ private static final Logger logger = command.Logger.myLogger();
+
+ public ScheduleList() {
+ this.scheduleList = new ArrayList();
+ }
+
+ public void addClass(Schedule schedule) {
+ this.scheduleList.add(schedule);
+ }
+
+ /**
+ * Overload addClass method to take in String input.
+ * Method to add new Class object to scheduleList.
+ *
+ * @param input User input class details.
+ * @throws NoCapExceptions Exception thrown when class already exists in timeslot or wrong formatting of input.
+ */
+ public void addClass(String input) throws NoCapExceptions {
+ String[] scheduleInfo = input.split("/");
+ if (!checkInput(scheduleInfo)) {
+ throw new NoCapExceptions("Please key in 4 variables for class details");
+ }
+ String day = scheduleInfo[0].toUpperCase(Locale.ROOT);
+ String time = scheduleInfo[1];
+ String location = scheduleInfo[2];
+ String comment = scheduleInfo[3];
+ if (isSlotFilled(day, time)) {
+ throw new NoCapExceptions("A class already exists in this timeslot!");
+ }
+ Schedule schedule = new Schedule(day, time, location, comment);
+ this.scheduleList.add(schedule);
+ logger.log(Level.INFO, "Schedule added successfully");
+ }
+
+ /**
+ * Method to remove a single schedule from schedulelist based on index.
+ * input index validity is checked.
+ *
+ * @param input index of class to be deleted.
+ * @throws ArrayIndexOutOfBoundsException Exception thrown when invalid index is given.
+ */
+ public void deleteClass(String input) {
+ int scheduleIndex = Integer.parseInt(input) - 1;
+ if (scheduleIndex < 0 || scheduleIndex >= scheduleList.size()) {
+ throw new ArrayIndexOutOfBoundsException("Invalid number value");
+ }
+ Ui.deleteScheduleMessage(scheduleList.get(scheduleIndex));
+ scheduleList.remove(get(scheduleIndex));
+ logger.log(Level.INFO, "Schedule deleted successfully");
+ }
+
+ public Schedule get(int index) {
+ assert index >= 0;
+ return this.scheduleList.get(index);
+ }
+
+ private boolean checkInput(String[] input) {
+ return input.length == 4 && !input[2].isBlank();
+ }
+
+ public int size() {
+ return this.scheduleList.size();
+ }
+
+ public Schedule getSchedule(int index) {
+ return this.scheduleList.get(index);
+ }
+
+ public ArrayList getScheduleList() {
+ return this.scheduleList;
+ }
+
+ /**
+ * Checks for whether a class already exists with the input day and time.
+ * Iterates through each module object in the moduleList and each schedule within the scheduleList of each module.
+ *
+ * @param day User input.
+ * @param time User input.
+ * @return true if a class with matching day and time is found.
+ */
+ private boolean isSlotFilled(String day, String time) {
+ for (Module m : NoCap.moduleList.getModuleList()) {
+ for (Schedule s : m.getScheduleList().scheduleList) {
+ if (Objects.equals(s.getDay(), day) && Objects.equals(s.getStartTime(), time)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Overrides toString() to show a formatted ScheduleList when printed.
+ *
+ * @return String of formatted ScheduleList
+ */
+ @Override
+ public String toString() {
+ int index = 1;
+ String schedulePrint = "";
+ for (Schedule s : scheduleList) {
+ if (s != null) {
+ schedulePrint = schedulePrint + String.valueOf(index) + ".\n";
+ schedulePrint = schedulePrint + s.toString() + "\n";
+ index++;
+ }
+ }
+ return schedulePrint;
+ }
+}
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/semester/Semester.java b/src/main/java/semester/Semester.java
new file mode 100644
index 0000000000..baa6cef0a8
--- /dev/null
+++ b/src/main/java/semester/Semester.java
@@ -0,0 +1,101 @@
+package semester;
+
+import command.NoCap;
+import exceptions.NoCapExceptions;
+import module.ModuleList;
+import module.Module;
+
+
+public class Semester {
+ protected int credits;
+ protected double points;
+ protected double cap;
+ protected String semester;
+ public ModuleList moduleList;
+
+ public Semester(String semester) {
+ this.semester = semester;
+ this.credits = 0;
+ this.points = 0;
+ this.cap = 0;
+ this.moduleList = new ModuleList();
+ }
+
+ /**
+ * For deserialization from JSON file.
+ */
+ public Semester() {
+ }
+
+ /**
+ * Method to update number of credits taken in the Semester.
+ * Iterates through every Module object in ModuleList and adds up each module's credits.
+ */
+ protected void updateCredits() {
+ int c = 0;
+ for (Module module : moduleList.getModuleList()) {
+ if (module.getCredits() > 0) {
+ c += module.getCredits();
+ }
+ }
+ credits = c;
+ }
+
+ /**
+ * Method to update the weighted points earned in the Semester.
+ * Iterates through every Module Object in ModuleList to return their grade points.
+ * Multiplies grade points with the number of credits to get weighted points earned.
+ */
+ protected void updatePoints() {
+ double p = 0;
+ for (Module module : moduleList.getModuleList()) {
+ if (module.getCredits() > 0) {
+ p += module.getCredits() * module.getPoints();
+ }
+ }
+ points = p;
+ }
+
+ public void updateCap() throws NoCapExceptions {
+ updateCredits();
+ updatePoints();
+ if (credits == 0) {
+ throw new NoCapExceptions("Unable to calculate cap as no credit assigned to any existing module");
+ }
+ cap = points / credits;
+ }
+
+ public int getCredits() {
+ return credits;
+ }
+
+ public double getPoints() {
+ return points;
+ }
+
+ public double getCap() {
+ return cap;
+ }
+
+ public ModuleList getModuleList() {
+ return this.moduleList;
+ }
+
+ public String getSemester() {
+ return this.semester;
+ }
+
+ public void printCap() throws NoCapExceptions {
+ updateCap();
+ System.out.println("This semester's CAP: " + (String)String.format("%.2f", getCap()));
+ }
+
+ @Override
+ public String toString() {
+ return "Credits: " + this.credits + System.lineSeparator()
+ + "Points: " + this.points + System.lineSeparator()
+ + "CAP: " + (this.cap) + System.lineSeparator()
+ + "Semester: " + this.semester + System.lineSeparator()
+ + "Modules: " + this.moduleList + System.lineSeparator();
+ }
+}
diff --git a/src/main/java/semester/SemesterList.java b/src/main/java/semester/SemesterList.java
new file mode 100644
index 0000000000..e4d109e06f
--- /dev/null
+++ b/src/main/java/semester/SemesterList.java
@@ -0,0 +1,174 @@
+package semester;
+
+import exceptions.NoCapExceptions;
+
+import java.util.ArrayList;
+
+public class SemesterList {
+ protected int credits;
+ protected double points;
+ protected double cap;
+ protected int accessedSemesterIndex;
+ private ArrayList semesterList;
+
+ public SemesterList() {
+ this.accessedSemesterIndex = 0;
+ this.credits = 0;
+ this.points = 0;
+ this.cap = 0;
+ this.semesterList = new ArrayList<>();
+ setUp();
+ }
+
+ //Getters fo Json serialization and deserialization
+ public int getCredits() {
+ return credits;
+ }
+
+ public double getPoints() {
+ return points;
+ }
+
+ public ArrayList getSemesterList() {
+ return semesterList;
+ }
+
+ /**
+ * Method to add 10 Semesters to semesterList by default in the constructor.
+ */
+ private void setUp() {
+ for (int i = 0; i < 10; i++) {
+ int year = (i / 2) + 1;
+ int sem = (i % 2) + 1;
+ String semesterDescription = "Y" + year + "S" + sem;
+ add(semesterDescription);
+ }
+ }
+
+ private void add(Semester semester) {
+ this.semesterList.add(semester);
+ }
+
+ /**
+ * Method to add a Semester to semesterList.
+ *
+ * @param semesterDescription User input description of the semester.
+ */
+ public void add(String semesterDescription) {
+ Semester semester = new Semester(semesterDescription);
+ this.semesterList.add(semester);
+ }
+
+ /**
+ * Method to print existing semesters in the semesterList.
+ */
+ public void printSemesters() {
+ int i = 1;
+ for (Semester semester : semesterList) {
+ System.out.println(i + " : " + semester.getSemester());
+ i++;
+ }
+ }
+
+ /**
+ * Method to return the Semester with the corresponding accessedSemesterIndex.
+ *
+ * @return Semester object.
+ */
+ public Semester extractAccessedSemester() {
+ return this.semesterList.get(getAccessedSemesterIndex());
+ }
+
+ private void updateCredits() {
+ int c = 0;
+ for (Semester semester : semesterList) {
+ semester.updateCredits();
+ c += semester.getCredits();
+ }
+ credits = c;
+ }
+
+ private void updatePoints() {
+ double p = 0;
+ for (Semester semester : semesterList) {
+ semester.updatePoints();
+ p += semester.getPoints();
+ }
+ points = p;
+ }
+
+ /**
+ * Method to update the aggregate CAP of all Semesters in semesterList.
+ */
+ public void updateCap() throws NoCapExceptions {
+ updateCredits();
+ updatePoints();
+ if (credits == 0) {
+ throw new NoCapExceptions("Unable to calculate cap as no credit assigned to any existing module");
+ }
+ cap = points / credits;
+ }
+
+ /**
+ * Method to set accessedSemesterIndex.
+ *
+ * @param i User input index.
+ */
+ public void setAccessedSemesterIndex(int i) {
+ if (i < 0 || i > 9) {
+ throw new ArrayIndexOutOfBoundsException("Please key in a number from 1-10");
+ }
+ this.accessedSemesterIndex = i;
+ }
+
+ public double getCap() {
+ return cap;
+ }
+
+ public int getAccessedSemesterIndex() {
+ return accessedSemesterIndex;
+ }
+
+ /**
+ * Method to get the Semester with the corresponding index in semesterList.
+ *
+ * @param index User input.
+ * @return Semester object.
+ */
+ public Semester get(int index) {
+ if (index < 0 || index > 9) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ return this.semesterList.get(index);
+ }
+
+ /**
+ * Method to print aggregated CAP and CAP of each Semester.
+ */
+ public void printAllCap() throws NoCapExceptions {
+ updateCap();
+ System.out.println("Cumulative CAP: " + (String)String.format("%.2f", getCap()));
+ for (Semester semester : semesterList) {
+ if (semester.getCredits() > 0) {
+ System.out.println(semester.getSemester() + ": " + (String)String.format("%.2f", semester.getCap()));
+ } else {
+ System.out.println(semester.getSemester() + ": 0.00");
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder string = new StringBuilder();
+ string.append("Cumulative Credits: ").append(this.credits).append(System.lineSeparator())
+ .append("Points: ").append(this.points).append(System.lineSeparator())
+ .append("CAP: ").append(this.cap).append(System.lineSeparator())
+ .append("Semesters: ").append(System.lineSeparator());
+ int semesterCount = 1;
+ for (Semester semester : semesterList) {
+ string.append(semesterCount).append(". ").append(System.lineSeparator())
+ .append(semester).append(System.lineSeparator());
+ }
+ return string.toString();
+ }
+}
diff --git a/src/main/java/task/GradableTask.java b/src/main/java/task/GradableTask.java
new file mode 100644
index 0000000000..7fa3309eac
--- /dev/null
+++ b/src/main/java/task/GradableTask.java
@@ -0,0 +1,44 @@
+package task;
+
+import exceptions.NoCapExceptions;
+
+import java.time.DateTimeException;
+
+public class GradableTask extends Task {
+ protected int weightage;
+
+ /**
+ * Constructor for GradableTask, which inherits from Task.
+ *
+ * @param description Description of GradableTask
+ * @param date Deadline of GradableTask
+ * @param weightage Weightage of GradableTask
+ */
+ public GradableTask(String description, String date, int weightage) {
+ super(description, date);
+ this.weightage = weightage;
+ }
+
+ /**
+ * For deserialization from JSON file.
+ */
+ public GradableTask() {
+ }
+
+ public int getWeightage() {
+ return this.weightage;
+ }
+
+ public boolean isDone() {
+ return isDone;
+ }
+
+ /**
+ * Reformats GradableTask for easier viewing.
+ */
+ @Override
+ public String toString() {
+ return getDescription() + " by: " + createFormattedDeadline() + " Weightage "
+ + this.weightage + "% " + createStatusIcon();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/task/GradableTaskList.java b/src/main/java/task/GradableTaskList.java
new file mode 100644
index 0000000000..7bb7c06453
--- /dev/null
+++ b/src/main/java/task/GradableTaskList.java
@@ -0,0 +1,165 @@
+package task;
+
+
+import command.NoCap;
+import command.VisualiseGradable;
+import command.parser.DateParser;
+import command.parser.Parser;
+import command.Ui;
+import exceptions.NoCapExceptions;
+import command.Ui;
+import command.parser.ParserChecks;
+
+
+import java.net.StandardSocketOptions;
+import java.time.DateTimeException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class GradableTaskList extends TaskList {
+ public static final int MAXWEIGHTAGE = 100;
+ public static final int MINWEIGHTAGE = 5;
+
+ protected ArrayList gradableTaskList;
+ private static final Logger logger = command.Logger.myLogger();
+
+ public GradableTaskList() {
+ this.gradableTaskList = new ArrayList<>();
+ }
+
+ public ArrayList getGradableTaskList() {
+ return gradableTaskList;
+ }
+
+ protected static String getDate(String description) {
+ try {
+ int datePos = description.indexOf(ParserChecks.START_OF_DATE) + LENGTH_OF_MARKER;
+ int weightagePos = description.indexOf(ParserChecks.START_OF_WEIGHTAGE);
+ return description.substring(datePos, weightagePos).trim();
+ } catch (StringIndexOutOfBoundsException e) {
+ return "";
+ }
+ }
+
+ private static int getWeightage(String description) {
+ try {
+ int weightagePos = description.indexOf(ParserChecks.START_OF_WEIGHTAGE) + 2;
+ int weightage = Integer.parseInt(description.substring(weightagePos).trim());
+ if (weightage < MINWEIGHTAGE || weightage > MAXWEIGHTAGE) {
+ throw new NoCapExceptions("wrong weightage");
+ }
+ return weightage;
+ } catch (NumberFormatException | NoCapExceptions e) {
+ Ui.wrongWeightage();
+ return 0;
+ }
+ }
+
+ private boolean checkTotalWeightage(int w) {
+ int total = 0;
+ for (GradableTask g : gradableTaskList) {
+ total += g.getWeightage();
+ }
+ total += w;
+
+ return total <= MAXWEIGHTAGE;
+ }
+
+ private boolean inputChecks(int weightage, String date, String description) {
+ if (weightage == 0) {
+ return false;
+ }
+ if (!checkTotalWeightage(weightage)) {
+ Ui.wrongWeightageSplits();
+ return false;
+ } else if (date.isBlank()) {
+ Ui.missingDate();
+ return false;
+ } else if (description.isBlank()) {
+ Ui.missingDescription();
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Method to add a gradableTask to GradableTaskList.
+ * Input format is checked before calling the constructor for GradableTask.
+ * Total weightage for all GradableTasks in GradableTaskList is checked. If total weightage is more than 100
+ * an error message is shown.
+ *
+ * @param module Name of module GradableTask is apart of
+ * @param userInput Input of GradableTask data from User.
+ */
+ public void addGradableTask(String module, String userInput) {
+ logger.log(Level.INFO, "Successfully added task");
+ String date = getDate(userInput);
+ int weightage = getWeightage(userInput);
+ String description = removeDate(userInput);
+ if (inputChecks(weightage, date, description)) {
+ try {
+ DateParser.parseDate(date);
+ GradableTask newGradableTask = new GradableTask(description, date, weightage);
+ this.gradableTaskList.add(taskCount, newGradableTask);
+ this.taskCount = gradableTaskList.size();
+ Ui.addTaskMessage(newGradableTask, module);
+ } catch (DateTimeException e) {
+ Ui.wrongDateTimeFormat();
+ }
+ }
+ }
+
+ public GradableTask getGradableTask(int index) {
+ logger.log(Level.INFO, "Get GradableTask");
+ return this.gradableTaskList.get(index);
+ }
+
+ public int size() {
+ logger.log(Level.INFO, "Get size of task list");
+ return this.gradableTaskList.size();
+ }
+
+ public GradableTask getGradableTaskFromIndex(String input) {
+ int index;
+ GradableTask g = null;
+ try {
+ index = Integer.parseInt(input) - 1;
+ if (isValidIndex(index)) {
+ g = gradableTaskList.get(index);
+ }
+ } catch (IndexOutOfBoundsException | NumberFormatException e) {
+ Ui.printInvalidIndex();
+ }
+ return g;
+ }
+
+ boolean isValidIndex(int index) {
+ if (index < 0) {
+ Ui.printInvalidIndex();
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Overrides toString() to show a formatted GradableTaskList when printed.
+ *
+ * @return String of formatted GradableTaskList
+ */
+ @Override
+ public String toString() {
+ int index = 1;
+ String gradableTaskPrint = "";
+ for (GradableTask g : gradableTaskList) {
+ if (g != null) {
+ gradableTaskPrint = gradableTaskPrint + String.valueOf(index) + " ";
+ gradableTaskPrint = gradableTaskPrint + g.toString() + "\r\n";
+ index++;
+ }
+ }
+ return gradableTaskPrint;
+ }
+
+}
diff --git a/src/main/java/task/OverallTask.java b/src/main/java/task/OverallTask.java
new file mode 100644
index 0000000000..9324fddede
--- /dev/null
+++ b/src/main/java/task/OverallTask.java
@@ -0,0 +1,95 @@
+package task;
+
+import java.util.Comparator;
+
+/**
+ * Task class used for listing out all available tasks in a semester.
+ * Inherits from task class but adds a moduleName, weightage and gradable attribute.
+ */
+public class OverallTask extends Task {
+ //Constants for comparators
+ private static final int RIGHT_HEAVY = -1;
+ private static final int LEFT_HEAVY = 1;
+ private static final int EQUAL = 0;
+
+ private final String moduleName;
+ private final boolean isGradable;
+ private int weightage;
+
+ /**
+ * Constructor for overall task which takes in a Task object.
+ *
+ * @param task Task object used to construct the OverallTask object
+ * @param moduleName Name of the module of the task
+ */
+ public OverallTask(Task task, String moduleName) {
+ super(task.description, task.date);
+ this.isDone = task.isDone;
+ this.moduleName = moduleName;
+ this.isGradable = false;
+ this.isLate = task.isLate;
+ }
+
+ /**
+ * Constructor for overall task which takes in a GradableTask object.
+ *
+ * @param task GradableTask object used to construct the OverallTask object
+ * @param moduleName Name of the module of the task
+ */
+ public OverallTask(GradableTask task, String moduleName) {
+ super(task.description, task.date);
+ this.isDone = task.isDone;
+ this.moduleName = moduleName;
+ this.isGradable = true;
+ this.weightage = task.weightage;
+ this.isLate = task.isLate;
+ }
+
+ public boolean isGradable() {
+ return isGradable;
+ }
+
+ /**
+ * Comparator used to compare deadlines between OverallTask objects.
+ * An overall task that has an earlier deadline is considered less than an overall task with a
+ * later deadline, and vice versa. If both tasks have the same deadline, they are
+ * considered equal.
+ */
+ public static Comparator dateComparator = Comparator.comparing(t -> t.deadline);
+
+
+ /**
+ * Comparator used to compare "done" status between OverallTask objects.
+ * If task1 is done while task2 is not, task1 is greater than task2 and vice versa.
+ * If both task1 and task2 have the same status, they are considered equal.
+ */
+ public static Comparator statusComparator = (task1, task2) -> {
+ if (task1.isDone && !task2.isDone) {
+ return LEFT_HEAVY;
+ } else if (!task1.isDone && task2.isDone) {
+ return RIGHT_HEAVY;
+ } else {
+ return EQUAL;
+ }
+ };
+
+ private String getGradableString() {
+ return isGradable ? "[G]" : "[ ]";
+ }
+
+ private String getWeightageString() {
+ return isGradable ? "[Weightage: " + this.weightage + "%]" : "";
+ }
+
+ private String getModuleNameString() {
+ return "[" + moduleName + "]";
+ }
+
+ @Override
+ public String toString() {
+ return getModuleNameString() + getGradableString() + super.toString() + " " + getWeightageString();
+ }
+
+
+
+}
diff --git a/src/main/java/task/OverallTaskList.java b/src/main/java/task/OverallTaskList.java
new file mode 100644
index 0000000000..4b8c470746
--- /dev/null
+++ b/src/main/java/task/OverallTaskList.java
@@ -0,0 +1,164 @@
+package task;
+
+import command.Ui;
+import module.Module;
+import module.ModuleList;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+/**
+ * Represents a list of OverallTask objects. Used for listing all tasks in a semester.
+ */
+public class OverallTaskList extends TaskList {
+ private static final Logger logger = command.Logger.myLogger();
+
+ protected ArrayList overallTaskList;
+
+ /**
+ * Constructor which takes in a ModuleList object.
+ * Adds in all tasks in the ModuleList object into OverallTaskList.
+ *
+ * @param moduleList The ModuleList object containing the tasks to be added.
+ */
+ public OverallTaskList(ModuleList moduleList) {
+ this.overallTaskList = new ArrayList<>();
+ addAllModuleListTasks(moduleList);
+ }
+
+ private void addAllModuleListTasks(ModuleList moduleList) {
+ for (Module module : moduleList.getModuleList()) {
+ String moduleName = module.getModuleName();
+ addAllNormalTasks(module, moduleName);
+ addAllGradableTasks(module, moduleName);
+ }
+ logger.log(Level.INFO, "Add all tasks from module list to overall task list");
+ }
+
+ private void addAllGradableTasks(Module module, String moduleName) {
+ for (GradableTask gradableTask : module.getGradableTaskList().gradableTaskList) {
+ gradableTask.updateOverdue();
+ overallTaskList.add(new OverallTask(gradableTask, moduleName));
+ }
+ }
+
+ private void addAllNormalTasks(Module module, String moduleName) {
+ for (Task task : module.getTaskList().taskList) {
+ task.updateOverdue();
+ overallTaskList.add(new OverallTask(task, moduleName));
+ }
+ }
+
+ /**
+ * Sorts all tasks by deadline in the task list and prints it out to output.
+ * The method lists tasks in ascending order of deadlines.
+ */
+ public void sortByDateAndPrint() {
+ List newTaskList = overallTaskList
+ .stream()
+ .sorted(OverallTask.dateComparator)
+ .collect(Collectors.toList());
+ Ui.printOverallListOrderedByDate(newTaskList);
+ logger.log(Level.INFO, "Sort overall task by date");
+ }
+
+ /**
+ * Sorts all tasks in task list by status and prints it out to the output.
+ * The method lists unfinished tasks first, then followed by tasks that have been done.
+ */
+ public void sortByStatusAndPrint() {
+ List newTaskList = overallTaskList
+ .stream()
+ .sorted(OverallTask.statusComparator)
+ .collect(Collectors.toList());
+ Ui.printOverallListOrderedByStatus(newTaskList);
+ logger.log(Level.INFO, "Sort overall task by status");
+ }
+
+ /**
+ * Prints all tasks due within the next week.Uses isWeekly() method
+ * inherited from TaskList.
+ */
+ public void printWeeklyTasks() {
+ List newTaskList = overallTaskList
+ .stream()
+ .filter(this::isWeekly)
+ .collect(Collectors.toList());
+ Ui.printOverallWeeklyTasks(newTaskList);
+ logger.log(Level.INFO, "print overall weekly tasks");
+ }
+
+ /**
+ * Prints all tasks due within the month. Uses isMonthly() method
+ * inherited from TaskList.
+ */
+ public void printMonthlyTasks() {
+ List newTaskList = overallTaskList
+ .stream()
+ .filter(this::isMonthly)
+ .collect(Collectors.toList());
+ Ui.printOverallMonthlyTasks(newTaskList);
+ logger.log(Level.INFO, "print overall monthly tasks");
+ }
+
+ /**
+ * Prints all tasks due within a year. Uses isYearly() method
+ * inherited from TaskList.
+ */
+ public void printYearlyTasks() {
+ List newTaskList = overallTaskList
+ .stream()
+ .filter(this::isYearly)
+ .collect(Collectors.toList());
+ Ui.printOverallYearlyTasks(newTaskList);
+ logger.log(Level.INFO, "print overall yearly tasks");
+ }
+
+ /**
+ * Prints all gradable tasks in the task list.
+ */
+ public void printGradableTasks() {
+ List newTaskList = overallTaskList
+ .stream()
+ .filter(OverallTask::isGradable)
+ .collect(Collectors.toList());
+ Ui.printGradableTasks(newTaskList);
+ logger.log(Level.INFO, "print gradable tasks");
+ }
+
+ /**
+ * Prints all non-gradable tasks in the task list.
+ */
+ public void printNormalTasks() {
+ List newTaskList = overallTaskList
+ .stream()
+ .filter(t -> (
+ !t.isGradable()
+ ))
+ .collect(Collectors.toList());
+ Ui.printNormalTasks(newTaskList);
+ logger.log(Level.INFO, "print normal tasks");
+ }
+
+ /**
+ * Prints all the tasks in the task list without any sorting.
+ */
+ public void printAllTasks() {
+ Ui.printAllOverallTasks(overallTaskList);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder out = new StringBuilder();
+ for (OverallTask task : overallTaskList) {
+ out.append(task.toString()).append(System.lineSeparator());
+ }
+ return out.toString();
+ }
+
+
+}
diff --git a/src/main/java/task/Priority.java b/src/main/java/task/Priority.java
new file mode 100644
index 0000000000..d28f7e31d5
--- /dev/null
+++ b/src/main/java/task/Priority.java
@@ -0,0 +1,7 @@
+package task;
+
+public enum Priority {
+ EQUAL,
+ LOW,
+ HIGH
+}
diff --git a/src/main/java/task/Task.java b/src/main/java/task/Task.java
new file mode 100644
index 0000000000..795026b704
--- /dev/null
+++ b/src/main/java/task/Task.java
@@ -0,0 +1,143 @@
+package task;
+
+
+import command.parser.DateParser;
+import command.Ui;
+
+import java.time.DateTimeException;
+import java.time.LocalDateTime;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+public class Task {
+ private static final Logger logger = command.Logger.myLogger();
+ private static final String LATE_SYMBOL = "[LATE]";
+ private static final String DONE_SYMBOL = "[X]";
+ private static final String EMPTY_SYMBOL = "[ ]";
+ private static final String EMPTY_SPACE = " ";
+ protected String description;
+ protected boolean isDone;
+ protected boolean isLate;
+ protected String date;
+ protected LocalDateTime deadline;
+
+
+ /**
+ * temp message.
+ *
+ * @param description The description of the task given by the user
+ */
+ public Task(String description, String date) {
+ setDescription(description);
+ setDate(date);
+ setDone(false);
+ setLate(false);
+ }
+
+ /**
+ * For deserialization from JSON file.
+ */
+ public Task() {
+ }
+
+ //Getters
+ public String getDescription() {
+ return this.description;
+ }
+
+ public String getDate() {
+ return this.date;
+ }
+
+ public boolean isDone() {
+ return isDone;
+ }
+
+ /**
+ * Needed for serialization of data.
+ */
+ public LocalDateTime getDeadline() {
+ return deadline;
+ }
+
+ private void setDeadline(LocalDateTime deadline) {
+ this.deadline = deadline;
+ }
+
+ //Setters
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public void setDone(boolean done) {
+ isDone = done;
+ }
+
+ public void setDate(String date) {
+ this.date = date;
+ setDeadline(DateParser.parseDate(date));
+ }
+
+ public void parseDeadline(String date) throws DateTimeException {
+ try {
+ logger.log(Level.INFO, "Successfully set Task deadline...");
+ setDate(date);
+ updateOverdue();
+ Ui.printUpdateTaskDeadline(this);
+ } catch (DateTimeException e) {
+ Ui.wrongDateTimeFormat();
+ }
+ }
+
+ public String createFormattedDeadline() throws NullPointerException {
+ try {
+ return DateParser.dateStringOutput(this.deadline);
+ } catch (NullPointerException e) {
+ Ui.invalidDate();
+ }
+ return "";
+ }
+
+ public void markDone() {
+ logger.log(Level.INFO, "Successfully marked Task as done...");
+ this.isDone = true;
+ Ui.printMarkDoneMessage(this);
+ }
+
+ public void markNotDone() {
+ logger.log(Level.INFO, "Successfully marked Task as not done...");
+ this.isDone = false;
+ Ui.printMarkNotDoneMessage(this);
+ }
+
+ public void setLate(boolean isLate) {
+ this.isLate = isLate;
+ }
+
+ /**
+ * Checks if the task is overdue by checking the deadline of the task
+ * against the system clock. If it is overdue, update the task attribute.
+ */
+ public void updateOverdue() throws NullPointerException {
+ try {
+ LocalDateTime currentDateTime = LocalDateTime.now();
+ this.isLate = !(this.isDone) && currentDateTime.isAfter(this.deadline);
+ } catch (NullPointerException e) {
+ Ui.printInvalidIndex();
+ }
+ }
+
+ private String createLateIcon() {
+ return this.isLate ? LATE_SYMBOL : "";
+ }
+
+ public String createStatusIcon() {
+ return this.isDone ? DONE_SYMBOL : EMPTY_SYMBOL;
+ }
+
+ public String toString() {
+ return createLateIcon() + createStatusIcon() + EMPTY_SPACE + getDescription()
+ + " by: " + createFormattedDeadline();
+ }
+}
diff --git a/src/main/java/task/TaskList.java b/src/main/java/task/TaskList.java
new file mode 100644
index 0000000000..46ada175c2
--- /dev/null
+++ b/src/main/java/task/TaskList.java
@@ -0,0 +1,354 @@
+package task;
+
+import command.Ui;
+import command.parser.DateParser;
+import command.parser.ParserChecks;
+
+import java.time.DateTimeException;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.Period;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Locale;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class TaskList {
+ protected static final String EMPTY_STRING = "";
+ protected static final int DAYS_IN_A_WEEK = 7;
+ protected static final int DAYS_IN_A_MONTH = 31;
+ protected static final int DAYS_IN_A_YEAR = 366;
+ protected static final int LENGTH_OF_MARKER = 3;
+ private static final Logger logger = command.Logger.myLogger();
+ protected ArrayList taskList;
+ protected int taskCount;
+
+ public TaskList() {
+ this.taskList = new ArrayList<>();
+ }
+
+ //Getters
+ public int getTaskCount() {
+ logger.log(Level.INFO, "Get task count");
+ return taskCount;
+ }
+
+ public ArrayList getTaskList() {
+ logger.log(Level.INFO, "Get task list");
+ return taskList;
+ }
+
+ public int size() {
+ logger.log(Level.INFO, "Get size of task list");
+ return this.taskList.size();
+ }
+
+ //Setters
+ public void setTaskList(ArrayList taskList) {
+ logger.log(Level.INFO, "Set task list");
+ this.taskList = taskList;
+ }
+
+ public void setTaskCount(int taskCount) {
+ logger.log(Level.INFO, "Set task count");
+ this.taskCount = taskCount;
+ }
+
+ //assert index is valid
+ public Task get(int index) {
+ logger.log(Level.INFO, "Get task");
+ return this.taskList.get(index);
+ }
+
+ public void delete(Task task) {
+ logger.log(Level.INFO, "Successfully deleted task");
+ taskList.remove(task);
+ this.taskCount = taskList.size();
+ }
+
+ protected static String getDate(String description) {
+ try {
+ int datePos = description.indexOf(ParserChecks.START_OF_DATE) + LENGTH_OF_MARKER;
+ return description.substring(datePos).trim();
+ } catch (StringIndexOutOfBoundsException e) {
+ return EMPTY_STRING;
+ }
+ }
+
+ /**
+ * Trims the user input to get the description of the task.
+ * This is done by removing the date component from the input string.
+ *
+ * @param description the task description input by the user that also contains the deadline of the task.
+ * @return a string that contains only the task description.
+ */
+ protected static String removeDate(String description) {
+ try {
+ int datePos = description.indexOf(ParserChecks.START_OF_DATE);
+ return description.substring(0, datePos).trim();
+ } catch (StringIndexOutOfBoundsException e) {
+ return EMPTY_STRING;
+ }
+ }
+
+ /**
+ * Checks for the validity of the new task input and trim the input for the
+ * important components such as date and task description.
+ * If there is no error, then update the task list with the new task.
+ *
+ * @param module module that the task is for
+ * @param userInput user input that consist of the task description and deadline of the task
+ */
+ public void addTask(String module, String userInput) {
+ String date = getDate(userInput);
+ if (date.isBlank()) {
+ Ui.missingDate();
+ return;
+ }
+ String description = removeDate(userInput);
+ if (description.isBlank()) {
+ Ui.missingDescription();
+ return;
+ }
+ try {
+ LocalDateTime newTaskDeadline = DateParser.parseDate(date);
+ if (hasDuplicateDescription(description) && hasDuplicateDeadline(newTaskDeadline)) {
+ Ui.duplicateTaskError();
+ return;
+ }
+ if (hasDuplicateDescription(description)) {
+ updateTaskDeadline(date, description);
+ Ui.taskUpdateMessage();
+ return;
+ }
+ updateTaskList(module, date, description);
+ } catch (DateTimeException e) {
+ Ui.wrongDateTimeFormat();
+ }
+ }
+
+ /**
+ * Update the selected task deadline with a new user input deadline.
+ *
+ * @param date the deadline of the task
+ * @param description the description of the task
+ */
+ private void updateTaskDeadline(String date, String description) {
+ for (Task task : this.taskList) {
+ if (description.toLowerCase(Locale.ROOT).equals(task.getDescription().toLowerCase(Locale.ROOT))) {
+ this.taskList.remove(task);
+ Task newTask = new Task(description, date);
+ this.taskList.add(newTask);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Add new task to the task list.
+ *
+ * @param module the module that is currently accessed
+ * @param date the deadline that the user input for the task
+ * @param description the description of the task that the user added
+ */
+ private void updateTaskList(String module, String date, String description) {
+ Task newTask = new Task(description, date);
+ this.taskList.add(taskCount, newTask);
+ this.taskCount = taskList.size();
+ Ui.addTaskMessage(newTask, module);
+ logger.log(Level.INFO, "Successfully added task");
+ }
+
+ /**
+ * Checks if there is duplication in the task list.
+ * This is done by iterating through the task list.
+ *
+ * @param newTaskDescription the new task description input by user
+ * @return a boolean value, true if there is duplication found
+ */
+ public boolean hasDuplicateDescription(String newTaskDescription) {
+ for (Task task : this.taskList) {
+ String taskDescription = task.getDescription().toLowerCase(Locale.ROOT);
+ if (newTaskDescription.toLowerCase(Locale.ROOT).equals(taskDescription)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean hasDuplicateDeadline(LocalDateTime newTaskDate) {
+ for (Task task : this.taskList) {
+ LocalDateTime taskDate = task.getDeadline();
+ if (newTaskDate.equals(taskDate)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Custom comparator to determine if the task is of higher priority than the other based on custom condition
+ * such as task deadline. The task deadline is of LocalDateTime data type.
+ * A task with a more recent deadline will be 'greater' than a task with a later deadline.
+ * Therefore, tasks with deadline closer to the current system date will be sorted to
+ * the top of the list if this comparator is used for sorting.
+ */
+ protected static final Comparator sortByDate = Comparator.comparing(t -> t.deadline);
+
+ /**
+ * Custom comparator to determine if the task is of higher priority than the other based on custom condition
+ * such as completion status. If the task is completed, it will have a LOW priority.
+ * If a task is not completed, it will have a HIGH priority.
+ * Sorting a list of task using this comparator to sort will put the tasks that is not completed
+ * at the top of the list while the tasks that are done will be sorted to the bottom of the list.
+ */
+ protected static final Comparator sortByStatus = (t1, t2) -> {
+ if (t1.isDone && !t2.isDone) {
+ return Priority.HIGH.compareTo(Priority.LOW);
+ }
+ if (!t1.isDone && t2.isDone) {
+ return Priority.LOW.compareTo(Priority.HIGH);
+ }
+ return Priority.EQUAL.ordinal();
+ };
+
+ /**
+ * Determines if the task fits under the weekly task list by checking its deadline against the system clock.
+ *
+ * @param t the task in the task list.
+ * @return true if the period of the current date to the task's deadline is within 7 days.
+ */
+ protected boolean isWeekly(Task t) {
+ LocalDate date = LocalDate.now();
+ Period p = Period.between(date, t.deadline.toLocalDate()).normalized();
+ int day = p.getYears() * DAYS_IN_A_YEAR + p.getMonths() * DAYS_IN_A_MONTH + p.getDays();
+ return day <= DAYS_IN_A_WEEK;
+ }
+
+ /**
+ * Determines if the task fits under the monthly task list by checking its deadline against the system clock.
+ *
+ * @param t the task in the task list.
+ * @return true if the period of the current date to the task's deadline is within 30 days.
+ */
+ protected boolean isMonthly(Task t) {
+ LocalDate date = LocalDate.now();
+ Period p = Period.between(date, t.deadline.toLocalDate()).normalized();
+ int day = p.getYears() * DAYS_IN_A_YEAR + p.getMonths() * DAYS_IN_A_MONTH + p.getDays();
+ return day <= DAYS_IN_A_MONTH;
+ }
+
+ /**
+ * Determines if the task fits under the yearly task list by checking its deadline against the system clock.
+ *
+ * @param t the task in the task list.
+ * @return true if the period of the current date to the task's deadline is within a year.
+ */
+ protected boolean isYearly(Task t) {
+ LocalDate date = LocalDate.now();
+ Period p = Period.between(date, t.deadline.toLocalDate()).normalized();
+ int day = p.getYears() * DAYS_IN_A_YEAR + p.getMonths() * DAYS_IN_A_MONTH + p.getDays();
+ return day <= DAYS_IN_A_YEAR;
+ }
+
+ public void showAllWeekly(String module) {
+ logger.log(Level.INFO, "Printing weekly tasks list...");
+ ArrayList list = new ArrayList<>(weeklyTaskList());
+ Ui.printWeeklyTaskList(module, list.size());
+ printTasks(list);
+ }
+
+ public void showAllMonthly(String module) {
+ logger.log(Level.INFO, "Printing monthly tasks list...");
+ ArrayList list = new ArrayList<>(monthlyTaskList());
+ Ui.printMonthlyTaskList(module, list.size());
+ printTasks(list);
+ }
+
+ public void showAllYearly(String module) {
+ logger.log(Level.INFO, "Printing yearly tasks list...");
+ ArrayList list = new ArrayList<>(yearlyTaskList());
+ Ui.printYearlyTaskList(module, list.size());
+ printTasks(list);
+ }
+
+ /**
+ * Get all weekly tasks in a list.
+ *
+ * @return an arraylist which is the list of weekly tasks
+ */
+ public ArrayList weeklyTaskList() {
+ ArrayList list = new ArrayList<>();
+ for (Task task : taskList) {
+ assert (task != null);
+ if (isWeekly(task)) {
+ list.add(task);
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Get all monthly tasks in a list.
+ *
+ * @return an arraylist which is the list of monthly tasks
+ */
+ public ArrayList monthlyTaskList() {
+ ArrayList list = new ArrayList<>();
+ for (Task task : taskList) {
+ assert (task != null);
+ if (isMonthly(task)) {
+ list.add(task);
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Get all yearly tasks in a list.
+ *
+ * @return an arraylist which is the list of yearly tasks
+ */
+ public ArrayList yearlyTaskList() {
+ ArrayList list = new ArrayList<>();
+ for (Task task : taskList) {
+ assert (task != null);
+ if (isYearly(task)) {
+ list.add(task);
+ }
+ }
+ return list;
+ }
+
+ public void printTasks(ArrayList taskList) {
+ int index = 1;
+ for (Task task : taskList) {
+ if (task != null) {
+ task.updateOverdue();
+ System.out.print(index + ".");
+ System.out.println(task);
+ index++;
+ }
+ }
+ }
+
+ public void sortTaskListByDate(String module) {
+ logger.log(Level.INFO, "Sorting tasks list by date...");
+ taskList.sort(sortByDate);
+ Ui.printSortListByDate(module);
+ }
+
+ public void sortTaskListByStatus(String module) {
+ logger.log(Level.INFO, "Sorting tasks list by status...");
+ taskList.sort(sortByStatus);
+ Ui.printSortListByStatus(module);
+ }
+
+ @Override
+ public String toString() {
+ return taskList.toString();
+ }
+}
+
+
diff --git a/src/test/java/command/VisualiseGradableTest.java b/src/test/java/command/VisualiseGradableTest.java
new file mode 100644
index 0000000000..c2d2503de4
--- /dev/null
+++ b/src/test/java/command/VisualiseGradableTest.java
@@ -0,0 +1,52 @@
+package command;
+
+import org.junit.jupiter.api.Test;
+import task.GradableTaskList;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertLinesMatch;
+
+class VisualiseGradableTest {
+
+ @Test
+ void print() {
+ GradableTaskList gradableTaskList = new GradableTaskList();
+ gradableTaskList.addGradableTask("cs1010", "lecture quizzes /by 12/12/2021 1600 /w 10");
+ gradableTaskList.addGradableTask("cs1010", "classPart /by 12/12/2021 1600 /w 15");
+ gradableTaskList.addGradableTask("cs1010", "Finals /by 12/12/2021 1600 /w 55");
+ gradableTaskList.addGradableTask("cs1010", "att /by 12/12/2021 1600 /w 15");
+ gradableTaskList.addGradableTask("cs1010", "labs /by 12/12/2021 1600 /w 5");
+ gradableTaskList.getGradableTask(1).setDone(true);
+ gradableTaskList.getGradableTask(2).setDone(true);
+ VisualiseGradable v = new VisualiseGradable(gradableTaskList);
+ ByteArrayOutputStream read = new ByteArrayOutputStream();
+ PrintStream save = new PrintStream(read);
+ System.setOut(save);
+ v.print();
+ List actualLines = List.of(read.toString().split("\r\n"));
+ List expectedLines = Collections.singletonList(
+ "BREAKDOWN:" + System.lineSeparator()
+ + "<==10%===><=====15%=====><=========================55%=========================>"
+ + "<=====15%=====><5%=>" + System.lineSeparator()
+ + "##########@@@@@@@@@@@@@@@&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"
+ + "&&&&&&&&&+++++++++++++++#####" + System.lineSeparator()
+ + "|---1----||------2------||--------------------------3------------------"
+ + "--------||------4------||-5-|" + System.lineSeparator()
+ + "1: lecture quizzes" + System.lineSeparator()
+ + "2: classPart" + System.lineSeparator()
+ + "3: Finals" + System.lineSeparator()
+ + "4: att" + System.lineSeparator()
+ + "5: labs" + System.lineSeparator()
+ + System.lineSeparator()
+ + "1 lecture quizzes by: 12 Dec 2021 04:00 PM Weightage 10% [ ]\r\n"
+ + "2 classPart by: 12 Dec 2021 04:00 PM Weightage 15% [X]\r\n"
+ + "3 Finals by: 12 Dec 2021 04:00 PM Weightage 55% [X]\r\n"
+ + "4 att by: 12 Dec 2021 04:00 PM Weightage 15% [ ]\r\n"
+ + "5 labs by: 12 Dec 2021 04:00 PM Weightage 5% [ ]");
+ assertLinesMatch(actualLines, actualLines);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/command/parser/CommandTest.java b/src/test/java/command/parser/CommandTest.java
new file mode 100644
index 0000000000..8626d9f470
--- /dev/null
+++ b/src/test/java/command/parser/CommandTest.java
@@ -0,0 +1,79 @@
+package command.parser;
+
+import command.Logger;
+import command.NoCap;
+import command.storage.StorageDecoder;
+import module.Module;
+import module.ModuleList;
+import org.junit.jupiter.api.Test;
+import semester.Semester;
+import semester.SemesterList;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CommandTest {
+
+
+ @Test
+ public void variousCommands_missingDescription_ErrorMessage() {
+ NoCap.moduleList = new ModuleList();
+ Parser parser = new Parser();
+
+ parser.chooseTask("add");
+ parser.chooseTask("delete");
+ parser.chooseTask("list");
+
+ parser.chooseTask("/m CS1010 addclass");
+ parser.chooseTask("/m CS1010 addtask");
+ parser.chooseTask("/m CS1010 addgradable");
+ parser.chooseTask("/m CS1010 addgrade");
+ parser.chooseTask("/m CS1010 addcredit");
+
+ }
+
+ @Test
+ public void commandEdit_success() {
+ NoCap.moduleList = new ModuleList();
+ Command command = new Command();
+
+ command.commandAddModule("cs1010");
+ Module module = NoCap.moduleList.get(0);
+
+ command.commandAddTask(module, "test1 /by 10 10 10");
+
+ command.commandEditDescription(module, "0", "invalid index");
+ assertEquals(module.getTaskList().toString(), "[[ ] test1 by: 10 Oct 2010 12:00 AM]");
+
+ command.commandEditDescription(module, "1", "test2");
+ assertEquals(module.getTaskList().toString(), "[[ ] test2 by: 10 Oct 2010 12:00 AM]");
+
+ command.commandEditDeadline(module, "1", "invalid date");
+ assertEquals(module.getTaskList().toString(), "[[ ] test2 by: 10 Oct 2010 12:00 AM]");
+
+ command.commandEditDeadline(module, "1", "11 11 11");
+ assertEquals(module.getTaskList().toString(), "[[LATE][ ] test2 by: 11 Nov 2011 12:00 AM]");
+ }
+
+ @Test
+ public void variousCommands_Success() {
+ NoCap.moduleList = new ModuleList();
+ Parser parser = new Parser();
+
+ parser.chooseTask("add cs1010");
+ parser.chooseTask("add cs1010");
+
+ parser.chooseTask("/m cs1010 addgradable finalss dasda sda asdasd /by 10/10/10 /w 70");
+ parser.chooseTask("/m cs1010 addgradable assignment /by 10/10/10 /w 70");
+ parser.chooseTask("/m cs1010 gradabledone 2");
+
+ parser.chooseTask("list task");
+
+
+ }
+
+}
diff --git a/src/test/java/command/parser/DateParserTest.java b/src/test/java/command/parser/DateParserTest.java
new file mode 100644
index 0000000000..0f014bec8c
--- /dev/null
+++ b/src/test/java/command/parser/DateParserTest.java
@@ -0,0 +1,28 @@
+package command.parser;
+
+import org.junit.jupiter.api.Test;
+import java.time.LocalDateTime;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class DateParserTest {
+ @Test
+ public void parseDate_variousFormat_success() {
+ assertEquals("2020-12-01T00:00", DateParser.parseDate("2020-12-01").toString());
+ assertEquals("2020-12-01T18:12", DateParser.parseDate("01 12 2020 18:12").toString());
+ assertEquals("2020-12-01T18:12", DateParser.parseDate("01 12 2020 1812").toString());
+ assertEquals("2020-12-01T00:00", DateParser.parseDate("01 12 2020").toString());
+ assertEquals("2020-12-01T00:00", DateParser.parseDate("01 12 20").toString());
+ assertEquals("2020-12-01T00:00", DateParser.parseDate("01 12 20").toString());
+ assertEquals("2020-12-01T18:12", DateParser.parseDate("01/12/2020 18:12").toString());
+ assertEquals("2020-12-01T18:12", DateParser.parseDate("01/12/2020 1812").toString());
+ assertEquals("2020-12-01T00:00", DateParser.parseDate("01/12/2020").toString());
+ assertEquals("2021-12-01T00:00", DateParser.parseDate("01/12/21").toString());
+ }
+
+ @Test
+ public void dateStringOutput_success() {
+ LocalDateTime testCase = DateParser.parseDate("01/12/20");
+ assertEquals("1 Dec 2020 12:00 AM", DateParser.dateStringOutput(testCase));
+ }
+
+}
diff --git a/src/test/java/command/parser/ParserChecksTest.java b/src/test/java/command/parser/ParserChecksTest.java
new file mode 100644
index 0000000000..b25b8c07eb
--- /dev/null
+++ b/src/test/java/command/parser/ParserChecksTest.java
@@ -0,0 +1,84 @@
+package command.parser;
+
+import org.junit.jupiter.api.Test;
+import task.TaskList;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public class ParserChecksTest {
+
+ ParserChecks testParserChecks = new ParserChecks();
+
+ @Test
+ public void getTaskFromIndex_invalidIndex_returnNull() {
+ TaskList a = new TaskList();
+ a.addTask("cs1010", "Read Book A /by 12/12/2021 1600");
+ a.addTask("cs1010", "Read Book B /by 13/12/2021 1600");
+ assertNull(testParserChecks.getTaskFromIndex("0", a.getTaskList()));
+ assertNull(testParserChecks.getTaskFromIndex("3", a.getTaskList()));
+ assertNull(testParserChecks.getTaskFromIndex("abc", a.getTaskList()));
+ }
+
+ @Test
+ public void getTaskFromIndex_validIndex_returnTask() {
+ TaskList a = new TaskList();
+ a.addTask("cs1010", "Read Book A /by 12/12/2021 1600");
+ a.addTask("cs1010", "Read Book B /by 13/12/2021 1600");
+ assertEquals(testParserChecks.getTaskFromIndex("1", a.getTaskList()), a.get(0));
+ assertEquals(testParserChecks.getTaskFromIndex("2", a.getTaskList()), a.get(1));
+ }
+
+ @Test
+ public void getTaskFromKeyword_keywordNotFound_returnNull() {
+ TaskList a = new TaskList();
+ a.addTask("cs1010", "Read Book A /by 12/12/2021 1600");
+ a.addTask("cs1010", "Read Book B /by 13/12/2021 1600");
+ assertNull(testParserChecks.getTaskFromKeyword("xyz", a.getTaskList()));
+ }
+
+ @Test
+ public void getTaskFromKeyword_EmptyTaskList_returnNull() {
+ TaskList a = new TaskList();
+ assertNull(testParserChecks.getTaskFromKeyword("xyz", a.getTaskList()));
+ }
+
+ @Test
+ public void getTaskFromKeyword_MatchOne_returnMatch() {
+ TaskList a = new TaskList();
+ a.addTask("cs1010", "Read Book A /by 12/12/2021 1600");
+ a.addTask("cs1010", "Read Book B /by 13/12/2021 1600");
+ String input = "1";
+ InputStream in;
+
+ in = new ByteArrayInputStream(input.getBytes());
+ System.setIn(in);
+ assertEquals(testParserChecks.getTaskFromKeyword("Book A", a.getTaskList()), a.get(0));
+ in = new ByteArrayInputStream(input.getBytes());
+ System.setIn(in);
+ assertEquals(testParserChecks.getTaskFromKeyword("Book B", a.getTaskList()), a.get(1));
+ }
+
+ @Test
+ public void getTaskFromKeyword_MatchBoth_returnIndexChoice() {
+ TaskList a = new TaskList();
+ a.addTask("cs1010", "Read Book A /by 12/12/2021 1600");
+ a.addTask("cs1010", "Read Book B /by 13/12/2021 1600");
+ String input;
+ InputStream in;
+
+ input = "1";
+ in = new ByteArrayInputStream(input.getBytes());
+ System.setIn(in);
+ assertEquals(testParserChecks.getTaskFromKeyword("Book", a.getTaskList()), a.get(0));
+
+ input = "2";
+ in = new ByteArrayInputStream(input.getBytes());
+ System.setIn(in);
+ assertEquals(testParserChecks.getTaskFromKeyword("Book", a.getTaskList()), a.get(1));
+ }
+
+}
diff --git a/src/test/java/command/parser/ParserTest.java b/src/test/java/command/parser/ParserTest.java
new file mode 100644
index 0000000000..7bf620b006
--- /dev/null
+++ b/src/test/java/command/parser/ParserTest.java
@@ -0,0 +1,42 @@
+package command.parser;
+
+import org.junit.jupiter.api.Test;
+import task.TaskList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ParserTest {
+
+ @Test
+ public void splitInput_withDescription_success() {
+ Parser testParser = new Parser();
+ testParser.chooseTask("Add taskDescription");
+ assertEquals("Add", testParser.getTaskType());
+ assertEquals("taskDescription", testParser.getTaskDescription());
+ }
+
+ @Test
+ public void splitInput_withoutDescription_success() {
+ Parser testParser = new Parser();
+ testParser.chooseTask("Help");
+ assertEquals("Help", testParser.getTaskType());
+ assertEquals("", testParser.getTaskDescription());
+ }
+
+ @Test
+ public void splitInput_EmptyString_success() {
+ Parser testParser = new Parser();
+ testParser.chooseTask("");
+ assertEquals("", testParser.getTaskType());
+ assertEquals("", testParser.getTaskDescription());
+ }
+
+
+ @Test
+ public void getTaskFromKeyword_keywordNotFound_printError() {
+ TaskList a = new TaskList();
+ a.addTask("cs1010", "Read Book A /by 12/12/2021 1600");
+ a.addTask("cs1010", "Read Book B /by 13/12/2021 1600");
+ }
+
+}
diff --git a/src/test/java/command/storage/StorageDecoderTest.java b/src/test/java/command/storage/StorageDecoderTest.java
new file mode 100644
index 0000000000..034db8ec55
--- /dev/null
+++ b/src/test/java/command/storage/StorageDecoderTest.java
@@ -0,0 +1,94 @@
+package command.storage;
+
+import exceptions.NoCapExceptions;
+import module.Module;
+import module.ModuleList;
+import schedule.Schedule;
+import org.junit.jupiter.api.Test;
+import semester.SemesterList;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+class StorageDecoderTest {
+ private static final String ROOT = System.getProperty("user.dir");
+ private static final Path FILE_PATH = Paths.get(ROOT, "data", "data.json");
+
+ @Test
+ public void decodeSemesterList_normalSemesterList_success() {
+ SemesterList semesters = new SemesterList();
+ ModuleList modules = semesters.extractAccessedSemester().getModuleList();
+ try {
+ modules.add(new Module("CS2102"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ modules.get(0).addTask("sleep /by 21/08/2022 1600");
+ //Change semester index
+ semesters.setAccessedSemesterIndex(1);
+ modules = semesters.extractAccessedSemester().getModuleList();
+ try {
+ modules.add(new Module("CS2112"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.get(0).addCredits(4);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.get(0).addClass(new Schedule("WED", "1200", "E3", "Tutorial"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ //Change semester index
+ semesters.setAccessedSemesterIndex(2);
+ modules = semesters.extractAccessedSemester().getModuleList();
+ try {
+ modules.add(new Module("CS2132"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.get(0).addClass(new Schedule("MON", "1200", "D3", "Lecture"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.get(0).addClass(new Schedule("TUE", "1200", "D3", "Lecture"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ StorageEncoder.encodeAndSaveSemesterListToJson(semesters);
+ SemesterList loadedSemesters = StorageDecoder.decodeJsonToSemesterList();
+ assertEquals(semesters.toString(), loadedSemesters.toString());
+ }
+
+ @Test
+ public void decodeSemesterList_emptyList_success() {
+ SemesterList modules = new SemesterList();
+ StorageEncoder.encodeAndSaveSemesterListToJson(modules);
+ SemesterList loadedSemesters = StorageDecoder.decodeJsonToSemesterList();
+ assertEquals(loadedSemesters.toString(), modules.toString());
+ }
+
+ @Test
+ public void decodeSemesterList_noFile_success() {
+ try {
+ if (Files.exists(FILE_PATH)) {
+ Files.delete(FILE_PATH);
+ }
+ } catch (IOException e) {
+ fail(e);
+ }
+ SemesterList loadedSemesters = StorageDecoder.decodeJsonToSemesterList();
+ assertEquals(loadedSemesters.toString(), new SemesterList().toString());
+
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/command/storage/StorageEncoderTest.java b/src/test/java/command/storage/StorageEncoderTest.java
new file mode 100644
index 0000000000..32aff950c5
--- /dev/null
+++ b/src/test/java/command/storage/StorageEncoderTest.java
@@ -0,0 +1,350 @@
+package command.storage;
+
+import exceptions.NoCapExceptions;
+import module.Module;
+import module.ModuleList;
+import schedule.Schedule;
+import org.junit.jupiter.api.Test;
+import semester.SemesterList;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+
+class StorageEncoderTest {
+
+ private static final String ROOT = System.getProperty("user.dir");
+ private static final Path FILE_PATH = Paths.get(ROOT, "data", "data.json");
+ private static final Path DIRECTORY_PATH = Paths.get(ROOT, "data");
+
+ @Test
+ public void encodeModuleList_normalModuleList_success() {
+ SemesterList semesters = new SemesterList();
+ ModuleList modules = semesters.extractAccessedSemester().getModuleList();
+ try {
+ modules.add(new Module("CS2102"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.add(new Module("CS2112"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.add(new Module("CS2132"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ modules.get(0).addTask("sleep /by 21/08/2022 1600");
+ try {
+ modules.get(0).addCredits(4);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.get(2).addClass(new Schedule("MON", "1200", "E3", "Bad"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.get(0).addClass(new Schedule("MON", "1200", "D3", "Bad"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.get(0).addClass(new Schedule("TUE", "1200", "D3", "Bad"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ StorageEncoder.encodeAndSaveSemesterListToJson(semesters);
+ assertTrue(Files.exists(FILE_PATH));
+ try {
+ String fileContent = Files.readString(FILE_PATH);
+ String expectedString = "{\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"accessedSemesterIndex\" : 0,\n"
+ + " \"semesterList\" : [ {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y1S1\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ {\n"
+ + " \"letterGrade\" : \"NIL\",\n"
+ + " \"points\" : 0.0,\n"
+ + " \"moduleName\" : \"CS2102\",\n"
+ + " \"taskList\" : {\n"
+ + " \"taskList\" : [ {\n"
+ + " \"description\" : \"sleep\",\n"
+ + " \"date\" : \"21/08/2022 1600\",\n"
+ + " \"deadline\" : [ 2022, 8, 21, 16, 0 ],\n"
+ + " \"done\" : false\n"
+ + " } ],\n"
+ + " \"taskCount\" : 1\n"
+ + " },\n"
+ + " \"scheduleList\" : {\n"
+ + " \"scheduleList\" : [ {\n"
+ + " \"startTime\" : \"1200\",\n"
+ + " \"location\" : \"D3\",\n"
+ + " \"day\" : \"MON\",\n"
+ + " \"comment\" : \"Bad\"\n"
+ + " }, {\n"
+ + " \"startTime\" : \"1200\",\n"
+ + " \"location\" : \"D3\",\n"
+ + " \"day\" : \"TUE\",\n"
+ + " \"comment\" : \"Bad\"\n"
+ + " } ]\n"
+ + " },\n"
+ + " \"credits\" : 4,\n"
+ + " \"gradableTaskList\" : {\n"
+ + " \"taskList\" : [ ],\n"
+ + " \"taskCount\" : 0,\n"
+ + " \"gradableTaskList\" : [ ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"letterGrade\" : \"NIL\",\n"
+ + " \"points\" : 0.0,\n"
+ + " \"moduleName\" : \"CS2112\",\n"
+ + " \"taskList\" : {\n"
+ + " \"taskList\" : [ ],\n"
+ + " \"taskCount\" : 0\n"
+ + " },\n"
+ + " \"scheduleList\" : {\n"
+ + " \"scheduleList\" : [ ]\n"
+ + " },\n"
+ + " \"credits\" : 0,\n"
+ + " \"gradableTaskList\" : {\n"
+ + " \"taskList\" : [ ],\n"
+ + " \"taskCount\" : 0,\n"
+ + " \"gradableTaskList\" : [ ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"letterGrade\" : \"NIL\",\n"
+ + " \"points\" : 0.0,\n"
+ + " \"moduleName\" : \"CS2132\",\n"
+ + " \"taskList\" : {\n"
+ + " \"taskList\" : [ ],\n"
+ + " \"taskCount\" : 0\n"
+ + " },\n"
+ + " \"scheduleList\" : {\n"
+ + " \"scheduleList\" : [ {\n"
+ + " \"startTime\" : \"1200\",\n"
+ + " \"location\" : \"E3\",\n"
+ + " \"day\" : \"MON\",\n"
+ + " \"comment\" : \"Bad\"\n"
+ + " } ]\n"
+ + " },\n"
+ + " \"credits\" : 0,\n"
+ + " \"gradableTaskList\" : {\n"
+ + " \"taskList\" : [ ],\n"
+ + " \"taskCount\" : 0,\n"
+ + " \"gradableTaskList\" : [ ]\n"
+ + " }\n"
+ + " } ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y1S2\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y2S1\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y2S2\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y3S1\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y3S2\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y4S1\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y4S2\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y5S1\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y5S2\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ ]\n"
+ + " }\n"
+ + " } ]\n"
+ + "}";
+ String expectedOutput = expectedString.replaceAll("\n", System.lineSeparator());
+ assertEquals(expectedOutput, fileContent);
+ } catch (IOException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void encodeSemesterList_emptySemesterList_success() {
+ SemesterList semesters = new SemesterList();
+ StorageEncoder.encodeAndSaveSemesterListToJson(semesters);
+ assertTrue(Files.exists(FILE_PATH));
+ try {
+ String fileContent = Files.readString(FILE_PATH);
+ String expectedString = "{\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"accessedSemesterIndex\" : 0,\n"
+ + " \"semesterList\" : [ {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y1S1\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y1S2\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y2S1\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y2S2\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y3S1\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y3S2\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y4S1\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y4S2\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y5S1\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ ]\n"
+ + " }\n"
+ + " }, {\n"
+ + " \"credits\" : 0,\n"
+ + " \"points\" : 0.0,\n"
+ + " \"cap\" : 0.0,\n"
+ + " \"semester\" : \"Y5S2\",\n"
+ + " \"moduleList\" : {\n"
+ + " \"moduleList\" : [ ]\n"
+ + " }\n"
+ + " } ]\n"
+ + "}";
+ String expectedOutput = expectedString.replaceAll("\n", System.lineSeparator());
+ assertEquals(expectedOutput, fileContent);
+ } catch (IOException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void encodeModuleList_noFile_success() {
+ try {
+ if (Files.exists(DIRECTORY_PATH)) {
+ Files.delete(FILE_PATH);
+ Files.delete(DIRECTORY_PATH);
+ }
+ } catch (IOException e) {
+ fail(e.getMessage());
+ }
+ SemesterList semesters = new SemesterList();
+ StorageEncoder.encodeAndSaveSemesterListToJson(semesters);
+ assertTrue(Files.exists(FILE_PATH));
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/module/ModuleListTest.java b/src/test/java/module/ModuleListTest.java
new file mode 100644
index 0000000000..e5403af804
--- /dev/null
+++ b/src/test/java/module/ModuleListTest.java
@@ -0,0 +1,122 @@
+package module;
+
+import exceptions.NoCapExceptions;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ModuleListTest {
+ @Test
+ void get_success() {
+ ModuleList moduleList = new ModuleList();
+ Module m = null;
+ try {
+ m = new Module("cs2113T");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ moduleList.add(m);
+ assertEquals(m, moduleList.get(0));
+ }
+
+ @Test
+ void find_success() {
+ ModuleList moduleList = new ModuleList();
+ Module m = null;
+ try {
+ m = new Module("cs2113T");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ moduleList.add(m);
+ assertEquals(m, moduleList.find("cs2113T"));
+ }
+
+ @Test
+ void size_success() {
+ ModuleList moduleList = new ModuleList();
+ Module m = null;
+ try {
+ m = new Module("cs2113T");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ moduleList.add(m);
+ assertEquals(1, moduleList.size());
+ }
+
+ @Test
+ void testDelete() {
+ Module m1 = null;
+ try {
+ m1 = new Module("cs2113T");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ Module m2 = null;
+ try {
+ m2 = new Module("Cs2040c");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ Module m3 = null;
+ try {
+ m3 = new Module("ma1508E");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ ModuleList moduleList1 = new ModuleList();
+ moduleList1.add(m1);
+ moduleList1.add(m2);
+ moduleList1.add(m3);
+ moduleList1.delete("1");
+ ModuleList expectedList = new ModuleList();
+ expectedList.add(m2);
+ expectedList.add(m3);
+ assertEquals(expectedList.toString(), moduleList1.toString());
+ }
+
+ /*
+ @Test
+ void toString_success() {
+ ModuleList moduleList = new ModuleList();
+ Module m1 = new Module("cs2113T");
+ moduleList.add(m1);
+ Module m2 = new Module("cs2040c");
+ moduleList.add(m2);
+ Module m3 = new Module("MA1508E");
+ moduleList.add(m3);
+ String expected = "1" + System.lineSeparator()
+ + "Module name: cs2113T" + System.lineSeparator()
+ + "CREDITS: 0" + System.lineSeparator()
+ + "--------------------------- " + System.lineSeparator()
+ + "SCHEDULE: " + System.lineSeparator()
+ + "--------------------------- " + System.lineSeparator()
+ + "GRADE: null" + System.lineSeparator()
+ + "TASKS: []" + System.lineSeparator()
+ + "BREAKDOWN: " + System.lineSeparator()
+ + "" + System.lineSeparator()
+ + "2" + System.lineSeparator()
+ + "Module name: cs2040c" + System.lineSeparator()
+ + "CREDITS: 0" + System.lineSeparator()
+ + "--------------------------- " + System.lineSeparator()
+ + "SCHEDULE: " + System.lineSeparator()
+ + "--------------------------- " + System.lineSeparator()
+ + "GRADE: null" + System.lineSeparator()
+ + "TASKS: []" + System.lineSeparator()
+ + "BREAKDOWN: " + System.lineSeparator()
+ + "" + System.lineSeparator()
+ + "3" + System.lineSeparator()
+ + "Module name: MA1508E" + System.lineSeparator()
+ + "CREDITS: 0" + System.lineSeparator()
+ + "--------------------------- " + System.lineSeparator()
+ + "SCHEDULE: " + System.lineSeparator()
+ + "--------------------------- " + System.lineSeparator()
+ + "GRADE: null" + System.lineSeparator()
+ + "TASKS: []" + System.lineSeparator()
+ + "BREAKDOWN: " + System.lineSeparator()
+ + System.lineSeparator();
+ assertEquals(expected, moduleList.toString());
+ }
+ */
+}
diff --git a/src/test/java/module/ModuleTest.java b/src/test/java/module/ModuleTest.java
new file mode 100644
index 0000000000..eb9ea338fa
--- /dev/null
+++ b/src/test/java/module/ModuleTest.java
@@ -0,0 +1,290 @@
+package module;
+
+import command.NoCap;
+import exceptions.NoCapExceptions;
+import schedule.Schedule;
+import semester.SemesterList;
+import task.TaskList;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+
+class ModuleTest {
+ @Test
+ void testAddGrade() {
+ Module m1 = null;
+ try {
+ m1 = new Module("cs2113T");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ Module m2 = null;
+ try {
+ m2 = new Module("cs2040c");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ Module m3 = null;
+ try {
+ m3 = new Module("ma1508E");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ m1.addGrade("A+");
+ m2.addGrade("A-");
+ m3.addGrade("B");
+ assertEquals("A+", m1.letterGrade);
+ assertEquals("A-", m2.letterGrade);
+ assertEquals("B", m3.letterGrade);
+ }
+
+ @Test
+ void testAddCredit() {
+ Module m1 = null;
+ try {
+ m1 = new Module("cs2113T");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ Module m2 = null;
+ try {
+ m2 = new Module("cs2040c");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ Module m3 = null;
+ try {
+ m3 = new Module("ma1508E");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ m1.addCredits(2);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ m2.addCredits(4);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ m3.addCredits(6);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ assertEquals(2, m1.credits);
+ assertEquals(4, m2.credits);
+ assertEquals(6, m3.credits);
+ }
+
+ @Test
+ void getCredits() {
+ Module m1 = null;
+ try {
+ m1 = new Module("cs2113T");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ Module m2 = null;
+ try {
+ m2 = new Module("cs2040c");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ Module m3 = null;
+ try {
+ m3 = new Module("ma1508E");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ m1.addCredits(2);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ m2.addCredits(4);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ m3.addCredits(6);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ assertEquals(2, m1.getCredits());
+ assertEquals(4, m2.getCredits());
+ assertEquals(6, m3.getCredits());
+ }
+
+ @Test
+ void getLetterGrade() {
+ Module m1 = null;
+ try {
+ m1 = new Module("cs2113T");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ Module m2 = null;
+ try {
+ m2 = new Module("cs2040c");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ Module m3 = null;
+ try {
+ m3 = new Module("ma1508E");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ m1.addGrade("A+");
+ m2.addGrade("A-");
+ m3.addGrade("B");
+ assertEquals("A+", m1.getLetterGrade());
+ assertEquals("A-", m2.getLetterGrade());
+ assertEquals("B", m3.getLetterGrade());
+ }
+
+ @Test
+ void getModuleName() {
+ Module m1 = null;
+ try {
+ m1 = new Module("cs2113T");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ Module m2 = null;
+ try {
+ m2 = new Module("cs2040c");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ Module m3 = null;
+ try {
+ m3 = new Module("ma1508E");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ assertEquals("cs2113T", m1.getModuleName());
+ assertEquals("cs2040c", m2.getModuleName());
+ assertEquals("ma1508E", m3.getModuleName());
+ }
+
+ @Test
+ void getTaskList() {
+ Module m = null;
+ try {
+ m = new Module("cs2113T");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ m.addTask("read /by 1800");
+ TaskList taskList = new TaskList();
+ taskList.addTask("cs2113T", "read /by 1800");
+
+ assertEquals(taskList.toString(), m.getTaskList().toString());
+ }
+
+ @Test
+ void getScheduleList() {
+ NoCap.moduleList = new ModuleList();
+ ModuleList moduleList = NoCap.moduleList;
+ Module m = null;
+ try {
+ m = new Module("cs2113T");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ moduleList.add(m);
+ try {
+ m.addClass("WED/0800/E1-01/tut");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ assertEquals(new Schedule("WED", "0800", "E1-01", "tut").toString(), m.get(0).toString());
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ void size() {
+ NoCap.moduleList = new ModuleList();
+ ModuleList moduleList = NoCap.moduleList;
+ Module m = null;
+ try {
+ m = new Module("cs2113T");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ moduleList.add(m);
+ try {
+ m.addClass("WED/0800/E1-01/tut");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ m.addClass("TUE/0800/E1-01/lect");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ m.addClass("MON/1000/E1-01/lect");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ assertEquals(3, m.size());
+ }
+
+ @Test
+ void testToString() {
+ NoCap.moduleList = new ModuleList();
+ ModuleList moduleList = NoCap.moduleList;
+ Module m = null;
+ try {
+ m = new Module("cs2113T");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ moduleList.add(m);
+ m.addGrade("A+");
+ try {
+ m.addCredits(4);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ m.addClass("WED/0800/E1-01/tut");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ m.addClass("TUE/0800/E1-01/lect");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ m.addClass("MON/1000/E1-01/lect");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ m.addTask("read /by 11/11/2021 1900");
+ m.addTask("do tutorial /by 12/11/2021 2000");
+ m.addGradableTask("Assignment /by 12/12/2021 1600 /w 30");
+ m.addGradableTask("Finals /by 15/12/2021 1600 /w 50");
+ String exp = "Module name: cs2113T\n"
+ + "CREDITS: 4\n" + "--------------------------- \n" + "SCHEDULE: \n" + "1.\n"
+ + "Day: WED\n" + "Start Time: 0800\n" + "Location: E1-01\n" + "Comments: tut\n" + "2.\n"
+ + "Day: TUE\n" + "Start Time: 0800\n" + "Location: E1-01\n" + "Comments: lect\n" + "3.\n"
+ + "Day: MON\n" + "Start Time: 1000\n" + "Location: E1-01\n" + "Comments: lect\n"
+ + "--------------------------- \n" + "GRADE: A+\n"
+ + "TASKS: [[ ] read by: 11 Nov 2021 07:00 PM, [ ] do tutorial by: 12 Nov 2021 08:00 PM]\n"
+ + "BREAKDOWN: \n1 " + "Assignment by: 12 Dec 2021 04:00 PM Weightage 30% [ ]\r\n"
+ + "2 " + "Finals by: 15 Dec 2021 04:00 PM Weightage 50% [ ]\r\n";
+ assertEquals(exp, m.toString());
+
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/schedule/ScheduleListTest.java b/src/test/java/schedule/ScheduleListTest.java
new file mode 100644
index 0000000000..b453e777ad
--- /dev/null
+++ b/src/test/java/schedule/ScheduleListTest.java
@@ -0,0 +1,85 @@
+package schedule;
+
+import command.NoCap;
+import exceptions.NoCapExceptions;
+import module.Module;
+import module.ModuleList;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ScheduleListTest {
+
+ @Test
+ void size_success() {
+ ScheduleList s = new ScheduleList();
+ try {
+ s.addClass(new Schedule("WED", "0800", "E1-01", "tutorial"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ s.addClass(new Schedule("MON", "0800", "e-learning", "tutorial"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ assertEquals(2, s.size());
+ }
+
+ @Test
+ void getSchedule_success() {
+ NoCap.moduleList = new ModuleList();
+ ModuleList moduleList = NoCap.moduleList;
+ Module m = null;
+ try {
+ m = new Module("cs2113T");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ moduleList.add(m);
+
+ Schedule expected = null;
+ try {
+ expected = new Schedule("WED", "0800", "E1-01", "tutorial");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ m.addClass(expected);
+ try {
+ m.addClass(new Schedule("MON", "0800", "e-learning", "tutorial"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+
+ assertEquals(expected, m.getScheduleList().getSchedule(0));
+ }
+
+ @Test
+ void getScheduleList_success() {
+ Module m = null;
+ try {
+ m = new Module("cs2113T");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ ScheduleList expected = new ScheduleList();
+ m.setScheduleList(expected);
+ assertEquals(expected, m.getScheduleList());
+ }
+
+ @Test
+ void testDelete() throws NoCapExceptions {
+ ScheduleList scheduleList1 = new ScheduleList();
+ Schedule s1 = new Schedule("WED", "0800", "E1-01", "tutorial");
+ Schedule s2 = new Schedule("MON", "1000", "ZOOM", "lecture");
+ Schedule s3 = new Schedule("TUE", "1500", "ARC", "lab");
+ scheduleList1.addClass(s1);
+ scheduleList1.addClass(s2);
+ scheduleList1.addClass(s3);
+ scheduleList1.deleteClass("1");
+ ScheduleList scheduleList2 = new ScheduleList();
+ scheduleList2.addClass(s2);
+ scheduleList2.addClass(s3);
+ assertEquals(scheduleList1.toString(), scheduleList2.toString());
+ }
+}
diff --git a/src/test/java/schedule/ScheduleTest.java b/src/test/java/schedule/ScheduleTest.java
new file mode 100644
index 0000000000..4afc119626
--- /dev/null
+++ b/src/test/java/schedule/ScheduleTest.java
@@ -0,0 +1,59 @@
+package schedule;
+
+import command.NoCap;
+import exceptions.NoCapExceptions;
+import module.Module;
+import module.ModuleList;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ScheduleTest {
+
+ @Test
+ void getLocation_success() {
+ Schedule s = null;
+ try {
+ s = new Schedule("WED", "0800", "E1-01", "tutorial");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ assertEquals("E1-01", s.getLocation());
+ }
+
+ @Test
+ void getComment_success() {
+ Schedule s = null;
+ try {
+ s = new Schedule("WED", "0800", "E1-01", "tutorial");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ assert s != null;
+ assertEquals("tutorial", s.getComment());
+ }
+
+ @Test
+ void getDay_success() {
+ Schedule s = null;
+ try {
+ s = new Schedule("WED", "0800", "E1-01", "tutorial");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ assert s != null;
+ assertEquals("WED", s.getDay());
+ }
+
+ @Test
+ void getStartTime_success() {
+ Schedule s = null;
+ try {
+ s = new Schedule("WED", "0800", "E1-01", "tutorial");
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ assert s != null;
+ assertEquals("0800", s.getStartTime());
+ }
+}
diff --git a/src/test/java/semester/SemesterListTest.java b/src/test/java/semester/SemesterListTest.java
new file mode 100644
index 0000000000..af56ec1bd5
--- /dev/null
+++ b/src/test/java/semester/SemesterListTest.java
@@ -0,0 +1,161 @@
+package semester;
+
+import command.NoCap;
+import exceptions.NoCapExceptions;
+import module.Module;
+import module.ModuleList;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SemesterListTest {
+
+ @Test
+ void getCredits_success() {
+ NoCap.semesterList = new SemesterList();
+ try {
+ NoCap.semesterList.get(0).getModuleList().add(new Module("CS2040C"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(0).getModuleList().get(0).addCredits(4);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(0).updateCap();
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(1).getModuleList().add(new Module("CS2113T"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(1).getModuleList().get(0).addCredits(4);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(1).updateCap();
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.updateCap();
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ assertEquals(8, NoCap.semesterList.getCredits());
+ }
+
+ @Test
+ void getPoints_success() {
+ NoCap.semesterList = new SemesterList();
+ try {
+ NoCap.semesterList.get(0).getModuleList().add(new Module("CS2040C"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(0).getModuleList().get(0).addCredits(4);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ NoCap.semesterList.get(0).getModuleList().get(0).addGrade("A");
+ try {
+ NoCap.semesterList.get(0).updateCap();
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(1).getModuleList().add(new Module("CS2113T"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(1).getModuleList().get(0).addCredits(4);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ NoCap.semesterList.get(1).getModuleList().get(0).addGrade("A");
+ try {
+ NoCap.semesterList.get(1).updateCap();
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.updateCap();
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ assertEquals(40, NoCap.semesterList.getPoints());
+ }
+
+ @Test
+ void getCap_success() {
+ NoCap.semesterList = new SemesterList();
+ try {
+ NoCap.semesterList.get(0).getModuleList().add(new Module("CS2040C"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(0).getModuleList().get(0).addCredits(4);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ NoCap.semesterList.get(0).getModuleList().get(0).addGrade("A");
+ try {
+ NoCap.semesterList.get(0).updateCap();
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(1).getModuleList().add(new Module("CS2113T"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(1).getModuleList().get(0).addCredits(4);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ NoCap.semesterList.get(1).getModuleList().get(0).addGrade("B");
+ try {
+ NoCap.semesterList.get(1).updateCap();
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.updateCap();
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ assertEquals(4.25, NoCap.semesterList.getCap());
+ }
+
+ @Test
+ void getSemesterList_success() {
+ SemesterList expected = new SemesterList();
+ NoCap.semesterList = expected;
+ assertEquals(expected.getSemesterList(), NoCap.semesterList.getSemesterList());
+ }
+
+ @Test
+ void getAccessedSemesterIndex_success() {
+ NoCap.semesterList = new SemesterList();
+ NoCap.semesterList.setAccessedSemesterIndex(1);
+ assertEquals(1, NoCap.semesterList.getAccessedSemesterIndex());
+ }
+
+ @Test
+ void extractAccessedSemester_success() {
+ SemesterList expected = new SemesterList();
+ NoCap.semesterList = expected;
+ NoCap.semesterList.setAccessedSemesterIndex(1);
+ assertEquals(expected.get(1), NoCap.semesterList.extractAccessedSemester());
+ }
+}
diff --git a/src/test/java/semester/SemesterTest.java b/src/test/java/semester/SemesterTest.java
new file mode 100644
index 0000000000..c0ba08b2df
--- /dev/null
+++ b/src/test/java/semester/SemesterTest.java
@@ -0,0 +1,138 @@
+package semester;
+
+import command.NoCap;
+import exceptions.NoCapExceptions;
+import module.Module;
+import module.ModuleList;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SemesterTest {
+
+ @Test
+ void getCredits_success() {
+ NoCap.semesterList = new SemesterList();
+ try {
+ NoCap.semesterList.get(0).getModuleList().add(new Module("CS2040C"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(0).getModuleList().get(0).addCredits(4);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(0).updateCap();
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(0).getModuleList().add(new Module("CS2113T"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(0).getModuleList().get(1).addCredits(4);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(0).updateCap();
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ assertEquals(8, NoCap.semesterList.get(0).getCredits());
+ }
+
+ @Test
+ void getPoints_success() {
+ NoCap.semesterList = new SemesterList();
+ try {
+ NoCap.semesterList.get(0).getModuleList().add(new Module("CS2040C"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(0).getModuleList().get(0).addCredits(4);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ NoCap.semesterList.get(0).getModuleList().get(0).addGrade("A");
+ try {
+ NoCap.semesterList.get(0).updateCap();
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(0).getModuleList().add(new Module("CS2113T"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(0).getModuleList().get(1).addCredits(4);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ NoCap.semesterList.get(0).getModuleList().get(1).addGrade("A");
+ try {
+ NoCap.semesterList.get(0).updateCap();
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ assertEquals(40, NoCap.semesterList.get(0).getPoints());
+ }
+
+ @Test
+ void getCap_success() {
+ NoCap.semesterList = new SemesterList();
+ try {
+ NoCap.semesterList.get(0).getModuleList().add(new Module("CS2040C"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(0).getModuleList().get(0).addCredits(4);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ NoCap.semesterList.get(0).getModuleList().get(0).addGrade("A");
+ try {
+ NoCap.semesterList.get(0).updateCap();
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(0).getModuleList().add(new Module("CS2113T"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ NoCap.semesterList.get(0).getModuleList().get(1).addCredits(4);
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ NoCap.semesterList.get(0).getModuleList().get(1).addGrade("B");
+ try {
+ NoCap.semesterList.get(0).updateCap();
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ assertEquals(4.25, NoCap.semesterList.get(0).getCap());
+ }
+
+ @Test
+ void getModuleList_success() {
+ ModuleList expected = new ModuleList();
+ Semester s = new Semester();
+ s.moduleList = expected;
+ assertEquals(expected, s.getModuleList());
+ }
+
+ @Test
+ void getSemester_success() {
+ Semester s = new Semester("Y1S1");
+ assertEquals("Y1S1", s.getSemester());
+ }
+}
diff --git a/src/test/java/task/GradableTaskListTest.java b/src/test/java/task/GradableTaskListTest.java
new file mode 100644
index 0000000000..4808b7affb
--- /dev/null
+++ b/src/test/java/task/GradableTaskListTest.java
@@ -0,0 +1,42 @@
+package task;
+
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+
+class GradableTaskListTest extends TaskListTest {
+
+ @Test
+ void getGradableTaskList() {
+ GradableTaskList gradableTaskList = new GradableTaskList();
+ gradableTaskList.addGradableTask("cs1010", "Assignment /by 12/12/2021 1600 /w 30");
+ gradableTaskList.addGradableTask("cs1010", "Finals /by 12/12/2021 1600 /w 50");
+ GradableTask g1 = new GradableTask("Assignment", "12/12/2021 1600", 30);
+ GradableTask g2 = new GradableTask("Finals", "12/12/2021 1600", 50);
+ assertEquals(g1.toString(), gradableTaskList.getGradableTask(0).toString());
+ assertEquals(g2.toString(), gradableTaskList.getGradableTask(1).toString());
+ }
+
+ @Test
+ void testToString() {
+ GradableTaskList gradableTaskList = new GradableTaskList();
+ gradableTaskList.addGradableTask("cs1010", "Assignment /by 12/12/2021 1600 /w 30");
+ gradableTaskList.addGradableTask("cs1010", "Finals /by 12/12/2021 1600 /w 50");
+ String expected = "1 Assignment by: 12 Dec 2021 04:00 PM Weightage 30% [ ]\r\n"
+ + "2 Finals by: 12 Dec 2021 04:00 PM Weightage 50% [ ]\r\n";
+ assertEquals(expected, gradableTaskList.toString());
+ }
+
+ @Test
+ void testGet() {
+ GradableTaskList gradableTaskList = new GradableTaskList();
+ gradableTaskList.addGradableTask("cs1010", "Assignment /by 12/12/2021 1600 /w 30");
+ gradableTaskList.addGradableTask("cs1010", "Finals /by 12/12/2021 1600 /w 50");
+ assertEquals("Assignment by: 12 Dec 2021 04:00 PM Weightage 30% [ ]",
+ gradableTaskList.getGradableTask(0).toString());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/task/GradableTaskTest.java b/src/test/java/task/GradableTaskTest.java
new file mode 100644
index 0000000000..f6f2eddf7d
--- /dev/null
+++ b/src/test/java/task/GradableTaskTest.java
@@ -0,0 +1,21 @@
+package task;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class GradableTaskTest {
+
+ @Test
+ void getWeightage() {
+ GradableTask gradableTask = new GradableTask("Assignment", "23/11/2012 1600", 30);
+ assertEquals(30, gradableTask.getWeightage());
+ }
+
+ @Test
+ void testToString() {
+ GradableTask gradableTask = new GradableTask("Assignment", "23/11/2012 1600", 30);
+ String expected = "Assignment by: 23 Nov 2012 04:00 PM Weightage 30% [ ]";
+ assertEquals(expected, gradableTask.toString());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/task/OverallTaskListTest.java b/src/test/java/task/OverallTaskListTest.java
new file mode 100644
index 0000000000..4253d2290a
--- /dev/null
+++ b/src/test/java/task/OverallTaskListTest.java
@@ -0,0 +1,367 @@
+package task;
+
+import exceptions.NoCapExceptions;
+import module.Module;
+import module.ModuleList;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertLinesMatch;
+
+class OverallTaskListTest extends TaskListTest {
+
+ @Test
+ void addModuleList_normalList_success() {
+ ModuleList modules = new ModuleList();
+ try {
+ modules.add(new Module("CS2102"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.add(new Module("CS2112"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.add(new Module("CS2132"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ modules.get(0).addTask("sleep /by 21/08/2022 1600");
+ modules.get(1).addTask("sleep /by 21/08/2022 1600");
+ modules.get(2).addTask("sleep /by 21/08/2022 1600");
+ modules.get(0).addGradableTask("assignment /by 21/08/2022 /w 50");
+ OverallTaskList taskList = new OverallTaskList(modules);
+ String expected = "[CS2102][ ][ ] sleep by: 21 Aug 2022 04:00 PM " + System.lineSeparator()
+ + "[CS2102][G][ ] assignment by: 21 Aug 2022 12:00 AM [Weightage: 50%]" + System.lineSeparator()
+ + "[CS2112][ ][ ] sleep by: 21 Aug 2022 04:00 PM " + System.lineSeparator()
+ + "[CS2132][ ][ ] sleep by: 21 Aug 2022 04:00 PM " + System.lineSeparator();
+ assertEquals(expected, taskList.toString());
+ }
+
+ @Test
+ void addModuleList_emptyList_success() {
+ ModuleList modules = new ModuleList();
+ OverallTaskList taskList = new OverallTaskList(modules);
+ String expected = "";
+ assertEquals(expected, taskList.toString());
+ }
+
+ @Test
+ void addModuleList_taskListOnly_success() {
+ ModuleList modules = new ModuleList();
+ OverallTaskList taskList = new OverallTaskList(modules);
+ try {
+ modules.add(new Module("CS2102"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ modules.get(0).addTask("assignment /by 21/08/2022 1600");
+ String expected = "";
+ assertEquals(expected, taskList.toString());
+ }
+
+ @Test
+ void addModuleList_gradableListOnly_success() {
+ ModuleList modules = new ModuleList();
+ OverallTaskList taskList = new OverallTaskList(modules);
+ try {
+ modules.add(new Module("CS2102"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ modules.get(0).addGradableTask("assignment /by 21/08/2022 /w 50");
+ String expected = "";
+ assertEquals(expected, taskList.toString());
+ }
+
+ @Test
+ void printModuleList_normalList_success() {
+ ModuleList modules = new ModuleList();
+ try {
+ modules.add(new Module("CS2102"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.add(new Module("CS2112"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.add(new Module("CS2132"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ modules.get(0).addTask("sleep /by 21/08/2022 1600");
+ modules.get(1).addTask("sleep /by 21/08/2022 1600");
+ modules.get(2).addTask("sleep /by 21/08/2022 1600");
+ OverallTaskList taskList = new OverallTaskList(modules);
+ ByteArrayOutputStream read = new ByteArrayOutputStream();
+ PrintStream save = new PrintStream(read);
+ System.setOut(save);
+ taskList.printAllTasks();
+ List actualLines = List.of(read.toString().split("/n"));
+ List expectedLines = Collections.singletonList(
+ "All tasks: " + System.lineSeparator()
+ + "1. [CS2102][ ][ ] sleep by: 21 Aug 2022 04:00 PM " + System.lineSeparator()
+ + "2. [CS2112][ ][ ] sleep by: 21 Aug 2022 04:00 PM " + System.lineSeparator()
+ + "3. [CS2132][ ][ ] sleep by: 21 Aug 2022 04:00 PM " + System.lineSeparator());
+ assertLinesMatch(expectedLines, actualLines);
+ }
+
+ @Test
+ void printModuleList_emptyList_success() {
+ ModuleList modules = new ModuleList();
+ OverallTaskList taskList = new OverallTaskList(modules);
+ ByteArrayOutputStream read = new ByteArrayOutputStream();
+ PrintStream save = new PrintStream(read);
+ System.setOut(save);
+ taskList.printAllTasks();
+ List actualLines = List.of(read.toString().split("/n"));
+ List expectedLines = Collections.singletonList(
+ "All tasks: " + System.lineSeparator()
+ + "You have no tasks" + System.lineSeparator());
+ assertLinesMatch(expectedLines, actualLines);
+ }
+
+ @Test
+ void sortModuleListByDate_normalList_success() {
+ ModuleList modules = new ModuleList();
+ try {
+ modules.add(new Module("CS2102"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.add(new Module("CS2112"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.add(new Module("CS2132"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ modules.get(0).addTask("sleep /by 23/08/2022 1600");
+ modules.get(1).addTask("sleep /by 21/08/2022 1600");
+ modules.get(2).addTask("sleep /by 20/08/2022 1600");
+ OverallTaskList taskList = new OverallTaskList(modules);
+ ByteArrayOutputStream read = new ByteArrayOutputStream();
+ PrintStream save = new PrintStream(read);
+ System.setOut(save);
+ taskList.sortByDateAndPrint();
+ List actualLines = List.of(read.toString().split("/n"));
+ List expectedLines = Collections.singletonList(
+ "Tasks sorted by date: " + System.lineSeparator()
+ + "1. [CS2132][ ][ ] sleep by: 20 Aug 2022 04:00 PM " + System.lineSeparator()
+ + "2. [CS2112][ ][ ] sleep by: 21 Aug 2022 04:00 PM " + System.lineSeparator()
+ + "3. [CS2102][ ][ ] sleep by: 23 Aug 2022 04:00 PM " + System.lineSeparator());
+ assertLinesMatch(expectedLines, actualLines);
+ }
+
+ @Test
+ void sortModuleListByStatus_normalList_success() {
+ ModuleList modules = new ModuleList();
+ try {
+ modules.add(new Module("CS2102"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.add(new Module("CS2112"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.add(new Module("CS2132"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ modules.get(0).addTask("sleep /by 23/08/2022 1600");
+ modules.get(1).addTask("sleep /by 21/08/2022 1600");
+ modules.get(2).addTask("sleep /by 20/08/2022 1600");
+ modules.get(1).getTaskList().taskList.get(0).markDone();
+ OverallTaskList taskList = new OverallTaskList(modules);
+ ByteArrayOutputStream read = new ByteArrayOutputStream();
+ PrintStream save = new PrintStream(read);
+ System.setOut(save);
+ taskList.sortByStatusAndPrint();
+ List actualLines = List.of(read.toString().split("/n"));
+ List expectedLines = Collections.singletonList(
+ "Tasks sorted by status: " + System.lineSeparator()
+ + "1. [CS2102][ ][ ] sleep by: 23 Aug 2022 04:00 PM " + System.lineSeparator()
+ + "2. [CS2132][ ][ ] sleep by: 20 Aug 2022 04:00 PM " + System.lineSeparator()
+ + "3. [CS2112][ ][X] sleep by: 21 Aug 2022 04:00 PM " + System.lineSeparator());
+ assertLinesMatch(expectedLines, actualLines);
+ }
+
+ @Test
+ void printWeeklyTask_normalList_success() {
+ ModuleList modules = new ModuleList();
+ try {
+ modules.add(new Module("CS2102"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.add(new Module("CS2112"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.add(new Module("CS2132"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ modules.get(0).addTask("sleep /by 23/08/2022 1600");
+ modules.get(1).addTask("sleep /by 21/10/2021 1600");
+ modules.get(2).addTask("sleep /by 20/11/2021 1600");
+ modules.get(2).addTask("sleep /by 21/11/2021 1600");
+ OverallTaskList taskList = new OverallTaskList(modules);
+ ByteArrayOutputStream read = new ByteArrayOutputStream();
+ PrintStream save = new PrintStream(read);
+ System.setOut(save);
+ taskList.printWeeklyTasks();
+ List actualLines = List.of(read.toString().split("/n"));
+ List expectedLines = Collections.singletonList(
+ "Weekly tasks: " + System.lineSeparator()
+ + "1. [CS2112][ ][LATE][ ] sleep by: 21 Oct 2021 04:00 PM " + System.lineSeparator());
+ assertLinesMatch(expectedLines, actualLines);
+ }
+
+ @Test
+ void printMonthlyTask_normalList_success() {
+ ModuleList modules = new ModuleList();
+ try {
+ modules.add(new Module("CS2102"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.add(new Module("CS2112"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.add(new Module("CS2132"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ modules.get(0).addTask("sleep /by 23/08/2022 1600");
+ modules.get(1).addTask("sleep /by 21/10/2021 1600");
+ modules.get(2).addTask("sleep /by 12/11/2021 1600");
+ modules.get(2).addTask("sleep /by 10/11/2021 1600");
+ OverallTaskList taskList = new OverallTaskList(modules);
+ ByteArrayOutputStream read = new ByteArrayOutputStream();
+ PrintStream save = new PrintStream(read);
+ System.setOut(save);
+ taskList.printMonthlyTasks();
+ List actualLines = List.of(read.toString().split("/n"));
+ List expectedLines = Collections.singletonList(
+ "Monthly tasks: " + System.lineSeparator()
+ + "1. [CS2112][ ][LATE][ ] sleep by: 21 Oct 2021 04:00 PM " + System.lineSeparator()
+ + "2. [CS2132][ ][ ] sleep by: 10 Nov 2021 04:00 PM " + System.lineSeparator());
+ assertLinesMatch(expectedLines, actualLines);
+ }
+
+ @Test
+ void printYearlyTask_normalList_success() {
+ ModuleList modules = new ModuleList();
+ try {
+ modules.add(new Module("CS2102"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.add(new Module("CS2112"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.add(new Module("CS2132"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ modules.get(0).addTask("sleep /by 23/12/2022 1600");
+ modules.get(1).addTask("sleep /by 21/10/2021 1600");
+ modules.get(2).addTask("sleep /by 20/11/2021 1600");
+ modules.get(2).addTask("sleep /by 21/11/2021 1600");
+ OverallTaskList taskList = new OverallTaskList(modules);
+ ByteArrayOutputStream read = new ByteArrayOutputStream();
+ PrintStream save = new PrintStream(read);
+ System.setOut(save);
+ taskList.printYearlyTasks();
+ List actualLines = List.of(read.toString().split("/n"));
+ List expectedLines = Collections.singletonList(
+ "Yearly tasks: " + System.lineSeparator()
+ + "1. [CS2112][ ][LATE][ ] sleep by: 21 Oct 2021 04:00 PM " + System.lineSeparator()
+ + "2. [CS2132][ ][ ] sleep by: 21 Nov 2021 04:00 PM " + System.lineSeparator());
+ assertLinesMatch(expectedLines, actualLines);
+ }
+
+ @Test
+ void printNormalTask_normalList_success() {
+ ModuleList modules = new ModuleList();
+ try {
+ modules.add(new Module("CS2102"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.add(new Module("CS2112"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ modules.get(0).addTask("sleep /by 23/12/2022 1600");
+ modules.get(1).addTask("sleep /by 21/10/2021 1600");
+ modules.get(0).addGradableTask("assignment /by 23/12/2022 /w 20");
+ OverallTaskList taskList = new OverallTaskList(modules);
+ ByteArrayOutputStream read = new ByteArrayOutputStream();
+ PrintStream save = new PrintStream(read);
+ System.setOut(save);
+ taskList.printNormalTasks();
+ List actualLines = List.of(read.toString().split("/n"));
+ List expectedLines = Collections.singletonList(
+ "Non-gradable tasks: " + System.lineSeparator()
+ + "1. [CS2102][ ][ ] sleep by: 23 Dec 2022 04:00 PM " + System.lineSeparator()
+ + "2. [CS2112][ ][LATE][ ] sleep by: 21 Oct 2021 04:00 PM " + System.lineSeparator());
+ assertLinesMatch(expectedLines, actualLines);
+ }
+
+ @Test
+ void printGradableTask_normalList_success() {
+ ModuleList modules = new ModuleList();
+ try {
+ modules.add(new Module("CS2102"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ try {
+ modules.add(new Module("CS2112"));
+ } catch (NoCapExceptions e) {
+ e.printStackTrace();
+ }
+ modules.get(0).addTask("sleep /by 23/12/2022 1600");
+ modules.get(1).addTask("sleep /by 21/10/2021 1600");
+ modules.get(0).addGradableTask("assignment /by 23/12/2022 /w 20");
+ OverallTaskList taskList = new OverallTaskList(modules);
+ ByteArrayOutputStream read = new ByteArrayOutputStream();
+ PrintStream save = new PrintStream(read);
+ System.setOut(save);
+ taskList.printGradableTasks();
+ List actualLines = List.of(read.toString().split("/n"));
+ List expectedLines = Collections.singletonList(
+ "Gradable tasks: " + System.lineSeparator()
+ + "1. [CS2102][G][ ] assignment by: 23 Dec 2022 12:00 AM "
+ + "[Weightage: 20%]" + System.lineSeparator());
+ assertLinesMatch(expectedLines, actualLines);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/task/OverallTaskTest.java b/src/test/java/task/OverallTaskTest.java
new file mode 100644
index 0000000000..9b9d01c711
--- /dev/null
+++ b/src/test/java/task/OverallTaskTest.java
@@ -0,0 +1,101 @@
+package task;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+class OverallTaskTest {
+ @Test
+ void overallTask_normalTask_success() {
+ Task task = new Task("Assignment", "19/09/2021 1600");
+ OverallTask overallTask = new OverallTask(task, "CS2113");
+ assertFalse(overallTask.isGradable());
+ assertEquals("[CS2113][ ][ ] Assignment by: 19 Sep 2021 04:00 PM ", overallTask.toString());
+ }
+
+ @Test
+ void overallTask_gradableTask_success() {
+ GradableTask task = new GradableTask("Quiz", "19/09/2021 1600", 50);
+ OverallTask overallTask = new OverallTask(task, "CS2113");
+ assertTrue(overallTask.isGradable());
+ assertEquals("[CS2113][G][ ] Quiz by: 19 Sep 2021 04:00 PM [Weightage: 50%]", overallTask.toString());
+ }
+
+ @Test
+ void dateComparator_equal_success() {
+ Task task1 = new Task("Assignment", "19/09/2021 1600");
+ OverallTask overallTask1 = new OverallTask(task1, "CS2113");
+ GradableTask task2 = new GradableTask("Quiz", "19/09/2021 1600", 50);
+ OverallTask overallTask2 = new OverallTask(task2, "CS2113");
+ int result = OverallTask.dateComparator.compare(overallTask1, overallTask2);
+ assertEquals(result, 0);
+ }
+
+ @Test
+ void dateComparator_leftHeavy_success() {
+ Task task1 = new Task("Assignment", "19/09/2021 1600");
+ OverallTask overallTask1 = new OverallTask(task1, "CS2113");
+ GradableTask task2 = new GradableTask("Quiz", "19/09/2021 1800", 50);
+ OverallTask overallTask2 = new OverallTask(task2, "CS2113");
+ int result = OverallTask.dateComparator.compare(overallTask1, overallTask2);
+ assertTrue(result < 0);
+ }
+
+ @Test
+ void dateComparator_rightHeavy_success() {
+ Task task1 = new Task("Assignment", "19/09/2021 1800");
+ OverallTask overallTask1 = new OverallTask(task1, "CS2113");
+ GradableTask task2 = new GradableTask("Quiz", "19/09/2021 1600", 50);
+ OverallTask overallTask2 = new OverallTask(task2, "CS2113");
+ int result = OverallTask.dateComparator.compare(overallTask1, overallTask2);
+ assertTrue(result > 0);
+ }
+
+ @Test
+ void statusComparator_bothDone_success() {
+ Task task1 = new Task("Assignment", "19/09/2021 1600");
+ task1.markDone();
+ OverallTask overallTask1 = new OverallTask(task1, "CS2113");
+ GradableTask task2 = new GradableTask("Quiz", "19/09/2021 1600", 50);
+ task2.markDone();
+ OverallTask overallTask2 = new OverallTask(task2, "CS2113");
+ int result = OverallTask.statusComparator.compare(overallTask1, overallTask2);
+ assertEquals(result, 0);
+ }
+
+ @Test
+ void statusComparator_bothNotDone_success() {
+ Task task1 = new Task("Assignment", "19/09/2021 1600");
+ OverallTask overallTask1 = new OverallTask(task1, "CS2113");
+ GradableTask task2 = new GradableTask("Quiz", "19/09/2021 1600", 50);
+ OverallTask overallTask2 = new OverallTask(task2, "CS2113");
+ int result = OverallTask.statusComparator.compare(overallTask1, overallTask2);
+ assertEquals(result, 0);
+ }
+
+ @Test
+ void statusComparator_leftHeavy_success() {
+ Task task1 = new Task("Assignment", "19/09/2021 1600");
+ task1.markDone();
+ OverallTask overallTask1 = new OverallTask(task1, "CS2113");
+ GradableTask task2 = new GradableTask("Quiz", "19/09/2021 1600", 50);
+ OverallTask overallTask2 = new OverallTask(task2, "CS2113");
+ int result = OverallTask.statusComparator.compare(overallTask1, overallTask2);
+ assertTrue(result > 0);
+ }
+
+ @Test
+ void statusComparator_rightHeavy_success() {
+ Task task1 = new Task("Assignment", "19/09/2021 1600");
+ OverallTask overallTask1 = new OverallTask(task1, "CS2113");
+ GradableTask task2 = new GradableTask("Quiz", "19/09/2021 1600", 50);
+ task2.markDone();
+ OverallTask overallTask2 = new OverallTask(task2, "CS2113");
+ int result = OverallTask.statusComparator.compare(overallTask1, overallTask2);
+ assertTrue(result < 0);
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/test/java/task/TaskListTest.java b/src/test/java/task/TaskListTest.java
new file mode 100644
index 0000000000..de07afd955
--- /dev/null
+++ b/src/test/java/task/TaskListTest.java
@@ -0,0 +1,374 @@
+package task;
+
+
+import command.parser.DateParser;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertLinesMatch;
+
+
+public class TaskListTest {
+ protected static final LocalDateTime REF_DATE = LocalDateTime.now().withHour(0);
+ protected static final DateTimeFormatter FORMAT = DateTimeFormatter.ofPattern("dd/MM/yyyy hhmm");
+ protected static final String DATE_A = REF_DATE.plusDays(1).plusHours(10).format(FORMAT);
+ protected static final String DATE_B = REF_DATE.plusDays(6).plusHours(12).format(FORMAT);
+ protected static final String DATE_C = REF_DATE.plusDays(13).plusHours(15).format(FORMAT);
+ protected static final String DATE_D = REF_DATE.plusYears(2).plusHours(15).format(FORMAT);
+ protected static final String DATE_E = REF_DATE.plusMonths(2).plusHours(15).format(FORMAT);
+ protected static final String DATE_OVERDUE = REF_DATE.minusMonths(2).plusHours(15).format(FORMAT);
+
+ @Test
+ void get_success() {
+ TaskList a = new TaskList();
+ a.addTask("cs1010", "Read Book A /by 12/12/2021 1600");
+ a.addTask("cs1010", "Read Book B /by 13/12/2021 1600");
+ assertEquals("[ ] Read Book A by: 12 Dec 2021 04:00 PM", a.get(0).toString());
+ assertEquals("[ ] Read Book B by: 13 Dec 2021 04:00 PM", a.get(1).toString());
+ }
+
+ @Test
+ void addTask_success() {
+ TaskList tasks = new TaskList();
+ tasks.addTask("cs1010", "Read Book /by 12/12/2021 1600");
+ assertEquals("[ ] Read Book by: 12 Dec 2021 04:00 PM", tasks.get(0).toString());
+ }
+
+ @Test
+ void getTaskCount_success() {
+ TaskList tasks = new TaskList();
+ tasks.addTask("cs1010", "Read Book /by 12/12/2021 1600");
+ tasks.addTask("cs1010", "Return Book /by 13/12/2021 1600");
+ assertEquals(2, tasks.getTaskCount());
+ }
+
+ @Test
+ void getTaskList_success() {
+ TaskList a = new TaskList();
+ TaskList b = new TaskList();
+ a.addTask("cs1010", "Read Book A /by 12/12/2021 1600");
+ a.addTask("cs1010", "Read Book B /by 13/12/2021 1600");
+ b.addTask("cs1010", "Buy Book A /by 14/12/2021 1600");
+ b.addTask("cs1010", "Buy Book B /by 15/12/2021 1600");
+ ArrayList list = new ArrayList<>();
+ list.addAll(a.getTaskList());
+ list.addAll(b.getTaskList());
+ assertEquals(4, list.size());
+ assertEquals("[ ] Read Book A by: 12 Dec 2021 04:00 PM", list.get(0).toString());
+ assertEquals("[ ] Read Book B by: 13 Dec 2021 04:00 PM", list.get(1).toString());
+ assertEquals("[ ] Buy Book A by: 14 Dec 2021 04:00 PM", list.get(2).toString());
+ assertEquals("[ ] Buy Book B by: 15 Dec 2021 04:00 PM", list.get(3).toString());
+ }
+
+ @Test
+ void setTaskList_success() {
+ TaskList a = new TaskList();
+ TaskList b = new TaskList();
+ a.addTask("cs1010", "Read Book A /by 12/12/2021 1600");
+ a.addTask("cs1010", "Read Book B /by 13/12/2021 1600");
+ b.addTask("cs1010", "Buy Book A /by 14/12/2021 1600");
+ b.addTask("cs1010", "Buy Book B /by 15/12/2021 1600");
+ a.setTaskList(b.getTaskList());
+ assertEquals(2, a.getTaskCount());
+ assertEquals("[ ] Buy Book A by: 14 Dec 2021 04:00 PM", a.get(0).toString());
+ assertEquals("[ ] Buy Book B by: 15 Dec 2021 04:00 PM", a.get(1).toString());
+
+ }
+
+ @Test
+ void setTaskCount_success() {
+ TaskList a = new TaskList();
+ a.setTaskCount(10);
+ assertEquals(10, a.getTaskCount());
+ }
+
+ @Test
+ void delete_success() {
+ TaskList a = new TaskList();
+ a.addTask("cs1010", "Read Book A /by 12/12/2021 1600");
+ a.addTask("cs1010", "Read Book B /by 13/12/2021 1600");
+ assertEquals("[ ] Read Book A by: 12 Dec 2021 04:00 PM", a.get(0).toString());
+ assertEquals("[ ] Read Book B by: 13 Dec 2021 04:00 PM", a.get(1).toString());
+ a.delete(a.get(0));
+ assertEquals("[ ] Read Book B by: 13 Dec 2021 04:00 PM", a.get(0).toString());
+ }
+
+ @Test
+ void printTaskList_success() {
+ TaskList a = new TaskList();
+ a.addTask("cs1010", "Read Book C /by 14/12/2021 1600");
+ a.addTask("cs1010", "Read Book B /by 13/12/2021 1600");
+ a.addTask("cs1010", "Read Book A /by 12/12/2021 1600");
+ a.addTask("cs1010", "Read Book D /by 12/12/2021 1100");
+ // Create a stream to hold the output
+ ByteArrayOutputStream read = new ByteArrayOutputStream();
+ PrintStream save = new PrintStream(read);
+ // Tell Java to use your special stream
+ System.setOut(save);
+ // Print some output: goes to your special stream
+ a.printTasks(a.taskList);
+ List actualLines = List.of(read.toString().split("/n"));
+ List expectedLines = Collections.singletonList(
+ "1.[ ] Read Book C by: 14 Dec 2021 04:00 PM" + System.lineSeparator()
+ + "2.[ ] Read Book B by: 13 Dec 2021 04:00 PM" + System.lineSeparator()
+ + "3.[ ] Read Book A by: 12 Dec 2021 04:00 PM" + System.lineSeparator()
+ + "4.[ ] Read Book D by: 12 Dec 2021 11:00 AM" + System.lineSeparator());
+ assertLinesMatch(expectedLines, actualLines);
+ }
+
+ private List getWeeklyStrings(String module, int taskCount) {
+ return Collections.singletonList("Task List for " + module.toUpperCase(Locale.ROOT) + ":"
+ + System.lineSeparator()
+ + "There are " + taskCount + " tasks due within 7 days" + System.lineSeparator()
+ + "1.[ ] Read Book B by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_B))
+ + System.lineSeparator()
+ + "2.[ ] Read Book A by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_A))
+ + System.lineSeparator());
+ }
+
+ @Test
+ void showAllWeekly_success() {
+ TaskList a = new TaskList();
+ //a.addTask("cs1010", "Read Book C /by ");
+ a.addTask("cs1010", "Read Book B /by " + DATE_B);
+ a.addTask("cs1010", "Read Book A /by " + DATE_A);
+ a.addTask("cs1010", "Read Book C /by " + DATE_C);
+
+ // Create a stream to hold the output
+ String module = "moduleName";
+ ByteArrayOutputStream read = new ByteArrayOutputStream();
+ PrintStream save = new PrintStream(read);
+ // Tell Java to use your special stream
+ System.setOut(save);
+ // Print some output: goes to your special stream
+ a.showAllWeekly(module);
+ List actualLines = List.of(read.toString().split("/n"));
+ List expectedLines = getWeeklyStrings(module, a.weeklyTaskList().size());
+ assertLinesMatch(expectedLines, actualLines);
+ }
+
+ private List getMonthlyStrings(String module, int taskCount) {
+ return Collections.singletonList("Task List for " + module.toUpperCase(Locale.ROOT) + ":"
+ + System.lineSeparator()
+ + "There are " + taskCount + " tasks due within a month" + System.lineSeparator()
+ + "1.[ ] Read Book B by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_B))
+ + System.lineSeparator()
+ + "2.[ ] Read Book A by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_A))
+ + System.lineSeparator()
+ + "3.[ ] Read Book C by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_C))
+ + System.lineSeparator());
+ }
+
+ @Test
+ void showAllMonthly_success() {
+ TaskList a = new TaskList();
+ a.addTask("cs1010", "Read Book B /by " + DATE_B);
+ a.addTask("cs1010", "Read Book A /by " + DATE_A);
+ a.addTask("cs1010", "Read Book D /by " + DATE_D);
+ a.addTask("cs1010", "Read Book C /by " + DATE_C);
+ a.addTask("cs1010", "Read Book E /by " + DATE_E);
+ // Create a stream to hold the output
+ ByteArrayOutputStream read = new ByteArrayOutputStream();
+ PrintStream save = new PrintStream(read);
+ // Tell Java to use your special stream
+ System.setOut(save);
+ // Print some output: goes to your special stream
+ String module = "moduleName";
+ a.showAllMonthly(module);
+ List actualLines = List.of(read.toString().split("/n"));
+ List expectedLines = getMonthlyStrings(module, a.monthlyTaskList().size());
+ assertLinesMatch(expectedLines, actualLines);
+ }
+
+ private List getYearlyStrings(String module, int taskCount) {
+ return Collections.singletonList("Task List for " + module.toUpperCase(Locale.ROOT) + ":"
+ + System.lineSeparator()
+ + "There are " + taskCount + " tasks due within a year" + System.lineSeparator()
+ + "1.[ ] Read Book C by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_C))
+ + System.lineSeparator()
+ + "2.[ ] Read Book B by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_B))
+ + System.lineSeparator()
+ + "3.[ ] Read Book A by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_A))
+ + System.lineSeparator()
+ + "4.[ ] Read Book E by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_E))
+ + System.lineSeparator()
+ + "5.[LATE][ ] Read Book F by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_OVERDUE))
+ + System.lineSeparator());
+ }
+
+ @Test
+ void showAllYearly_success() {
+ TaskList a = new TaskList();
+ a.addTask("cs1010", "Read Book C /by " + DATE_C);
+ a.addTask("cs1010", "Read Book B /by " + DATE_B);
+ a.addTask("cs1010", "Read Book A /by " + DATE_A);
+ a.addTask("cs1010", "Read Book D /by " + DATE_D);
+ a.addTask("cs1010", "Read Book E /by " + DATE_E);
+ a.addTask("cs1010", "Read Book F /by " + DATE_OVERDUE);
+ // Create a stream to hold the output
+ ByteArrayOutputStream read = new ByteArrayOutputStream();
+ PrintStream save = new PrintStream(read);
+ // Tell Java to use your special stream
+ System.setOut(save);
+ // Print some output: goes to your special stream
+ String module = "moduleName";
+ a.showAllYearly(module);
+ List actualLines = List.of(read.toString().split("/n"));
+ List expectedLines = getYearlyStrings(module, a.yearlyTaskList().size());
+ assertLinesMatch(expectedLines, actualLines);
+ }
+
+ @Test
+ void sortByDate_equal_success() {
+ Task t1 = new Task("Assignment", DATE_E);
+ Task t2 = new Task("Quiz", DATE_E);
+ int result = TaskList.sortByDate.compare(t1, t2);
+ assertEquals(result, 0);
+ }
+
+ @Test
+ void sortByDate_leftHeavy_success() {
+ Task t1 = new Task("Assignment", DATE_E);
+ Task t2 = new Task("Quiz", DATE_D);
+ int result = TaskList.sortByDate.compare(t1, t2);
+ assertTrue(result < 0);
+ }
+
+ @Test
+ void sortByDate_rightHeavy_success() {
+ Task t1 = new Task("Assignment", DATE_D);
+ Task t2 = new Task("Quiz", DATE_E);
+ int result = TaskList.sortByDate.compare(t1, t2);
+ assertTrue(result > 0);
+ }
+
+ @Test
+ void sortByStatus_bothDone_success() {
+ Task t1 = new Task("Assignment", DATE_A);
+ Task t2 = new Task("Homework", DATE_A);
+ t1.markDone();
+ t2.markDone();
+ int result = TaskList.sortByStatus.compare(t1, t2);
+ assertEquals(result, 0);
+ }
+
+ @Test
+ void sortByStatus_bothNotDone_success() {
+ Task t1 = new Task("Assignment", DATE_A);
+ Task t2 = new Task("Homework", DATE_A);
+ int result = TaskList.sortByStatus.compare(t1, t2);
+ assertEquals(result, 0);
+ }
+
+ @Test
+ void sortByStatus_leftHeavy_success() {
+ Task t1 = new Task("Assignment", DATE_A);
+ Task t2 = new Task("Homework", DATE_A);
+ t1.markDone();
+ int result = TaskList.sortByStatus.compare(t1, t2);
+ assertTrue(result > 0);
+ }
+
+ @Test
+ void sortByStatus_rightHeavy_success() {
+ Task t1 = new Task("Assignment", DATE_A);
+ Task t2 = new Task("Homework", DATE_A);
+ t2.markDone();
+ int result = TaskList.sortByStatus.compare(t1, t2);
+ assertTrue(result < 0);
+ }
+
+ private List getSortedDateStrings(String module) {
+ return Collections.singletonList(module.toUpperCase(Locale.ROOT) + " successfully sorted by date"
+ + System.lineSeparator()
+ + "1.[LATE][ ] Read Book F by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_OVERDUE))
+ + System.lineSeparator()
+ + "2.[ ] Read Book A by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_A))
+ + System.lineSeparator()
+ + "3.[ ] Read Book B by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_B))
+ + System.lineSeparator()
+ + "4.[ ] Read Book C by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_C))
+ + System.lineSeparator()
+ + "5.[ ] Read Book E by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_E))
+ + System.lineSeparator()
+ + "6.[ ] Read Book D by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_D))
+ + System.lineSeparator());
+ }
+
+ @Test
+ void printTaskListSortedByDate_success() {
+ TaskList a = new TaskList();
+ a.addTask("cs1010", "Read Book C /by " + DATE_C);
+ a.addTask("cs1010", "Read Book B /by " + DATE_B);
+ a.addTask("cs1010", "Read Book A /by " + DATE_A);
+ a.addTask("cs1010", "Read Book D /by " + DATE_D);
+ a.addTask("cs1010", "Read Book E /by " + DATE_E);
+ a.addTask("cs1010", "Read Book F /by " + DATE_OVERDUE);
+ // Create a stream to hold the output
+ ByteArrayOutputStream read = new ByteArrayOutputStream();
+ PrintStream save = new PrintStream(read);
+ // Tell Java to use your special stream
+ System.setOut(save);
+ // Print some output: goes to your special stream
+ String module = "moduleName";
+ a.sortTaskListByDate(module);
+ a.printTasks(a.getTaskList());
+ List actualLines = List.of(read.toString().split("/n"));
+ List expectedLines = getSortedDateStrings(module);
+ assertLinesMatch(expectedLines, actualLines);
+ }
+
+ private List getSortedStatusStrings(String module) {
+ return Collections.singletonList(module.toUpperCase(Locale.ROOT) + " successfully sorted by status"
+ + System.lineSeparator()
+ + "1.[ ] Read Book D by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_D))
+ + System.lineSeparator()
+ + "2.[ ] Read Book E by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_E))
+ + System.lineSeparator()
+ + "3.[LATE][ ] Read Book F by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_OVERDUE))
+ + System.lineSeparator()
+ + "4.[X] Read Book C by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_C))
+ + System.lineSeparator()
+ + "5.[X] Read Book B by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_B))
+ + System.lineSeparator()
+ + "6.[X] Read Book A by: " + DateParser.dateStringOutput(DateParser.parseDate(DATE_A))
+ + System.lineSeparator());
+ }
+
+ @Test
+ void printTaskListSortedByStatus_success() {
+ TaskList a = new TaskList();
+ a.addTask("cs1010", "Read Book C /by " + DATE_C);
+ a.addTask("cs1010", "Read Book B /by " + DATE_B);
+ a.addTask("cs1010", "Read Book A /by " + DATE_A);
+ a.addTask("cs1010", "Read Book D /by " + DATE_D);
+ a.addTask("cs1010", "Read Book E /by " + DATE_E);
+ a.addTask("cs1010", "Read Book F /by " + DATE_OVERDUE);
+ a.get(2).markDone();
+ a.get(1).markDone();
+ a.get(0).markDone();
+ // Create a stream to hold the output
+ ByteArrayOutputStream read = new ByteArrayOutputStream();
+ PrintStream save = new PrintStream(read);
+ // Tell Java to use your special stream
+ System.setOut(save);
+ // Print some output: goes to your special stream
+ String module = "moduleName";
+ a.sortTaskListByStatus(module);
+ a.printTasks(a.getTaskList());
+ List actualLines = List.of(read.toString().split("/n"));
+ List expectedLines = getSortedStatusStrings(module);
+ assertLinesMatch(expectedLines, actualLines);
+ }
+}
+
diff --git a/src/test/java/task/TaskTest.java b/src/test/java/task/TaskTest.java
new file mode 100644
index 0000000000..a098edf229
--- /dev/null
+++ b/src/test/java/task/TaskTest.java
@@ -0,0 +1,90 @@
+package task;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+public class TaskTest {
+ @Test
+ void markDone_success() {
+ Task task = new Task("Submit Assignment 1", "19/09/2021 1600");
+ task.markDone();
+ assertTrue(task.isDone());
+ }
+
+ @Test
+ void markNotDone_success() {
+ Task task = new Task("Submit Assignment 1", "19/09/2021 1600");
+ task.markNotDone();
+ assertFalse(task.isDone());
+ }
+
+ @Test
+ void getStatusIcon_success() {
+ Task task = new Task("Submit Assignment 1", "19/09/2021 1600");
+ task.markDone();
+ task.createStatusIcon();
+ assertEquals("[X]", task.createStatusIcon());
+ }
+
+ @Test
+ void getDescription_success() {
+ Task task = new Task("Submit Assignment 1", "19/09/2021 1600");
+ assertEquals("Submit Assignment 1", task.getDescription());
+ }
+
+ @Test
+ void getDate_success() {
+ Task task = new Task("Submit Assignment 1", "19/09/2021 1600");
+ assertEquals("19/09/2021 1600", task.getDate());
+
+ }
+
+ @Test
+ void setDescription_success() {
+ Task task = new Task("Submit Assignment 1", "19/09/2021 1600");
+ task.setDescription("Review Assignment 1");
+ assertEquals("Review Assignment 1", task.getDescription());
+ }
+
+ @Test
+ void setDone_success() {
+ Task task = new Task("Submit Assignment 1", "19/09/2021 1600");
+ task.markDone();
+ task.setDone(false);
+ assertFalse(task.isDone());
+ }
+
+ @Test
+ void setDate_success() {
+ Task task = new Task("Submit Assignment 1", "20/09/2021 1600");
+ task.setDate("20/10/2021");
+ assertEquals("20/10/2021", task.getDate());
+ }
+
+ @Test
+ void setLate_success() {
+ Task task = new Task("Submit Assignment 1", "20/09/2021 1600");
+ task.setLate(true);
+ assertTrue(task.isLate);
+ }
+
+ @Test
+ void updateOverdue_success() {
+ Task task = new Task("Submit Assignment 1", "20/09/2021 1600");
+ assertFalse(task.isLate);
+ task.updateOverdue();
+ assertTrue(task.isLate);
+ }
+
+ @Test
+ void createFormattedDeadline_success() {
+ Task task = new Task("Submit Assignment 1", "20/09/2021 1600");
+ String actual = task.createFormattedDeadline();
+ String expected = "20 SEP 2021 4PM";
+
+ }
+
+}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 892cb6cae7..cd8f2710b3 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,9 +1,10 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
-
-What is your name?
-Hello James Gosling
+No save file found, starting with an empty template
+Welcome to NoCap
+You are now accessing semester: Y1S1
+
+
+Invalid Input!
+
+
+Thank you for using NoCap!
+Have a nice day!
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index f6ec2e9f95..1329553ca2 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -1 +1,2 @@
-James Gosling
\ No newline at end of file
+James Gosling
+bye
\ No newline at end of file
diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh
index 1dcbd12021..4f33ccf7fa 100755
--- a/text-ui-test/runtest.sh
+++ b/text-ui-test/runtest.sh
@@ -20,4 +20,4 @@ then
else
echo "Test failed!"
exit 1
-fi
+fi
\ No newline at end of file