messageList` is initialised. `messageList` is used to store the string inputs to be returned to `CommandResult`.
+
+**Step 6.** If `modulesList` is not empty, `moduleList` is iterated to search for the index of modules of the specified year and semester.
+When the year and semester of the module in `moduleList` matches the specified year and semester, its index is used to call `removeModule` of `ModelManager`.
+`removeModule(index)` is called to delete that module from `moduleList`.
+
+**Step 7.** `clearYearAndSemMods` returns `messageList` to `CommandResult`, and a new `CommandResult()` is constructed with the message to be printed to the user.
+
+**Step 8.** The `CommandResult` object is passed to the `Ui` component with a `printMessage()` method which prints the formatted message to the Command Line Interface.
+This prints the String **"Cleared!"** to indicate that the `clear y/1 s/1` command has been executed successfully.
+
+The following sequence diagram shows how the `clear y/1 s/1` command works:
+
+
+### Save planner to local drive
+The Save to local drive feature allows user to save the `ModuleList` and `User` details to a `penus.txt` file. It is facilitated by FileStorage and is executed after the `execute()` of a command by `LogicManager`.
+
+Given below is an example of how the saving mechanism behaves at each step.
+
+**Step 1.** The application is started for the first time which creates a `/data` directory and an empty text file `penus.txt` in that directory for file saving. The user inputs a valid command and is successfully executed, returning a `CommandResult` to `LogicManager`. When this is returned, the `StorageManager` executes the method `saveStorage()` acccepting the `ModuleList` and `User` objects as its arguments.
+
+**Step 2.** In `StorageManager` the `saveStorage()` method calls the `save()` method and passes the `ModuleList` and `User` objects to the `FileStorage`.
+
+**Step 3.** A `FileWriter` object is instantiated with the filepath `/data/penus.txt` and writes to the text file. If the `User` object has valid attributes of `name` and `course`, the first line written would be the User's name and course in the format `User ### [NAME] ### [COURSE]`.
+
+_Example:_
+```
+User ### Albert ### Computer Engineering
+```
+
+**Step 4.** The next few lines of the file would then be written with the modules in the `ModuleList`. The list is iterated through and the `FileWriter` writes an encoded format of the module to the file with the `encode()` method of a `Module`. The `encode()` method formats the attributes of a `Module` into the format of : `[STATUS] ### [MODULECODE] ### [YEAR] ### [SEMESTER]` (`### [GRADE]` if module is `Taken`)
+
+_Example:_
+```
+Taken ### CS2113 ### 2 ### 1 ### A+
+Plan ### CS2105 ### 3 ### 1
+```
+
+**Step 5.** The `FileWriter` is closed and the command flow continues as usual.
+
+The next time a user starts the program with a saved `penus.txt`:
+
+**Step 6.** Upon starting the program, the `start()` method of `Penus` is executed where the `StorageManager` would execute `loadStorage()` and `loadUser()` respectively into the `ModelManager` constructor. These methods call the `retrieveMods()` and `retrieveUser()` methods of the `FileStorage` respectively.
+
+**Step 7a.** In `retrieveMods()`, a `Scanner` and a `ArrayList` is instantiated. It takes in the lines of the `penus.txt` file which does not contain the keyword `User` and decodes it with `decodeModule()` method to return a `Module`. This `Module` is then added to the list. This process loops until there is no next line (aka the end of the file). This `ArrayList` is then passed back to the `ModelManager` constructor.
+
+**Step 7b.** In `retrieveUser()`, a `Scanner` and a `User` object is instantiated. The `Scanner` takes in the first line of the `penus.txt` file. If the keyword `User` is found, the line is decoded and sets the attribute `name` and `course` of the `User` object. This `User` is then passed back to the `ModelManager` constructor.
+
+**Step 8.** The `ModelManager` constructs the `ModuleList` and `User` object with the received objects respectively from the `StorageManager`. The user's saved state can then be continued on.
+
+Below is a class diagram of the classes pertaining to the save feature (some details omitted for simplicity):
+
+
+
+
+### [Proposed] Handle CS/CU modules
+The proposed mechanism is facilitated by `ModuleRetriever` and would allow users to identify CS/CU modules and add only CS or CU grade to them.
+>Note: In the current release, any grade can be assigned to a CS/CU module. It is up to users to verify if the module is CS/CU-able. Otherwise, CAP calculated will be wrong.
+
+**Step 1**. Implement a new method `isCSCU()` in `ModuleRetriever`. This method should communicate with the NUSMods API and check the `gradingBasisDescription` key of the JSON object. A boolean would be returned, indicating if the module is CS or CU graded.
+
+**Step 2**. Implement the `isCSCU()` method in the `TakenCommand` and `MarkCommand` commands. Check whether the indicated module only accepts CS or CU grade. An exception `InvalidGradeException` is thrown if the input grade is not CS/CU. Else the module is added.
+
+
+
+## Appendix A: Product scope
+
+### Target user profile
+- NUS engineering students
+- prefers desktop CLI over other available planner application(s)
+- prefers typing to mouse interactions
+- does not want to keep referring to NUSMods website
### Value proposition
+Manage a planner faster and more efficiently than a typical mouse/GUI driven application. As NUS engineering students have an extensive number of modules to cover in their 4 years of study in addition to their busy schedules, this app aims to help them plan modules more efficiently without the need to keep referring to different websites and GUIs (example NUSMods and a scheduler site).
+
+## Appendix B: User Stories
+Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*`
+
+| Priority | Version | As a ... | I want to ... | So that I can ... |
+|:--------:|:-------:|:------------------------:|:---------------------------------------|:--------------------------------------------------------------------|
+| *** | v1.0 | student | add modules to the planner | keep track of my planned and taken modules for my study term |
+| *** | v1.0 | student | remove modules | remove modules that I have accidentally added |
+| *** | v1.0 | student | list all the modules in my planner | see all modules I am planning for and have taken for the study term |
+| *** | v1.0 | student | mark a planned module as taken | update my planner accordingly |
+| *** | v1.0 | penultimate year student | display graduation status | graduate on time |
+| ** | v2.0 | student | see realtime module details | understand module details without referring to other websites |
+| ** | v2.0 | student | check whether I meet the prerequisites | plan my timetable with less worries |
+| *** | v2.0 | student | keep track of my CAP | I do not need to calculate my CAP after every semester |
+| *** | v2.0 | new user | see usage instructions | refer to them in case I forget the application commands |
+| ** | v2.0 | returning user | save my planner | I do not need to re-add my modules |
+| *** | v2.0 | new user | include my course | plan for course-specific core modules |
+| * | v2.1 | undergraduate student | include S/U for S/U-able modules | get an accurate CAP displayed |
+
+
+## Appendix C: Non-Functional Requirements
+- PENUS should work on any mainstream OS as long as it has Java 11 or above installed.
+- PENUS requires a **stable internet connection** as NUSMods API is used.
+- PENUS should be able to hold up to 1000 modules without a noticeable sluggishness in performance for typical usage.
+- 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.
+- Module data is limited to what is available on NUSMods, i.e. PENUS has module data for all modules offered by NUS in AY22/23 Semester 1 and 2. It is recommended to input updated modules offered in AY22/23 onwards.
+
+## Appendix D: Glossary
+
+* *Mainstream OS* - Windows, Linux, Unix, OS-X
+* *CLI* - Command Line Interface
+* *API* - Application Programming Interface
+
+
+
+## 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
+2. Download the jar file and copy into an empty folder
+3. Open a command terminal, change your directory to the folder you put the `penus.jar` file in, and use the `java -jar penus.jar` command to run the application. A CLI should appear in a few seconds.
+
+### Plan command
+1. Test case: `plan CS2113 y/2 s/2`
+ Expected output: A planned module is successfully added
+ Example:
+ ```
+ ___________________________________________________________
+ Module has been added:
+ Plan CS2113 year 2 semester 2
+ You have 1 module(s) in your planner
+ ___________________________________________________________
+ ```
+
+2. Test case: `plan CS0000 y/2 s/2`
+ Expected output: Error as invalid module is added
+ Example:
+ ```
+ ___________________________________________________________
+ Error: Invalid module. Please try again
+ ___________________________________________________________
+ ```
+
+3. Test case: `plan CS2113 y/0 s/2`
+ Expected output: Error as year is invalid
+ Example:
+ ```
+ ___________________________________________________________
+ Error: Year must be 1 to 4. Please try again.
+ ___________________________________________________________
+ ```
+
+4. Other incorrect plan commands to try:
+ - `plan CS2113 y/a s/2` or `plan CS2113 y/2 s/a`
+ - `plan CS2113 y/1 s/2 23`
+ - `plan CS2113 y/1 s/2 g/A`
+ Expected output: Similar to previous
+
+### Remove command
+1. Prerequisites: Add a few valid modules using `plan` or `taken`
+
+2. Test case: `remove CS2113` (where CS2113 is a module that exists in the list)
+Expected output: Module successfully removed
+Example:
+```
+ ___________________________________________________________
+ Module has been removed:
+ Plan CS2113 year 1 semester 2
+ You have 0 module(s) in your planner
+ ___________________________________________________________
+```
+
+3. Test case: `remove CS3244` (where CS3244 does not exist in the list)
+Expected output: Error as module does not exist within the listt
+Example:
+```
+ ___________________________________________________________
+ Error: No such module exists!
+ ___________________________________________________________
+```
+
+### Mark command
+1. Prerequisites: Add a few valid modules using `plan`
+
+2. Test case: `mark CS2113 g/A+` (where CS2113 is a planned module that exists in the list)
+Expected output: Successfully marked CS2113, converting it to a taken module
+Example:
+```
+ ___________________________________________________________
+ Module has been taken:
+ Taken CS2113 year 1 semester 1 A+
+ ___________________________________________________________
+```
+
+3. Test case: `mark CS2113`
+Expected output: Error as `g/GRADE` is not provided
+Example:
+```
+ ___________________________________________________________
+ Error: Try again in the format: mark MODULE_CODE g/GRADE
+ ___________________________________________________________
+```
+
+4. Test case: `mark CS2001 g/A`
+Expected output: Error as the module does not exist in the list
+Example:
+```
+ ___________________________________________________________
+ Error: No such module exists!
+ ___________________________________________________________
+```
+
+5. Test case: `mark CS2113 g/N`
+Expected output: Error as the grade is invalid
+Example:
+```
+ ___________________________________________________________
+ Error: Grade is not valid
+ ___________________________________________________________
+```
+
+6. Other incorrect mark commands
+- `mark CS2113 g/9` or `mark CS2113 g/9 1`
+- `mark g/9`
+Expected output: Similar as previous
-{Describe the value proposition: what problem does it solve?}
+### Loading data
+1. Edit the `/data/penus.txt` file which appears within the same folder as `penus.jar` before launching the program
+> Note: If a user is to be initialised, User MUST be at the TOP of the .txt file
-## User Stories
+2. Test case:
+```
+User ### John ### Computer Engineering
+Taken ### CS2113 ### 1 ### 1 ### A+
+```
+Expected: Program launches successfully with:
+- User: John (course: computer engineering)
+- CS2113 as a taken module in year 1 semester 1 with grade A+
-|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|
+3. Test case:
+```
+User ### John1 ### Computer Engineering
+```
+Expected: Error as Name has an integer
-## Non-Functional Requirements
+4. Test case:
+```
+User ### John ### Computer Hacking
+```
+Expected: Error as Course is invalid
-{Give non-functional requirements}
+5. Test case:
+```
+Plan ### CS0000 ### 1 ### 1
+```
+Expected: Error as module code is an invalid module
-## Glossary
+6. Test case:
+```
+Taken ### CS2113 ### 1 ### 1
+```
+Expected: Error as grade must be included
-* *glossary item* - Definition
+7. Test case:
+```
+Plan ### CS2113 ### 1 ### 1 ### A+
+```
+Expected: Grade is ignored, program launches successfully
-## Instructions for manual testing
+8. Other incorrect inputs:
+```
+Use ### John ### Computer Engineering
+Plan ### CS2113 ### s ### 1
+Plan ### CS2103 ### 1 ### 0
+Taken ### CS2100 ### 1 ### 1 ### n
+Plan #### CS2107 #### 1 #### 1
+```
+Expected: Similar to previous
-{Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing}
+### Details command
+1. Test case: `details CS2040c`
+ Expected output: Relevant details for CS2040C will be displayed
+ Example:
+ ```
+ ___________________________________________________________
+ CS2040C Data Structures and Algorithms
+ This module introduces students to the design and implementation of fundamental
+ data structures and algorithms. The module covers basic data structures
+ (linked lists, stacks, queues, hashtables, binary heaps, trees, and graphs),
+ searching and sorting algorithms, basic analysis of algorithms, and basic
+ object-oriented programming concepts.
+ Pre-Requisites: CS1010 or its equivalent
+ MCs: 4
+ Module cannot be SU-ed.
+ ___________________________________________________________
+ ```
+2. Test case: `details ABCDE`
+ Expected output: Error message will be shown
+ Example:
+ ```
+ ___________________________________________________________
+ ABCDE This module code is invalid. Try again.
+ ___________________________________________________________
+ ```
\ No newline at end of file
diff --git a/docs/README.md b/docs/README.md
index bbcc99c1e7..98ba4f2b4e 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,6 +1,6 @@
-# Duke
+# PENUS 👾
-{Give product intro here}
+Planning Engineering with NUS (PENUS) is a desktop app that helps engineering students oversee and plan their modules in their university life!
Useful links:
* [User Guide](UserGuide.md)
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index abd9fbe891..67b9a4a45c 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -1,42 +1,340 @@
-# User Guide
+# 📜 PENUS User Guide
+
+
+ ___ _____ ______ ___ ___ ___ _________
+/ \/ \ / \ / \/ \ / \ / \
+| __ \ | \/ || | | || _ |
+| |__| | | | || | | || \ |
+\ / | | | || | | |\ \_ /
+/ ___/___ | | | || | | | \_ \
+| | / \ | | | || | | |/ \ \
+|_______|/ <> _\| | | || \__/ || \_ |
+\ /| \____| /\ |\ /| |
+ \__|__/ \______/\___/ \_____/ \__________/ \_________/
+
## Introduction
-{Give a product intro}
-## Quick Start
+**Planning Engineering with NUS (PENUS)** is a desktop app for managing and planning your modules in your university life!
+Users are able to add modules that they have taken, plan for future modules and access useful features such as checking their graduation criteria and calculating their CAP. By using NUSmods API, users can retrieve important module details with a stroke of the keyboard.
+It is optimised for use via a Command Line Interface (CLI).
+For students that can type fast, PENUS can help them plan and track their modules for all four years of their time in university more efficiently.
+
+
+## Table of Contents 📔
+- [Quick Start](#quick-start-⚙️)
+- [NUSMods API](#NUSMods-API)
+- [Features](#features-👾)
+ + [Initialisation: `init`](#initialisation-init)
+ + [Help: `help`](#help-help)
+ + [Add taken modules: `taken`](#add-taken-modules-taken)
+ + [Plan untaken modules: `plan`](#plan-untaken-modules-plan)
+ + [Remove a module: `remove`](#remove-a-module-remove)
+ + [Mark module as taken: `mark`](#mark-module-as-taken-mark)
+ + [View modules: `list`](#view-modules-list)
+ + [View graduation status: `status`](#view-graduation-status-status)
+ + [View module details: `details`](#view-module-details-details)
+ + [Clear modules: `clear`](#clear-modules-clear)
+ + [Exit: `exit`](#exit-the-program-exit)
+ + [Saving the data](#saving-the-data)
+ + [Editing the data file](#editing-the-data-file)
+- [FAQ](#faq-💻)
+- [Command Summary](#command-summary-🔑)
-{Give steps to get started quickly}
+## Quick Start ⚙️
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 PENUS from [here](https://github.com/AY2223S2-CS2113-T11-2/tp/releases/download/v2.0/penus.jar).
+3. Copy the file to the folder you want to use as the home folder for your PENUS.
+4. Open a command terminal, cd into the folder you put the jar file in, and use the `java -jar penus.jar` command to run the application.
+5. Type the command in the Command Line Interface and press Enter to execute it.
+_Some example commands you can try:_
+ - `init n/John c/4`: sets user name as John and course as Computer Engineering
+ - `taken CS2113 y/2 s/2 g/A+`: Adds CS2113 to Year 2 Semester 2 with grade A+.
+ - `plan CS2040C y/1 s/2`: Adds CS2040C to Year 1 Semester 2 as untaken
+ - `list`: list all modules in the planner with overall CAP
+ - `remove CS2113`: Deletes the module CS2113
+ - `clear`: Deletes all modules in the planner
+ - `status`: gets the status of core modules and MCs taken.
+ - `exit`: exits the application.
+6. Refer to features below for details of each command
+
+## NUSMods API
+Several of our features access the NUSMods API to retrieve data for the modules.
+These features include `taken`, `plan`, `list`, `status` and `details`.
+
+
+
+Here are some points to note:
+- Please ensure you have a **stable internet connection** when using PENUS.
+- Retrieving data from the NUSMods API may take a while, please expect some loading time when using the features listed above.
+
+## Features 👾
+
+### Initialisation: `init`
+On startup, there will be a prompt for first time users to type `init` to start the initialisation process.
+
+
+
+Format:`init n/[NAME] c/[COURSE NUMBER]`
+
+
+
+| Course Number | Course |
+|---------------|-------------------------------------|
+| 1 | Biomedical Engineering |
+| 2 | Chemical Engineering |
+| 3 | Civil Engineering |
+| 4 | Computer Engineering |
+| 5 | Electrical Engineering |
+| 6 | Environmental Engineering |
+| 7 | Industrial and Systems Engineering |
+| 8 | Mechanical Engineering |
+
+Example:
+- `init n/John Doe c/1` Initiates a user with the name `John` and course `Biomedical Engineering`.
+
+Note:
+- Each program is limited to 1 user, ie. Initialisation more than once will overwrite the current user `name` and `course` and not create a
+separate profile
+
+
+
+### Help: `help`
+Shows a message with the format and functionality of all features.
+
+
+
+Format:`help`
+
+
+
+### Add taken modules: `taken`
+Adds a module to the planner as a taken module.
+
+
+
+Format:`taken [MODULE CODE] y/[YEAR] s/[SEMESTER] g/[GRADE]`
+
+
+
+Example:
+- `taken CG1111A y/1 s/1 g/A+` means that you have `taken` and completed the `CG1111A` module in `Year 1` `Semester 1` with `A+` grade
+
+
+
+
+### Plan untaken modules: `plan`
+Adds a module to the planner as a module that has not been taken or completed.
+
+
+
+Format:`plan [MODULE CODE] y/[YEAR] s/[SEMESTER]`
+
+
+
+Example:
+- `plan CG2111A y/1 s/2` means that you `plan` on taking `CG2111A` in `Year 1` `Semester 2`.
+
+
+
+### Remove a module: `remove`
+
+Removes a module from the planner.
+
+
+
+Format:`remove [MODULE CODE]`
+
+
+
+Example:
+- `remove CS2113`
+
+
+
+### Mark module as taken: `mark`
+Marks the module that has been taken and update its grade.
+
Module must already have been added to the planner using the `plan` command.
+
+
+
+Format:`mark [MODULE CODE] g/[GRADE]`
+
+
+
+Example:
+- `mark CG2111A g/A+`
+
+
+
+### View modules: `list`
+Displays a list of all modules taken or planned in a specified Year and/or Semester.
+- If Year/Semester is not specified, then all modules will be listed.
+- Filter is optional
+- The difference between a `plan` and `taken` module is indicated by the presence of a grade. (only `taken` module has a grade)
+
+
+
+Format:`list [FILTER]`
+
+
+
+| Filter | Action | Example |
+|-----------------------|----------------------------------------|-----------------|
+| [empty] | Lists all modules in the planner | `list` |
+| y/[YEAR] | Lists modules in the specific year | `list y/1` |
+| y/[YEAR] s/[SEMESTER] | Lists modules in the specific semester | `list y/1 s/1` |
+
+Example:
+- `list` Display all modules taken.
+
+
+
+- `list y/2` Displays modules taken in Year 2.
+
+
+
+- `list y/2 s/2` Displays modules taken in Year 2 Semester 2.
+
+
+
+
+
+### View graduation status: `status`
+Displays the status of core modules* and MCs taken.
+
+**Core modules are based on AY22/23*
+
+
+
+Format:`status`
+
+
+
+Example: `status` *(course: Computer Engineering)*
+
+
+
+
+Note:
+- The status command may take a while to load. Please ensure a stable internet connection.
+
+
+
+### View module details: `details`
+Display the module title, description, pre-requisites, MCs and SU option.
+
+Please ensure you have a stable internet connection when using this command.
+
+
+
+Format: `details [MODULECODE]`
+
+
+
+Example:
+- `details CS1010`
+
+
+
+
+
+### Clear modules: `clear`
+Clears all modules in a specified Year and/or Semester.
+- If neither Year nor Semester are specified, then all modules in the planner will be cleared.
+- Filter is optional.
+
+
+
+Format:`clear [FILTER]`
+
+
+
+| Filter | Action | Example |
+|-----------------------|----------------------------------------|-----------------|
+| `[empty]` | Clears all modules in the planner | `clear` |
+| `y/[YEAR]` | Clears modules in the specific year | `clear y/1` |
+| `y/[YEAR] s/[SEMESTER]` | Clears modules in the specific semester | `clear y/1 s/1` |
+
+Example:
+- `clear` Clears all modules in the planner.
+
+
+
+
+- `clear y/1` Clears all modules planned/taken in Year 1.
+
+
+
+- `clear y/2 s/1` Clears all modules planned/taken in Year 2 Semester 1.
+
+
+
+
+
+### Exit the program: `exit`
+Exits the program.
+
+
+
+Format: `exit`
+
+
+
+
+### Saving the data
+PENUS's data are saved in the hard disk automatically after exiting the program.
+There is no need to save manually.
+
+
-## Features
+### Editing the data file
+PENUS's data are saved as a .txt file in `[JAR file location]/data/penus.txt`.
+Edits must be made according to the formatting of the data.
+- User MUST be at the **top of the file**
+- If 2 Users are declared, only the first declared User will be initialised
+- Line breaks/spacings are allowed
+- Inclusive of valid formatting of parameters as mentioned in features above
-{Give detailed description of each feature}
+
-### Adding a todo: `todo`
-Adds a new item to the list of todo items.
+Format:
-Format: `todo n/TODO_NAME d/DEADLINE`
+| Line | Description | Format |
+|------------|--------------------------|---------------------------------------------------------------------------------------------|
+| 1 | User's Name and Course | User ### NAME ### COURSE_NAME |
+| 2 to n | Taken or Planned Modules | Taken ### MODULECODE ### YEAR ### SEM ### GRADE
Plan ### MODULECODE ### YEAR ### SEM |
-* The `DEADLINE` can be in a natural language format.
-* The `TODO_NAME` cannot contain punctuation.
+
-Example of usage:
+Example:
-`todo n/Write the rest of the User Guide d/next week`
+
-`todo n/Refactor the User Guide to remove passive voice d/13/04/2020`
+
-## FAQ
+## FAQ 💻
**Q**: How do I transfer my data to another computer?
-**A**: {your answer here}
+**A**: Install the app in the other computer and overwrite the empty data file it creates with the file in `/data/penus.txt` that contains the data of your previous PENUS application.
-## Command Summary
+
-{Give a 'cheat sheet' of commands here}
+## Command Summary 🔑
-* Add todo `todo n/TODO_NAME d/DEADLINE`
+| Command | Format |
+|--------------|-----------------------------------------------|
+| **init** | `init n/NAME c/COURSE NUMBER` |
+| **help** | `help` |
+| **taken** | `taken MODULE_CODE y/YEAR s/SEMESTER g/GRADE` |
+| **plan** | `plan MODULE_CODE y/YEAR s/SEMESTER` |
+| **remove** | `remove MODULE_CODE` |
+| **mark** | `mark MODULE_CODE` |
+| **list** | `list (FILTER)` |
+| **status** | `status` |
+| **details** | `details MODULE_CODE` |
+| **clear** | `clear (FILTER)` |
+| **exit** | `exit` |
diff --git a/docs/team/ansenn.md b/docs/team/ansenn.md
new file mode 100644
index 0000000000..9b6f65cfe1
--- /dev/null
+++ b/docs/team/ansenn.md
@@ -0,0 +1,54 @@
+# Ansenn - Project Portfolio Page
+
+### Project: PENUS
+PENUS is a desktop app for NUS engineering students to manage and plan their modules in their university life. The user interacts with it using a CLI. It is written in Java, and has about 6 kLoC.
+
+Given below are my contributions to the project.
+
+- **Feature** : List command `list`
+ - What it does: Displays a list of all modules taken or planned in a specified Year and/or Semester.
+ - Highlights : Has a flexible format to its inputs with 3 different variations, which made it harder to parse the user's input and check for exceptions.
+
+- **Feature** : Help Command `help`
+ - What it does: Displays the list of accepted commands, and each command's purpose and use.
+
+- **Feature**: Clear Command `clear`
+ - What it does: Clears all modules added to the planner in a specified Year and/or Semester.
+ - Highlights : Similar to the `list` command, the `clear` command has a flexible format to its inputs with 3 different variations, which made it harder to parse the user's input and check for exceptions.
+
+- **Enhancement** : Changed the PENUS logo
+ - Improved the logo of PENUS
+
+- **Code contributed**: [RepoSense link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=ansenn&breakdown=true)
+
+- **Contributions to UG**:
+ - Edit formatting of various commands in the UG
+ - Improved clarity of the UG in response to PE-D comments
+ - Written Documentation for:
+ - List Command
+ - Help Command
+ - Clear Command
+
+- **Contributions to DG**:
+ - Written Documentation for:
+ - List Command
+ - Help Command
+ - Clear Command
+ - Sequence diagram for Clear Command (See extracts)
+
+- **Contributions to team-based tasks**:
+ - Assigned teammates for milestones and issues
+ - Organised and assigned PE-D issues to self and teammates
+ - Wrote and edited javadoc for some methods
+
+- **Review/mentoring contributions:**:
+ - PRs reviewed : [\#15](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/15), [\#43](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/43), [\#198](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/198)
+
+- **Contributions beyond the project team**:
+ - Reported 11 bugs for PE-D
+
+- **Contributions to the Developer Guide (Extracts)**:
+
+Clear Command Sequence diagram:
+
+
\ No newline at end of file
diff --git a/docs/team/bentohset.md b/docs/team/bentohset.md
new file mode 100644
index 0000000000..33d3024aec
--- /dev/null
+++ b/docs/team/bentohset.md
@@ -0,0 +1,88 @@
+### Project: PENUS
+PENUS is a desktop app for NUS engineering students to manage and plan their modules in their university life. The user interacts with it using a CLI. It is written in Java, and has about 6 kLoC.
+
+Given below are my contributions to the project.
+
+- **Feature**: Plan and Taken command
+ - What it does: Adds a module (plan or taken) to the module list.
+ - Highlights: Decided on design and chose to implement by overloading a constructor.
+- **Feature**: Mark Command
+- **Feature**: Remove Command
+- **Feature**: Exit Command
+- **Feature**: Storage saving and loading
+ - What it does: enables saving data from the module list by encoding it and loads upon initialising the application
+ - Highlights: Required in-depth understanding of Scanner API as 2 different types of classes needed to be read (User and Module). Challenging as all errors that the Parser class handled will need to be implemented for the loading of the .txt file.
+
+- **Feature**: Resource retrieving from .txt file
+ - Justification: Allows user to read preset core module details for the status command.
+ - What it does: retrieves data from a .txt file with all core module details gathered. Saves it as a resource.
+ - Highlights: Used `getResourceAsStream()` java API. Challenging as it required in-depth understanding of folder architecture and gradle. The implementation was also challenging as it required a different way to scan input. Had to manually edit vscode and intellij settings to enable resources.
+
+- **Enhancement**: Overhaul architecture to adopt MVC design pattern
+ - Justification: Improves abstraction and OOP significantly and made it easier for teammates to work on features independently as working on previous codebase asynchronously made the code messy.
+ - Highlights: Affected all existing code. Required an in-depth understanding of design patterns. The implementation too was challenging as it required changes to existing commands on top of additional methods and classes.
+ - Credits: [AddressBook 2](https://github.com/se-edu/addressbook-level2) and [AddressBook 3](https://github.com/se-edu/addressbook-level3).
+
+- **Enchancement**: JUnit 100% branch coverage for PlanCommand, TakenCommand, RemoveCommand, MarkCommand
+
+- **Code contributed**: [RepoSense link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=bentohset&breakdown=true)
+
+- **Contributions to UG**:
+ - Table of contents and links
+ - Quick start, FAQ, Command summary
+ - Emojis to enhance look
+
+- **Contributions to DG**:
+ - Table of contents and overall structure
+ - Acknowledgements, Design, Appendix A, B, C, D and E sections
+ - Implementation section: Add module, Remove module, Mark module, Save planner, [Proposed] handle CS/CU
+ - Diagrams: (refer to extract)
+
+- **Contributions to team-based tasks**:
+ - Set up GitHub team org and repo
+ - Release management for v1.0 on GitHub
+ - Set up issue tracker and milestones
+ - Maintain issue tracker and milestones
+ - Add javadoc to most methods
+
+- **Review/mentoring contributions:**:
+ - PRs reviewed ([\#14](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/14), [\#30](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/30), [\#44](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/44), [\#48](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/48), [\#65](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/65), [\#67](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/67), [\#72](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/72), [\#186](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/186), [\#196](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/196))
+
+- **Contributions beyond the project team**:
+ - Reported 16 bugs for PE-D
+
+- **Contributions to the Developer Guide (Extracts)**:
+
+**Architecture diagram:**
+
+
+
+Ui class diagram: | Logic object diagram:
+:-------------------------:|:-------------------------:
+
|
+**Model object diagram:** | **Storage object diagram:**
+
|
+
+**Add module sequence diagram:**
+
+
+
+**Remove module sequence diagram:**
+
+
+
+**Mark module sequence diagram:**
+
+
+
+**List sequence diagram:**
+
+
+
+**Grade class diagram:**
+
+
+
+**Save feature class diagram:**
+
+
diff --git a/docs/team/chiayuxuan.md b/docs/team/chiayuxuan.md
new file mode 100644
index 0000000000..5bade634fe
--- /dev/null
+++ b/docs/team/chiayuxuan.md
@@ -0,0 +1,33 @@
+# Chia Yu Xuan - Project Portfolio Page
+
+## Overview
+Project PENUS
+PENUS is a desktop app for NUS engineering students to manage and plan their modules
+in their university life. The user interacts with it using a CLI.
+It is written in Java, and has about 6 kLoC.
+
+### Summary of Contributions
+
+- **New Functionality**: CAP calculator
+ - What it does: Provide a method to calculate CAP for use in List Command, which
+ displays CAP for Semester and Overall.
+- **Improved Feature**: Help Command
+ - What it does: Provides user with a list of commands and its description in the CLI.
+- **Code contributed**: [RepoSense link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=chiayuxuan&breakdown=true)
+
+
+- **Contributions to DG**:
+ - Implementation of CAP calculator
+
+
+- **Contributions to team-based tasks**:
+ - Written JUnit tests
+ - Added Javadocs
+
+
+- **Contributions beyond the project team**:
+ - Reported 9 bugs for PE-D
+
+
+
+
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/sriram-senthilkr.md b/docs/team/sriram-senthilkr.md
new file mode 100644
index 0000000000..30e9a866cb
--- /dev/null
+++ b/docs/team/sriram-senthilkr.md
@@ -0,0 +1,60 @@
+### Project: PENUS
+PENUS is a desktop app for NUS engineering students to manage and plan their modules in their university life. The user interacts with it using a CLI. It is written in Java, and has about 6 kLoC.
+
+Given below are my contributions to the project.
+
+- **Feature**: Details command `details`
+ - What it does: Retrieves details of any module that is in NUSMods, such as its title, description,
+ Pre-requisites, Number of Modular Credits and whether it can be SU-ed.
+ - Highlights: Will be able to display each individual detail on its own, so even if one detail is not available,
+ it will not affect the command's functionality.
+
+- **Feature**: Connecting to external NUSMods API
+ - Justification: User will be able to retrieve details of a wide range of NUS modules. Hardcoding all the
+ modules will not be feasible, and will limit the functionality for the user.
+ - What it does: Creates a "GET" request to the NUSMods API for a particular module, and retrieves all the
+ information given from the API regarding the module.
+ - Highlights: Required in-depth understanding of `HttpURLConnection` in Java. Used "GET" requests to retrieve for
+ individual modules.
+
+- **Feature**: Working with JSON file to retrieve individual details from NUSMods API
+ - Justification: NUSMods API returns a JSON file of the all the details required, which is read as String in
+ Java. It is stored as JSON again to retrieve details easily.
+ - What it does: Parses retrieved String back as JSONObject, and stores in JSONArray.
+ - Highlights: Had to install external dependency `com.googlecode.json-simple`. Challenging to use `JSONParser`,
+ `JSONArray`, and `JSONObject` to retrieve data from the API, as the API had nested information that were hard to
+ parse and use using the parser.
+
+- **Tests Written**:
+ - Module Retriever Tests
+ - Details Compiler Tests
+
+- **Enhancement**: Made any type of information from NUSMods API retrievable, regardless of its JSON formatting.
+
+- **Code contributed**: [RepoSense link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=sriram-senthilkr&breakdown=true)
+
+- **Contributions to UG**:
+ - Written documentation for:
+ - Get Details feature
+
+- **Contributions to DG**:
+ - Implementation section: Get Module details
+ - Diagrams (refer to extract): get details sequence
+
+- **Contributions to team-based tasks**:
+ - Brainstormed for possible tP ideas
+ - Assigned teammates for milestone
+ - Add javadoc to most methods
+ - User Stories
+
+- **Review/mentoring contributions:**:
+ - PRs reviewed [#196](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/196), [#201](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/201)
+
+- **Contributions beyond the project team**:
+ - Reported 11 bugs for PE-D
+
+- **Contributions to the Developer Guide (Extracts)**:
+
+Get Details sequence diagram:
+
+
\ No newline at end of file
diff --git a/docs/team/tayjiunyuan.md b/docs/team/tayjiunyuan.md
new file mode 100644
index 0000000000..c7bd9ba83f
--- /dev/null
+++ b/docs/team/tayjiunyuan.md
@@ -0,0 +1,64 @@
+### Tay Jiun Yuan's Project Portfolio Page
+### Project: PENUS
+PENUS is a desktop app for NUS engineering students to manage and plan their modules in their university life. The user interacts with it using a CLI. It is written in Java, and has about 6 kLoC.
+Given below are my contributions to the project.
+
+- **New Feature**: Initialization Command
+ - What it does: Initialises the User's Name and Course
+ - Justification: Name allows users to personalize their PENUS experience and course allows the status command to retrieve the core modules of the user.
+ - Highlights: Improved usability as it is not feasible to have the user type out their entire course name as it would be prone to misspelling/ wrong format. Decided to map each course to a number instead. Created a new class User to store the information.
+
+- **New Feature**: Get core modules of user
+ - What it does: Retrieves a list of the core modules of the user.
+ - Justification: Allows the Status Command to retrieve the core module information of the user based on his/her course.
+ - Highlights: Hardcoded each course's core modules (found through each course's website) into a txt file and created a method getCoreMods() to read the txt file and add each core module code under the correct course in a hashmap.
+
+- **New Feature**: Status Command
+ - What it does: Displays the user's name, course, core module status and total module credits taken
+ - Justification: Provides a summary of the user's progress to graduation.
+ - Highlights: Very challenging to implement due to the number of components. Retrieving the status of each core module requires calling the methods to get the core module codes, checking if the user has taken it. Also required API calls to retrieve the module information and separate methods to get the MC progress of the user. Very difficult to achieve everything whilst maintaining strict OOP practices.
+
+- **New Feature**: Sample Data
+ - What it does: Creates an instance of the model, with sample data.
+ - Justification: When testing, the model can be instantiated with pre-added data, instead of manually adding the data in through commands.
+
+- **Tests Written**:
+ - Initialisation Command Tests
+ - Status Command Tests
+ - Parser Tests (for initialisation parser and status parser)
+ - ModuleList Tests (for getGEXX() methods)
+ - Sample Data Tests
+ - Clear Command Tests
+
+- **Enhancement**: Planned new architecture to adopt MVC design pattern
+ - Justification: Improves abstraction and OOP significantly and made it easier for teammates to work on features independently as working on previous codebase asynchronously made the code messy. Made architecture diagrams cleaner and simple.
+ - Highlights: Came up with the rough planning of refactored architecture adopting MVC pattern with Benjamin. Only responsible for planning and not implementation.
+ - Credits: [AddressBook 2](https://github.com/se-edu/addressbook-level2) and [AddressBook 3](https://github.com/se-edu/addressbook-level3).
+
+- **Code contributed**: [RepoSense link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=tayjiunyuan&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+
+- **Contributions to UG**:
+ - Reformatted the entire UG from Google Docs into md file
+ - Written Documentation for:
+ - Introduction
+ - NUSModsAPI
+ - Status Feature
+ - Initialisation Feature
+ - Editing Data File (partial)
+
+- **Contributions to DG**:
+ - Status Command
+ - Wrote documentation for Implementation and drew UML Sequence Diagram
+ - Initialisation Command
+ - Wrote documentation for Implementation and drew UML Sequence Diagram
+
+- **Contributions to team-based tasks**:
+ - Assigned teammates for milestones
+ - Opened and managed milestone v2.1
+ - User Stories
+
+- **Review/mentoring contributions:**:
+ - PRs reviewed/merged: [#14](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/14), [#25](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/25), [#27](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/27), [#186](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/186), [#191](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/191), [#196](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/196), [#198](https://github.com/AY2223S2-CS2113-T11-2/tp/pull/198)
+
+- **Contributions beyond the project team**:
+ - Reported 5 bugs for PE-D
diff --git a/docs/ugmedia/clear1.png b/docs/ugmedia/clear1.png
new file mode 100644
index 0000000000..d6afcab22d
Binary files /dev/null and b/docs/ugmedia/clear1.png differ
diff --git a/docs/ugmedia/clear2.png b/docs/ugmedia/clear2.png
new file mode 100644
index 0000000000..a7aac83e15
Binary files /dev/null and b/docs/ugmedia/clear2.png differ
diff --git a/docs/ugmedia/clear3.png b/docs/ugmedia/clear3.png
new file mode 100644
index 0000000000..8f43823bbb
Binary files /dev/null and b/docs/ugmedia/clear3.png differ
diff --git a/docs/ugmedia/details1.png b/docs/ugmedia/details1.png
new file mode 100644
index 0000000000..1ad6dc6d13
Binary files /dev/null and b/docs/ugmedia/details1.png differ
diff --git a/docs/ugmedia/list1.png b/docs/ugmedia/list1.png
new file mode 100644
index 0000000000..c6e542629a
Binary files /dev/null and b/docs/ugmedia/list1.png differ
diff --git a/docs/ugmedia/list2.png b/docs/ugmedia/list2.png
new file mode 100644
index 0000000000..4e9a799457
Binary files /dev/null and b/docs/ugmedia/list2.png differ
diff --git a/docs/ugmedia/list3.png b/docs/ugmedia/list3.png
new file mode 100644
index 0000000000..4c61a8fc2b
Binary files /dev/null and b/docs/ugmedia/list3.png differ
diff --git a/docs/ugmedia/penustxt.png b/docs/ugmedia/penustxt.png
new file mode 100644
index 0000000000..0aa0a19ec1
Binary files /dev/null and b/docs/ugmedia/penustxt.png differ
diff --git a/docs/ugmedia/status1.png b/docs/ugmedia/status1.png
new file mode 100644
index 0000000000..231ef2133b
Binary files /dev/null and b/docs/ugmedia/status1.png differ
diff --git a/docs/ugmedia/status2.png b/docs/ugmedia/status2.png
new file mode 100644
index 0000000000..cea9fc8f23
Binary files /dev/null and b/docs/ugmedia/status2.png differ
diff --git a/docs/uml/CAP.png b/docs/uml/CAP.png
new file mode 100644
index 0000000000..85c25e6df5
Binary files /dev/null and b/docs/uml/CAP.png differ
diff --git a/docs/uml/diagrams/AddModSequence.png b/docs/uml/diagrams/AddModSequence.png
new file mode 100644
index 0000000000..a2639d456b
Binary files /dev/null and b/docs/uml/diagrams/AddModSequence.png differ
diff --git a/docs/uml/diagrams/Architecture.png b/docs/uml/diagrams/Architecture.png
new file mode 100644
index 0000000000..13e5b1c650
Binary files /dev/null and b/docs/uml/diagrams/Architecture.png differ
diff --git a/docs/uml/diagrams/ClearCommandSequence.png b/docs/uml/diagrams/ClearCommandSequence.png
new file mode 100644
index 0000000000..cc69ac47c7
Binary files /dev/null and b/docs/uml/diagrams/ClearCommandSequence.png differ
diff --git a/docs/uml/diagrams/DetailsSequence.png b/docs/uml/diagrams/DetailsSequence.png
new file mode 100644
index 0000000000..89a40249aa
Binary files /dev/null and b/docs/uml/diagrams/DetailsSequence.png differ
diff --git a/docs/uml/diagrams/GradeClass.png b/docs/uml/diagrams/GradeClass.png
new file mode 100644
index 0000000000..a07251ddd5
Binary files /dev/null and b/docs/uml/diagrams/GradeClass.png differ
diff --git a/docs/uml/diagrams/InitSequenceDiagram.png b/docs/uml/diagrams/InitSequenceDiagram.png
new file mode 100644
index 0000000000..1a977de9a9
Binary files /dev/null and b/docs/uml/diagrams/InitSequenceDiagram.png differ
diff --git a/docs/uml/diagrams/ListSequence.png b/docs/uml/diagrams/ListSequence.png
new file mode 100644
index 0000000000..73001bcc47
Binary files /dev/null and b/docs/uml/diagrams/ListSequence.png differ
diff --git a/docs/uml/diagrams/LogicClass.png b/docs/uml/diagrams/LogicClass.png
new file mode 100644
index 0000000000..13925f8eb3
Binary files /dev/null and b/docs/uml/diagrams/LogicClass.png differ
diff --git a/docs/uml/diagrams/MarkModSequence.png b/docs/uml/diagrams/MarkModSequence.png
new file mode 100644
index 0000000000..177e337e2e
Binary files /dev/null and b/docs/uml/diagrams/MarkModSequence.png differ
diff --git a/docs/uml/diagrams/ModelClass.png b/docs/uml/diagrams/ModelClass.png
new file mode 100644
index 0000000000..03b321d016
Binary files /dev/null and b/docs/uml/diagrams/ModelClass.png differ
diff --git a/docs/uml/diagrams/RemoveModSequence.png b/docs/uml/diagrams/RemoveModSequence.png
new file mode 100644
index 0000000000..0315d05cd6
Binary files /dev/null and b/docs/uml/diagrams/RemoveModSequence.png differ
diff --git a/docs/uml/diagrams/SaveFeatureClass.png b/docs/uml/diagrams/SaveFeatureClass.png
new file mode 100644
index 0000000000..096e62a4b0
Binary files /dev/null and b/docs/uml/diagrams/SaveFeatureClass.png differ
diff --git a/docs/uml/diagrams/StatusSequenceDiagram.png b/docs/uml/diagrams/StatusSequenceDiagram.png
new file mode 100644
index 0000000000..94973febdb
Binary files /dev/null and b/docs/uml/diagrams/StatusSequenceDiagram.png differ
diff --git a/docs/uml/diagrams/StorageClass.png b/docs/uml/diagrams/StorageClass.png
new file mode 100644
index 0000000000..20eff549a2
Binary files /dev/null and b/docs/uml/diagrams/StorageClass.png differ
diff --git a/docs/uml/diagrams/UiClass.png b/docs/uml/diagrams/UiClass.png
new file mode 100644
index 0000000000..3eb800d769
Binary files /dev/null and b/docs/uml/diagrams/UiClass.png differ
diff --git a/docs/uml/sourcecode/AddModSequence.puml b/docs/uml/sourcecode/AddModSequence.puml
new file mode 100644
index 0000000000..3314af848e
--- /dev/null
+++ b/docs/uml/sourcecode/AddModSequence.puml
@@ -0,0 +1,77 @@
+@startuml
+participant User
+participant ":Ui" as Ui
+
+box "Logic" #LightBlue
+participant ":LogicManager" as LogicManager
+participant ":Parser" as Parser
+participant "command:PlanCommand" as PlanCommand
+participant "result: CommandResult" as CommandResult
+end box
+
+box "Model" #LightPink
+participant ":ModelManager" as ModelManager
+participant ":ModuleList" as ModuleList
+end box
+
+
+User -> Ui: getUserCommand() "plan CS2113 y/1 s/2"
+activate Ui
+Ui -> LogicManager: getCommand()
+activate LogicManager
+
+
+LogicManager -> Parser: parseCommand()
+activate Parser
+
+
+create PlanCommand
+Parser -> PlanCommand
+activate PlanCommand
+
+PlanCommand --> Parser: command
+deactivate PlanCommand
+
+
+Parser --> LogicManager: command
+deactivate Parser
+
+
+LogicManager -> PlanCommand: execute()
+activate PlanCommand
+
+
+
+PlanCommand -> ModelManager: addModule()
+activate ModelManager
+
+ModelManager -> ModuleList: addModule()
+activate ModuleList
+
+ModuleList -> ModuleList: add()
+
+ModuleList --> ModelManager:
+deactivate ModuleList
+
+ModelManager --> PlanCommand
+deactivate ModelManager
+
+create CommandResult
+PlanCommand -> CommandResult
+activate CommandResult
+CommandResult --> PlanCommand
+deactivate CommandResult
+destroy CommandResult
+
+
+PlanCommand --> LogicManager: result
+deactivate PlanCommand
+destroy PlanCommand
+
+LogicManager --> Ui: printResult()
+deactivate LogicManager
+
+Ui --> User
+deactivate Ui
+
+@enduml
\ No newline at end of file
diff --git a/docs/uml/sourcecode/Architecture.puml b/docs/uml/sourcecode/Architecture.puml
new file mode 100644
index 0000000000..bd4836df02
--- /dev/null
+++ b/docs/uml/sourcecode/Architecture.puml
@@ -0,0 +1,53 @@
+@startuml
+!include
+!include
+!include
+
+skinparam classAttributeIconSize 0
+hide empty members
+hide circle
+
+class "<$user>" as User
+
+class "<$globe_internet>" as Web
+
+class "<$documents>" as Harddisk
+
+package PENUS <> {
+ class UI #LightGreen {
+
+ }
+ class Logic #LightBlue {
+
+ }
+ class Model #LightPink {
+
+ }
+ class Main {
+
+ }
+ class Storage #LightYellow {
+
+ }
+ class Commons #LightCyan {
+
+ }
+}
+
+User ..> UI
+UI --> Logic
+UI --> Model
+Logic --> Model
+Logic --> Storage
+Main -> Storage
+Main -> UI
+Main -> Logic
+Main -> Model
+Web --> Commons
+
+Storage ..> Model
+Storage ..> Harddisk
+Commons <.right. Logic
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/uml/sourcecode/ClearCommandSequence.puml b/docs/uml/sourcecode/ClearCommandSequence.puml
new file mode 100644
index 0000000000..f1fb20d63a
--- /dev/null
+++ b/docs/uml/sourcecode/ClearCommandSequence.puml
@@ -0,0 +1,108 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+participant User
+participant ":Ui" as Ui
+
+box "Logic" #LightBlue
+participant ":LogicManager" as LogicManager
+participant ":Parser" as Parser
+participant "command:ClearCommand" as ClearCommand
+participant "result:CommandResult" as CommandResult
+end box
+
+box "Model" #LightPink
+participant ":ModelManager" as ModelManager
+participant ":ModuleList" as ModuleList
+participant ":Module" as Module
+end box
+
+User -> Ui: getUserCommand() "clear y/1 s/1"
+
+activate Ui
+Ui -> LogicManager: getCommand()
+activate LogicManager
+
+
+LogicManager -> Parser: parseCommand()
+activate Parser
+
+create ClearCommand
+Parser -> ClearCommand
+activate ClearCommand
+
+ClearCommand --> Parser: command
+deactivate ClearCommand
+
+Parser --> LogicManager: command
+deactivate Parser
+
+LogicManager -> ClearCommand: execute()
+
+activate ClearCommand
+
+ClearCommand -> ModelManager: getModuleList().size()
+activate ModelManager
+ModelManager --> ClearCommand : size
+deactivate ModelManager
+
+loop index >= 0
+
+ClearCommand -> ModelManager : getModuleList()
+activate ModelManager
+ModelManager -> ModuleList : getModule()
+activate ModuleList
+ModuleList -> Module : getYear()
+activate Module
+Module --> ModuleList : year
+deactivate Module
+ModuleList --> ModelManager
+deactivate ModuleList
+ModelManager --> ClearCommand
+deactivate ModelManager
+
+ClearCommand -> ModelManager : getModuleList()
+activate ModelManager
+ModelManager -> ModuleList : getModule()
+activate ModuleList
+ModuleList -> Module : getSem()
+activate Module
+Module --> ModuleList : sem
+deactivate Module
+ModuleList --> ModelManager
+deactivate ModuleList
+ModelManager --> ClearCommand
+deactivate ModelManager
+
+ClearCommand -> ModelManager: removeModule(index)
+
+activate ModelManager
+
+ModelManager -> ModuleList: removeModule(index)
+activate ModuleList
+
+ModuleList -> ModuleList: remove(index)
+
+ModuleList --> ModelManager:
+deactivate ModuleList
+deactivate ModelManager
+end
+
+create CommandResult
+
+ClearCommand -> CommandResult
+activate CommandResult
+CommandResult --> ClearCommand
+deactivate CommandResult
+destroy CommandResult
+
+ClearCommand --> LogicManager: result
+deactivate ClearCommand
+destroy ClearCommand
+
+LogicManager --> Ui: printResult()
+deactivate LogicManager
+
+Ui --> User
+deactivate Ui
+@enduml
\ No newline at end of file
diff --git a/docs/uml/sourcecode/DetailsSequence.puml b/docs/uml/sourcecode/DetailsSequence.puml
new file mode 100644
index 0000000000..36d9d013c9
--- /dev/null
+++ b/docs/uml/sourcecode/DetailsSequence.puml
@@ -0,0 +1,123 @@
+@startuml
+participant User
+participant ":Ui" as Ui
+
+box "Logic" #LightBlue
+participant ":LogicManager" as LogicManager
+participant ":Parser" as Parser
+participant "command: DetailsCommand" as DetailsCommand
+box "Commons"
+participant ":DetailsCompiler" as DetailsCompiler
+participant ":ModuleRetriever" as ModuleRetriever
+end box
+participant "result: CommandResult" as CommandResult
+end box
+
+box "Model" #LightPink
+
+end box
+
+
+User -> Ui: getUserCommand() "details CS2113"
+activate Ui
+
+Ui -> LogicManager: getCommand()
+activate LogicManager
+
+LogicManager -> Parser: parseCommand()
+activate Parser
+
+create DetailsCommand
+Parser -> DetailsCommand
+activate DetailsCommand
+
+DetailsCommand --> Parser: command
+deactivate DetailsCommand
+
+Parser --> LogicManager: command
+deactivate Parser
+
+
+LogicManager -> DetailsCommand: execute()
+activate DetailsCommand
+
+
+
+
+
+
+DetailsCommand -> DetailsCompiler: getDetails('CS2113')
+activate DetailsCompiler
+
+DetailsCompiler -> ModuleRetriever: isValidMod('CS2113')
+activate ModuleRetriever
+ModuleRetriever -> ModuleRetriever: getData2223('CS2113')
+activate ModuleRetriever
+deactivate ModuleRetriever
+ModuleRetriever --> DetailsCompiler: boolean value
+deactivate ModuleRetriever
+
+DetailsCompiler -> ModuleRetriever: getTitle2223('CS2113')
+activate ModuleRetriever
+ModuleRetriever -> ModuleRetriever: getData2223('CS2113')
+activate ModuleRetriever
+deactivate ModuleRetriever
+ModuleRetriever --> DetailsCompiler: title
+deactivate ModuleRetriever
+
+DetailsCompiler -> ModuleRetriever: getDescription('CS2113')
+activate ModuleRetriever
+ModuleRetriever -> ModuleRetriever: getData2223('CS2113')
+activate ModuleRetriever
+deactivate ModuleRetriever
+ModuleRetriever --> DetailsCompiler: description
+deactivate ModuleRetriever
+
+
+DetailsCompiler -> ModuleRetriever: getPrerequisite('CS2113')
+activate ModuleRetriever
+ModuleRetriever -> ModuleRetriever: getData2223('CS2113')
+activate ModuleRetriever
+deactivate ModuleRetriever
+ModuleRetriever --> DetailsCompiler: prereqs
+deactivate ModuleRetriever
+
+
+DetailsCompiler -> ModuleRetriever: getModuleCredit2223('CS2113')
+activate ModuleRetriever
+ModuleRetriever -> ModuleRetriever: getData2223('CS2113')
+activate ModuleRetriever
+deactivate ModuleRetriever
+ModuleRetriever --> DetailsCompiler: credits
+deactivate ModuleRetriever
+
+DetailsCompiler -> ModuleRetriever: getSUstatus('CS2113')
+activate ModuleRetriever
+ModuleRetriever -> ModuleRetriever: getData2223('CS2113')
+activate ModuleRetriever
+deactivate ModuleRetriever
+ModuleRetriever --> DetailsCompiler: suStatus
+deactivate ModuleRetriever
+
+DetailsCompiler -> DetailsCommand: result
+deactivate DetailsCompiler
+
+create CommandResult
+DetailsCommand -> CommandResult
+activate CommandResult
+CommandResult --> DetailsCommand
+deactivate CommandResult
+destroy CommandResult
+
+DetailsCommand --> LogicManager: result
+deactivate DetailsCommand
+destroy DetailsCommand
+
+LogicManager --> Ui: printResult()
+deactivate LogicManager
+
+Ui --> User
+deactivate Ui
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/uml/sourcecode/GradeClass.puml b/docs/uml/sourcecode/GradeClass.puml
new file mode 100644
index 0000000000..0a2ec1fae0
--- /dev/null
+++ b/docs/uml/sourcecode/GradeClass.puml
@@ -0,0 +1,30 @@
+@startuml
+hide empty members
+hide circle
+skinparam classAttributeIconSize 0
+
+class Grade {
+ +getGradePoint(grade: String): double
+ +isValid(grade: String): Boolean
+ +calculateOverallCAP(moduleList: List): double
+ +calculateSemCAP(semArray: List): double
+ +getOverallCAP(moduleList: List): String
+ +getSemCAP(semArray: List): String
+}
+
+class ListCommand {
+ +execute(model: ModelManager): CommandResult
+}
+
+class ModelManager{
+ #moduleList: ModuleList
+}
+
+class ModuleRetriever {
+ +getModuleCredit(module: String): String
+}
+
+Grade <-right- ListCommand: uses <
+Grade -down-> ModuleRetriever: uses >
+ListCommand -down-> ModelManager: refers to >
+@enduml
\ No newline at end of file
diff --git a/docs/uml/sourcecode/InitCommandSequenceDiagram.puml b/docs/uml/sourcecode/InitCommandSequenceDiagram.puml
new file mode 100644
index 0000000000..30535b7a03
--- /dev/null
+++ b/docs/uml/sourcecode/InitCommandSequenceDiagram.puml
@@ -0,0 +1,70 @@
+@startuml
+participant user
+participant ":Ui" as Ui
+
+box "Logic" #LightBlue
+participant ":LogicManager" as LogicManager
+participant ":Parser" as Parser
+participant "command:InitCommand" as InitCommand
+participant "result:CommandResult" as CommandResult
+end box
+
+box "Model" #LightPink
+participant ":ModelManager" as ModelManager
+participant ":User" as User
+end box
+
+
+user -> Ui: getUserCommand() "init n/John c/4"
+activate Ui
+
+Ui -> LogicManager: getCommand()
+activate LogicManager
+
+LogicManager -> Parser: parseCommand()
+activate Parser
+
+create InitCommand
+Parser -> InitCommand
+activate InitCommand
+
+InitCommand --> Parser: command
+deactivate InitCommand
+
+
+Parser --> LogicManager: command
+deactivate Parser
+
+
+LogicManager -> InitCommand: execute()
+activate InitCommand
+
+InitCommand -> ModelManager: setUserName()
+activate ModelManager
+
+ModelManager -> User: setName()
+activate User
+
+InitCommand -> ModelManager: setUserCourse()
+
+ModelManager -> User: setCourse()
+deactivate User
+deactivate ModelManager
+
+create CommandResult
+"InitCommand" -> CommandResult
+activate CommandResult
+CommandResult --> "InitCommand"
+deactivate CommandResult
+destroy CommandResult
+
+"InitCommand" -> LogicManager: result
+deactivate "InitCommand"
+destroy "InitCommand"
+
+LogicManager -> Ui: printResult()
+deactivate "LogicManager"
+
+Ui -> user
+
+@enduml
\ No newline at end of file
diff --git a/docs/uml/sourcecode/ListSequence.puml b/docs/uml/sourcecode/ListSequence.puml
new file mode 100644
index 0000000000..7ba5116a49
--- /dev/null
+++ b/docs/uml/sourcecode/ListSequence.puml
@@ -0,0 +1,76 @@
+@startuml
+participant User
+participant ":Ui" as Ui
+
+box "Logic" #LightBlue
+participant ":LogicManager" as LogicManager
+participant ":Parser" as Parser
+participant "command:ListCommand" as ListCommand
+participant ":Grade" as Grade
+participant "result:CommandResult" as CommandResult
+end box
+
+box "Model" #LightPink
+participant ":ModelManager" as ModelManager
+end box
+
+box "Common"
+
+end box
+
+
+User -> Ui: getUserCommand() "list"
+
+activate Ui
+Ui -> LogicManager: getCommand()
+activate LogicManager
+
+
+LogicManager -> Parser: parseCommand()
+activate Parser
+
+create ListCommand
+Parser -> ListCommand
+activate ListCommand
+
+ListCommand --> Parser: command
+deactivate ListCommand
+
+
+Parser --> LogicManager: command
+deactivate Parser
+
+LogicManager -> ListCommand: execute()
+
+activate ListCommand
+
+ListCommand -> ModelManager: getModuleListObj()
+activate ModelManager
+ModelManager --> ListCommand: moduleList
+deactivate ModelManager
+
+ListCommand -> Grade: getOverallCAP()
+Grade --> ListCommand: gradePoint
+
+create CommandResult
+
+ListCommand -> CommandResult
+activate CommandResult
+CommandResult --> ListCommand
+deactivate CommandResult
+destroy CommandResult
+
+ListCommand --> LogicManager: result
+deactivate ListCommand
+destroy ListCommand
+
+LogicManager --> Ui: printResult()
+deactivate LogicManager
+
+Ui --> User
+deactivate Ui
+
+
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/uml/sourcecode/LogicClass.puml b/docs/uml/sourcecode/LogicClass.puml
new file mode 100644
index 0000000000..d74d21dc40
--- /dev/null
+++ b/docs/uml/sourcecode/LogicClass.puml
@@ -0,0 +1,32 @@
+@startuml
+hide empty members
+hide circle
+skinparam classAttributeIconSize 0
+
+package Logic {
+ class LogicManager
+ class XYZCommand
+ class Parser
+ class "{abstract}\nCommand" as Command
+ class CommandResult
+}
+
+package Storage {}
+
+package Model {}
+
+class Main #FFFFFF
+
+Main ..> LogicManager
+LogicManager -right-> Model
+LogicManager -right-> Storage
+
+LogicManager .right.> Command : executes >
+LogicManager -left->"1" Parser
+Parser ..> XYZCommand : creates >
+Command .up.> CommandResult : produces >
+LogicManager .down.> CommandResult
+XYZCommand -up-|> Command
+note right of XYZCommand: XYZCommand = PlanCommand, \nRemoveCommand, etc
+
+@enduml
\ No newline at end of file
diff --git a/docs/uml/sourcecode/MarkModSequence.puml b/docs/uml/sourcecode/MarkModSequence.puml
new file mode 100644
index 0000000000..74a2a519b6
--- /dev/null
+++ b/docs/uml/sourcecode/MarkModSequence.puml
@@ -0,0 +1,87 @@
+@startuml
+participant User
+participant ":Ui" as Ui
+
+box "Logic" #LightBlue
+participant ":LogicManager" as LogicManager
+participant ":Parser" as Parser
+participant "command:MarkCommand" as MarkCommand
+participant "result:CommandResult" as CommandResult
+end box
+
+box "Model" #LightPink
+participant ":ModelManager" as ModelManager
+participant ":ModuleList" as ModuleList
+participant "module:Module" as Module
+end box
+
+
+User -> Ui: getUserCommand() "mark CS2113 g/A+"
+activate Ui
+
+Ui -> LogicManager: getCommand()
+activate LogicManager
+
+LogicManager -> Parser: parseCommand()
+activate Parser
+
+create MarkCommand
+Parser -> MarkCommand
+activate MarkCommand
+
+MarkCommand --> Parser: command
+deactivate MarkCommand
+
+
+Parser --> LogicManager: command
+deactivate Parser
+
+
+LogicManager -> MarkCommand: execute()
+activate MarkCommand
+
+
+MarkCommand -> ModelManager: getModuleList()
+activate ModelManager
+ModelManager --> MarkCommand: index
+deactivate ModelManager
+
+MarkCommand -> ModelManager: markModule(i)
+activate ModelManager
+
+ModelManager -> ModuleList: getModule(i)
+activate ModuleList
+
+ModuleList --> ModelManager: module
+deactivate ModuleList
+
+ModelManager -> Module: markModule(i, grade)
+activate Module
+Module -> Module: Mark()
+
+Module --> ModelManager
+deactivate Module
+
+ModelManager --> MarkCommand
+deactivate ModelManager
+
+create CommandResult
+
+
+MarkCommand -> CommandResult
+activate CommandResult
+CommandResult --> MarkCommand
+deactivate CommandResult
+destroy CommandResult
+
+MarkCommand --> LogicManager: result
+deactivate MarkCommand
+destroy MarkCommand
+
+LogicManager --> Ui: printResult()
+deactivate LogicManager
+
+Ui --> User
+deactivate Ui
+
+@enduml
\ No newline at end of file
diff --git a/docs/uml/sourcecode/ModelClass.puml b/docs/uml/sourcecode/ModelClass.puml
new file mode 100644
index 0000000000..f2c165b5ea
--- /dev/null
+++ b/docs/uml/sourcecode/ModelClass.puml
@@ -0,0 +1,21 @@
+@startuml
+hide empty members
+hide circle
+skinparam classAttributeIconSize 0
+
+package Model{
+ class ModelManager
+ class ModuleList
+ class User
+ class Module
+}
+
+Class Storage #FFFFFF
+Storage ..> ModelManager: core module details >
+
+ModelManager -> "1" ModuleList
+ModelManager --> "1" User
+ModuleList --> "*" Module
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/uml/sourcecode/RemoveModSequence.puml b/docs/uml/sourcecode/RemoveModSequence.puml
new file mode 100644
index 0000000000..2e1e7adb3b
--- /dev/null
+++ b/docs/uml/sourcecode/RemoveModSequence.puml
@@ -0,0 +1,84 @@
+@startuml
+participant User
+participant ":Ui" as Ui
+
+box "Logic" #LightBlue
+participant ":LogicManager" as LogicManager
+participant ":Parser" as Parser
+participant "command:RemoveCommand" as RemoveCommand
+participant "result:CommandResult" as CommandResult
+end box
+
+box "Model" #LightPink
+participant ":ModelManager" as ModelManager
+participant ":ModuleList" as ModuleList
+end box
+
+
+User -> Ui: getUserCommand() "remove CS2113"
+
+activate Ui
+Ui -> LogicManager: getCommand()
+activate LogicManager
+
+
+LogicManager -> Parser: parseCommand()
+activate Parser
+
+create RemoveCommand
+Parser -> RemoveCommand
+activate RemoveCommand
+
+RemoveCommand --> Parser: command
+deactivate RemoveCommand
+
+
+Parser --> LogicManager: command
+deactivate Parser
+
+LogicManager -> RemoveCommand: execute()
+
+activate RemoveCommand
+
+RemoveCommand -> ModelManager: getModuleList()
+activate ModelManager
+ModelManager --> RemoveCommand: index
+deactivate ModelManager
+
+RemoveCommand -> ModelManager: removeModule(i)
+
+activate ModelManager
+
+ModelManager -> ModuleList: removeModule(i)
+activate ModuleList
+
+ModuleList -> ModuleList: remove(i)
+
+ModuleList --> ModelManager:
+deactivate ModuleList
+
+ModelManager --> RemoveCommand
+deactivate ModelManager
+
+create CommandResult
+
+RemoveCommand -> CommandResult
+activate CommandResult
+CommandResult --> RemoveCommand
+deactivate CommandResult
+destroy CommandResult
+
+RemoveCommand --> LogicManager: result
+deactivate RemoveCommand
+destroy RemoveCommand
+
+LogicManager --> Ui: printResult()
+deactivate LogicManager
+
+Ui --> User
+deactivate Ui
+
+
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/uml/sourcecode/SaveFeatureClass.puml b/docs/uml/sourcecode/SaveFeatureClass.puml
new file mode 100644
index 0000000000..866176b5c3
--- /dev/null
+++ b/docs/uml/sourcecode/SaveFeatureClass.puml
@@ -0,0 +1,40 @@
+@startuml
+hide empty members
+hide circle
+skinparam classAttributeIconSize 0
+
+class FileStorage {
+ #file: File
+ #dataDirectory: String
+ #filePath: String
+ -decodeModule(module: String): Module
+ +save(moduleList: ModuleList, user: User)
+ +retrieveMods(): List
+ +retrieveUser(): User
+}
+
+class StorageManager {
+ #storage: FileStorage
+ +loadStorage(): List
+ +loadUser(): User
+ +saveStorage(moduleList: ModuleList, user: User)
+}
+
+class Penus {
+ #storage: StorageManager
+ #model: ModelManager
+ -start()
+ -runCommandLoopUntilExitCommand()
+ -executeCommand(command: Command): CommandResult
+ +main(args: String[])
+ +run()
+}
+
+class ModelManager {}
+
+StorageManager -right-> "1" FileStorage
+Penus <-down-> "1" StorageManager: retrieves and saves data
+Penus -right-> "1" ModelManager: updates model
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/uml/sourcecode/StatusSequenceDiagram.puml b/docs/uml/sourcecode/StatusSequenceDiagram.puml
new file mode 100644
index 0000000000..4893a4ed7f
--- /dev/null
+++ b/docs/uml/sourcecode/StatusSequenceDiagram.puml
@@ -0,0 +1,82 @@
+@startuml
+participant User
+participant ":Ui" as Ui
+
+box "Logic" #LightBlue
+participant ":LogicManager" as LogicManager
+participant ":Parser" as Parser
+participant "command:StatusCommand" as StatusCommand
+participant "result:CommandResult" as CommandResult
+end box
+
+box "Model" #LightPink
+participant ":ModelManager" as ModelManager
+end box
+
+box "Utils" #LightGrey
+participant ":Module Retriever" as ModuleRetriever
+participant ":MCsTaken" as MCsTaken
+end box
+
+User -> Ui: getUserCommand() "status"
+activate Ui
+
+Ui -> LogicManager: getCommand()
+activate LogicManager
+
+LogicManager -> Parser: parseCommand()
+activate Parser
+Parser -> LogicManager: status command
+deactivate Parser
+
+LogicManager -> "StatusCommand" : execute()
+activate "StatusCommand"
+
+"StatusCommand" -> ModelManager: getUserName()
+activate ModelManager
+ModelManager -> "StatusCommand": name
+"StatusCommand" -> ModelManager: getUserCourse()
+ModelManager -> "StatusCommand": course
+"StatusCommand" -> ModelManager: getTakenCoreMods()
+ModelManager -> "StatusCommand": takenCoreMods
+"StatusCommand" -> ModelManager: getUntakenCoreMods()
+ModelManager -> "StatusCommand": untakenCoreMods
+deactivate ModelManager
+
+
+"StatusCommand" -> MCsTaken: getNumberOfMCsTaken()
+activate MCsTaken
+MCsTaken -> "StatusCommand": total MCs taken
+deactivate MCsTaken
+
+"StatusCommand" -> ModelManager: getNumberOfCoreMCsTaken()
+activate ModelManager
+ModelManager -> "StatusCommand": number of core module MCs taken
+deactivate ModelManager
+
+loop For each Module Code
+ "StatusCommand" -> ModuleRetriever: moduleDetailsString()
+ activate ModuleRetriever
+ ModuleRetriever -> "StatusCommand": moduleDetailsString
+ deactivate ModuleRetriever
+end
+
+
+create CommandResult
+"StatusCommand" -> CommandResult
+activate CommandResult
+CommandResult --> "StatusCommand"
+deactivate CommandResult
+destroy CommandResult
+
+"StatusCommand" -> LogicManager: result
+deactivate "StatusCommand"
+destroy "StatusCommand"
+
+LogicManager -> Ui: printResult()
+deactivate "LogicManager"
+
+
+Ui -> User
+
+@enduml
diff --git a/docs/uml/sourcecode/StorageClass.puml b/docs/uml/sourcecode/StorageClass.puml
new file mode 100644
index 0000000000..f26ed3cdaa
--- /dev/null
+++ b/docs/uml/sourcecode/StorageClass.puml
@@ -0,0 +1,17 @@
+@startuml
+hide empty members
+hide circle
+skinparam classAttributeIconSize 0
+
+package Storage {
+ class StorageManager
+ class FileStorage
+ class ResourceStorage
+ class StorageDecoder
+}
+
+StorageManager -left-> "1" FileStorage
+StorageManager --> "1" ResourceStorage
+
+FileStorage ..> StorageDecoder: uses >
+@enduml
\ No newline at end of file
diff --git a/docs/uml/sourcecode/UiClass.puml b/docs/uml/sourcecode/UiClass.puml
new file mode 100644
index 0000000000..3aa603bc2b
--- /dev/null
+++ b/docs/uml/sourcecode/UiClass.puml
@@ -0,0 +1,42 @@
+@startuml
+hide circle
+skinparam classAttributeIconSize 0
+hide empty members
+
+class Ui {
+ #in: Scanner
+ #out: PrintStream
+ #DIVIDER: String
+ +getUserCommand(): String
+ +printMessage(message: String...)
+ +printResult(result: CommandResult)
+ +printWelcome()
+ +printExit()
+}
+
+class Penus {
+ #ui: Ui
+ #storage: StorageManager
+ #model: ModelManager
+ #logic: LogicManager
+ -start()
+ -runCommandLoopUntilExitCommand()
+ -executeCommand(command: Command): CommandResult
+ -getCommand(commandText: String): Command
+ -exit()
+ +main(args: String[])
+ +run()
+}
+
+class Scanner {
+ +nextLine()
+}
+class PrintStream {
+ +println()
+}
+
+Penus --> "1" Ui
+Ui .. Scanner: gets input from >
+Ui .. PrintStream: prints to >
+
+@enduml
\ No newline at end of file
diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java
deleted file mode 100644
index 5c74e68d59..0000000000
--- a/src/main/java/seedu/duke/Duke.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package seedu.duke;
-
-import java.util.Scanner;
-
-public class Duke {
- /**
- * Main entry-point for the java.duke.Duke application.
- */
- public static void main(String[] args) {
- String logo = " ____ _ \n"
- + "| _ \\ _ _| | _____ \n"
- + "| | | | | | | |/ / _ \\\n"
- + "| |_| | |_| | < __/\n"
- + "|____/ \\__,_|_|\\_\\___|\n";
- System.out.println("Hello from\n" + logo);
- System.out.println("What is your name?");
-
- Scanner in = new Scanner(System.in);
- System.out.println("Hello " + in.nextLine());
- }
-}
diff --git a/src/main/java/seedu/penus/Penus.java b/src/main/java/seedu/penus/Penus.java
new file mode 100644
index 0000000000..9a2fe4927d
--- /dev/null
+++ b/src/main/java/seedu/penus/Penus.java
@@ -0,0 +1,116 @@
+package seedu.penus;
+
+import seedu.penus.common.exceptions.NoInternetException;
+import seedu.penus.logic.utils.ModuleRetriever;
+import seedu.penus.ui.Ui;
+import seedu.penus.logic.LogicManager;
+import seedu.penus.model.ModelManager;
+import seedu.penus.storage.StorageManager;
+import seedu.penus.logic.commands.Command;
+import seedu.penus.logic.commands.CommandResult;
+import seedu.penus.logic.commands.ExitCommand;
+
+public class Penus {
+
+ private Ui ui;
+ private StorageManager storage;
+ private ModelManager model;
+ private LogicManager logic;
+
+ public static void main(String[] args) {
+ new Penus().run();
+ }
+
+ /** Run program till termination */
+ public void run() {
+ start();
+ runCommandLoopUntilExitCommand();
+ exit();
+ }
+
+ /**
+ * Sets up required objects, loads data from file and prints welcome message
+ */
+ private void start() {
+ this.ui = new Ui();
+ this.storage = new StorageManager();
+ try {
+ ModuleRetriever.connectionChecker();
+ } catch (NoInternetException e) {
+ ui.printMessage(e.getMessage());
+ }
+ try {
+ this.model = new ModelManager(
+ storage.loadUser(),
+ storage.loadStorage(),
+ storage.loadCoreDetails(),
+ storage.loadCoreModList()
+ );
+ } catch (Exception e) {
+ ui.printStorageError(e.getMessage());
+ //unchecked exception to exit application upon initialising with error
+ throw new RuntimeException(e);
+ }
+
+ this.logic = new LogicManager(model, storage);
+ ui.printWelcome();
+ }
+
+ /**
+ * Reads the user command and executes it until user issues exit command
+ */
+ private void runCommandLoopUntilExitCommand() {
+ Command command;
+ do {
+ CommandResult result = null;
+ String userCommandText = ui.getUserCommand();
+ command = getCommand(userCommandText);
+ if (command != null) {
+ result = executeCommand(command);
+ }
+ if (result != null && result.isArray) {
+ ui.printResultArray(result);
+ }
+ if (result != null && !result.isArray) {
+ ui.printResultString(result);
+ }
+
+ } while (!ExitCommand.isExit(command));
+ }
+
+ /**
+ * Executes the command and returns the result
+ * @param command user command
+ * @return CommandResult
+ */
+ private CommandResult executeCommand(Command command) {
+ CommandResult result = null;
+ try {
+ result = logic.execute(command);
+
+ } catch (Exception e) {
+ ui.printMessage(e.getMessage());
+ }
+ return result;
+ }
+
+ /**
+ * Directs the command input to the LogicManager to retrieve the Command object
+ * @param commandText
+ * @return Command object relating to the commandText
+ */
+ private Command getCommand(String commandText) {
+ Command command = null;
+ try {
+ command = logic.getCommand(commandText);
+ } catch (Exception e) {
+ ui.printMessage(e.getMessage());
+ }
+ return command;
+ }
+
+ /** Exits */
+ private void exit() {
+ System.exit(0);
+ }
+}
diff --git a/src/main/java/seedu/penus/common/Messages.java b/src/main/java/seedu/penus/common/Messages.java
new file mode 100644
index 0000000000..5f15cee3d3
--- /dev/null
+++ b/src/main/java/seedu/penus/common/Messages.java
@@ -0,0 +1,18 @@
+package seedu.penus.common;
+
+public class Messages {
+ public static final String MESSAGE_GOODBYE = "Bye see you again!";
+ public static final String MESSAGE_WELCOME =
+ "Welcome to PENUS!\n \tEnter help for a list of commands or init to start";
+ public static final String LOGO = "\n"
+ + "\t ___ _____ ______ ___ ___ ___ _________\n"
+ + "\t/ \\/ \\ / \\ / \\/ \\ / \\ / \\ \n"
+ + "\t| __ \\ | \\/ || | | || _ |\n"
+ + "\t| |__| | | | || | | || \\ |\n"
+ + "\t\\ / | | | || | | |\\ \\_ /\n"
+ + "\t/ ___/___ | | | || | | | \\_ \\\n"
+ + "\t| | / \\ | | | || | | |/ \\ \\\n"
+ + "\t|_______|/ <> _\\| | | || \\__/ || \\_ |\n"
+ + "\t\\ /| \\____| / \\ |\\ /| |\n"
+ + "\t \\__|__/ \\______/\\___/ \\_____/ \\__________/ \\_________/\n";
+}
diff --git a/src/main/java/seedu/penus/common/exceptions/DuplicateModuleException.java b/src/main/java/seedu/penus/common/exceptions/DuplicateModuleException.java
new file mode 100644
index 0000000000..f10c2edc16
--- /dev/null
+++ b/src/main/java/seedu/penus/common/exceptions/DuplicateModuleException.java
@@ -0,0 +1,8 @@
+package seedu.penus.common.exceptions;
+
+public class DuplicateModuleException extends PenusException {
+ private static final String message = "This module has already been added to the list. Please try again.";
+ public DuplicateModuleException() {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/penus/common/exceptions/InvalidCommandException.java b/src/main/java/seedu/penus/common/exceptions/InvalidCommandException.java
new file mode 100644
index 0000000000..75426667e3
--- /dev/null
+++ b/src/main/java/seedu/penus/common/exceptions/InvalidCommandException.java
@@ -0,0 +1,13 @@
+package seedu.penus.common.exceptions;
+
+public class InvalidCommandException extends PenusException {
+ private static final String MESSAGE = "This command is unsupported. Please try again";
+
+ public InvalidCommandException(String message) {
+ super(message);
+ }
+
+ public InvalidCommandException() {
+ super(MESSAGE);
+ }
+}
diff --git a/src/main/java/seedu/penus/common/exceptions/InvalidFormatException.java b/src/main/java/seedu/penus/common/exceptions/InvalidFormatException.java
new file mode 100644
index 0000000000..dfbd165ddd
--- /dev/null
+++ b/src/main/java/seedu/penus/common/exceptions/InvalidFormatException.java
@@ -0,0 +1,11 @@
+package seedu.penus.common.exceptions;
+
+public class InvalidFormatException extends PenusException {
+ public InvalidFormatException(String... missing) {
+ super("Wrong Format! Please include " + String.join(" and ", missing));
+ }
+
+ public InvalidFormatException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/penus/common/exceptions/InvalidGradeException.java b/src/main/java/seedu/penus/common/exceptions/InvalidGradeException.java
new file mode 100644
index 0000000000..c9e237e105
--- /dev/null
+++ b/src/main/java/seedu/penus/common/exceptions/InvalidGradeException.java
@@ -0,0 +1,7 @@
+package seedu.penus.common.exceptions;
+
+public class InvalidGradeException extends PenusException {
+ public InvalidGradeException() {
+ super("Grade is not valid");
+ }
+}
diff --git a/src/main/java/seedu/penus/common/exceptions/InvalidIndexException.java b/src/main/java/seedu/penus/common/exceptions/InvalidIndexException.java
new file mode 100644
index 0000000000..64426ac288
--- /dev/null
+++ b/src/main/java/seedu/penus/common/exceptions/InvalidIndexException.java
@@ -0,0 +1,13 @@
+package seedu.penus.common.exceptions;
+
+public class InvalidIndexException extends PenusException {
+ private static final String MESSAGE = "This command is unsupported. Please try again";
+
+ public InvalidIndexException(String message) {
+ super(message);
+ }
+
+ public InvalidIndexException() {
+ super(MESSAGE);
+ }
+}
diff --git a/src/main/java/seedu/penus/common/exceptions/InvalidModuleAPIException.java b/src/main/java/seedu/penus/common/exceptions/InvalidModuleAPIException.java
new file mode 100644
index 0000000000..64e4616a42
--- /dev/null
+++ b/src/main/java/seedu/penus/common/exceptions/InvalidModuleAPIException.java
@@ -0,0 +1,10 @@
+package seedu.penus.common.exceptions;
+
+public class InvalidModuleAPIException extends PenusException {
+ private static final String message = "The module's details were not able to be retrieved," +
+ " as it is not available in the API.";
+ public InvalidModuleAPIException() {
+ super(message);
+ }
+
+}
diff --git a/src/main/java/seedu/penus/common/exceptions/InvalidModuleException.java b/src/main/java/seedu/penus/common/exceptions/InvalidModuleException.java
new file mode 100644
index 0000000000..b2bf542bb7
--- /dev/null
+++ b/src/main/java/seedu/penus/common/exceptions/InvalidModuleException.java
@@ -0,0 +1,11 @@
+package seedu.penus.common.exceptions;
+
+public class InvalidModuleException extends PenusException {
+ public InvalidModuleException(String module) {
+ super("The module code must be given.");
+ }
+
+ public InvalidModuleException() {
+ super("Invalid module. Please try again");
+ }
+}
diff --git a/src/main/java/seedu/penus/common/exceptions/InvalidSemesterException.java b/src/main/java/seedu/penus/common/exceptions/InvalidSemesterException.java
new file mode 100644
index 0000000000..2176df3db7
--- /dev/null
+++ b/src/main/java/seedu/penus/common/exceptions/InvalidSemesterException.java
@@ -0,0 +1,8 @@
+package seedu.penus.common.exceptions;
+
+public class InvalidSemesterException extends PenusException {
+ private static final String message = "The semester number is invalid. Please try again";
+ public InvalidSemesterException(String s) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/penus/common/exceptions/InvalidYearException.java b/src/main/java/seedu/penus/common/exceptions/InvalidYearException.java
new file mode 100644
index 0000000000..18f509afde
--- /dev/null
+++ b/src/main/java/seedu/penus/common/exceptions/InvalidYearException.java
@@ -0,0 +1,8 @@
+package seedu.penus.common.exceptions;
+
+public class InvalidYearException extends PenusException {
+
+ public InvalidYearException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/penus/common/exceptions/NoInternetException.java b/src/main/java/seedu/penus/common/exceptions/NoInternetException.java
new file mode 100644
index 0000000000..d3342eee0c
--- /dev/null
+++ b/src/main/java/seedu/penus/common/exceptions/NoInternetException.java
@@ -0,0 +1,8 @@
+package seedu.penus.common.exceptions;
+
+public class NoInternetException extends PenusException{
+ private static final String message = "You are not connected to the internet! Connect now to prevent any errors.";
+ public NoInternetException() {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/penus/common/exceptions/PenusException.java b/src/main/java/seedu/penus/common/exceptions/PenusException.java
new file mode 100644
index 0000000000..6dedb6add3
--- /dev/null
+++ b/src/main/java/seedu/penus/common/exceptions/PenusException.java
@@ -0,0 +1,7 @@
+package seedu.penus.common.exceptions;
+
+public class PenusException extends Exception {
+ PenusException(String message) {
+ super("Error: " + message);
+ }
+}
diff --git a/src/main/java/seedu/penus/logic/LogicManager.java b/src/main/java/seedu/penus/logic/LogicManager.java
new file mode 100644
index 0000000000..da995916fe
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/LogicManager.java
@@ -0,0 +1,43 @@
+package seedu.penus.logic;
+
+import seedu.penus.model.ModelManager;
+import seedu.penus.storage.StorageManager;
+import seedu.penus.logic.parser.Parser;
+import seedu.penus.logic.commands.CommandResult;
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.logic.commands.Command;
+
+public class LogicManager {
+ private final ModelManager model;
+ private final StorageManager storage;
+ private final Parser parser;
+
+ public LogicManager(ModelManager model, StorageManager storage) {
+ this.model = model;
+ this.storage = storage;
+ this.parser = new Parser();
+ }
+
+ /**
+ * Executes the command of a Command object and saves the moduleList and user to the storage file
+ * @param command command
+ * @return CommandResult from the execution of the command
+ * @throws PenusException exception
+ */
+ public CommandResult execute(Command command) throws PenusException {
+ CommandResult commandResult = command.execute(model);
+ storage.saveStorage(model.getModuleList(), model.getUser());
+
+ return commandResult;
+ }
+
+ /**
+ * Directs the commandText string to the Parser object for parsing commands
+ * @param commandText string
+ * @return Command object
+ * @throws PenusException exception
+ */
+ public Command getCommand(String commandText) throws PenusException {
+ return parser.parseCommand(commandText);
+ }
+}
diff --git a/src/main/java/seedu/penus/logic/commands/ClearCommand.java b/src/main/java/seedu/penus/logic/commands/ClearCommand.java
new file mode 100644
index 0000000000..0be19eb4e0
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/commands/ClearCommand.java
@@ -0,0 +1,113 @@
+package seedu.penus.logic.commands;
+
+import java.util.ArrayList;
+import java.util.List;
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.model.ModelManager;
+import seedu.penus.model.Module;
+
+public class ClearCommand extends Command {
+
+ public static final String COMMAND_WORD = "clear";
+
+ public static final String MESSAGE = "Cleared!";
+
+ public final int year;
+ public final int semester;
+
+ public ClearCommand() {
+ this.year = 0;
+ this.semester = 0;
+ }
+
+ //Overloaded constructor for filter
+ public ClearCommand(int year, int semester) {
+ this.year = year;
+ this.semester = semester;
+ }
+
+ @Override
+ public CommandResult execute(ModelManager model) throws PenusException {
+ List messageArray = new ArrayList<>();
+ List moduleList = model.getModuleListObj();
+
+ //clear all modules
+ if (this.year == 0 && this.semester == 0) {
+ List clearAllModules = clearAllMods(moduleList);
+ messageArray.addAll(clearAllModules);
+ }
+
+ //clear specific year only
+ if (this.year != 0 && this.semester == 0) {
+ List clearYearModules = clearYearMods(model);
+ messageArray.addAll(clearYearModules);
+ }
+
+ //list year and sem specific
+ if (this.year != 0 && this.semester != 0) {
+ List clearYearAndSemModules = clearYearAndSemMods(model);
+ messageArray.addAll(clearYearAndSemModules);
+ }
+
+ return new CommandResult(messageArray, true);
+ }
+
+ //clear all modules method
+ private List clearAllMods(List moduleList) {
+ List messageList = new ArrayList<>();
+ if (moduleList.isEmpty()) {
+ messageList.add("No modules to clear.");
+
+ } else {
+ moduleList.clear();
+ messageList.add(MESSAGE);
+ }
+ return messageList;
+ }
+
+ //clear specific year method
+ private List clearYearMods(ModelManager model) {
+ List messageList = new ArrayList<>();
+ messageList.add("Clearing modules for Year " + this.year);
+
+ int listSize = model.getModuleList().size();
+
+ if (listSize == 0) {
+
+ messageList.add("No modules to clear.");
+ } else {
+
+ for (int index = listSize - 1; index >= 0; index -= 1) {
+ if (model.getModuleList().getModule(index).getYear().equals(this.year)) {
+ model.removeModule(index);
+ }
+ }
+ messageList.add(MESSAGE);
+ }
+ return messageList;
+ }
+
+ //list year and sem specific method
+ private List clearYearAndSemMods(ModelManager model) {
+ List messageList = new ArrayList<>();
+ messageList.add("Clearing modules for Year " + this.year + " and Semester " + this.semester);
+
+ int listSize = model.getModuleList().size();
+
+ if (listSize == 0) {
+
+ messageList.add("No modules to clear.");
+ } else {
+
+ for (int index = listSize - 1; index >= 0; index -= 1) {
+ if ((model.getModuleList().getModule(index).getYear() == this.year) &&
+ (model.getModuleList().getModule(index).getSem() == this.semester)) {
+ model.removeModule(index);
+ }
+ }
+ messageList.add(MESSAGE);
+ }
+ return messageList;
+ }
+
+}
diff --git a/src/main/java/seedu/penus/logic/commands/Command.java b/src/main/java/seedu/penus/logic/commands/Command.java
new file mode 100644
index 0000000000..b3cc11c98e
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/commands/Command.java
@@ -0,0 +1,16 @@
+package seedu.penus.logic.commands;
+
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.model.ModelManager;
+
+public abstract class Command {
+
+ /**
+ * Executes the command and returns the result message.
+ *
+ * @param model {@code ModelManager} which the command operates on
+ * @return CommandResult with the feedback message for display
+ * @throws PenusException if an error occurs during the execution
+ */
+ public abstract CommandResult execute(ModelManager model) throws PenusException;
+}
diff --git a/src/main/java/seedu/penus/logic/commands/CommandResult.java b/src/main/java/seedu/penus/logic/commands/CommandResult.java
new file mode 100644
index 0000000000..ee5a504645
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/commands/CommandResult.java
@@ -0,0 +1,34 @@
+package seedu.penus.logic.commands;
+
+import java.util.List;
+
+public class CommandResult {
+ public final String feedbackToUser;
+
+ public final List feedbackArray;
+
+ public final boolean isArray;
+
+ /**
+ * Stores a string feedbackToUser to return the feedback message from an executed command
+ * @param feedbackToUser string
+ * @param isArray boolean
+ */
+ public CommandResult(String feedbackToUser, boolean isArray) {
+ this.feedbackToUser = feedbackToUser;
+ this.feedbackArray = null;
+ this.isArray = isArray;
+ }
+
+ /**
+ * Overloaded constructor for printing arrays
+ * Stores a List of string feedbackArray to return the feedback message from an executed command
+ * @param feedbackArray string
+ * @param isArray boolean
+ */
+ public CommandResult(List feedbackArray, boolean isArray) {
+ this.feedbackToUser = null;
+ this.feedbackArray = feedbackArray;
+ this.isArray = isArray;
+ }
+}
diff --git a/src/main/java/seedu/penus/logic/commands/DetailsCommand.java b/src/main/java/seedu/penus/logic/commands/DetailsCommand.java
new file mode 100644
index 0000000000..23aca1728d
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/commands/DetailsCommand.java
@@ -0,0 +1,24 @@
+package seedu.penus.logic.commands;
+
+import seedu.penus.model.ModelManager;
+import seedu.penus.logic.utils.DetailsCompiler;
+
+public class DetailsCommand extends Command {
+ public static final String COMMAND_WORD = "details";
+ public final String moduleCode;
+
+ /**
+ * Creates a DetailsCommand with the moduleCode for querying during execution
+ * @param moduleCode string
+ */
+ public DetailsCommand(String moduleCode) {
+ this.moduleCode = moduleCode;
+ }
+
+ @Override
+ public CommandResult execute(ModelManager model) {
+ String result = DetailsCompiler.getDetails(this.moduleCode);
+
+ return new CommandResult(moduleCode + " " + result, false);
+ }
+}
diff --git a/src/main/java/seedu/penus/logic/commands/ExitCommand.java b/src/main/java/seedu/penus/logic/commands/ExitCommand.java
new file mode 100644
index 0000000000..f12604b75c
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/commands/ExitCommand.java
@@ -0,0 +1,22 @@
+package seedu.penus.logic.commands;
+
+import seedu.penus.model.ModelManager;
+
+public class ExitCommand extends Command {
+ public static final String COMMAND_WORD = "exit";
+ public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting PENUS ...\n\tBye see you again!";
+
+ /**
+ * Static method which signals the end of the program, called in main running loop
+ * @param command Command object
+ * @return boolean true if the given Command is an instance of ExitCommand
+ */
+ public static boolean isExit(Command command) {
+ return command instanceof ExitCommand; // instanceof returns false if it is null
+ }
+
+ @Override
+ public CommandResult execute(ModelManager model) {
+ return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false);
+ }
+}
diff --git a/src/main/java/seedu/penus/logic/commands/HelpCommand.java b/src/main/java/seedu/penus/logic/commands/HelpCommand.java
new file mode 100644
index 0000000000..6729ed78fd
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/commands/HelpCommand.java
@@ -0,0 +1,54 @@
+package seedu.penus.logic.commands;
+
+import seedu.penus.model.ModelManager;
+
+public class HelpCommand extends Command {
+ public static final String COMMAND_WORD = "help";
+
+ @Override
+ public CommandResult execute(ModelManager model) {
+ return new CommandResult(
+ ("\n\t" + String.format("%-52s %s", "Command", "Description") +
+ "\n\t" + String.format("%-52s %s", "-------", "-----------") +
+ "\n\t" + String.format("%-52s %s",
+ "clear [FILTER]", "Clears modules in the specified Year or Semester.") +
+ "\n\t" + String.format("%-52s %s", "",
+ "If [FILTER] is not specified, then all modules will cleared.") +
+ "\n\n\t" + String.format("%-52s %s", "exit", "Exits the program.") +
+ "\n\n\t" + String.format("%-52s %s", "list [FILTER]",
+ "Displays a list of all modules taken or planned") +
+ "\n\t" + String.format("%-52s %s", "", "in the specified Year or Semester.") +
+ "\n\t" + String.format("%-52s %s", "",
+ "If [FILTER] is not specified, then all modules will shown.") +
+ "\n\n\t" + String.format("%-52s %s", "mark [MODULE CODE] g/[GRADE]",
+ "Marks the module that has been cleared, while updating its grades.") +
+ String.format("%n %n \t") +
+ String.format("%-52s %s", "plan [MODULE CODE] y/[YEAR] s/[SEMESTER]",
+ "Adds a module to the planner as an untaken module.") +
+ "\n\n\t" + String.format("%-52s %s",
+ "remove [MODULECODE]", "Removes a module from the planner.") +
+ "\n\n\t" + String.format("%-52s %s",
+ "status", "Displays the status of Core Modules and MCs taken.") +
+ "\n\n\t" + String.format("%-52s %s",
+ "taken [MODULE CODE] y/[YEAR] s/[SEMESTER] g/[GRADE]",
+ "Adds a module to the planner as a module you have already taken.") +
+ "\n\n\t" + String.format("%-52s %s", "details [MODULE CODE]",
+ "Displays the details of given module, including") +
+ "\n\t" + String.format("%-52s %s", "",
+ "Title, Description, Prerequisites, Module Credits") +
+ "\n\t" + String.format("%-52s %s", "", "and if it can be SU-ed.") +
+ "\n\n\t" + String.format("%-52s %s",
+ "init n/[NAME] c/[COURSE NUMBER]", "Initialize User.") +
+ "\n\n\t" + String.format("%-52s %s", "", "[COURSE NUMBER] -> [COURSE NAME]") +
+ "\n\t" + String.format("%-52s %s", "", "1 -> Biomedical Engineering") +
+ "\n\t" + String.format("%-52s %s", "", "2 -> Chemical Engineering") +
+ "\n\t" + String.format("%-52s %s", "", "3 -> Civil Engineering") +
+ "\n\t" + String.format("%-52s %s", "", "4 -> Computer Engineering") +
+ "\n\t" + String.format("%-52s %s", "", "5 -> Electrical Engineering") +
+ "\n\t" + String.format("%-52s %s", "", "6 -> Environmental Engineering") +
+ "\n\t" + String.format("%-52s %s", "", "7 -> Industrial and Systems Engineering") +
+ "\n\t" + String.format("%-52s %s", "", "8 -> Mechanical Engineering") +
+ "\n\t"
+ ), false);
+ }
+}
diff --git a/src/main/java/seedu/penus/logic/commands/InitCommand.java b/src/main/java/seedu/penus/logic/commands/InitCommand.java
new file mode 100644
index 0000000000..f8b3983d4d
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/commands/InitCommand.java
@@ -0,0 +1,46 @@
+package seedu.penus.logic.commands;
+
+import seedu.penus.common.exceptions.InvalidCommandException;
+import seedu.penus.model.ModelManager;
+
+public class InitCommand extends Command { //set username and course, command: init n/Jiun Yuan c/1
+ public static final String COMMAND_WORD = "init";
+
+ public static final String MESSAGE = "Initialization Complete";
+
+ public final String name;
+
+ public final Integer courseCode;
+
+ public InitCommand (String name, int courseCode ) {
+ this.name = name;
+ this.courseCode = courseCode;
+ }
+
+ @Override
+ public CommandResult execute(ModelManager model) throws InvalidCommandException {
+ String course = "";
+ model.setUserName(name);
+ switch(courseCode) {
+ case 1: course = "Biomedical Engineering";
+ break;
+ case 2: course = "Chemical Engineering";
+ break;
+ case 3: course = "Civil Engineering";
+ break;
+ case 4: course = "Computer Engineering";
+ break;
+ case 5: course = "Electrical Engineering";
+ break;
+ case 6: course = "Environmental Engineering";
+ break;
+ case 7: course = "Industrial and Systems Engineering";
+ break;
+ case 8: course = "Mechanical Engineering";
+ break;
+ default: throw new InvalidCommandException("Enter within the index. Please initialize again");
+ }
+ model.setUserCourse(course);
+ return new CommandResult(MESSAGE, false);
+ }
+}
diff --git a/src/main/java/seedu/penus/logic/commands/ListCommand.java b/src/main/java/seedu/penus/logic/commands/ListCommand.java
new file mode 100644
index 0000000000..44994fac4d
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/commands/ListCommand.java
@@ -0,0 +1,131 @@
+package seedu.penus.logic.commands;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.logic.utils.Grade;
+import seedu.penus.model.ModelManager;
+import seedu.penus.model.Module;
+
+public class ListCommand extends Command {
+ public static final String COMMAND_WORD = "list";
+
+ public static final String MESSAGE = "List of";
+
+ public final int year;
+ public final int semester;
+
+ public ListCommand() {
+ this.year = 0;
+ this.semester = 0;
+ }
+
+ //Overloaded constructor for filter
+ public ListCommand(int year, int semester) {
+ this.year = year;
+ this.semester = semester;
+ }
+
+ @Override
+ public CommandResult execute(ModelManager model) throws PenusException {
+ List messageArray = new ArrayList<>();
+ Map>> modules = new HashMap<>();
+ List moduleList = model.getModuleListObj();
+ for (Module currMod : moduleList) {
+ int currYear = currMod.getYear();
+ int currSem = currMod.getSem();
+ String[] arr = new String[] { currMod.getCode(), currMod.getGrade() };
+ modules.computeIfAbsent(currYear, k -> new HashMap<>())
+ .computeIfAbsent(currSem,k -> new ArrayList<>())
+ .add(arr);
+ }
+
+ //list all modules
+ if (this.year == 0 && this.semester == 0) {
+ List allModules = getAllMods(modules);
+ messageArray.addAll(allModules);
+ }
+
+ //list for year only
+ if (this.year != 0 && this.semester == 0) {
+ List yearModules = getYearMods(modules);
+ messageArray.addAll(yearModules);
+ }
+
+ //list year and sem specific
+ if (this.year != 0 && this.semester != 0) {
+ List semModules = getSemMods(modules);
+ messageArray.addAll(semModules);
+ }
+
+ messageArray.add("");
+ messageArray.add(Grade.getOverallCAP(moduleList));
+
+ return new CommandResult(messageArray, true);
+ }
+
+ private List getAllMods(Map>> modules) {
+ List messageList = new ArrayList<>();
+ for (int y = 1; y < 5; y++) {
+ for (int s = 1; s <= 2; s++) {
+ messageList.add("- Year " + y + " Semester " + s + " -");
+
+ List modulesInYearSem = modules.getOrDefault(y, new HashMap<>())
+ .getOrDefault(s, new ArrayList<>());
+
+ if (modulesInYearSem.isEmpty()) {
+ messageList.add("No modules taken/added.");
+ } else {
+ for (String[] string : modulesInYearSem) {
+ messageList.add(string[0] + " " + string[1]);
+ }
+ }
+ messageList.add("");
+ }
+ }
+ return messageList;
+ }
+
+ private List getYearMods(Map>> modules) throws PenusException {
+ List messageList = new ArrayList<>();
+ for (int s = 1; s <= 2; s++) {
+ messageList.add("- Year " + this.year + " Semester " + s + " -");
+
+ List modulesInYear = modules.getOrDefault(this.year, new HashMap<>())
+ .getOrDefault(s, new ArrayList<>());
+
+ if (modulesInYear.isEmpty()) {
+ messageList.add("No modules taken/added");
+ } else {
+ for (String[] string : modulesInYear) {
+ messageList.add(string[0] + " " + string[1]);
+ }
+ }
+ messageList.add(Grade.getSemCAP(modulesInYear));
+ messageList.add("");
+ }
+
+ return messageList;
+ }
+
+ private List getSemMods(Map>> modules) throws PenusException {
+ List messageList = new ArrayList<>();
+ messageList.add("- Year " + this.year + " Semester " + this.semester + " -");
+
+ List modulesInYearAndSem = modules.getOrDefault(this.year, new HashMap<>())
+ .getOrDefault(this.semester, new ArrayList<>());
+
+ if (modulesInYearAndSem.isEmpty()) {
+ messageList.add("No modules taken/added");
+ } else {
+ for (String[] string : modulesInYearAndSem) {
+ messageList.add(string[0] + " " + string[1]);
+ }
+ }
+ messageList.add(Grade.getSemCAP(modulesInYearAndSem));
+
+ return messageList;
+ }
+}
diff --git a/src/main/java/seedu/penus/logic/commands/MarkCommand.java b/src/main/java/seedu/penus/logic/commands/MarkCommand.java
new file mode 100644
index 0000000000..4ab632d93f
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/commands/MarkCommand.java
@@ -0,0 +1,52 @@
+package seedu.penus.logic.commands;
+
+import seedu.penus.common.exceptions.InvalidCommandException;
+import seedu.penus.logic.utils.ModuleRetriever;
+import seedu.penus.model.ModelManager;
+import seedu.penus.model.Module;
+import seedu.penus.model.ModuleList;
+
+public class MarkCommand extends Command {
+ public static final String COMMAND_WORD = "mark";
+ public static final String MESSAGE =
+ "Module has been taken:\n"
+ + "\t %s";
+
+ public final String moduleCode;
+ public final String grade;
+
+
+ // Creates a PlanCommand with the moduleCode and grade
+ public MarkCommand(String moduleCode, String grade) {
+ this.moduleCode = moduleCode;
+ this.grade = grade;
+ }
+
+ /*
+ * Searches for the index of the moduleCode in the list and passes it to ModelManager to mark the module by index
+ */
+ @Override
+ public CommandResult execute(ModelManager model) throws InvalidCommandException {
+ int index = -1;
+ ModuleList list = model.getModuleList();
+ for (int i = 0; i < list.size(); i++) {
+ if (list.getModule(i).getCode().equals(moduleCode)) {
+ index = i;
+ }
+ }
+ if (index == -1) {
+ throw new InvalidCommandException("No such module exists!");
+ }
+
+ if (this.grade.equals("S") || this.grade.equals("U")) {
+ if (!ModuleRetriever.getSUstatus(this.moduleCode)) {
+ throw new InvalidCommandException("The module cannot be Su-ed");
+ }
+ }
+
+ model.markModule(index, this.grade);
+ Module markedModule = model.getModule(index);
+
+ return new CommandResult(String.format(MESSAGE, markedModule), false);
+ }
+}
diff --git a/src/main/java/seedu/penus/logic/commands/PlanCommand.java b/src/main/java/seedu/penus/logic/commands/PlanCommand.java
new file mode 100644
index 0000000000..ce82343b2f
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/commands/PlanCommand.java
@@ -0,0 +1,46 @@
+package seedu.penus.logic.commands;
+
+import seedu.penus.common.exceptions.DuplicateModuleException;
+import seedu.penus.common.exceptions.InvalidModuleException;
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.logic.utils.ModuleRetriever;
+import seedu.penus.model.ModelManager;
+import seedu.penus.model.Module;
+
+/**
+ * Adds a Plan module to the list
+ */
+public class PlanCommand extends Command {
+ public static final String COMMAND_WORD = "plan";
+ public static final String MESSAGE =
+ "Module has been added:\n"
+ + "\t %s\n"
+ + "\tYou have %s module(s) in your planner";
+
+ public final Module plan;
+
+ /**
+ * Creates a PlanCommand to add the specified {@code Module}
+ * by constructing a new Module object with the given parameters
+ * @param moduleCode string
+ * @param semester int
+ * @param year int
+ */
+ public PlanCommand(String moduleCode, int year, int semester) {
+ this.plan = new Module(moduleCode, year, semester);
+ }
+
+ @Override
+ public CommandResult execute(ModelManager model) throws PenusException {
+ if (model.hasModule(plan)) {
+ throw new DuplicateModuleException();
+ }
+ if (!ModuleRetriever.isValidMod(this.plan.getCode())) {
+ throw new InvalidModuleException();
+ }
+ model.addModule(plan);
+
+ return new CommandResult(String.format(MESSAGE, plan, model.getSize()), false);
+ }
+}
+
diff --git a/src/main/java/seedu/penus/logic/commands/RemoveCommand.java b/src/main/java/seedu/penus/logic/commands/RemoveCommand.java
new file mode 100644
index 0000000000..dea68b248d
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/commands/RemoveCommand.java
@@ -0,0 +1,45 @@
+package seedu.penus.logic.commands;
+
+import seedu.penus.common.exceptions.InvalidCommandException;
+import seedu.penus.model.ModelManager;
+import seedu.penus.model.ModuleList;
+import seedu.penus.model.Module;
+
+public class RemoveCommand extends Command {
+ public static final String COMMAND_WORD = "remove";
+ public static final String MESSAGE =
+ "Module has been removed:\n"
+ + "\t %s\n"
+ + "\tYou have %s module(s) in your planner";
+
+ public final String moduleCode;
+
+ /**
+ * Creates a PlanCommand to with the moduleCode attribute
+ * @param moduleCode string
+ */
+ public RemoveCommand(String moduleCode) {
+ this.moduleCode = moduleCode;
+ }
+
+ /**
+ * Searches for the index of the moduleCode in the list and passes it to ModelManager to remove the module by index
+ */
+ @Override
+ public CommandResult execute(ModelManager model) throws InvalidCommandException {
+ int index = -1;
+ ModuleList list = model.getModuleList();
+ for (int i = 0; i < list.size(); i++) {
+ if (list.getModule(i).getCode().equals(moduleCode)) {
+ index = i;
+ }
+ }
+ if (index == -1) {
+ throw new InvalidCommandException("No such module exists!");
+ }
+ Module removedModule = model.getModule(index);
+ model.removeModule(index);
+
+ return new CommandResult(String.format(MESSAGE, removedModule, model.getSize()), false);
+ }
+}
diff --git a/src/main/java/seedu/penus/logic/commands/StatusCommand.java b/src/main/java/seedu/penus/logic/commands/StatusCommand.java
new file mode 100644
index 0000000000..e83214974c
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/commands/StatusCommand.java
@@ -0,0 +1,128 @@
+package seedu.penus.logic.commands;
+
+import seedu.penus.common.exceptions.InvalidCommandException;
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.logic.utils.MCsTaken;
+import seedu.penus.logic.utils.ModuleRetriever;
+import seedu.penus.model.ModelManager;
+import seedu.penus.model.ModuleList;
+import seedu.penus.ui.Ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class StatusCommand extends Command {
+ public static final String COMMAND_WORD = "status";
+
+ public List getTakenCoreModsList(ModelManager model) {
+ List coreMods = model.getCoreModList().get(model.getUserCourse());
+ List takenCoreMods = new ArrayList<>();
+ ModuleList moduleList = model.getModuleList();
+ if (!model.getGEC().equals("")){
+ takenCoreMods.add(model.getGEC());
+ }
+ if (!model.getGESS().equals("")){
+ takenCoreMods.add(model.getGESS());
+ }
+ if (!model.getGEN().equals("")){
+ takenCoreMods.add(model.getGEN());
+ }
+ for (String coreModCode : coreMods) {
+ for (int i = 0; i < moduleList.size(); i++) {
+ String moduleCode = moduleList.getModule(i).getCode();
+ boolean isCurrentUserModuleTaken = moduleList.getModule(i).getStatus().equals("Taken");
+ if (coreModCode.equals(moduleCode) && isCurrentUserModuleTaken) {
+ takenCoreMods.add(moduleCode);
+ break;
+ }
+ }
+ }
+ return takenCoreMods;
+ }
+
+ public List getUntakenCoreModsList(ModelManager model) {
+ List coreMods = model.getCoreModList().get(model.getUserCourse());
+ List untakenCoreMods = new ArrayList<>();
+ ModuleList moduleList = model.getModuleList();
+ for (String coreModCode : coreMods) {
+ boolean isCoreModTaken = false;
+ for (int i = 0; i < moduleList.size(); i++) {
+ String moduleCode = moduleList.getModule(i).getCode();
+ boolean isCurrentUserModuleTaken = moduleList.getModule(i).getStatus().equals("Taken");
+ if (coreModCode.equals(moduleCode) && isCurrentUserModuleTaken) {
+ isCoreModTaken = true;
+ break;
+ }
+ }
+ if (!isCoreModTaken) {
+ untakenCoreMods.add(coreModCode);
+ }
+ }
+ return untakenCoreMods;
+ }
+
+ public int getNumberOfCoreMCsTaken (ModelManager model) {
+ int numberOfElectiveMCs = 0;
+ List takenCoreModsList = getTakenCoreModsList(model);
+ for (String moduleCode : takenCoreModsList) {
+ numberOfElectiveMCs = numberOfElectiveMCs +
+ Integer.parseInt(ModuleRetriever.getModuleCredit2223(moduleCode));
+ }
+ return numberOfElectiveMCs;
+ }
+
+ public String moduleCodeToString(String moduleCode) {
+ return moduleCode + " "+ ModuleRetriever.getTitle2223(moduleCode)
+ + " MCs: " + ModuleRetriever.getModuleCredit2223(moduleCode);
+ }
+
+
+ @Override
+ public CommandResult execute(ModelManager model) throws PenusException {
+ if (model.getUserName().equals("")) {
+ throw new InvalidCommandException(
+ "Please initialise first with the init command!"
+ );
+ }
+ Ui.showLoadingAnimation();
+ StringBuilder sb = new StringBuilder();
+ List takenCoreModsList = getTakenCoreModsList(model);
+ List untakenCoreModsList = getUntakenCoreModsList(model);
+ int totalMCsTaken = MCsTaken.numberOfMcsTaken(model.getModuleList().modules);
+ int coreMCsTaken = getNumberOfCoreMCsTaken(model);
+ int electiveMCsTaken = totalMCsTaken - coreMCsTaken;
+ List messageArray = new ArrayList<>();
+ messageArray.add("-------------------------- User --------------------------");
+ messageArray.add("\tUser: " + model.getUserName());
+ messageArray.add("\tCourse: " + model.getUserCourse());
+ messageArray.add("\t------------------- Core Modules Taken --------------------");
+ for (String s : takenCoreModsList){
+ messageArray.add("\t" + moduleCodeToString(s));
+ }
+ messageArray.add("\t----------------- Core Modules Not Taken ------------------");
+ if (model.getGEC().equals("")){
+ messageArray.add("\tGECXXXX");
+ }
+ if (model.getGESS().equals("")){
+ messageArray.add("\tGESSXXXX");
+ }
+ if (model.getGEN().equals("")){
+ messageArray.add("\tGENXXXX");
+ }
+ for (String s : untakenCoreModsList){
+ messageArray.add("\t" + moduleCodeToString(s));
+ }
+ messageArray.add("\t------------------------ MCs Status -----------------------");
+ messageArray.add("\tCore Modules MCs Taken: " + Integer.toString(coreMCsTaken));
+ messageArray.add("\tElective MCs Taken: " + Integer.toString(electiveMCsTaken));
+ messageArray.add("\tTotal MCs Taken: " + Integer.toString(totalMCsTaken) + "/160");
+
+ for (String s : messageArray){
+ sb.append(s).append("\n");
+ }
+
+ String message = sb.toString();
+ Ui.stopLoadingAnimation();
+ return new CommandResult(message, false);
+ }
+}
diff --git a/src/main/java/seedu/penus/logic/commands/TakenCommand.java b/src/main/java/seedu/penus/logic/commands/TakenCommand.java
new file mode 100644
index 0000000000..e57e04d352
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/commands/TakenCommand.java
@@ -0,0 +1,52 @@
+package seedu.penus.logic.commands;
+
+import seedu.penus.common.exceptions.DuplicateModuleException;
+import seedu.penus.common.exceptions.InvalidCommandException;
+import seedu.penus.common.exceptions.InvalidModuleException;
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.logic.utils.ModuleRetriever;
+import seedu.penus.model.ModelManager;
+import seedu.penus.model.Module;
+
+/**
+ * Adds a Taken module to the list
+ */
+public class TakenCommand extends Command {
+ public static final String COMMAND_WORD = "taken";
+ public static final String MESSAGE =
+ "Module has been added:\n"
+ + "\t %s\n"
+ + "\tYou have %s module(s) in your planner";
+ public final Module taken;
+
+ /**
+ * Creates a TakenCommand to add the specified {@code Module}
+ * by constructing a new Module object with the given parameters
+ * @param moduleCode string
+ * @param year int
+ * @param semester int
+ * @param grade string
+ */
+ public TakenCommand(String moduleCode, int year, int semester, String grade) {
+ this.taken = new Module(moduleCode, year, semester, grade);
+ }
+
+ @Override
+ public CommandResult execute(ModelManager model) throws PenusException {
+ if (!ModuleRetriever.isValidMod(this.taken.getCode())) {
+ throw new InvalidModuleException();
+ }
+ if (taken.getGrade().equals("U") || taken.getGrade().equals("S")) {
+ if (!ModuleRetriever.getSUstatus(taken.getCode())){
+ throw new InvalidCommandException("The module cannot be SU-ed");
+ }
+ }
+ if (model.hasModule(taken)) {
+ throw new DuplicateModuleException();
+ }
+
+ model.addModule(taken);
+
+ return new CommandResult(String.format(MESSAGE, taken, model.getSize()), false);
+ }
+}
diff --git a/src/main/java/seedu/penus/logic/parser/CliSyntax.java b/src/main/java/seedu/penus/logic/parser/CliSyntax.java
new file mode 100644
index 0000000000..ce8ec0d3b7
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/parser/CliSyntax.java
@@ -0,0 +1,23 @@
+package seedu.penus.logic.parser;
+
+public class CliSyntax {
+ public static final String LIST = "list";
+ public static final String STATUS = "status";
+ public static final String EXIT = "exit";
+ public static final String MARK = "mark";
+ public static final String REMOVE = "remove";
+ public static final String PLAN = "plan";
+ public static final String TAKEN = "taken";
+ public static final String PREREQ = "prerequisite";
+ public static final String DESC = "description";
+ public static final String TITLE = "title";
+ public static final String MC = "modulecredit";
+ public static final String DETAILS = "details";
+ public static final String HELP = "help";
+ public static final String INIT = "init";
+ public static final String CLEAR = "clear";
+
+ public static final String PREFIX_YEAR = "y/";
+ public static final String PREFIX_SEMESTER = "s/";
+ public static final String PREFIX_GRADE = "g/";
+}
diff --git a/src/main/java/seedu/penus/logic/parser/Parser.java b/src/main/java/seedu/penus/logic/parser/Parser.java
new file mode 100644
index 0000000000..e229eac63b
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/parser/Parser.java
@@ -0,0 +1,370 @@
+package seedu.penus.logic.parser;
+
+import static seedu.penus.logic.parser.CliSyntax.LIST;
+import static seedu.penus.logic.parser.CliSyntax.STATUS;
+import static seedu.penus.logic.parser.CliSyntax.EXIT;
+import static seedu.penus.logic.parser.CliSyntax.MARK;
+import static seedu.penus.logic.parser.CliSyntax.REMOVE;
+import static seedu.penus.logic.parser.CliSyntax.PLAN;
+import static seedu.penus.logic.parser.CliSyntax.TAKEN;
+
+import static seedu.penus.logic.parser.CliSyntax.DETAILS;
+import static seedu.penus.logic.parser.CliSyntax.HELP;
+import static seedu.penus.logic.parser.CliSyntax.INIT;
+import static seedu.penus.logic.parser.CliSyntax.CLEAR;
+
+import seedu.penus.common.exceptions.InvalidCommandException;
+import seedu.penus.common.exceptions.InvalidFormatException;
+import seedu.penus.common.exceptions.InvalidGradeException;
+import seedu.penus.common.exceptions.InvalidModuleException;
+import seedu.penus.common.exceptions.InvalidSemesterException;
+import seedu.penus.common.exceptions.InvalidYearException;
+import seedu.penus.common.exceptions.PenusException;
+
+import seedu.penus.logic.utils.Grade;
+import seedu.penus.logic.commands.ClearCommand;
+import seedu.penus.logic.commands.Command;
+import seedu.penus.logic.commands.PlanCommand;
+import seedu.penus.logic.commands.TakenCommand;
+import seedu.penus.logic.commands.RemoveCommand;
+import seedu.penus.logic.commands.DetailsCommand;
+import seedu.penus.logic.commands.ExitCommand;
+import seedu.penus.logic.commands.InitCommand;
+import seedu.penus.logic.commands.ListCommand;
+import seedu.penus.logic.commands.MarkCommand;
+import seedu.penus.logic.commands.StatusCommand;
+import seedu.penus.logic.commands.HelpCommand;
+
+public class Parser {
+ /**
+ * Parses user input into a Command object for execution.
+ * @param userInput input string from user
+ * @return Command object if no arguments needed, xYZParser(arguments) if arguments needed
+ * @throws PenusException invalid command
+ */
+ public Command parseCommand(String userInput) throws PenusException {
+ String[] inputArray = userInput.split(" ", 2);
+ String command = inputArray[0];
+ String arguments = "";
+ if (inputArray.length > 1) {
+ arguments = inputArray[1];
+ }
+ switch (command) {
+ case INIT:
+ return initParser(arguments);
+
+ case HELP:
+ return new HelpCommand();
+
+ case PLAN:
+ return planParser(arguments);
+
+ case TAKEN:
+ return takenParser(arguments);
+
+ case MARK:
+ return markParser(arguments);
+
+ case LIST:
+ return listParser(arguments);
+
+ case STATUS:
+ return new StatusCommand();
+
+ case REMOVE:
+ return removeParser(arguments);
+
+ case DETAILS:
+ return detailsParser(arguments);
+
+ case CLEAR:
+ return clearParser(arguments);
+
+ case EXIT:
+ return new ExitCommand();
+
+ default:
+ throw new InvalidCommandException();
+ }
+ }
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the PlanCommand
+ * and returns an PlanCommand object for execution.
+ * @param args aguments
+ * @return Command object
+ * @throws PenusException if the user input does not conform the expected format
+ */
+ public Command planParser(String args) throws PenusException {
+ if (args.contains("g/")) {
+ throw new InvalidFormatException("Grade should not be included!");
+ }
+ String[] planDetails = args.split(" y/| s/", 3);
+ if (planDetails.length != 3 || planDetails[1].length() == 0 || planDetails[2].length() == 0) {
+ throw new InvalidFormatException("Try again in the format: plan MODULE_CODE y/YEAR s/SEM");
+ }
+
+ String moduleCode = planDetails[0].toUpperCase().trim();
+ int year;
+ int semester;
+ try {
+ year = Integer.parseInt(planDetails[1].trim());
+ semester = Integer.parseInt(planDetails[2].trim());
+ } catch (NumberFormatException e) {
+ throw new InvalidFormatException("y/ or s/ Must be specified as an integer!");
+ }
+
+ if (year < 1 || year > 4) {
+ throw new InvalidYearException("Year must be 1 to 4. Please try again.");
+ }
+
+ if (semester != 1 && semester != 2) {
+ throw new InvalidSemesterException("Semester must be 1 or 2!");
+ }
+ return new PlanCommand(moduleCode, year, semester);
+ }
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the TakenCommand
+ * and returns an TakenCommand object for execution.
+ * @param args arguments
+ * @return Command object
+ * @throws PenusException if the user input does not conform the expected format
+ */
+ public Command takenParser(String args) throws PenusException {
+ String[] takenDetails = args.split(" y/| s/| g/", 4);
+ if (takenDetails.length != 4) {
+ throw new InvalidFormatException("Try again in the format: taken MODULE_CODE y/YEAR s/SEM g/GRADE");
+ }
+ if (takenDetails[1].length() == 0 || takenDetails[2].length() == 0 || takenDetails[3].length() == 0) {
+ throw new InvalidFormatException("Try again, y/ s/ g/ cannot be empty");
+ }
+ String moduleCode = takenDetails[0].toUpperCase().trim();
+ int year;
+ int semester;
+ try {
+ year = Integer.parseInt(takenDetails[1].trim());
+ semester = Integer.parseInt(takenDetails[2].trim());
+ } catch (NumberFormatException e) {
+ throw new InvalidFormatException("y/ or s/ Must be specified as an integer!");
+ }
+
+ String grade = takenDetails[3].toUpperCase().trim();
+ if (!Grade.isValid(grade)) {
+ throw new InvalidGradeException();
+ }
+ if (year < 1 || year > 4) {
+ throw new InvalidYearException("Year must be 1 to 4. Please try again.");
+ }
+ if (semester != 1 && semester != 2) {
+ throw new InvalidFormatException("Semester must be 1 or 2!");
+ }
+
+ return new TakenCommand(moduleCode, year, semester, grade);
+ }
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the
+ * MarkCommand and returns an MarkCommand object for execution.
+ *
+ * @param args string
+ * @return command object
+ * @throws PenusException if the user input does not conform the expected format
+ */
+ public Command markParser(String args) throws PenusException {
+ if (!args.contains("g/")) {
+ throw new InvalidFormatException("\tTry again in the format: mark MODULE_CODE g/GRADE");
+ }
+ String[] details = args.split(" g/");
+ if (!Grade.isValid(details[1].trim())) {
+ throw new InvalidGradeException();
+ }
+ String moduleCode = details[0].toUpperCase().trim();
+ String grade = details[1].toUpperCase().trim();
+
+ return new MarkCommand(moduleCode, grade);
+ }
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the
+ * ListCommand and returns an ListCommand object for execution.
+ *
+ * @param args string
+ * @return Command object
+ * @throws PenusException if the user input does not conform the expected format
+ */
+ public Command listParser(String args) throws PenusException {
+ if (args.equals("") || args.equals(" ")) {
+ // list command with all modules
+ return new ListCommand();
+ }
+
+ if (args.contains("s/") && !args.contains("y/")) { // Semester specified but year not specified
+ throw new InvalidFormatException(
+ "\tTry again, y/ must not be empty if s/ is not empty. " +
+ "To show modules for that semester, please specify the year of study.");
+ }
+
+ String[] details = args.split("y/| s/", 3);
+ int year = 0;
+ int semester = 0;
+ try {
+ year = Integer.parseInt(details[1].trim());
+ } catch (NumberFormatException nfe) {
+ throw new InvalidFormatException("\tYear must be specified as an integer!");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new InvalidFormatException("\tTry again in the format: list y/YEAR s/SEM or list y/YEAR or list");
+ }
+ if (year < 1 || year > 4) {
+ throw new InvalidFormatException("\tYear must be 1 to 4. Please try again.");
+ }
+
+ if (details.length == 2) {
+ return new ListCommand(year, 0);
+ } else if (details.length == 3) {
+ if (args.contains("s/")) {
+ try {
+ semester = Integer.parseInt(details[2].trim());
+ } catch (NumberFormatException nfe) {
+ throw new InvalidFormatException("\tSemester must be specified as an integer!");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new InvalidFormatException(
+ "\tTry again in the format: list y/YEAR s/SEM or list y/YEAR or list");
+ }
+ }
+
+ if (semester != 1 && semester != 2) {
+ throw new InvalidFormatException("\tSemester must be 1 or 2!");
+ }
+ return new ListCommand(year, semester);
+ } else {
+ throw new InvalidFormatException("\tTry again in the format: list y/YEAR s/SEM or list y/YEAR or list");
+ }
+ }
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the RemoveCommand
+ * and returns an RemoveCommand object for execution.
+ * @param args arguments
+ * @return command object
+ * @throws PenusException if the user input does not conform the expected format
+ */
+ public Command removeParser(String args) throws PenusException {
+ String moduleCode = args.toUpperCase().trim();
+ if (args.equals("")) {
+ throw new InvalidModuleException(args);
+ }
+ if (args.equals(" ")) {
+ throw new InvalidModuleException("\tPlease specify a module");
+ }
+
+ return new RemoveCommand(moduleCode);
+ }
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the DetailsCommand
+ * and returns an DetailsCommand object for execution.
+ * @param args arguments
+ * @return command object
+ * @throws PenusException if the user input does not conform the expected format
+ */
+ public Command detailsParser(String args) throws PenusException {
+ String[] details = args.split(" ");
+ if (args.equals("") || args.equals(" ") || details.length >= 2) {
+ throw new InvalidModuleException(args);
+ }
+ String moduleCode = args.toUpperCase().trim();
+
+ return new DetailsCommand(moduleCode);
+ }
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the InitCommand
+ * and returns an InitCommand object for execution.
+ * @param args arguments
+ * @return command object
+ * @throws PenusException if the user input does not conform the expected format
+ */
+ public Command initParser (String args) throws PenusException {
+ int courseCode;
+ String [] initDetails = args.split ("n/| c/");
+ if (initDetails.length != 3) {
+ throw new InvalidFormatException("Try again in the format: init n/NAME c/COURSE CODE");
+ }
+ if (initDetails[1].length() == 0 || initDetails[2].length() == 0) {
+ throw new InvalidFormatException("Try again, n/ c/ cannot be empty");
+ }
+ String name = initDetails[1].trim();
+ if (!name.matches("[a-zA-Z ]+")){
+ throw new InvalidFormatException("Name must only include letters and spaces.");
+ }
+ try {
+ courseCode = Integer.parseInt(initDetails[2].trim());
+ } catch (NumberFormatException e){
+ throw new InvalidFormatException("c/ must be an integer");
+ }
+ return new InitCommand(name, courseCode);
+
+ }
+
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the ClearCommand
+ * and returns an ClearCommand object for execution.
+ *
+ * @param args arguments
+ * @return command object
+ * @throws PenusException if the user input does not conform the expected format
+ */
+ public Command clearParser(String args) throws PenusException{
+ if (args.equals("") || args.equals(" ")) {
+ //clear all modules
+ return new ClearCommand();
+ }
+
+ // Semester specified but year not specified
+ if (args.contains("s/") && !args.contains("y/")) {
+ throw new InvalidFormatException(
+ "\tTry again, y/ must not be empty if s/ is not empty. " +
+ "To clear modules for that semester, please specify the year of study.");
+ }
+
+ String[] details = args.split("y/| s/",3);
+
+ //Default values
+ int year = 0;
+ int semester = 0;
+
+ try {
+ year = Integer.parseInt(details[1].trim());
+ } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
+ throw new InvalidFormatException("Year must be specified as an integer!");
+ }
+ //Invalid year
+ if (year < 1 || year > 4) {
+ throw new InvalidFormatException("Year must be within 1 to 4");
+ }
+
+ //Only year specified, semester = 0
+ if (details.length == 2) {
+ return new ClearCommand(year,semester);
+
+ } else if (details.length == 3) { //Year and Semester specified
+ if (args.contains("s/")) {
+ try {
+ semester = Integer.parseInt(details[2].trim());
+ } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
+ throw new InvalidFormatException("Semester must be specified as an integer!");
+ }
+ }
+
+ //Invalid semester
+ if (semester != 1 && semester != 2) {
+ throw new InvalidFormatException("Semester must be 1 or 2!");
+ }
+ return new ClearCommand(year, semester);
+ } else {
+ throw new InvalidFormatException("Try again in the format: clear y/YEAR s/SEM or clear y/YEAR or clear");
+ }
+ }
+}
diff --git a/src/main/java/seedu/penus/logic/utils/DetailsCompiler.java b/src/main/java/seedu/penus/logic/utils/DetailsCompiler.java
new file mode 100644
index 0000000000..a42fbc42d2
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/utils/DetailsCompiler.java
@@ -0,0 +1,58 @@
+package seedu.penus.logic.utils;
+
+public class DetailsCompiler extends ModuleRetriever{
+ protected String moduleCode;
+
+ /**
+ * Retrieves details of given module code, and displays error message if information is not available
+ * @param module string
+ */
+ public static String getDetails(String module) {
+ String title;
+ String description;
+ String prereqs;
+ String credits;
+ String suStatusDescription;
+
+ try {
+ if (!isValidMod(module)) {
+ return "This module code is invalid. Try again.";
+ };
+ } catch (Exception e) {
+ return "This module code is invalid. Try again.";
+ }
+
+ try {
+ title = getTitle2223(module);
+ } catch (Exception e) {
+ title = "\tTitle is not available";
+ }
+ try {
+ description = "\t" + getDescription(module);
+ } catch (Exception e) {
+ description = "\tDescription is not available";
+ }
+ try {
+ prereqs = "\tPre-Requisites: " + getPrerequisite(module);
+ } catch (Exception e) {
+ prereqs = "\tPre-Requisites information is not available";
+ }
+ try {
+ credits = "\tMCs: " + getModuleCredit2223(module);
+ } catch (Exception e) {
+ credits = "\tModular Credits information is not available";
+ }
+ try {
+ boolean suStatus = getSUstatus(module);
+ if (suStatus) {
+ suStatusDescription = "\tModule can be SU-ed.";
+ } else {
+ suStatusDescription = "\tModule cannot be SU-ed.";
+ }
+ } catch (Exception e) {
+ suStatusDescription = "\tSU information is not available";
+ }
+
+ return title + "\n" + description + "\n" + prereqs + "\n" + credits + "\n" + suStatusDescription;
+ }
+}
diff --git a/src/main/java/seedu/penus/logic/utils/Grade.java b/src/main/java/seedu/penus/logic/utils/Grade.java
new file mode 100644
index 0000000000..bea38661aa
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/utils/Grade.java
@@ -0,0 +1,205 @@
+package seedu.penus.logic.utils;
+
+import java.util.List;
+import java.util.Arrays;
+
+import seedu.penus.common.exceptions.InvalidGradeException;
+import seedu.penus.model.Module;
+
+public class Grade {
+ /**
+ * Converts a {@code String} grade into its associated gradepoint
+ *
+ * @param grade string
+ * @return {@code double} corresponding value of the grade
+ * @throws InvalidGradeException invalid grade
+ */
+ public static double getGradePoint(String grade) throws InvalidGradeException {
+ double gradePoint;
+ switch (grade.toUpperCase()) {
+ case "A+":
+ case "A":
+ gradePoint = 5.0;
+ break;
+
+ case "A-":
+ gradePoint = 4.5;
+ break;
+
+ case "B+":
+ gradePoint = 4.0;
+ break;
+
+ case "B":
+ gradePoint = 3.5;
+ break;
+
+ case "B-":
+ gradePoint = 3.0;
+ break;
+
+ case "C+":
+ gradePoint = 2.5;
+ break;
+
+ case "C":
+ gradePoint = 2.0;
+ break;
+
+ case "D+":
+ gradePoint = 1.5;
+ break;
+
+ case "D":
+ gradePoint = 1.0;
+ break;
+
+ case "F":
+ gradePoint = 0.0;
+ break;
+
+ default:
+ throw new InvalidGradeException();
+
+ }
+ return gradePoint;
+ }
+
+ /**
+ * Checks if a grade {@code String} is valid and within expected inputs
+ *
+ * @param grade string
+ * @return Boolean true if grade is valid
+ */
+ public static Boolean isValid(String grade) {
+ List validGrades = Arrays.asList(
+ "A+", "A", "A-",
+ "B+", "B", "B-",
+ "C+", "C",
+ "D+", "D",
+ "F", "S", "U",
+ "CS", "CU"
+ );
+ return validGrades.contains(grade.toUpperCase());
+ }
+
+ /**
+ * For every module taken, calculate weighted score = number of MC * grade
+ * Sum up weighted score for all mods and divide by total MCs taken thus far
+ * S/U grades are not calculated for in Overall CAP
+ *
+ * @param moduleList the list containing all modules taken
+ * @return total CAP for all mods taken thus far
+ * @throws InvalidGradeException if there exists an unidentified Grade type
+ */
+ public static double calculateOverallCAP(List moduleList) throws InvalidGradeException {
+ double cap;
+ int numOfSUMods = 0;
+ int numOfModsTaken = 0;
+ int numOfModsPlanned = 0;
+ double totalScore = 0.0;
+ double totalMC = 0.0;
+ String numberOfMCs;
+ for (Module module : moduleList) {
+ if (module.getStatus().equals("Taken")) {
+ numOfModsTaken++;
+ if (module.getGrade().matches("S|U|CS|CU")) {
+ numOfSUMods++;
+ } else {
+ numberOfMCs = ModuleRetriever.getModuleCredit2223(module.getCode());
+ double weightedScore = Double.parseDouble(numberOfMCs) *
+ module.getGradePoint();
+ totalScore += weightedScore;
+ totalMC += Double.parseDouble(numberOfMCs);
+ }
+ } else {
+ numOfModsPlanned++;
+ }
+ }
+ if (numOfModsPlanned == moduleList.size()) {//all mods are planned
+ cap = 0.0;
+ } else if (numOfSUMods == numOfModsTaken) { //all mods are SU
+ cap = 5.0;
+ } else {
+ cap = totalScore / totalMC;
+ }
+ return cap;
+ }
+
+ /**
+ * For every module taken in a semester, calculate weighted score = number of MC
+ * * grade
+ * Sum up weighted score for all mods and divide by total MCs taken in the
+ * semester
+ * S/U grades are not calculated for in Semester CAP
+ *
+ * @param semArray list of String array containing moduleCode and moduleGrade
+ * @return total CAP for a particular semester
+ * @throws InvalidGradeException if there exists an unidentified Grade type
+ */
+ public static double calculateSemCAP(List semArray) throws InvalidGradeException {
+ double cap;
+ int numOfSUMods = 0;
+ double totalScore = 0.0;
+ double totalMC = 0.0;
+ String numberOfMCs;
+ for (String[] module : semArray) {
+ String moduleCode = module[0];
+ String moduleGrade = module[1];
+ if (moduleGrade.matches("S|U|CS|CU")) {
+ numOfSUMods++;
+ }
+ if (!moduleGrade.matches("S|U|CS|CU") && !moduleGrade.equals("")) {
+ numberOfMCs = ModuleRetriever.getModuleCredit2223(moduleCode);
+ double weightedScore = Double.parseDouble(numberOfMCs) *
+ Grade.getGradePoint(moduleGrade);
+ totalScore += weightedScore;
+ totalMC += Double.parseDouble(numberOfMCs);
+ }
+ }
+ if (numOfSUMods == semArray.size()) {//all mods in sem are SU-ed
+ cap = 5.0;
+ } else if ((totalScore == 0.0) || (totalMC == 0.0)) {//all mods are planned
+ cap = 0.0;
+ } else {
+ cap = totalScore / totalMC;
+ }
+ return cap;
+ }
+
+ /**
+ * Calls calculateOverallCAP and prints the overall CAP to 2 decimal places
+ *
+ * @param moduleList the list containing all modules taken
+ * @return capMessage String
+ * @throws InvalidGradeException if there exists an unidentified Grade
+ */
+ public static String getOverallCAP(List moduleList) throws InvalidGradeException {
+ String capMessage;
+ if (moduleList.isEmpty()) {
+ capMessage = "Overall CAP : 0.00\n";
+ } else {
+ Double overallCAP = calculateOverallCAP(moduleList);
+ capMessage = String.format("Overall CAP : %.2f\n", overallCAP);
+ }
+ return capMessage;
+ }
+
+ /**
+ * Calls calculateSemCAP and prints the semester CAP to 2 decimal places
+ *
+ * @param semArray list of String array containing moduleCode and moduleGrade
+ * @return semCapMessage String
+ * @throws InvalidGradeException if there exists an unidentified Grade
+ */
+ public static String getSemCAP(List semArray) throws InvalidGradeException {
+ String semCapMessage;
+ if (semArray.isEmpty()) {
+ semCapMessage = "Semester CAP : 0.00\n";
+ } else {
+ Double semCAP = calculateSemCAP(semArray);
+ semCapMessage = String.format("Semester CAP : %.2f\n", semCAP);
+ }
+ return semCapMessage;
+ }
+}
diff --git a/src/main/java/seedu/penus/logic/utils/MCsTaken.java b/src/main/java/seedu/penus/logic/utils/MCsTaken.java
new file mode 100644
index 0000000000..71f7fa9b17
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/utils/MCsTaken.java
@@ -0,0 +1,22 @@
+package seedu.penus.logic.utils;
+
+import java.util.List;
+import seedu.penus.model.Module;
+
+public class MCsTaken {
+ /**
+ * Iterates throught he moduleList and adds the number of MCs of each taken module to the sum
+ * The MCs are retreieved from the ModuleRetriever API from NUSMods.
+ * @param moduleList list of modules
+ * @return int total number of MCs of the taken modules in the list
+ */
+ public static int numberOfMcsTaken(List moduleList) {
+ int numberOfMcs = 0;
+ for (Module module : moduleList) {
+ if (module.getStatus().equals("Taken")) {
+ numberOfMcs = numberOfMcs + Integer.parseInt(ModuleRetriever.getModuleCredit2223(module.getCode()));
+ }
+ }
+ return numberOfMcs;
+ }
+}
diff --git a/src/main/java/seedu/penus/logic/utils/ModuleRetriever.java b/src/main/java/seedu/penus/logic/utils/ModuleRetriever.java
new file mode 100644
index 0000000000..32015f06e1
--- /dev/null
+++ b/src/main/java/seedu/penus/logic/utils/ModuleRetriever.java
@@ -0,0 +1,205 @@
+package seedu.penus.logic.utils;
+
+import org.json.simple.JSONObject;
+import org.json.simple.JSONArray;
+import org.json.simple.parser.JSONParser;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Scanner;
+import seedu.penus.common.exceptions.InvalidModuleAPIException;
+import seedu.penus.common.exceptions.InvalidModuleException;
+import seedu.penus.common.exceptions.NoInternetException;
+
+
+// test comment
+public class ModuleRetriever {
+ public static JSONObject moduleInfo2223;
+ public static JSONObject moduleInfo2122;
+ public static void connectionChecker() throws NoInternetException {
+ try {
+ // Public API:
+ // https://api.nusmods.com/v2/2022-2023/modules/.json
+
+
+ URL url = new URL("https://api.nusmods.com/v2/2022-2023/modules/CS1231.json");
+
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("GET");
+ conn.connect();
+
+ // check if connect is made
+ int responseCode = conn.getResponseCode();
+
+ // 200 OK
+ if (responseCode != 200) {
+ throw new Exception();
+ }
+ } catch (Exception e) {
+ throw new NoInternetException();
+ }
+ }
+
+ public static void getData2223(String module) throws InvalidModuleAPIException{
+ try {
+ // Public API:
+ // https://api.nusmods.com/v2/2022-2023/modules/.json
+
+ module = module.toUpperCase();
+
+ URL url = new URL("https://api.nusmods.com/v2/2022-2023/modules/" + module + ".json");
+
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("GET");
+ conn.connect();
+
+ // check if connect is made
+ int responseCode = conn.getResponseCode();
+
+ // 200 OK
+ if (responseCode != 200) {
+ throw new InvalidModuleAPIException();
+ } else {
+ Scanner scanner = new Scanner(url.openStream());
+ String informationString = scanner.nextLine();
+
+ // Close scanner
+ scanner.close();
+
+ // JSON simple library Setup with Maven is used to convert strings to JSON
+ JSONParser parser = new JSONParser();
+ Object obj = parser.parse(String.valueOf(informationString));
+ JSONArray dataObject = new JSONArray();
+ dataObject.add(obj);
+
+ // Get the first JSON object in the JSON array
+ moduleInfo2223 = (JSONObject) dataObject.get(0);
+ }
+ } catch (Exception e) {
+ moduleInfo2122 = null;
+ }
+ }
+
+ public static void getData2122(String module) throws InvalidModuleAPIException {
+ try {
+ // Public API:
+ // https://api.nusmods.com/v2/2021-2022/modules/.json
+
+ module = module.toUpperCase();
+
+ URL url = new URL("https://api.nusmods.com/v2/2021-2022/modules/" + module + ".json");
+
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("GET");
+ conn.connect();
+
+ // check if connect is made
+ int responseCode = conn.getResponseCode();
+
+ // 200 OK
+ if (responseCode != 200) {
+ throw new InvalidModuleAPIException();
+ } else {
+ Scanner scanner = new Scanner(url.openStream());
+ String informationString = scanner.nextLine();
+
+ // Close scanner
+ scanner.close();
+
+ // JSON simple library Setup with Maven is used to convert strings to JSON
+ JSONParser parser = new JSONParser();
+ Object obj = parser.parse(String.valueOf(informationString));
+ JSONArray dataObject = new JSONArray();
+ dataObject.add(obj);
+
+ // Get the first JSON object in the JSON array
+ moduleInfo2122 = (JSONObject) dataObject.get(0);
+
+ //test code
+ }
+ } catch (Exception e) {
+ moduleInfo2122 = null;
+ }
+ }
+
+ public static String getPrerequisite(String module) {
+ try {
+ getData2122(module);
+ return (String) moduleInfo2122.get("prerequisite");
+ } catch (InvalidModuleAPIException e) {
+ return null;
+ }
+ }
+
+ public static String getDescription(String module) {
+ try {
+ getData2223(module);
+ return (String) moduleInfo2223.get("description");
+ } catch (InvalidModuleAPIException e) {
+ return null;
+ }
+ }
+
+ public static String getTitle2223(String module) {
+ try {
+ getData2223(module);
+ return (String) moduleInfo2223.get("title");
+ } catch (InvalidModuleAPIException e) {
+ return null;
+ }
+
+ }
+
+ public static String getModuleCredit2223(String module) {
+ try {
+ getData2223(module);
+ return (String) moduleInfo2223.get("moduleCredit");
+ } catch (InvalidModuleAPIException e) {
+ return null;
+ }
+ }
+
+ public static String getTitle2122(String module) {
+ try {
+ getData2122(module);
+ return (String) moduleInfo2122.get("title");
+ } catch (InvalidModuleAPIException e) {
+ return null;
+ }
+ }
+
+ public static String getModuleCredit2122(String module) {
+ //Temporary fix, will make code cleaner if I can think of a better solution
+ if (module.equals("ES2631")) {
+ return "4";
+ }
+ try {
+ getData2122(module);
+ return (String) moduleInfo2122.get("moduleCredit");
+ } catch (InvalidModuleAPIException e) {
+ return null;
+ }
+ }
+
+ public static Boolean getSUstatus(String module) {
+ try {
+ getData2223(module);
+ JSONObject attributes = (JSONObject) moduleInfo2223.get("attributes");
+ Boolean suStatus = (Boolean) attributes.get("su");
+
+ return suStatus != null;
+ } catch (InvalidModuleAPIException e) {
+ return null;
+ }
+ }
+
+ public static Boolean isValidMod(String module) throws InvalidModuleException {
+ try {
+ moduleInfo2223 = null;
+ getData2223(module);
+ } catch (InvalidModuleAPIException e) {
+ throw new InvalidModuleException();
+ }
+ return moduleInfo2223 != null;
+
+ }
+}
diff --git a/src/main/java/seedu/penus/model/ModelManager.java b/src/main/java/seedu/penus/model/ModelManager.java
new file mode 100644
index 0000000000..4d867d8163
--- /dev/null
+++ b/src/main/java/seedu/penus/model/ModelManager.java
@@ -0,0 +1,132 @@
+package seedu.penus.model;
+
+import static java.util.Objects.requireNonNull;
+import java.util.HashMap;
+import java.util.List;
+
+public class ModelManager {
+ private ModuleList moduleList;
+ private final User user;
+ private final List coreDetails;
+ private final HashMap> coreModList;
+
+
+ public ModelManager(User user,
+ List list,
+ List coreDetails,
+ HashMap> coreModList) {
+ this.user = user;
+ this.moduleList = new ModuleList(list);
+ this.coreDetails = coreDetails;
+ this.coreModList = coreModList;
+ }
+
+ //=============================== Module List ===================================
+ public ModuleList getModuleList() {
+ return this.moduleList;
+ }
+
+ public List getModuleListObj() {
+ return moduleList.getModuleList();
+ }
+
+ /**
+ * Checks if the moduleList contains the specified Module
+ * @param module module
+ * @return boolean true if module exists
+ */
+ public boolean hasModule(Module module) {
+ requireNonNull(module);
+ return moduleList.hasModule(module);
+ }
+
+ public boolean hasModuleCode(String moduleCode) {
+ requireNonNull(moduleCode);
+ return moduleList.hasModuleCode(moduleCode);
+ }
+
+ /**
+ * Adds the specificed Module to the moduleList
+ * @param module Module
+ */
+ public void addModule(Module module) {
+ requireNonNull(module);
+ moduleList.addModule(module);
+ }
+
+ /**
+ * removes the specified module by its index from the moduleList
+ * @param index int
+ */
+ public void removeModule(int index) {
+ moduleList.removeModule(index);
+ }
+
+ /**
+ * Marks the specified module by getting the Module from the moduleList by its index
+ * @param index int
+ * @param grade string
+ */
+ public void markModule(int index, String grade) {
+ Module module = moduleList.getModule(index);
+ module.markTaken(grade);
+ }
+
+ public int getSize() {
+ return moduleList.size();
+ }
+
+ public Module getModule(int index) {
+ return moduleList.getModule(index);
+ }
+
+ public Module getModuleByCode(String moduleCode) {
+ return moduleList.getModuleByCode(moduleCode);
+ }
+
+ public String getGESS() {
+ return moduleList.getGESS();
+ }
+
+ public String getGEC() {
+ return moduleList.getGEC();
+ }
+
+ public String getGEN() {
+ return moduleList.getGEN();
+ }
+
+ public void setModuleList(List moduleList) {
+ this.moduleList = new ModuleList(moduleList);
+ }
+
+ //=============================== User ==========================================
+ public User getUser() {
+ return user;
+ }
+
+ public String getUserName() {
+ return user.getName();
+ }
+
+ public String getUserCourse() {
+ return user.getCourse();
+ }
+
+ public void setUserName(String name) {
+ user.setName(name);
+ }
+
+ public void setUserCourse(String course) {
+ user.setCourse(course);
+ }
+
+ //==============================Core mods =======================================
+ public List getCoreDetails() {
+ return coreDetails;
+ }
+
+ public HashMap> getCoreModList() {
+ return coreModList;
+ }
+}
diff --git a/src/main/java/seedu/penus/model/Module.java b/src/main/java/seedu/penus/model/Module.java
new file mode 100644
index 0000000000..ddf01a382a
--- /dev/null
+++ b/src/main/java/seedu/penus/model/Module.java
@@ -0,0 +1,81 @@
+package seedu.penus.model;
+
+import seedu.penus.common.exceptions.InvalidGradeException;
+import seedu.penus.logic.utils.Grade;
+
+public class Module {
+ protected String moduleCode;
+ protected Integer year;
+ protected Integer semester;
+ protected String grade;
+ protected boolean isTaken;
+
+ public Module(String moduleCode, Integer year, Integer semester) {
+ this.moduleCode = moduleCode;
+ this.year = year;
+ this.semester = semester;
+ this.isTaken = false;
+ }
+
+ //overloaded method for taken modules
+ public Module(String moduleCode, Integer year, Integer semester, String grade) {
+ this.moduleCode = moduleCode;
+ this.year = year;
+ this.semester = semester;
+ this.grade = grade;
+ this.isTaken = true;
+ }
+
+ public String getCode() {
+ return this.moduleCode;
+ }
+
+ public Integer getYear() {
+ return this.year;
+ }
+
+ public Integer getSem() {
+ return this.semester;
+ }
+
+ public void markTaken(String grade) {
+ this.isTaken = true;
+ this.grade = grade;
+ }
+
+ public void markUntaken() {
+ this.isTaken = false;
+ this.grade = "";
+ }
+
+ public String getStatus() {
+ return (isTaken ? "Taken" : "Plan");
+ }
+
+ public String getGrade() {
+ return (isTaken ? grade : "");
+ }
+
+ public double getGradePoint() throws InvalidGradeException {
+ return Grade.getGradePoint(this.grade);
+ }
+
+ @Override
+ public String toString() {
+ return this.getStatus() + " " + this.moduleCode + " year "
+ + this.year + " semester " + this.semester + " " + getGrade();
+ }
+
+ public String encode() {
+ String encoded = null;
+ if (getStatus() == "Taken") {
+ encoded = String.format("%s ### %s ### %s ### %s ### %s",
+ getStatus(), this.moduleCode, this.year, this.semester, this.grade);
+ } else if (getStatus() == "Plan") {
+ encoded = String.format("%s ### %s ### %s ### %s",
+ getStatus(), this.moduleCode, this.year, this.semester);
+ }
+
+ return encoded;
+ }
+}
diff --git a/src/main/java/seedu/penus/model/ModuleList.java b/src/main/java/seedu/penus/model/ModuleList.java
new file mode 100644
index 0000000000..327a80bc76
--- /dev/null
+++ b/src/main/java/seedu/penus/model/ModuleList.java
@@ -0,0 +1,141 @@
+package seedu.penus.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ModuleList {
+ public final List modules;
+
+ /**
+ * Overloaded constructor for the creation of a ModuleList object.
+ */
+ public ModuleList() {
+ this.modules = new ArrayList<>();
+ }
+
+ /**
+ * Constructor for the creation of a ModuleList object.
+ *
+ * @param mods List of mods to be included in ModuleLIst after creation.
+ */
+ public ModuleList(List mods) {
+ this.modules = mods;
+ }
+
+
+
+ /**
+ * Gets the list of modules.
+ *
+ * @return List of modules inline separated by commas
+ */
+ public List getModuleList() {
+ return this.modules;
+ }
+
+ /**
+ * Gets a module from the module list by index
+ *
+ * @param index the index of the module
+ * @return the module corresponding to the given index
+ */
+ public Module getModule(int index) {
+ return modules.get(index);
+ }
+
+ public Module getModuleByCode(String moduleCode) {
+ for (Module m : modules) {
+ if (m.getCode().equals(moduleCode)) {
+ return m;
+ }
+ }
+ return null;
+ }
+
+ public boolean hasModule(Module module) {
+ for (Module m : modules) {
+ if (m.getCode().equals(module.getCode())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean hasModuleCode(String moduleCode) {
+ for (Module m : modules) {
+ if (m.getCode().equals(moduleCode)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Adds a given module to the ModuleList object.
+ *
+ * @param module The module to be added.
+ */
+ public void addModule(Module module) {
+ this.modules.add(module);
+ }
+
+ /**
+ * Deletes the module identified by its code from the ModuleList
+ *
+ * @param index The index corresponding to the module to be deleted.
+ */
+ public void removeModule(int index) {
+ this.modules.remove(index);
+ }
+
+ /**
+ * Returns the number of modules in the module list
+ *
+ * @return the number of modules in the module list
+ */
+ public int size() {
+ return modules.size();
+ }
+
+ /**
+ * Returns the module code of GESS module if exist and taken, "" if does not exist
+ *
+ * @return the module code of GESS module if exist and taken, "" if does not exist
+ */
+ public String getGESS() {
+ for (Module module : modules) {
+ if (module.moduleCode.substring(0, 4).equals("GESS") && module.isTaken) {
+ return module.moduleCode;
+ }
+ }
+ return "";
+ }
+
+ /**
+ * Returns the module code of GEC module if exist and taken, "" if does not exist
+ *
+ * @return the module code of GEC module if exist and taken, "" if does not exist
+ */
+ public String getGEC() {
+ for (Module module : modules) {
+ if (module.moduleCode.substring(0, 3).equals("GEC") && module.isTaken) {
+ return module.moduleCode;
+ }
+ }
+ return "";
+ }
+
+ /**
+ * Returns the module code of GEN module if exist and taken, "" if does not exist
+ *
+ * @return the module code of GEN module if exist and taken, "" if does not exist
+ */
+ public String getGEN() {
+ for (Module module : modules) {
+ if (module.moduleCode.substring(0, 3).equals("GEN") && module.isTaken) {
+ return module.moduleCode;
+ }
+ }
+ return "";
+ }
+}
diff --git a/src/main/java/seedu/penus/model/User.java b/src/main/java/seedu/penus/model/User.java
new file mode 100644
index 0000000000..24b4dc016b
--- /dev/null
+++ b/src/main/java/seedu/penus/model/User.java
@@ -0,0 +1,39 @@
+package seedu.penus.model;
+
+public class User {
+ public String name;
+
+ public String course;
+
+ public User() {
+ this.name = "";
+ this.course = "";
+ }
+
+ //overloaded method for initialising prev user
+ public User(String name, String course) {
+ this.name = name;
+ this.course = course;
+ }
+
+ public void setName(String inputName) {
+ this.name = inputName;
+ }
+
+ public void setCourse(String inputCourse){
+ this.course = inputCourse;
+ }
+
+ public String getName(){
+ return this.name;
+ }
+
+ public String getCourse(){
+ return this.course;
+ }
+
+ public String encode() {
+ return String.format("User ### %s ### %s", this.name, this.course);
+ }
+}
+
diff --git a/src/main/java/seedu/penus/storage/FileStorage.java b/src/main/java/seedu/penus/storage/FileStorage.java
new file mode 100644
index 0000000000..84e33d6a13
--- /dev/null
+++ b/src/main/java/seedu/penus/storage/FileStorage.java
@@ -0,0 +1,139 @@
+package seedu.penus.storage;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Scanner;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.FileNotFoundException;
+import seedu.penus.common.exceptions.DuplicateModuleException;
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.model.Module;
+import seedu.penus.model.ModuleList;
+import seedu.penus.model.User;
+
+public class FileStorage {
+ public File file;
+ public String dataDirectory;
+ public String filePath;
+
+ /**
+ * Constructor for the File Manager object.
+ * Creates a File object according to the relative path /data/penus.txt to store the data
+ *
+ * Initializes a /data/ folder and penus.txt if it does not exist
+ */
+ public FileStorage() {
+ this.dataDirectory = "./data/";
+ this.filePath = this.dataDirectory + "penus.txt";
+ this.file = new File(this.filePath);
+ File directory = new File(this.dataDirectory);
+ try {
+ if (!directory.exists()) {
+ directory.mkdirs();
+ }
+ if (!this.file.exists()) {
+ this.file.createNewFile();
+ }
+ } catch (IOException e) {
+ System.out.println(e);
+ }
+ }
+
+ /**
+ * Saves the current moduleList accumulated over the program's life and stores it in /data/penus.txt
+ *
+ * @param moduleList the moduleList containing all the user's modules
+ * @param user the user containing the user preferences
+ */
+ public void save(ModuleList moduleList, User user) {
+ try {
+ FileWriter writer = new FileWriter(this.filePath);
+ if (!user.getName().equals("") && !user.getCourse().equals("")) {
+ writer.write(user.encode() + "\n");
+ }
+ for (Module module : moduleList.getModuleList()) {
+ writer.write(module.encode() + "\n");
+ }
+ writer.close();
+ } catch (IOException e) {
+ System.out.println(e);
+ }
+ }
+
+ /**
+ * Retrieves any modules saved in /data/penus.txt if the directory exists.
+ * Decodes the contents of penus.txt into a moduleList object.
+ *
+ * @return moduleList the moduleList containing all the user's modules saved in storage
+ */
+ public List retrieveMods() throws PenusException {
+ Scanner scanner = null;
+ List moduleList = new ArrayList<>();
+ try {
+ scanner = new Scanner(this.file);
+ while (scanner.hasNextLine()) {
+ String encoded = scanner.nextLine();
+ if (encoded.length() == 0) {
+ continue;
+ }
+ if (encoded.contains("User")) {
+ continue;
+ }
+ Module decodedModule = StorageDecoder.decodemodule(encoded);
+ if (hasModule(decodedModule, moduleList)) {
+ throw new DuplicateModuleException();
+ }
+ moduleList.add(decodedModule);
+ }
+ } catch (FileNotFoundException e) {
+ System.out.println(e);
+ } finally {
+ if (scanner != null) {
+ scanner.close();
+ }
+ }
+
+ return moduleList;
+ }
+
+ /**
+ * Retrieves any User saved in /data/penus.txt if the directory exists.
+ * Decodes the contents of penus.txt into a User object.
+ *
+ * @return user the User containing the user details saved in storage
+ */
+ public User retrieveUser() throws PenusException {
+ Scanner scanner = null;
+ User user = new User();
+ try {
+ scanner = new Scanner(this.file);
+ if (scanner.hasNextLine()) {
+ String userLine = scanner.nextLine();
+ if (userLine.contains("User")) {
+ user = StorageDecoder.decodeUser(userLine);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ System.out.println(e);
+ } finally {
+ if (scanner != null) {
+ scanner.close();
+ }
+ }
+
+ return user;
+ }
+
+ private boolean hasModule(Module module, List moduleList) {
+ for (Module m : moduleList) {
+ if (m.getCode().equals(module.getCode())) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+
diff --git a/src/main/java/seedu/penus/storage/ResourceStorage.java b/src/main/java/seedu/penus/storage/ResourceStorage.java
new file mode 100644
index 0000000000..135c649e32
--- /dev/null
+++ b/src/main/java/seedu/penus/storage/ResourceStorage.java
@@ -0,0 +1,117 @@
+package seedu.penus.storage;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import java.util.HashMap;
+
+import java.util.Objects;
+
+
+/*
+ * IMPORTANT NOTE:
+ * in IntelliJ: right click /resources folder -> Mark Directory as -> Resources root
+ * in VSCode: check /.vscode/settings.json -> should include:
+ * "java.project.referencedLibraries": [
+ {
+ "scope": "java",
+ "path": "src/main/resources"
+ }
+ ]
+ * then in VSCode Explorer Tab -> Java Projects -> ... -> Clean Workspace
+ *
+ * returns "jar:file:/C:/path/to/project/build/resources/main/.txt"
+ * returns "file:/C:/path/to/project/src/main/resources/.txt"
+ */
+
+public class ResourceStorage {
+ public String coreModFile;
+ public String modDetailsFile;
+
+ public ResourceStorage() {
+ this.coreModFile = "core-modules.txt";
+ this.modDetailsFile = "core-module-details.txt";
+ }
+
+ /**
+ * Retrieves all module details in /resource/core-modules.txt
+ *
+ * Parses the content of core-modules.txt into a Hashmap
+ * Key: courseName String
+ * Value: coreModulesList Array
+ *
+ * @return HashMap
+ */
+ public HashMap> getCoreMods() {
+ HashMap > coreModHashMap = new HashMap<>();
+ String courseName = "";
+ BufferedReader reader;
+ List coreModulesList = new ArrayList<>();
+ try {
+ InputStreamReader stream = new InputStreamReader(
+ getClass().getClassLoader().getResourceAsStream(coreModFile)
+ );
+ reader = new BufferedReader(stream);
+ String line;
+ while ((line = reader.readLine()) != null) {
+ if (line.startsWith("##")) {
+ courseName = line.substring(2);
+ } else if (line.equals("END")){
+ List coreModulesListCopy = new ArrayList<>(coreModulesList);
+ coreModHashMap.put(courseName, coreModulesListCopy);
+ coreModulesList.clear();
+ } else {
+ coreModulesList.add(line);
+ }
+ }
+ reader.close();
+ } catch (IOException e) {
+ System.out.println(e);
+ }
+ return coreModHashMap;
+ }
+
+ /**
+ * Retrieves all module details in /resource/module-details.txt
+ *
+ * Parses the content of module-details.txt into a List of decoded modules.
+ * @return the List containing all the decoded modules.
+ */
+ public List getAllModuleDetails() {
+ BufferedReader reader;
+ List moduleDetailsList = new ArrayList<>();
+ try {
+ InputStreamReader stream = new InputStreamReader(
+ Objects.requireNonNull(getClass().getClassLoader().getResourceAsStream(modDetailsFile))
+ );
+ reader = new BufferedReader(stream);
+ String line;
+ while ((line = reader.readLine()) != null) {
+ String[] decodedModule = decodeModule(line);
+ moduleDetailsList.add(decodedModule);
+ }
+ reader.close();
+ } catch (IOException e) {
+ System.out.println(e);
+ }
+
+ return moduleDetailsList;
+ }
+
+ /**
+ * Decoder method to read a line of module-details.txt storage and splits the string
+ * into a string array
+ *
+ * Format:
+ * moduleCode ### moduleName ### numberOfMcs ### preRequisites ### coRequisites ###
+ * preclusions ### semOfferedIn ### canSU
+ * @param module the string corresponding to the lines of module-details.txt
+ * @return decoded String array
+ */
+ public String[] decodeModule(String module) {
+ return module.split(" ### ");
+ }
+}
diff --git a/src/main/java/seedu/penus/storage/StorageDecoder.java b/src/main/java/seedu/penus/storage/StorageDecoder.java
new file mode 100644
index 0000000000..f73af81301
--- /dev/null
+++ b/src/main/java/seedu/penus/storage/StorageDecoder.java
@@ -0,0 +1,97 @@
+package seedu.penus.storage;
+
+import java.util.Arrays;
+import java.util.List;
+import seedu.penus.common.exceptions.InvalidFormatException;
+import seedu.penus.common.exceptions.InvalidGradeException;
+import seedu.penus.common.exceptions.InvalidModuleException;
+import seedu.penus.common.exceptions.InvalidSemesterException;
+import seedu.penus.common.exceptions.InvalidYearException;
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.logic.utils.Grade;
+import seedu.penus.logic.utils.ModuleRetriever;
+import seedu.penus.model.User;
+import seedu.penus.model.Module;
+
+public class StorageDecoder {
+ public static User decodeUser(String userLine) throws PenusException {
+ User user = new User();
+ String[] components = userLine.split(" ### ");
+ if (components[1].length() == 0 || components[2].length() == 0) {
+ throw new InvalidFormatException("Try again, name and course cannot be empty");
+ }
+ String name = components[1].trim();
+ if (!name.matches("[a-zA-Z ]+")){
+ throw new InvalidFormatException("Name must only include letters and spaces.");
+ }
+
+ String course = components[2].trim();
+ List validCourse = Arrays.asList(
+ "Biomedical Engineering", "Chemical Engineering", "Civil Engineering",
+ "Computer Engineering", "Electrical Engineering", "Environmental Engineering",
+ "Industrial and Systems Engineering", "Mechanical Engineering"
+ );
+ if (!validCourse.contains(course)) {
+ throw new InvalidFormatException("Course is not valid!");
+ }
+ user.setName(name);
+ user.setCourse(course);
+
+
+ return user;
+ }
+
+ /**
+ * Decoder method to read a lines in storage and splits the string
+ * into a string array
+ * Format: Taken/Plan ### moduleCode ### year ### semester (### grade for taken)
+ * @param module String
+ * @return decoded Module object
+ */
+ public static Module decodemodule(String module) throws PenusException {
+ String[] components = module.split(" ### ");
+ String status = components[0].trim();
+ String moduleCode = components[1].trim().toUpperCase();
+ if (!ModuleRetriever.isValidMod(moduleCode)) {
+ throw new InvalidModuleException();
+ }
+ int year;
+ int semester;
+ try {
+ year = Integer.parseInt(components[2].trim());
+ semester = Integer.parseInt(components[3].trim());
+ } catch (NumberFormatException e) {
+ throw new InvalidFormatException("Year and semester must be integers.");
+ }
+ if (year < 1 || year > 4) {
+ throw new InvalidYearException("Year must be 1 to 4. Please try again.");
+ }
+ if (semester != 1 && semester != 2) {
+ throw new InvalidSemesterException("Semester must be 1 or 2!");
+ }
+ Module decoded = null;
+
+ switch (status) {
+ case "Taken":
+ if (components.length <= 4) {
+ throw new InvalidFormatException("Grade must be included for taken command");
+ }
+ String grade = components[4].trim().toUpperCase();
+ if (!Grade.isValid(grade)) {
+ throw new InvalidGradeException();
+ }
+ decoded = new Module(moduleCode, year, semester, grade);
+ break;
+
+ case "Plan":
+ decoded = new Module(moduleCode, year, semester);
+ break;
+
+ default:
+ break;
+ }
+ return decoded;
+ }
+
+
+}
diff --git a/src/main/java/seedu/penus/storage/StorageManager.java b/src/main/java/seedu/penus/storage/StorageManager.java
new file mode 100644
index 0000000000..a4cdf07ca4
--- /dev/null
+++ b/src/main/java/seedu/penus/storage/StorageManager.java
@@ -0,0 +1,40 @@
+package seedu.penus.storage;
+
+import java.util.HashMap;
+import java.util.List;
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.model.Module;
+import seedu.penus.model.User;
+import seedu.penus.model.ModuleList;
+
+public class StorageManager {
+ protected FileStorage storage;
+ protected ResourceStorage resource;
+
+ public StorageManager() {
+ this.storage = new FileStorage();
+ this.resource = new ResourceStorage();
+ }
+
+ //=======================file storage ==========================
+ public List loadStorage() throws PenusException {
+ return storage.retrieveMods();
+ }
+
+ public User loadUser() throws PenusException {
+ return storage.retrieveUser();
+ }
+
+ public void saveStorage(ModuleList list, User user) {
+ storage.save(list, user);
+ }
+
+ //========================resource getter =============================
+ public List loadCoreDetails() {
+ return resource.getAllModuleDetails();
+ }
+
+ public HashMap> loadCoreModList() {
+ return resource.getCoreMods();
+ }
+}
diff --git a/src/main/java/seedu/penus/testutils/SampleData.java b/src/main/java/seedu/penus/testutils/SampleData.java
new file mode 100644
index 0000000000..6c6cd6e17f
--- /dev/null
+++ b/src/main/java/seedu/penus/testutils/SampleData.java
@@ -0,0 +1,44 @@
+package seedu.penus.testutils;
+
+import seedu.penus.model.ModelManager;
+import seedu.penus.model.Module;
+import seedu.penus.model.User;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class SampleData {
+
+ public static Module sampleModule1 = new Module("CS2040C", 1, 1, "A");
+
+ public static Module sampleModule2 = new Module("CS1231", 1, 1, "A");
+
+ public static Module sampleModule3 = new Module("GESS1004", 1, 2, "A");
+
+ public static Module sampleModule4 = new Module("GEC1015", 1, 2);
+
+ public static Module sampleModule5 = new Module("BN1111", 2, 1, "A");
+
+ public static Module sampleModule6 = new Module("BN2111", 2, 2, "A");
+
+ public static User user = new User("bentohset", "Computer Engineering");
+
+ public static HashMap> sampleCoreModList = new HashMap>();
+ public static ModelManager getSampleModel() {
+ List dummyCoreMods = new ArrayList<>();
+ dummyCoreMods.add("CS2040C");
+ dummyCoreMods.add("CS1231");
+ dummyCoreMods.add("EE2211");
+ dummyCoreMods.add("EG1311");
+ sampleCoreModList.put("Computer Engineering", dummyCoreMods);
+ ModelManager model = new ModelManager(user, new ArrayList<>(), new ArrayList<>(), sampleCoreModList);
+ model.addModule(sampleModule1);
+ model.addModule(sampleModule2);
+ model.addModule(sampleModule3);
+ model.addModule(sampleModule4);
+ model.addModule(sampleModule5);
+ model.addModule(sampleModule6);
+ return model;
+ }
+}
diff --git a/src/main/java/seedu/penus/ui/Ui.java b/src/main/java/seedu/penus/ui/Ui.java
new file mode 100644
index 0000000000..8cf4da30c0
--- /dev/null
+++ b/src/main/java/seedu/penus/ui/Ui.java
@@ -0,0 +1,115 @@
+package seedu.penus.ui;
+
+import static seedu.penus.common.Messages.MESSAGE_GOODBYE;
+import static seedu.penus.common.Messages.MESSAGE_WELCOME;
+import static seedu.penus.common.Messages.LOGO;
+import static java.util.Objects.requireNonNull;
+
+import seedu.penus.logic.commands.CommandResult;
+
+import java.util.List;
+import java.util.Scanner;
+import java.io.PrintStream;
+import java.io.InputStream;
+
+public class Ui {
+ private static Thread loadingThread;
+ private static final String DIVIDER = "\t___________________________________________________________";
+
+ private final Scanner in;
+ private final PrintStream out;
+
+ public Ui() {
+ this(System.in, System.out);
+ }
+
+ public Ui(InputStream in, PrintStream out) {
+ this.in = new Scanner(in);
+ this.out = out;
+ }
+
+ public String getUserCommand() {
+ out.print("Enter command: ");
+
+ return in.nextLine();
+ }
+
+ public void printMessage(String... messages) {
+ out.println();
+ out.println(DIVIDER);
+ for (String m : messages) {
+ out.println("\t" + m);
+ }
+ out.println(DIVIDER);
+ }
+
+ public void printStorageError(String... messages) {
+ out.println();
+ out.println(DIVIDER);
+ for (String m : messages) {
+ out.println("\tStorage " + m);
+ }
+ out.println("\tPlease check ./data/penus.txt again");
+ out.println(DIVIDER);
+ }
+
+ public void printResultString(CommandResult result) {
+ requireNonNull(result);
+ printMessage(result.feedbackToUser);
+ }
+
+ public void printResultArray(CommandResult result) {
+ List feedbackArray = result.feedbackArray;
+ out.println();
+ out.println(DIVIDER);
+ for (String s : feedbackArray) {
+ out.println("\t" + s);
+ }
+ out.println(DIVIDER);
+
+ }
+
+ public void printWelcome() {
+ printMessage(
+ LOGO,
+ MESSAGE_WELCOME
+ );
+ }
+
+ public void printExit() {
+ printMessage(
+ MESSAGE_GOODBYE
+ );
+ }
+
+ public static void printStatus(List statusList){
+ for (String s : statusList){
+ System.out.println(s);
+ System.out.println();
+ }
+ }
+
+ public static void showLoadingAnimation() {
+ char[] animationChars = {'|', '/', '-', '\\' };
+ loadingThread = new Thread(() -> {
+ int i = 0;
+ while (!Thread.currentThread().isInterrupted()) {
+ System.out.print("\tLoading " + animationChars[i % 4] + "\r");
+ i++;
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ System.out.print("\n");
+ });
+ loadingThread.start();
+ }
+
+ public static void stopLoadingAnimation() {
+ if (loadingThread != null && loadingThread.isAlive()) {
+ loadingThread.interrupt();
+ }
+ }
+}
diff --git a/src/main/resources/core-module-details.txt b/src/main/resources/core-module-details.txt
new file mode 100644
index 0000000000..8fa9d6701e
--- /dev/null
+++ b/src/main/resources/core-module-details.txt
@@ -0,0 +1,28 @@
+CG1111A ### Engineering Principles and Practice I ### 4 ### 0 ### 0 ### BN2111, EE1111A, EE1111B, EE2111A ### Sem 1 ### 1
+CG2111A ### Engineering Principles and Practice II ### 4 ### CS1010 ### 0 ### CG1112 ### Sem 2 ### 1
+CS1231 ### Discrete Structures ### 4 ### must have completed 1 of 06 MATHEMATICS/07 FURTHER MATHEMATICS/21 PURE MATHEMATICS/22 APPLIED MATHEMATICS at a grade of at least E AND must be H2 OR must have completed 1 of MA1301/MA1301X at a grade of at least D ### 0 ### must not have completed 1 of MA1100/MA1100T at a grade of at least D ### Sem 1 & 2 ### 1
+CG2023 ### Signals and Systems ### 4 ### must have completed 1 of CE2407B/MA1506/MA1512 at a grade of at least D ### 0 ### If undertaking an Undergraduate Degree THEN must not have completed 1 of EE2023/EE2023E/TEE2023 at a grade of at least D ### Sem 2 ### 0
+CG2027 ### Transistor-level Digital Circuits ### 2 ### must have completed 1 of BN1102/CG1111/CG1111A/EE1112/EG1111/EG1112 at a grade of at least D ### 0 ### 0 ### Sem 1 ### 0
+CG2028 ### Computer Organization ### 2 ### must have completed 1 of CS1010/CS1010E at a grade of at least D AND must have completed EE2026 at a grade of at least D ### 0 ### 0 ### Sem 2 ### 0
+CG2271 ### Real-Time Operating Systems ### 4 ### must have completed 1 of "CS1020"/"CS1020E"/"CS2020"/CS2040/CS2040C at a grade of at least D ### 0 ### if undertaking an Undergraduate Degree then must not have completed 1 of "CS2271"/CS2106 at a grade of at least D ### Sem 2 ### 0
+CS2040C ### Data Structures and Algorithms ### 4 ### must have completed 1 of CS1010/CS1010E/CS1010J/CS1010S/CS1010X/CS1101S at a grade of at least D ### 0 ### must not have completed 1 of "CS1020"/"CS1020E"/"CS2010"/"CS2020"/CS2040/CS2040S at a grade of at least D ### Sem 1 & 2 ### 0
+CS2113 ### Software Engineering & Object-Oriented Programming ### 4 ### must have completed CS2040C at a grade of at least D or ( must have completed CS2030 at a grade of at least D and must have completed 1 of CS2040/CS2040S at a grade of at least D ) ### 0 ### must not have completed 1 of CS2103/CS2103T/CS2113T at a grade of at least D ### Sem 1 & 2 ### 0
+EE2026 ### Digital Design ### 4 ### must have completed 1 of "EG1111"/CG1111/CG1111A/CG1111A/EE1111/EE1111A/EE1111B/EE1112 at a grade of at least D ### 0 ### must not have completed EE2020 at a grade of at least D ### Sem 1 & 2 ### 0
+EE4204 ### Computer Networks ### 4 ### must have completed 1 of EE2012A/ESP2107/ST2334 at a grade of at least D OR (must be undertaking 2001CEGHON Bachelor of Engineering (Computer Engineering) (Hons) AND must be Year 2,3 or 4) ### 0 ### must not have completed 1 of CEG5101/CS2105/EE3204/EE5310/EE6310/TEE3204/TEE4204 at a grade of at least D ### Sem 1 & 2 ### 0
+MA1511 ### Engineering Calculus ### 2 ### must have completed 1 of 0006 / 0007 / 06 MATHEMATICS / 07 FURTHER MATHEMATICS / 21 PURE MATHEMATICS / 22 APPLIED MATHEMATICS / 99 O-LEVEL ADDITIONAL MATHEMATICS at a grade of at least E ### 0 ### must not have completed 1 of "EE1461"/"MA1102R"/"MA1506"/"MA1507"/"MA2501"/"PC2174"/MA1312/MA1505/MA1521/MA2002/MA2311/PC2134/YSC1216 at a grade of at least D ### Sem 1 & 2 ### 1
+MA1512 ### Differential Equations for Engineering ### 2 ### must have completed 1 of 0006 / 0007 / 06 MATHEMATICS / 07 FURTHER MATHEMATICS / 21 PURE MATHEMATICS / 22 APPLIED MATHEMATICS / 99 O-LEVEL ADDITIONAL MATHEMATICS at a grade of at least E ) ### 0 ### must not have completed 1 of "EE1461"/"MA1506"/"MA1507"/"PC2174"/PC2134 at a grade of at least D ### Sem 1 & 2 ### 1
+MA1508E ### Linear Algebra for Engineering ### 4 ### must have completed 1 of 0006 / 0007 / 06 MATHEMATICS / 07 FURTHER MATHEMATICS / 21 PURE MATHEMATICS / 22 APPLIED MATHEMATICS / 99 O-LEVEL ADDITIONAL MATHEMATICS at a grade of at least E ### 0 ### must not have completed 1 of "MA1101R"/"MA1506"/"MA1508"/MA1311/MA1513/MA2001/YSC2232 at a grade of at least D ### Sem 2 ### 1
+EG2401A ### Engineering Professionalism ### 2 ### must be Year 2,3 or 4 and must be in one of the cohorts to 2021 inclusive ### 0 ### 0 ### Sem 1 & 2 ### 0
+CP3880 ### Advanced Technology Attachment Programme ### 12 ### must have completed 1 of CS2101/ES1601/ES2007D/IS2101/NTW2001/NTW2017/NTW2028/NTW2029/NTW2030/NTW2031/NTW2032/NTW2033/NTW2034/UTW1001%/UWC2101% at a grade of at least D AND must have completed 1 of BT3103/CS2103/CS2103T/CS2113/CS2113T/IS3106 at a grade of at least D) ### 0 ### must not have completed EG3601 at a grade of at least D ### Sem 1 & 2 ### 0
+EG3611A ### Industrial Attachment ### 10 ### 0 ### 0 ### 0 ### Sem 1 & 2 ### 0
+ES2631 ### Critique and Communication of Thinking and Design ### 4 ### must be undertaking 1 of 2001CEGHON Bachelor of Engineering (Computer Engineering) (Hons), 0607ISEHON Bachelor of Engineering (Industrial and Systems Engineering) (Hons), 0609MEHON Bachelor of Engineering (Mechanical Engineering) (Hons), 0613EVEHON Bachelor of Engineering (Environmental Engineering) (Hons), 0605ESPHON Bachelor of Engineering (Engineering Science) (Hons), 0601BMEHON Bachelor of Engineering (Biomedical Engineering) (Hons), 0604ELEHON Bachelor of Engineering (Electrical Engineering) (Hons), 0608MSEHON Bachelor of Engineering (Materials Science and Engineering) (Hons), 0613CEHON Bachelor of Engineering (Civil Engineering) (Hons), 0602CHEHON Bachelor of Engineering (Chemical Engineering) (Hons), 0616IPMHON BEng (Infrastructure and Project Management) (Hons), 0616PFMHON BSc (Project and Facilities Management) (Hons)) AND (must be in one of the cohorts prior to 2013 inclusive OR must be in one of the cohorts from 2016 inclusive ) AND must have completed EP ENGLISH LANGUAGE PROFICIENCY TEST at a grade of at least N) ### 0 ### If undertaking an Undergraduate Degree THEN (( must not have completed 1 of any Courses (Modules) beginning with UTW1001) AND (must not be undertaking 1 of 1501TMBSPL UTown College Programme - Tembusu, 1501TMRSPL UTown Resident - Tembusu, 1502ANRSPL UTown Resident - CAPT, 1502ANGSPL UTown College Programme - CAPT, 1503RC4SPL UTown College Programme - RC4, 1503R4RSPL UTown Resident - RC4, 1520RVCSPL Ridge View Residential College Programme)) ### Sem 1 & 2 ### 1
+CS1010 ### Programming Methodology ### 4 ### 0 ### 0 ### must not have completed 1 of CS1010E/CS1010J/CS1010S/CS1010X/CS1101S at a grade of at least D ### Sem 1 & 2 ### 1
+GEA1000 ### Quantitative Reasoning with Data ### 4 ### 0 ### 0 ### 0 ### Sem 1 & 2 ### 1
+DTK1234 ### Design Thinking ### 4 ### 0 ### 0 ### 0 ### Sem 1 & 2 ### 1
+EG1311 ### Design and Make ### 4 ### 0 ### 0 ### 0 ### Sem 1 & 2 ### 1
+IE2141 ### Systems Thinking and Dynamics ### 4 ### 0 ### 0 ### must not have completed 1 of GEM1915, GET1011, IE2101, any Courses (Modules) beginning with UTC1702 ### Sem 1 & 2 ### 1
+EE2211 ### Introduction to Machine Learning ### 4 ### must have completed 1 of any Courses (Modules) beginning with CS1010 at a grade of at least D AND must have completed MA1511 at a grade of at least D AND must have completed 1 of CE2407B/MA1508E/MA1513 at a grade of at least D ### 0 ### 0 ### Sem 1 & 2 ### 0
+EG2501 ### Liveable Cities ### 4 ### 0 ### 0 ### 0 ### Sem 1 & 2 ### 1
+CDE2000 ### Creating Narratives ### 4 ### 0 ### 0 ### 0 ### Sem 1 & 2 ### 1
+PF1101 ### Fundamentals of Project Management ### 4 ### 0 ### 0 ### 0 ### Sem 1 & 2 ### 1
+CG4002 ### Computer Engineering Capstone Project ### 8 ### either of CG2028/EE2024 at a grade of at least D and 1 of CS2103/CS2103T/CS2113/CS2113T at a grade of at least D and CG2271 at a grade of at least D ### 0 ### CG3002 at a grade of at least D ### Sem 1 & 2 ### 0
diff --git a/src/main/resources/core-modules.txt b/src/main/resources/core-modules.txt
new file mode 100644
index 0000000000..6585f0af00
--- /dev/null
+++ b/src/main/resources/core-modules.txt
@@ -0,0 +1,227 @@
+##Computer Engineering
+ES2631
+CS1010
+GEA1000
+DTK1234
+EG1311
+IE2141
+EE2211
+EG2501
+CDE2000
+PF1101
+CG4002
+MA1511
+MA1512
+MA1508E
+EG2401A
+EG3611A
+CG1111A
+CG2111A
+CS1231
+CG2023
+CG2027
+CG2028
+CG2271
+CS2040C
+CS2113
+EE2026
+EE4204
+END
+##Electrical Engineering
+ES2631
+CS1010E
+GEA1000
+DTK1234
+EG1311
+IE2141
+EE2211
+EG2501
+CDE2000
+PF1101
+EE4002D
+MA1511
+MA1512
+MA1508E
+EG2401A
+EG3611A
+EE1111A
+EE2111A
+EE2012
+EE2023
+EE2026
+EE2027
+EE2022
+PC2020
+END
+##Biomedical Engineering
+ES2631
+CS1010E
+GEA1000
+DTK1234
+EG1311
+IE2141
+EE2211
+EG2501
+CDE2000
+PF1101
+BN4101
+MA1511
+CE2407A
+CE2407B
+MA1513
+EG2401A
+EG3611A
+BN1111
+BN2111
+BN2301
+BN2102
+BN2201
+BN2204
+BN2403
+BN3101A
+END
+##Chemical Engineering
+ES2631
+CS1010E
+GEA1000
+DTK1234
+EG1311
+IE2141
+EE2211
+EG2501
+CDE2000
+PF1101
+MA1511
+CE2407A
+CE2407B
+MA1513
+EG2401A
+EG3611A
+CN1101A
+CB2102
+CN2101
+CN2116
+CN2121
+CN2122A
+CN2125
+CN3101A
+CN3121
+CN3124A
+CN3132
+CN3135
+CN3421A
+CN4122
+CN4123R
+END
+##Civil Engineering
+ES2631
+CS1010E
+GEA1000
+DTK1234
+EG1311
+IE2141
+EE2211
+EG2501
+CDE2000
+PF1101
+CE4103R
+MA1511
+CE2407A
+CE2407B
+MA1513
+EG2401A
+EG3611A
+CE1103
+CE2155
+CE2134
+CE3115
+CE3116
+CE3121
+CE3132
+CE3155A
+CE3155B
+CE3165
+CE3166
+END
+##Environmental Engineering
+ES2631
+CS1010E
+GEA1000
+DTK1234
+EG1311
+IE2141
+EE2211
+EG2501
+CDE2000
+PF1101
+ESE4501R
+MA1511
+CE2407A
+CE2407B
+MA1513
+EG2401A
+EG3611A
+ESE2101
+ESE2102
+ESE2000
+ESE3101
+ESE2001
+ESE3301
+ESE3201
+ESE3401
+END
+##Industrial and Systems Engineering
+ES2631
+CS1010
+GEA1000
+DTK1234
+EG1311
+IE2141
+EE2211
+EG2501
+CDE2000
+PF1101
+IE3100R
+MA1511
+MA1512
+MA1508E
+EG2401A
+EG3611A
+IE1111R
+IE2111
+IE2100
+IE2110
+IE3101
+IE3110R
+CS2040
+ST2334
+END
+##Mechanical Engineering
+ES2631
+CS1010
+GEA1000
+DTK1234
+EG1311
+IE2141
+EE2211
+EG2501
+CDE2000
+PF1101
+ME4101A
+MA1505
+MA1512
+MA1513
+EG2401A
+EG3611A
+ME1102
+ME2104
+ME2102
+ME2112
+ME2115
+ME2121
+ME2134
+ME2142
+ME2162
+ME4102
+ME4103
+END
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/penus/PenusTest.java
similarity index 81%
rename from src/test/java/seedu/duke/DukeTest.java
rename to src/test/java/seedu/penus/PenusTest.java
index 2dda5fd651..be0ba80966 100644
--- a/src/test/java/seedu/duke/DukeTest.java
+++ b/src/test/java/seedu/penus/PenusTest.java
@@ -1,10 +1,10 @@
-package seedu.duke;
+package seedu.penus;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
-class DukeTest {
+class PenusTest {
@Test
public void sampleTest() {
assertTrue(true);
diff --git a/src/test/java/seedu/penus/logic/LogicManagerTest.java b/src/test/java/seedu/penus/logic/LogicManagerTest.java
new file mode 100644
index 0000000000..4508470eaf
--- /dev/null
+++ b/src/test/java/seedu/penus/logic/LogicManagerTest.java
@@ -0,0 +1,28 @@
+package seedu.penus.logic;
+
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.model.ModelManager;
+import seedu.penus.model.User;
+import seedu.penus.storage.StorageManager;
+import java.util.ArrayList;
+import java.util.HashMap;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class LogicManagerTest {
+ private LogicManager logicManager;
+ private final ModelManager model = new ModelManager(new User(), new ArrayList<>(),
+ new ArrayList<>(), new HashMap<>());
+ private final StorageManager storage = new StorageManager();
+
+ @BeforeEach
+ public void setUp() {
+ logicManager = new LogicManager(model, storage);
+ }
+
+ @Test
+ public void getCommand_invalidCommand_exceptionThrown() {
+ assertThrows(PenusException.class, () -> logicManager.getCommand("invalid command"));
+ }
+}
diff --git a/src/test/java/seedu/penus/logic/commands/ClearCommandTest.java b/src/test/java/seedu/penus/logic/commands/ClearCommandTest.java
new file mode 100644
index 0000000000..2685d618d2
--- /dev/null
+++ b/src/test/java/seedu/penus/logic/commands/ClearCommandTest.java
@@ -0,0 +1,54 @@
+package seedu.penus.logic.commands;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.model.ModelManager;
+import seedu.penus.testutils.SampleData;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+public class ClearCommandTest {
+ private ModelManager model;
+ private ClearCommand clearCommand;
+
+ @BeforeEach
+ public void setUp() {
+ model = SampleData.getSampleModel();
+ }
+
+ @Test
+ public void testClearYearAndSemModsSuccess() throws PenusException {
+ clearCommand = new ClearCommand(1, 1);
+ clearCommand.execute(model);
+ assertFalse(model.hasModuleCode("CS2040C"));
+ assertFalse(model.hasModuleCode("CS1231"));
+ assertTrue(model.hasModuleCode("GESS1004"));
+ assertTrue(model.hasModuleCode("GEC1015"));
+ assertTrue(model.hasModuleCode("BN1111"));
+ assertTrue(model.hasModuleCode("BN2111"));
+ assertEquals(4, model.getSize());
+ }
+
+ @Test
+ public void testClearYearModsSuccess() throws PenusException {
+ clearCommand = new ClearCommand(1, 0);
+ clearCommand.execute(model);
+ assertFalse(model.hasModuleCode("CS2040C"));
+ assertFalse(model.hasModuleCode("CS1231"));
+ assertFalse(model.hasModuleCode("GESS1004"));
+ assertFalse(model.hasModuleCode("GEC1015"));
+ assertTrue(model.hasModuleCode("BN1111"));
+ assertTrue(model.hasModuleCode("BN2111"));
+ assertEquals(2, model.getSize());
+ }
+
+ @Test
+ public void testClearAllModsSuccess() throws PenusException {
+ clearCommand = new ClearCommand(0, 0);
+ clearCommand.execute(model);
+ assertEquals(0, model.getSize());
+ }
+}
diff --git a/src/test/java/seedu/penus/logic/commands/DetailsCommandTest.java b/src/test/java/seedu/penus/logic/commands/DetailsCommandTest.java
new file mode 100644
index 0000000000..1cbd1b42de
--- /dev/null
+++ b/src/test/java/seedu/penus/logic/commands/DetailsCommandTest.java
@@ -0,0 +1,23 @@
+package seedu.penus.logic.commands;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.util.ArrayList;
+import java.util.HashMap;
+import seedu.penus.common.exceptions.InvalidCommandException;
+import seedu.penus.model.ModelManager;
+import seedu.penus.model.User;
+
+public class DetailsCommandTest {
+ private final ModelManager model = new ModelManager(new User(), new ArrayList<>(),
+ new ArrayList<>(), new HashMap<>());
+ private DetailsCommand command;
+
+ @Test
+ public void testValidModuleCodeSuccess() throws InvalidCommandException {
+ command = new DetailsCommand("CS2113");
+ CommandResult result = command.execute(model);
+
+ assertTrue(!result.feedbackToUser.contains("This information is not available"));
+ }
+}
diff --git a/src/test/java/seedu/penus/logic/commands/ExitCommandTest.java b/src/test/java/seedu/penus/logic/commands/ExitCommandTest.java
new file mode 100644
index 0000000000..fab52bde24
--- /dev/null
+++ b/src/test/java/seedu/penus/logic/commands/ExitCommandTest.java
@@ -0,0 +1,21 @@
+package seedu.penus.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.junit.jupiter.api.Test;
+
+
+
+public class ExitCommandTest {
+ @Test
+ public void isExit_returnsTrue() {
+ assertTrue(ExitCommand.isExit(new ExitCommand()));
+ }
+
+ @Test
+ public void testExecuteReturnsCorrectResult() {
+ CommandResult expectedResult = new CommandResult(ExitCommand.MESSAGE_EXIT_ACKNOWLEDGEMENT, false);
+ CommandResult actualResult = new ExitCommand().execute(null);
+ assertEquals(expectedResult.feedbackToUser, actualResult.feedbackToUser);
+ }
+}
diff --git a/src/test/java/seedu/penus/logic/commands/HelpCommandTest.java b/src/test/java/seedu/penus/logic/commands/HelpCommandTest.java
new file mode 100644
index 0000000000..1621e6c94e
--- /dev/null
+++ b/src/test/java/seedu/penus/logic/commands/HelpCommandTest.java
@@ -0,0 +1,65 @@
+package seedu.penus.logic.commands;
+
+import org.junit.jupiter.api.Test;
+import seedu.penus.model.ModelManager;
+import seedu.penus.model.User;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class HelpCommandTest {
+
+ @Test
+ public void testExecute() {
+ final ModelManager model = new ModelManager(new User(), new ArrayList<>(),
+ new ArrayList<>(), new HashMap<>());
+
+ CommandResult expected = new CommandResult(
+ ("\n\t" + String.format("%-52s %s", "Command", "Description") +
+ "\n\t" + String.format("%-52s %s", "-------", "-----------") +
+ "\n\t" + String.format("%-52s %s",
+ "clear [FILTER]", "Clears modules in the specified Year or Semester.") +
+ "\n\t" + String.format("%-52s %s", "",
+ "If [FILTER] is not specified, then all modules will cleared.") +
+ "\n\n\t" + String.format("%-52s %s", "exit", "Exits the program.") +
+ "\n\n\t" + String.format("%-52s %s", "list [FILTER]",
+ "Displays a list of all modules taken or planned") +
+ "\n\t" + String.format("%-52s %s", "", "in the specified Year or Semester.") +
+ "\n\t" + String.format("%-52s %s", "",
+ "If [FILTER] is not specified, then all modules will shown.") +
+ "\n\n\t" + String.format("%-52s %s", "mark [MODULE CODE] g/[GRADE]",
+ "Marks the module that has been cleared, while updating its grades.") +
+ String.format("%n %n \t") +
+ String.format("%-52s %s", "plan [MODULE CODE] y/[YEAR] s/[SEMESTER]",
+ "Adds a module to the planner as an untaken module.") +
+ "\n\n\t" + String.format("%-52s %s",
+ "remove [MODULECODE]", "Removes a module from the planner.") +
+ "\n\n\t" + String.format("%-52s %s",
+ "status", "Displays the status of Core Modules and MCs taken.") +
+ "\n\n\t" + String.format("%-52s %s",
+ "taken [MODULE CODE] y/[YEAR] s/[SEMESTER] g/[GRADE]",
+ "Adds a module to the planner as a module you have already taken.") +
+ "\n\n\t" + String.format("%-52s %s", "details [MODULE CODE]",
+ "Displays the details of given module, including") +
+ "\n\t" + String.format("%-52s %s", "",
+ "Title, Description, Prerequisites, Module Credits") +
+ "\n\t" + String.format("%-52s %s", "", "and if it can be SU-ed.") +
+ "\n\n\t" + String.format("%-52s %s",
+ "init n/[NAME] c/[COURSE NUMBER]", "Initialize User.") +
+ "\n\n\t" + String.format("%-52s %s", "", "[COURSE NUMBER] -> [COURSE NAME]") +
+ "\n\t" + String.format("%-52s %s", "", "1 -> Biomedical Engineering") +
+ "\n\t" + String.format("%-52s %s", "", "2 -> Chemical Engineering") +
+ "\n\t" + String.format("%-52s %s", "", "3 -> Civil Engineering") +
+ "\n\t" + String.format("%-52s %s", "", "4 -> Computer Engineering") +
+ "\n\t" + String.format("%-52s %s", "", "5 -> Electrical Engineering") +
+ "\n\t" + String.format("%-52s %s", "", "6 -> Environmental Engineering") +
+ "\n\t" + String.format("%-52s %s", "", "7 -> Industrial and Systems Engineering") +
+ "\n\t" + String.format("%-52s %s", "", "8 -> Mechanical Engineering") +
+ "\n\t"
+ ), false);
+ CommandResult actual = new HelpCommand().execute(model);
+ assertEquals(expected.feedbackToUser, actual.feedbackToUser);
+ }
+}
diff --git a/src/test/java/seedu/penus/logic/commands/InitCommandTest.java b/src/test/java/seedu/penus/logic/commands/InitCommandTest.java
new file mode 100644
index 0000000000..020940b89b
--- /dev/null
+++ b/src/test/java/seedu/penus/logic/commands/InitCommandTest.java
@@ -0,0 +1,84 @@
+package seedu.penus.logic.commands;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import seedu.penus.common.exceptions.InvalidCommandException;
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.model.ModelManager;
+import seedu.penus.model.User;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+
+public class InitCommandTest {
+ private final ModelManager modelManager = new ModelManager(new User(), new ArrayList<>(),
+ new ArrayList<>(), new HashMap<>());
+ private InitCommand initCommand;
+
+ @Test
+ public void testInitCommandSuccess() throws Exception {
+ initCommand = new InitCommand("John Doe", 1);
+ CommandResult commandResult = initCommand.execute(modelManager);
+ assertEquals("John Doe", modelManager.getUserName());
+ assertEquals("Biomedical Engineering", modelManager.getUserCourse());
+ }
+
+ @Test
+ public void testInitChemCommandSuccess() throws Exception {
+ initCommand = new InitCommand("John Doe", 2);
+ CommandResult commandResult = initCommand.execute(modelManager);
+ assertEquals("John Doe", modelManager.getUserName());
+ assertEquals("Chemical Engineering", modelManager.getUserCourse());
+ }
+ @Test
+ public void testInitCivilCommandSuccess() throws Exception {
+ initCommand = new InitCommand("John Doe", 3);
+ CommandResult commandResult = initCommand.execute(modelManager);
+ assertEquals("John Doe", modelManager.getUserName());
+ assertEquals("Civil Engineering", modelManager.getUserCourse());
+ }
+ @Test
+ public void testInitComputerCommandSuccess() throws Exception {
+ initCommand = new InitCommand("John Doe", 4);
+ CommandResult commandResult = initCommand.execute(modelManager);
+ assertEquals("John Doe", modelManager.getUserName());
+ assertEquals("Computer Engineering", modelManager.getUserCourse());
+ }
+ @Test
+ public void testInitElectricalCommandSuccess() throws Exception {
+ initCommand = new InitCommand("John Doe", 5);
+ CommandResult commandResult = initCommand.execute(modelManager);
+ assertEquals("John Doe", modelManager.getUserName());
+ assertEquals("Electrical Engineering", modelManager.getUserCourse());
+ }
+ @Test
+ public void testInitEnvCommandSuccess() throws Exception {
+ initCommand = new InitCommand("John Doe", 6);
+ CommandResult commandResult = initCommand.execute(modelManager);
+ assertEquals("John Doe", modelManager.getUserName());
+ assertEquals("Environmental Engineering", modelManager.getUserCourse());
+ }
+ @Test
+ public void testInitISECommandSuccess() throws Exception {
+ initCommand = new InitCommand("John Doe", 7);
+ CommandResult commandResult = initCommand.execute(modelManager);
+ assertEquals("John Doe", modelManager.getUserName());
+ assertEquals("Industrial and Systems Engineering", modelManager.getUserCourse());
+ }
+ @Test
+ public void testInitMechCommandSuccess() throws Exception {
+ initCommand = new InitCommand("John Doe", 8);
+ CommandResult commandResult = initCommand.execute(modelManager);
+ assertEquals("John Doe", modelManager.getUserName());
+ assertEquals("Mechanical Engineering", modelManager.getUserCourse());
+ }
+
+ @Test
+ public void testExecuteInvalidCourseCodeThrowsInvalidCommandException() {
+ initCommand = new InitCommand("John Doe", 10);
+ PenusException invalidCommandException = assertThrows(InvalidCommandException.class,
+ () -> initCommand.execute(modelManager));
+ assertEquals("Error: Enter within the index. Please initialize again", invalidCommandException.getMessage());
+ }
+}
diff --git a/src/test/java/seedu/penus/logic/commands/ListCommandTest.java b/src/test/java/seedu/penus/logic/commands/ListCommandTest.java
new file mode 100644
index 0000000000..ca6c4b6cc4
--- /dev/null
+++ b/src/test/java/seedu/penus/logic/commands/ListCommandTest.java
@@ -0,0 +1,70 @@
+package seedu.penus.logic.commands;
+
+import org.junit.jupiter.api.Test;
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.model.ModelManager;
+import seedu.penus.model.Module;
+import seedu.penus.model.User;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ListCommandTest {
+ private final ModelManager model = new ModelManager(new User(), new ArrayList<>(),
+ new ArrayList<>(), new HashMap<>());
+ private ListCommand command;
+
+ //add plan module, cap should be 0
+ @Test
+ public void testSuccessListNoFilter() throws PenusException {
+ Module module = new Module("CS2113", 2, 1);
+ model.addModule(module);
+ command = new ListCommand();
+
+ CommandResult result = command.execute(model);
+
+ assertEquals(result.feedbackArray.get(result.feedbackArray.size() - 1), "Overall CAP : 0.00\n");
+ }
+
+ //add taken module cap should be correct, filter list
+ @Test
+ public void testSuccessListFilterYearSem() throws PenusException {
+ Module module = new Module("CS2113", 2, 1, "B+");
+ model.addModule(module);
+ command = new ListCommand(2, 1);
+
+ CommandResult result = command.execute(model);
+
+ assertEquals(result.feedbackArray.get(result.feedbackArray.size() - 1), "Overall CAP : 4.00\n");
+ }
+
+ @Test
+ public void testSuccessListFilterYear() throws PenusException {
+ Module module = new Module("CS2113", 2, 1, "B+");
+ model.addModule(module);
+ command = new ListCommand(2,0);
+
+ CommandResult result = command.execute(model);
+
+ assertEquals(result.feedbackArray.get(result.feedbackArray.size() - 1), "Overall CAP : 4.00\n");
+ }
+
+ @Test
+ public void testSuccessEmptyListYearSem() throws PenusException {
+ command = new ListCommand(1,2);
+
+ CommandResult result = command.execute(model);
+
+ assertEquals(result.feedbackArray.get(result.feedbackArray.size() - 1), "Overall CAP : 0.00\n");
+ }
+
+ @Test
+ public void testSuccessEmptyListNoFilter() throws PenusException {
+ command = new ListCommand();
+
+ CommandResult result = command.execute(model);
+
+ assertEquals(result.feedbackArray.get(result.feedbackArray.size() - 1), "Overall CAP : 0.00\n");
+ }
+}
diff --git a/src/test/java/seedu/penus/logic/commands/MarkCommandTest.java b/src/test/java/seedu/penus/logic/commands/MarkCommandTest.java
new file mode 100644
index 0000000000..f716d03460
--- /dev/null
+++ b/src/test/java/seedu/penus/logic/commands/MarkCommandTest.java
@@ -0,0 +1,104 @@
+package seedu.penus.logic.commands;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import seedu.penus.model.ModelManager;
+import seedu.penus.model.Module;
+import seedu.penus.model.User;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import seedu.penus.common.exceptions.InvalidCommandException;
+import seedu.penus.common.exceptions.PenusException;
+
+public class MarkCommandTest {
+ private final ModelManager model = new ModelManager(new User(), new ArrayList<>(),
+ new ArrayList<>(), new HashMap<>());
+
+ private MarkCommand command;
+
+ @Test
+ public void testValidModuleCodeSuccess() throws InvalidCommandException {
+ Module module = new Module("CS2113", 2, 1);
+ model.addModule(module);
+
+ command = new MarkCommand("CS2113", "A");
+ CommandResult result = command.execute(model);
+ Module markedModule = model.getModuleByCode("CS2113");
+
+
+ assertEquals(String.format(MarkCommand.MESSAGE, module), result.feedbackToUser);
+ assertEquals(markedModule.getStatus(), "Taken");
+ }
+
+ @Test
+ public void testInValidModuleCodeException() throws InvalidCommandException {
+ Module module = new Module("CS2113", 2, 1);
+ model.addModule(module);
+ command = new MarkCommand("CS2103", "A");
+
+ assertThrows(InvalidCommandException.class, () -> command.execute(model));
+ }
+
+ // create and execute a MarkCommand on a non-existent module
+ @Test
+ public void testInvalidModuleCodeThrowsInvalidCommandException() {
+ MarkCommand markCommand = new MarkCommand("CS2113T", "A");
+ assertThrows(InvalidCommandException.class, () -> markCommand.execute(model));
+ }
+
+ // create and execute a MarkCommand with grade "S" on the module, cannot be SU-ed
+ @Test
+ public void executeSuUnsuccessfulThrowsInvalidCommandException() {
+ Module module = new Module("CS2040C", 2, 1);
+ model.addModule(module);
+
+ MarkCommand markCommand = new MarkCommand("CS2040C", "S");
+ assertThrows(InvalidCommandException.class, () -> markCommand.execute(model));
+ }
+
+ @Test
+ public void testValidSSuccess() throws PenusException {
+ Module module = new Module("CS1231", 2, 1);
+ model.addModule(module);
+
+ command = new MarkCommand("CS1231", "S");
+ CommandResult result = command.execute(model);
+ Module markedModule = model.getModuleByCode("CS2113");
+
+
+ assertEquals(String.format(MarkCommand.MESSAGE, module), result.feedbackToUser);
+ }
+
+ @Test
+ public void testValidUSuccess() throws PenusException {
+ Module module = new Module("CS1231", 2, 1);
+ model.addModule(module);
+
+ command = new MarkCommand("CS1231", "U");
+ CommandResult result = command.execute(model);
+ Module markedModule = model.getModuleByCode("CS2113");
+
+
+ assertEquals(String.format(MarkCommand.MESSAGE, module), result.feedbackToUser);
+ }
+
+ @Test
+ public void testInvalidUThrowsInvalidCommandException() throws PenusException {
+ Module module = new Module("CS2113", 2, 1);
+ model.addModule(module);
+ MarkCommand command = new MarkCommand("CS2113", "U");
+
+ assertThrows(InvalidCommandException.class, () -> command.execute(model));
+ }
+
+ @Test
+ public void testInvalidSThrowsInvalidCommandException() throws PenusException {
+ Module module = new Module("CS2113", 2, 1);
+ model.addModule(module);
+ MarkCommand command = new MarkCommand("CS2113", "S");
+
+ assertThrows(InvalidCommandException.class, () -> command.execute(model));
+ }
+}
diff --git a/src/test/java/seedu/penus/logic/commands/PlanCommandTest.java b/src/test/java/seedu/penus/logic/commands/PlanCommandTest.java
new file mode 100644
index 0000000000..874e1c27b8
--- /dev/null
+++ b/src/test/java/seedu/penus/logic/commands/PlanCommandTest.java
@@ -0,0 +1,49 @@
+package seedu.penus.logic.commands;
+
+import seedu.penus.common.exceptions.DuplicateModuleException;
+import seedu.penus.common.exceptions.InvalidModuleException;
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.model.ModelManager;
+import seedu.penus.model.User;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.BeforeEach;
+import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class PlanCommandTest {
+ private ModelManager model;
+ private PlanCommand command;
+
+ @BeforeEach
+ public void setup() {
+ model = new ModelManager(new User(), new ArrayList<>(), new ArrayList<>(), new HashMap<>());
+ }
+ @Test
+ public void testExecuteValidModuleSuccess() throws PenusException {
+ command = new PlanCommand("CS2113", 2, 2);
+ CommandResult result = command.execute(model);
+
+ assertEquals(String.format(PlanCommand.MESSAGE, command.plan, model.getSize()), result.feedbackToUser);
+ assertEquals(1, model.getSize());
+ }
+
+ @Test
+ public void testExecuteDuplicateModuleThrowsDuplicateModuleException() throws PenusException {
+ PlanCommand command1 = new PlanCommand("CS2113", 2, 2);
+ PlanCommand command2 = new PlanCommand("CS2113", 2, 2);
+
+ command1.execute(model);
+
+ assertThrows(DuplicateModuleException.class, () -> command2.execute(model));
+ }
+
+ @Test
+ public void testExecuteInvalidModuleThrowsInvalidModuleException() throws PenusException {
+ PlanCommand command = new PlanCommand("CS211300", 2, 2);
+
+ assertThrows(InvalidModuleException.class, () -> command.execute(model));
+ }
+}
diff --git a/src/test/java/seedu/penus/logic/commands/RemoveCommandTest.java b/src/test/java/seedu/penus/logic/commands/RemoveCommandTest.java
new file mode 100644
index 0000000000..d7c98da298
--- /dev/null
+++ b/src/test/java/seedu/penus/logic/commands/RemoveCommandTest.java
@@ -0,0 +1,49 @@
+package seedu.penus.logic.commands;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import org.junit.jupiter.api.Test;
+import seedu.penus.common.exceptions.InvalidCommandException;
+import seedu.penus.model.ModelManager;
+import seedu.penus.model.Module;
+import seedu.penus.model.User;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class RemoveCommandTest {
+ private final ModelManager model = new ModelManager(new User(), new ArrayList<>(),
+ new ArrayList<>(), new HashMap<>());
+
+ private RemoveCommand command;
+
+ @Test
+ public void testValidModuleSuccess() throws InvalidCommandException {
+ Module module = new Module("CS2113", 2, 1);
+ model.addModule(module);
+
+ command = new RemoveCommand("CS2113");
+
+ CommandResult result = command.execute(model);
+
+ assertEquals(String.format(RemoveCommand.MESSAGE, module, model.getSize()), result.feedbackToUser);
+ assertEquals(0, model.getSize());
+ }
+
+ //module does not exist
+ @Test
+ public void testInvalidModuleThrowsInvalidCommandException() {
+ command = new RemoveCommand("CS2113");
+
+ assertThrows(InvalidCommandException.class, () -> command.execute(model));
+ }
+
+ @Test
+ public void testExecute_moduleNotFound_throwsException() {
+
+ model.addModule(new Module("CS2113", 2, 2));
+ RemoveCommand command = new RemoveCommand("CS2105");
+
+ assertThrows(InvalidCommandException.class, () -> command.execute(model));
+ }
+}
diff --git a/src/test/java/seedu/penus/logic/commands/StatusCommandTest.java b/src/test/java/seedu/penus/logic/commands/StatusCommandTest.java
new file mode 100644
index 0000000000..f38d42defb
--- /dev/null
+++ b/src/test/java/seedu/penus/logic/commands/StatusCommandTest.java
@@ -0,0 +1,62 @@
+package seedu.penus.logic.commands;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.model.ModelManager;
+import seedu.penus.testutils.SampleData;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+
+public class StatusCommandTest {
+ private ModelManager model;
+ private StatusCommand statusCommand;
+
+ @BeforeEach
+ public void setUp() {
+ model = SampleData.getSampleModel();
+ statusCommand = new StatusCommand();
+ }
+ @Test
+ public void testGetTakenCoreModsListSuccess() {
+ assertEquals("GESS1004", statusCommand.getTakenCoreModsList(model).get(0));
+ assertEquals("CS2040C", statusCommand.getTakenCoreModsList(model).get(1));
+ assertEquals(3 , statusCommand.getTakenCoreModsList(model).size());
+ }
+
+ @Test
+ public void testGetUntakenCoreModsListSuccess() {
+ assertEquals("EE2211", statusCommand.getUntakenCoreModsList(model).get(0));
+ assertEquals(2 , statusCommand.getUntakenCoreModsList(model).size());
+ }
+
+ @Test
+ public void testModuleCodeToStringSuccess() {
+ assertEquals("DTK1234 Design Thinking MCs: 4", statusCommand.moduleCodeToString("DTK1234"));
+ assertEquals("EE2211 Introduction to Machine Learning MCs: 4", statusCommand.moduleCodeToString("EE2211"));
+ }
+
+ @Test
+ public void testStatusCommandSuccess() throws PenusException {
+ CommandResult result = statusCommand.execute(model);
+ String expectedString =
+ "-------------------------- User --------------------------\n" +
+ "\tUser: bentohset\n" +
+ "\tCourse: Computer Engineering\n" +
+ "\t------------------- Core Modules Taken --------------------\n" +
+ "\tGESS1004 Singapore and India: Emerging Relations MCs: 4\n" +
+ "\tCS2040C Data Structures and Algorithms MCs: 4\n" +
+ "\tCS1231 Discrete Structures MCs: 4\n" +
+ "\t----------------- Core Modules Not Taken ------------------\n" +
+ "\tGECXXXX\n" +
+ "\tGENXXXX\n" +
+ "\tEE2211 Introduction to Machine Learning MCs: 4\n" +
+ "\tEG1311 Design and Make MCs: 4\n" +
+ "\t------------------------ MCs Status -----------------------\n" +
+ "\tCore Modules MCs Taken: 12\n" +
+ "\tElective MCs Taken: 8\n" +
+ "\tTotal MCs Taken: 20/160\n";
+ assertEquals(expectedString, result.feedbackToUser);
+ }
+}
diff --git a/src/test/java/seedu/penus/logic/commands/TakenCommandTest.java b/src/test/java/seedu/penus/logic/commands/TakenCommandTest.java
new file mode 100644
index 0000000000..c57145a4d2
--- /dev/null
+++ b/src/test/java/seedu/penus/logic/commands/TakenCommandTest.java
@@ -0,0 +1,81 @@
+package seedu.penus.logic.commands;
+
+import seedu.penus.common.exceptions.DuplicateModuleException;
+import seedu.penus.common.exceptions.InvalidCommandException;
+import seedu.penus.common.exceptions.InvalidModuleException;
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.model.ModelManager;
+import seedu.penus.model.User;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class TakenCommandTest {
+ private final ModelManager model = new ModelManager(new User(), new ArrayList<>(),
+ new ArrayList<>(), new HashMap<>());
+
+ private TakenCommand command;
+
+ @Test
+ public void testValidModuleSuccess() throws PenusException {
+ command = new TakenCommand("CS2113", 2, 2, "A+");
+ CommandResult result = command.execute(model);
+
+ assertEquals(String.format(TakenCommand.MESSAGE, command.taken, model.getSize()), result.feedbackToUser);
+ assertEquals(1, model.getSize());
+ }
+
+ @Test
+ public void testDuplicateModuleThrowsDuplicateModuleException() throws PenusException {
+ TakenCommand command1 = new TakenCommand("CS2113", 2, 2, "A+");
+ TakenCommand command2 = new TakenCommand("CS2113", 2, 2, "B+");
+
+ command1.execute(model);
+
+ assertThrows(DuplicateModuleException.class, () -> command2.execute(model));
+ }
+
+ @Test
+ public void testInvalidModuleThrowsInvalidModuleException() throws PenusException {
+ TakenCommand command = new TakenCommand("CS211300 ", 2, 2, "A+");
+
+ assertThrows(InvalidModuleException.class, () -> command.execute(model));
+ }
+
+ //su
+ @Test
+ public void testInvalidSThrowsInvalidCommandException() throws PenusException {
+ TakenCommand command = new TakenCommand("CS2113", 2, 2, "S");
+
+ assertThrows(InvalidCommandException.class, () -> command.execute(model));
+ }
+
+ @Test
+ public void testInvalidUThrowsInvalidCommandException() throws PenusException {
+ TakenCommand command = new TakenCommand("CS2113", 2, 2, "U");
+
+ assertThrows(InvalidCommandException.class, () -> command.execute(model));
+ }
+
+ @Test
+ public void testValidUSuccess() throws PenusException {
+ TakenCommand command = new TakenCommand("CS1231", 2, 2, "S");
+ CommandResult result = command.execute(model);
+
+ assertEquals(String.format(TakenCommand.MESSAGE, command.taken, model.getSize()), result.feedbackToUser);
+ assertEquals(1, model.getSize());
+ }
+
+ @Test
+ public void testValidSSuccess() throws PenusException {
+ TakenCommand command = new TakenCommand("CS1231", 2, 2, "U");
+ CommandResult result = command.execute(model);
+
+ assertEquals(String.format(TakenCommand.MESSAGE, command.taken, model.getSize()), result.feedbackToUser);
+ assertEquals(1, model.getSize());
+ }
+}
diff --git a/src/test/java/seedu/penus/logic/parser/ParserTest.java b/src/test/java/seedu/penus/logic/parser/ParserTest.java
new file mode 100644
index 0000000000..2971ad23d1
--- /dev/null
+++ b/src/test/java/seedu/penus/logic/parser/ParserTest.java
@@ -0,0 +1,447 @@
+package seedu.penus.logic.parser;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.penus.common.exceptions.InvalidFormatException;
+import seedu.penus.common.exceptions.InvalidGradeException;
+import seedu.penus.common.exceptions.InvalidModuleException;
+import seedu.penus.common.exceptions.InvalidSemesterException;
+import seedu.penus.common.exceptions.InvalidYearException;
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.logic.commands.ClearCommand;
+import seedu.penus.logic.commands.Command;
+import seedu.penus.logic.commands.DetailsCommand;
+import seedu.penus.logic.commands.ExitCommand;
+import seedu.penus.logic.commands.HelpCommand;
+import seedu.penus.logic.commands.InitCommand;
+import seedu.penus.logic.commands.ListCommand;
+import seedu.penus.logic.commands.MarkCommand;
+import seedu.penus.logic.commands.PlanCommand;
+import seedu.penus.logic.commands.RemoveCommand;
+import seedu.penus.logic.commands.StatusCommand;
+import seedu.penus.logic.commands.TakenCommand;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class ParserTest {
+ private Parser parser;
+
+ @BeforeEach
+ public void setUp() {
+ parser = new Parser();
+ }
+
+ @Test
+ public void testInitParserValidInput() throws PenusException, PenusException {
+ String args = "init n/John Doe c/1";
+ Command result = parser.initParser(args);
+ assertTrue(result instanceof InitCommand);
+ InitCommand initCommand = (InitCommand) result;
+ assertEquals(initCommand.name, "John Doe");
+ assertEquals(initCommand.courseCode, 1);
+ }
+ @Test
+ public void testInitParserInvalidFormat() throws PenusException {
+ String args = "init n/John Doe";
+ PenusException invalidFormatException = assertThrows(InvalidFormatException.class,
+ ()->parser.parseCommand(args));
+ assertEquals("Error: Try again in the format: init n/NAME c/COURSE CODE",
+ invalidFormatException.getMessage());
+ }
+
+ @Test
+ public void testInitParserEmptyParameters() throws PenusException {
+ String args = "init n/ c/1";
+ PenusException invalidFormatException = assertThrows(InvalidFormatException.class,
+ ()->parser.parseCommand(args));
+ assertEquals("Error: Try again, n/ c/ cannot be empty", invalidFormatException.getMessage());
+ }
+
+ @Test
+ public void testInitParserCourseCodeNotInteger() throws PenusException {
+ String args = "init n/John Doe c/abc";
+ Exception invalidFormatException = assertThrows(InvalidFormatException.class, ()->parser.parseCommand(args));
+ assertEquals("Error: c/ must be an integer", invalidFormatException.getMessage());
+ }
+
+ @Test
+ public void testInitParserNameInteger() {
+ String args = "init n/John Doe2 c/3";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(args));
+ }
+
+ @Test
+ public void testInitParserEmptyField() {
+ String args = "init n/ c/";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(args));
+ }
+
+
+ @Test
+ public void testPlanParser_validInput() throws PenusException {
+ String input = "plan CS2113 y/1 s/1";
+
+ Command result = parser.parseCommand(input);
+ assertTrue(result instanceof PlanCommand);
+ PlanCommand command = (PlanCommand) result;
+ assertEquals(command.plan.getCode(), "CS2113");
+ assertEquals(command.plan.getYear(), 1);
+ assertEquals(command.plan.getSem(), 1);
+ }
+
+ //plan contains g/
+ @Test
+ public void testPlanParser_containsGrade() {
+ String input = "plan CS2113 y/1 s/1 g/A";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ //plan empty fields
+ @Test
+ public void testPlanParser_emptyField() {
+ String input = "plan CS2113 y/ s/1";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ //plan not valid integer
+ @Test
+ public void testPlanParser_invalidInteger() {
+ String input = "plan CS2113 y/g s/1";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ //plan year invalid
+ @Test
+ public void testPlanParser_invalidYear() {
+ String input = "plan CS2113 y/5 s/1";
+ assertThrows(InvalidYearException.class, () -> parser.parseCommand(input));
+ }
+
+ //plan sem invalid
+ @Test
+ public void testPlanParser_invalidSem() {
+ String input = "plan CS2113 y/2 s/0";
+ assertThrows(InvalidSemesterException.class, () -> parser.parseCommand(input));
+ }
+
+
+ @Test
+ public void testTakenParser_validInput() throws PenusException {
+ String input = "taken CS2113 y/1 s/1 g/A";
+
+ Command result = parser.parseCommand(input);
+ assertTrue(result instanceof TakenCommand);
+ TakenCommand command = (TakenCommand) result;
+ assertEquals(command.taken.getCode(), "CS2113");
+ assertEquals(command.taken.getYear(), 1);
+ assertEquals(command.taken.getSem(), 1);
+ assertEquals(command.taken.getGrade(), "A");
+ }
+
+ //taken dont contain g/\@Test
+ @Test
+ public void testTakenParser_missingGrade() {
+ String input = "taken CS2113 y/2 s/1";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ //taken empty fields
+ @Test
+ public void testTakenParser_emptyField() {
+ String input = "taken CS2113 y/ s/1 g/A";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+
+ //taken invalid integer
+ @Test
+ public void testTakenParser_invalidInteger() {
+ String input = "taken CS2113 y/g s/1 g/A";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ //taken invalid grade
+ @Test
+ public void testTakenParser_invalidGrade() {
+ String input = "taken CS2113 y/1 s/1 g/G";
+ assertThrows(InvalidGradeException.class, () -> parser.parseCommand(input));
+ }
+
+ //taken year invalid
+ @Test
+ public void testTakenParser_invalidYear() {
+ String input = "taken CS2113 y/999 s/1 g/A";
+ assertThrows(InvalidYearException.class, () -> parser.parseCommand(input));
+ }
+
+ //taken invalid sem
+ @Test
+ public void testTakenParser_invalidSem() {
+ String input = "taken CS2113 y/1 s/0 g/A";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+
+ @Test
+ public void testMarkParser_validInput() throws PenusException {
+ String input = "mark CS2113 g/A";
+
+ Command result = parser.parseCommand(input);
+ assertTrue(result instanceof MarkCommand);
+ MarkCommand command = (MarkCommand) result;
+ assertEquals(command.moduleCode, "CS2113");
+ assertEquals(command.grade, "A");
+ }
+
+ //mark missing g/
+ @Test
+ public void testMarkParser_missingGrade() {
+ String input = "mark CS2113";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ //mark invalid grade
+ @Test
+ public void testMarkParser_invalidGrade() {
+ String input = "mark CS2113 g/N";
+ assertThrows(InvalidGradeException.class, () -> parser.parseCommand(input));
+ }
+
+
+ @Test
+ public void testListParser_validInputNoFilter() throws PenusException {
+ String input = "list";
+
+ Command result = parser.parseCommand(input);
+ assertTrue(result instanceof ListCommand);
+ ListCommand command = (ListCommand) result;
+ assertEquals(command.year, 0);
+ assertEquals(command.semester, 0);
+ }
+
+ @Test
+ public void testListParser_validInput() throws PenusException {
+ String input = "list y/1";
+
+ Command result = parser.parseCommand(input);
+ assertTrue(result instanceof ListCommand);
+ ListCommand command = (ListCommand) result;
+ assertEquals(command.year, 1);
+ assertEquals(command.semester, 0);
+ }
+
+ @Test
+ public void testListParser_validInputYearSem() throws PenusException {
+ String input = "list y/1 s/1";
+
+ Command result = parser.parseCommand(input);
+ assertTrue(result instanceof ListCommand);
+ ListCommand command = (ListCommand) result;
+ assertEquals(command.year, 1);
+ assertEquals(command.semester, 1);
+ }
+
+ //list sem but not year specified
+ @Test
+ public void testListParser_yearUnspecified() {
+ String input = "list s/2";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ //list invalid integer
+ @Test
+ public void testListParser_invalidInteger() {
+ String input = "list y/g s/2";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ @Test
+ public void testListParser_invalidIntegerSem() {
+ String input = "list y/1 s/5,5";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+
+ //list year invalid
+ @Test
+ public void testListParser_invalidYear() {
+ String input = "list y/6 s/2";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ //list sem invalid
+ @Test
+ public void testListParser_invalidSemester() {
+ String input = "list y/1 s/0";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ //list empty year
+ @Test
+ public void testListParser_emptyYear() {
+ String input = "list y/";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ //list empty semester
+ @Test
+ public void testListParser_emptySem() {
+ String input = "list y/1 s/";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ //list empty year and semester
+ @Test
+ public void testListParser_emptyYearAndSem() {
+ String input = "list y/ s/";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ @Test
+ public void testRemoveParser_validInput() throws PenusException {
+ String input = "remove CS2113";
+
+ Command result = parser.parseCommand(input);
+ assertTrue(result instanceof RemoveCommand);
+ RemoveCommand command = (RemoveCommand) result;
+ assertEquals(command.moduleCode, "CS2113");
+ }
+
+ //remove without code
+ @Test
+ public void testRemoveParser_noCode() {
+ String input = "remove ";
+ assertThrows(InvalidModuleException.class, () -> parser.parseCommand(input));
+ }
+
+ @Test
+ public void testDetailsParser_validInput() throws PenusException {
+ String input = "details CS2113";
+
+ Command result = parser.parseCommand(input);
+ assertTrue(result instanceof DetailsCommand);
+ DetailsCommand command = (DetailsCommand) result;
+ assertEquals(command.moduleCode, "CS2113");
+ }
+
+ //detials no module code
+ @Test
+ public void testDetailsParser_noCode() {
+ String input = "details ";
+ assertThrows(InvalidModuleException.class, () -> parser.parseCommand(input));
+ }
+
+ @Test
+ public void testDetailsParser_noCode2() {
+ String input = "details ";
+ assertThrows(InvalidModuleException.class, () -> parser.parseCommand(input));
+ }
+
+ //clear
+ @Test
+ public void testClearParser_validInputNoFilter() throws PenusException {
+ String input = "clear";
+
+ Command result = parser.parseCommand(input);
+ assertTrue(result instanceof ClearCommand);
+ ClearCommand command = (ClearCommand) result;
+ assertEquals(command.year, 0);
+ assertEquals(command.semester, 0);
+ }
+
+ @Test
+ public void testClearParser_validInput() throws PenusException {
+ String input = "clear y/1";
+
+ Command result = parser.parseCommand(input);
+ assertTrue(result instanceof ClearCommand);
+ ClearCommand command = (ClearCommand) result;
+ assertEquals(command.year, 1);
+ assertEquals(command.semester, 0);
+ }
+
+ @Test
+ public void testClearParser_validInputYearSem() throws PenusException {
+ String input = "clear y/1 s/2";
+
+ Command result = parser.parseCommand(input);
+ assertTrue(result instanceof ClearCommand);
+ ClearCommand command = (ClearCommand) result;
+ assertEquals(command.year, 1);
+ assertEquals(command.semester, 2);
+ }
+
+ //clear year not specified but sem is
+ @Test
+ public void testClearParser_noYearButSem() {
+ String input = "clear s/2";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ //clear invalid integer
+ @Test
+ public void testClearParser_invalidInteger() {
+ String input = "clear y/2,2";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ //clear invalid year (not integer)
+ @Test
+ public void testClearParser_invalidYearInteger() {
+ String input = "clear y/g";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ //clear invalid year
+ @Test
+ public void testClearParser_invalidYear() {
+ String input = "clear y/1000";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ //clear invalid sem
+ @Test
+ public void testClearParser_invalidSem() {
+ String input = "clear y/1 s/0";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ @Test
+ public void testClearParser_invalidSemInteger() {
+ String input = "clear y/1 s/g";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ //clear empty fields
+ @Test
+ public void testClearParser_emptyField() {
+ String input = "clear y/ s/ ";
+ assertThrows(InvalidFormatException.class, () -> parser.parseCommand(input));
+ }
+
+ @Test
+ public void testHelpParser_validInput() throws PenusException {
+ String input = "help";
+
+ Command result = parser.parseCommand(input);
+ assertTrue(result instanceof HelpCommand);
+ }
+
+ @Test
+ public void testExitParser_validInput() throws PenusException {
+ String input = "exit";
+
+ Command result = parser.parseCommand(input);
+ assertTrue(result instanceof ExitCommand);
+ }
+
+ @Test
+ public void testStatusParser_validInput() throws PenusException {
+ String input = "status";
+
+ Command result = parser.parseCommand(input);
+ assertTrue(result instanceof StatusCommand);
+ }
+}
+
diff --git a/src/test/java/seedu/penus/logic/utils/DetailsCompilerTest.java b/src/test/java/seedu/penus/logic/utils/DetailsCompilerTest.java
new file mode 100644
index 0000000000..c85aec03f2
--- /dev/null
+++ b/src/test/java/seedu/penus/logic/utils/DetailsCompilerTest.java
@@ -0,0 +1,63 @@
+package seedu.penus.logic.utils;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class DetailsCompilerTest {
+
+ private DetailsCompiler details;
+
+ @Test
+ void testGetDetailsForCS2113() {
+ String moduleCode = "CS2113";
+ String actualOutput = details.getDetails(moduleCode);
+ String expectedOutput = "Software Engineering & Object-Oriented Programming\n\tThis module introduces the " +
+ "necessary skills for systematic and rigorous development of software systems. It covers " +
+ "requirements, design, implementation, quality assurance, and project management aspects of " +
+ "small-to-medium size multi-person software projects. The module uses the Object Oriented Programming" +
+ " paradigm. Students of this module will receive hands-on practice of tools commonly used in the " +
+ "industry, such as test automation tools, build automation tools, and code revisioning tools will be " +
+ "covered.\n\tPre-Requisites: CS2040C or ((CS2030 or its equivalent) and CS2040/S)\n\tMCs: 4\n\tModule" +
+ " cannot be SU-ed.";
+
+ assertEquals(expectedOutput, actualOutput);
+ }
+
+ @Test
+ void testGetDetailsForInvalidModule() {
+ String moduleCode = "INVALID MODULE";
+ String actualOutput = details.getDetails(moduleCode);
+ String expectedOutput = "This module code is invalid. Try again.";
+
+ assertEquals(expectedOutput, actualOutput);
+ }
+
+ @Test
+ void testGetDetailsForNull() {
+ String moduleCode = null;
+ String actualOutput = details.getDetails(moduleCode);
+ String expectedOutput = "This module code is invalid. Try again.";
+ assertEquals(expectedOutput, actualOutput);
+ }
+
+ // Test ES2631, exists in 22/23 but not 21/22
+ @Test
+ void testGetDetailsForNewModule() {
+ String moduleCode = "ES2631";
+ String actualOutput = details.getDetails(moduleCode);
+ String expectedOutput = "Critique and Communication of Thinking and Design\n\tThis " +
+ "module equips students with competencies requiring students to analyze, critique, " +
+ "and communicate engineering ideas in a systematic and thoughtful manner. " +
+ "Students will be introduced to a reasoning in engineering framework " +
+ "(Paul et al., 2019), as well as key principles of effective communication " +
+ "in the field of engineering, such as being purpose- and context-conscious " +
+ "and audience-centric (Irish & Weiss, 2013). These will be applied to analyze " +
+ "engineering ideas in both written and oral communication. Students will also " +
+ "engage in a group engineering conceptual design project aimed at promoting " +
+ "critical analysis and communication within groups.\n\tPre-Requisites " +
+ "information is not available\n\tMCs: 4\n\tModule can be SU-ed.";
+
+ assertEquals(expectedOutput, actualOutput);
+ }
+}
diff --git a/src/test/java/seedu/penus/logic/utils/GradeTest.java b/src/test/java/seedu/penus/logic/utils/GradeTest.java
new file mode 100644
index 0000000000..7d8bcb5bdb
--- /dev/null
+++ b/src/test/java/seedu/penus/logic/utils/GradeTest.java
@@ -0,0 +1,301 @@
+package seedu.penus.logic.utils;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.penus.common.exceptions.InvalidGradeException;
+import seedu.penus.model.Module;
+import seedu.penus.model.ModuleList;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class GradeTest {
+ static final String NO_OVERALL_CAP = "Overall CAP : 0.00\n";
+ static final String PERFECT_OVERALL_CAP = "Overall CAP : 5.00\n";
+ static final String PERFECT_SEM_CAP = "Semester CAP : 5.00\n";
+ static final String NO_SEM_CAP = "Semester CAP : 0.00\n";
+ private ModuleList moduleList;
+ private List semArray;
+
+ @BeforeEach
+ public void setUp() {
+ moduleList = new ModuleList();
+ semArray = new ArrayList<>();
+ }
+ @Test
+ void getGradePoint_throwsInvalidGradeException_success() {
+ assertThrows(InvalidGradeException.class, () -> {
+ Grade.getGradePoint("SU");
+ });
+ }
+ @Test
+ void getGradePoint_grade_success() throws InvalidGradeException {
+ assertEquals(0.0, Grade.getGradePoint("F"));
+ }
+
+ @Test
+ void getGradePoint_gradeAMinus_success() throws InvalidGradeException {
+ assertEquals(4.5, Grade.getGradePoint("A-"));
+ }
+
+ @Test
+ void getGradePoint_gradeBMinus_success() throws InvalidGradeException {
+ assertEquals(3.0, Grade.getGradePoint("B-"));
+ }
+
+ @Test
+ void getGradePoint_gradeCPlus_success() throws InvalidGradeException {
+ assertEquals(2.5, Grade.getGradePoint("C+"));
+ }
+
+ @Test
+ void getGradePoint_gradeC_success() throws InvalidGradeException {
+ assertEquals(2.0, Grade.getGradePoint("C"));
+ }
+
+ @Test
+ void getGradePoint_gradeDPlus_success() throws InvalidGradeException {
+ assertEquals(1.5, Grade.getGradePoint("D+"));
+ }
+
+ @Test
+ void getGradePoint_gradeD_success() throws InvalidGradeException {
+ assertEquals(1.0, Grade.getGradePoint("D"));
+ }
+
+ @Test
+ void isValid_lowercaseGrade_success() {
+ assertEquals(true, Grade.isValid("a+"));
+ }
+ @Test
+ void isValid_uppercaseGrade_success() {
+ assertEquals(true, Grade.isValid("S"));
+ }
+
+ @Test
+ void calculateOverallCAP_noSU_success() throws
+ InvalidGradeException {
+ moduleList.addModule(new Module("CS1010", 1, 1, "B+"));
+ moduleList.addModule(new Module("CS2113", 2, 2, "A+"));
+ double cap = Double.parseDouble(new DecimalFormat("#.##").
+ format(Grade.calculateOverallCAP(moduleList.getModuleList())));
+ assertEquals(4.50, cap);
+ }
+ @Test
+ void calculateOverallCAP_noSUGradeF_success() throws
+ InvalidGradeException {
+ moduleList.addModule(new Module("CS1010", 1, 1, "F"));
+ moduleList.addModule(new Module("CS2113", 2, 2, "F"));
+ double cap = Double.parseDouble(new DecimalFormat("#.##").
+ format(Grade.calculateOverallCAP(moduleList.getModuleList())));
+ assertEquals(0.00, cap);
+ }
+ @Test
+ void calculateOverallCAP_allSU_success() throws
+ InvalidGradeException {
+ moduleList.addModule(new Module("CS1010", 1, 1, "S"));
+ moduleList.addModule(new Module("PF1101", 2, 2, "U"));
+ double cap = Double.parseDouble(new DecimalFormat("#.##").
+ format(Grade.calculateOverallCAP(moduleList.getModuleList())));
+ assertEquals(5.00, cap);
+ }
+ @Test
+ void calculateOverallCAP_someSUGradeB_success() throws
+ InvalidGradeException {
+ moduleList.addModule(new Module("CS1010", 1, 1, "B"));
+ moduleList.addModule(new Module("PF1101", 2, 2, "U"));
+ double cap = Double.parseDouble(new DecimalFormat("#.##").
+ format(Grade.calculateOverallCAP(moduleList.getModuleList())));
+ assertEquals(3.50, cap);
+ }
+ @Test
+ void calculateOverallCAP_someSUGradeF_success() throws
+ InvalidGradeException {
+ moduleList.addModule(new Module("CS1010", 1, 1, "F"));
+ moduleList.addModule(new Module("PF1101", 2, 2, "U"));
+ double cap = Double.parseDouble(new DecimalFormat("#.##").
+ format(Grade.calculateOverallCAP(moduleList.getModuleList())));
+ assertEquals(0.00, cap);
+ }
+ @Test
+ void calculateOverallCAP_takenAndPlanGradeF_success() throws
+ InvalidGradeException {
+ moduleList.addModule(new Module("CS1010", 1, 1, "F"));
+ moduleList.addModule(new Module("PF1101", 2, 2));
+ double cap = Double.parseDouble(new DecimalFormat("#.##").
+ format(Grade.calculateOverallCAP(moduleList.getModuleList())));
+ assertEquals(0.00, cap);
+ }
+ @Test
+ void calculateOverallCAP_takenAndPlanGradeSU_success() throws
+ InvalidGradeException {
+ moduleList.addModule(new Module("CS1010", 1, 1, "CU"));
+ moduleList.addModule(new Module("PF1101", 2, 2));
+ double cap = Double.parseDouble(new DecimalFormat("#.##").
+ format(Grade.calculateOverallCAP(moduleList.getModuleList())));
+ assertEquals(5.00, cap);
+ }
+ @Test
+ void calculateOverallCAP_takenAndPlanGradeB_success() throws
+ InvalidGradeException {
+ moduleList.addModule(new Module("CS1010", 1, 1, "B"));
+ moduleList.addModule(new Module("PF1101", 2, 2));
+ double cap = Double.parseDouble(new DecimalFormat("#.##").
+ format(Grade.calculateOverallCAP(moduleList.getModuleList())));
+ assertEquals(3.50, cap);
+ }
+ @Test
+ void calculateOverallCAP_planOnly_success() throws
+ InvalidGradeException {
+ moduleList.addModule(new Module("EG2501", 2, 1));
+ moduleList.addModule(new Module("CG2023", 2, 2));
+ double cap = Double.parseDouble(new DecimalFormat("#.##").
+ format(Grade.calculateOverallCAP(moduleList.getModuleList())));
+ assertEquals(0.00, cap);
+ }
+ @Test
+ void calculateSemCAP_noSU_success() throws InvalidGradeException {
+ Module module1 = new Module("CS1010", 1, 1, "A+");
+ Module module2 = new Module("CG1111A", 1, 1, "A+");
+ Module module3 = new Module("CG2111A", 1, 1, "B+");
+ semArray.add(new String[] { module1.getCode(), module1.getGrade()});
+ semArray.add(new String[] { module2.getCode(), module2.getGrade()});
+ semArray.add(new String[] { module3.getCode(), module3.getGrade()});
+ double cap = Double.parseDouble(new DecimalFormat("#.##").
+ format(Grade.calculateSemCAP(semArray)));
+ assertEquals(4.67, cap);
+ }
+ @Test
+ void calculateSemCAP_noSUAndFGrade_success() throws InvalidGradeException {
+ Module module1 = new Module("CS1010", 1, 1, "F");
+ Module module2 = new Module("CG1111A", 1, 1, "F");
+ semArray.add(new String[] { module1.getCode(), module1.getGrade()});
+ semArray.add(new String[] { module2.getCode(), module2.getGrade()});
+ double cap = Double.parseDouble(new DecimalFormat("#.##").
+ format(Grade.calculateSemCAP(semArray)));
+ assertEquals(0.00, cap);
+ }
+
+ @Test
+ void calculateSemCAP_someSU_success() throws InvalidGradeException {
+ Module module1 = new Module("CS1010", 1, 1, "U");
+ Module module2 = new Module("PF1101", 1, 1, "A");
+ Module module3 = new Module("PF1101", 1, 1, "B+");
+ semArray.add(new String[] { module1.getCode(), module1.getGrade()});
+ semArray.add(new String[] { module2.getCode(), module2.getGrade()});
+ semArray.add(new String[] { module3.getCode(), module3.getGrade()});
+ double cap = Double.parseDouble(new DecimalFormat("#.##").
+ format(Grade.calculateSemCAP(semArray)));
+ assertEquals(4.50, cap);
+ }
+ @Test
+ void calculateSemCAP_someSUGradeF_success() throws InvalidGradeException {
+ Module module1 = new Module("CS1010", 1, 1, "CU");
+ Module module2 = new Module("CG1111A", 1, 1, "U");
+ Module module3 = new Module("PF1101", 1, 1, "F");
+ semArray.add(new String[] { module1.getCode(), module1.getGrade()});
+ semArray.add(new String[] { module2.getCode(), module2.getGrade()});
+ semArray.add(new String[] { module3.getCode(), module3.getGrade()});
+ double cap = Double.parseDouble(new DecimalFormat("#.##").
+ format(Grade.calculateSemCAP(semArray)));
+ assertEquals(0.00, cap);
+ }
+ @Test
+ void calculateSemCAP_allSU_success() throws InvalidGradeException {
+ Module module1 = new Module("CS1010", 1, 1, "U");
+ Module module2 = new Module("CG1111A", 1, 1, "U");
+ Module module3 = new Module("PF1101", 1, 1, "U");
+ semArray.add(new String[] { module1.getCode(), module1.getGrade()});
+ semArray.add(new String[] { module2.getCode(), module2.getGrade()});
+ semArray.add(new String[] { module3.getCode(), module3.getGrade()});
+ double cap = Double.parseDouble(new DecimalFormat("#.##").
+ format(Grade.calculateSemCAP(semArray)));
+ assertEquals(5.00, cap);
+ }
+ @Test
+ void calculateSemCAP_takenOnly_success() throws
+ InvalidGradeException {
+ Module module1 = new Module("EG2501", 2, 1, "A");
+ Module module2 = new Module("CG2111A", 2, 1, "B+");
+ semArray.add(new String[] { module1.getCode(), module1.getGrade()});
+ semArray.add(new String[] { module2.getCode(), module2.getGrade()});
+ double cap = Double.parseDouble(new DecimalFormat("#.##").
+ format(Grade.calculateSemCAP(semArray)));
+ assertEquals(4.50, cap);
+ }
+ @Test
+ void calculateSemCAP_takenAndPlanGradeA_success() throws
+ InvalidGradeException {
+ Module module1 = new Module("EG2501", 2, 1, "A");
+ Module module2 = new Module("CG2111A", 2, 1);
+ semArray.add(new String[] { module1.getCode(), module1.getGrade()});
+ semArray.add(new String[] { module2.getCode(), module2.getGrade()});
+ double cap = Double.parseDouble(new DecimalFormat("#.##").
+ format(Grade.calculateSemCAP(semArray)));
+ assertEquals(5.00, cap);
+ }
+ @Test
+ void calculateSemCAP_takenAndPlanGradeF_success() throws
+ InvalidGradeException {
+ Module module1 = new Module("EG2501", 2, 1, "F");
+ Module module2 = new Module("CG2111A", 2, 1);
+ semArray.add(new String[] { module1.getCode(), module1.getGrade()});
+ semArray.add(new String[] { module2.getCode(), module2.getGrade()});
+ double cap = Double.parseDouble(new DecimalFormat("#.##").
+ format(Grade.calculateSemCAP(semArray)));
+ assertEquals(0.00, cap);
+ }
+ @Test
+ void calculateSemCAP_takenAndPlanSU_success() throws
+ InvalidGradeException {
+ Module module1 = new Module("EG2501", 2, 1, "S");
+ Module module2 = new Module("CG2111A", 2, 1);
+ semArray.add(new String[] { module1.getCode(), module1.getGrade()});
+ semArray.add(new String[] { module2.getCode(), module2.getGrade()});
+ double cap = Double.parseDouble(new DecimalFormat("#.##").
+ format(Grade.calculateSemCAP(semArray)));
+ assertEquals(0.00, cap);
+ }
+ @Test
+ void calculateSemCAP_planOnly_success() throws InvalidGradeException {
+ Module module1 = new Module("EG2501", 2, 1);
+ Module module2 = new Module("CG2111A", 2, 1);
+ semArray.add(new String[] { module1.getCode(), module1.getGrade()});
+ semArray.add(new String[] { module2.getCode(), module2.getGrade()});
+ double cap = Double.parseDouble(new DecimalFormat("#.##").
+ format(Grade.calculateSemCAP(semArray)));
+ assertEquals(0.00, cap);
+ }
+ @Test
+ void getOverallCAP_noModsTaken_success() throws InvalidGradeException {
+ assertEquals(NO_OVERALL_CAP, Grade.getOverallCAP(moduleList.getModuleList()));
+ }
+ @Test
+ void getOverallCAP_modsTaken_success() throws InvalidGradeException {
+ moduleList.addModule(new Module("CS1010", 1, 1, "A+"));
+ moduleList.addModule(new Module("PF1101", 2, 2, "U"));
+ assertEquals(PERFECT_OVERALL_CAP, Grade.getOverallCAP(moduleList.getModuleList()));
+ }
+ @Test
+ void getOverallCAP_planOnly_success() throws InvalidGradeException {
+ moduleList.addModule(new Module("CS1010", 1, 1));
+ assertEquals(NO_OVERALL_CAP, Grade.getOverallCAP(moduleList.getModuleList()));
+ }
+ @Test
+ void getSemCAP_noModsTaken_success() throws InvalidGradeException {
+ assertEquals(NO_SEM_CAP, Grade.getSemCAP(semArray));
+ }
+ @Test
+ void getSemCAP_modsTaken_success() throws InvalidGradeException {
+ Module module1 = new Module("CS1010", 1, 1, "U");
+ Module module2 = new Module("CG1111A", 1, 1, "U");
+ semArray.add(new String[] { module1.getCode(), module1.getGrade()});
+ semArray.add(new String[] { module2.getCode(), module2.getGrade()});
+ assertEquals(PERFECT_SEM_CAP, Grade.getSemCAP(semArray));
+ }
+
+}
diff --git a/src/test/java/seedu/penus/logic/utils/ModuleRetrieverTest.java b/src/test/java/seedu/penus/logic/utils/ModuleRetrieverTest.java
new file mode 100644
index 0000000000..9a95de5cf1
--- /dev/null
+++ b/src/test/java/seedu/penus/logic/utils/ModuleRetrieverTest.java
@@ -0,0 +1,78 @@
+package seedu.penus.logic.utils;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ModuleRetrieverTest {
+
+ @Test
+ void testGetDescription() {
+ String module = "CS2103T";
+ String expectedDescription =
+ "This module introduces the necessary conceptual and analytical tools for " +
+ "systematic and rigorous development of software systems." +
+ " It covers four main areas of software development, " +
+ "namely object-oriented system analysis, object-oriented system" +
+ " modelling and design, implementation, and testing, " +
+ "with emphasis on system modelling and design and implementation of " +
+ "software modules that work cooperatively to " +
+ "fulfill the requirements of the system. Tools and techniques for software development," +
+ " such as Unified Modelling Language (UML)," +
+ " program specification, and testing methods, will be taught." +
+ " Major software engineering issues such as modularisation criteria, " +
+ "program correctness, and software quality will also be covered.";
+
+ assertEquals(expectedDescription, ModuleRetriever.getDescription(module));
+ }
+
+ @Test
+ void testGetPrerequisite() {
+ String module = "CS2103T";
+ String expectedPrerequisite = "For SoC students only. (CS1020 or its equivalent) or CS2020 or" +
+ " ((CS2030 or its equivalent) and (CS2040 or its equivalent))";
+
+ assertEquals(expectedPrerequisite, ModuleRetriever.getPrerequisite(module));
+ }
+
+ @Test
+ void testGetTitle2223() {
+ String module = "CS2103T";
+ String expectedTitle = "Software Engineering";
+
+ assertEquals(expectedTitle, ModuleRetriever.getTitle2223(module));
+ }
+
+ @Test
+ void testGetModuleCredit2223() {
+ String module = "CS2103T";
+ String expectedModuleCredit = "4";
+
+ assertEquals(expectedModuleCredit, ModuleRetriever.getModuleCredit2223(module));
+ }
+
+ @Test
+ void testGetTitle2122() {
+ String module = "CS2103T";
+ String expectedTitle = "Software Engineering";
+
+ assertEquals(expectedTitle, ModuleRetriever.getTitle2122(module));
+ }
+
+ @Test
+ void testGetModuleCredit2122() {
+ String module = "CS2103T";
+ String expectedModuleCredit = "4";
+
+ assertEquals(expectedModuleCredit, ModuleRetriever.getModuleCredit2122(module));
+ }
+
+ @Test
+ void testGetSUstatus() {
+ String module = "PF1101";
+ Boolean expectedSUstatus = true;
+
+ assertEquals(expectedSUstatus, ModuleRetriever.getSUstatus(module));
+ }
+
+}
+
diff --git a/src/test/java/seedu/penus/model/ModelManagerTest.java b/src/test/java/seedu/penus/model/ModelManagerTest.java
new file mode 100644
index 0000000000..913f5e5242
--- /dev/null
+++ b/src/test/java/seedu/penus/model/ModelManagerTest.java
@@ -0,0 +1,80 @@
+package seedu.penus.model;
+
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class ModelManagerTest {
+ private ModelManager modelManager;
+ private User user;
+ private List moduleList;
+ private List coreDetails;
+ private HashMap> coreModList;
+
+ @BeforeEach
+ public void setUp() {
+ user = new User("John Doe", "Computer Engineering");
+ moduleList = new ArrayList<>();
+ moduleList.add(new Module("CS1010", 1, 1));
+ moduleList.add(new Module("CS1231", 2, 1));
+ coreDetails = new ArrayList<>();
+ coreDetails.add(new String[]{"DTK1234", "Design Thinking", "4", "0"});
+ coreModList = new HashMap<>();
+ coreModList.put("Computer Engineering", new ArrayList<>());
+ coreModList.get("Computer Engineering").add("CS1010");
+ modelManager = new ModelManager(user, moduleList, coreDetails, coreModList);
+ }
+
+ @Test
+ public void hasModule_moduleInList_returnsTrue() {
+ assertTrue(modelManager.hasModule(new Module("CS1010", 1, 1)));
+ }
+
+ @Test
+ public void hasModule_moduleNotInList_returnsFalse() {
+ assertFalse(modelManager.hasModule(new Module("MA1508E", 1, 2)));
+ }
+
+ @Test
+ public void hasModuleCode_success() {
+ assertFalse(modelManager.hasModuleCode("CS2040C"));
+ assertTrue(modelManager.hasModuleCode("CS1231"));
+ }
+
+ @Test
+ public void addModule_validModule_success() {
+ modelManager.addModule(new Module("MA1508E", 1, 2));
+ assertEquals(3, modelManager.getSize());
+ }
+
+ @Test
+ public void removeModule_validIndex_success() {
+ modelManager.removeModule(1);
+ assertEquals(1, modelManager.getSize());
+ }
+
+ @Test
+ public void markModule_validIndexAndGrade_success() {
+ modelManager.markModule(0, "A+");
+ assertEquals("A+", modelManager.getModule(0).getGrade());
+ }
+
+ @Test
+ public void getUserName_returnsName() {
+ assertEquals("John Doe", modelManager.getUserName());
+ }
+
+ @Test
+ public void getUserCourse_returnsCourse() {
+ assertEquals("Computer Engineering", modelManager.getUserCourse());
+ }
+
+}
diff --git a/src/test/java/seedu/penus/model/ModuleListTest.java b/src/test/java/seedu/penus/model/ModuleListTest.java
new file mode 100644
index 0000000000..31e9976f5d
--- /dev/null
+++ b/src/test/java/seedu/penus/model/ModuleListTest.java
@@ -0,0 +1,59 @@
+package seedu.penus.model;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ModuleListTest {
+ private ModuleList list;
+
+ @BeforeEach
+ public void setUp() {
+ list = new ModuleList();
+ }
+
+ @Test
+ void addModuleTest() {
+ Module testModule = new Module("CS2113", 2, 2, "A+");
+ list.addModule(testModule);
+
+ assertEquals(1, list.size());
+ }
+
+ @Test
+ void removeModuleTest() {
+ Module unmarkedModule = new Module("CS2113", 2, 2);
+ list.addModule(unmarkedModule);
+
+ list.removeModule(0);
+ assertEquals(0, list.size());
+ }
+
+ @Test
+ void getGECTest() {
+ Module gecModule = new Module("GEC1015", 2, 2, "A+");
+ list.addModule(gecModule);
+ assertEquals("GEC1015", list.getGEC());
+ list.removeModule(0);
+ assertEquals("", list.getGEC());
+ }
+
+ @Test
+ void getGENTest() {
+ Module genModule = new Module("GEN2000", 2, 2, "A+");
+ list.addModule(genModule);
+ assertEquals("GEN2000", list.getGEN());
+ list.removeModule(0);
+ assertEquals("", list.getGEN());
+ }
+
+ @Test
+ void getGESSTest() {
+ Module gessModule = new Module("GESS1004", 2, 2, "A+");
+ list.addModule(gessModule);
+ assertEquals("GESS1004", list.getGESS());
+ list.removeModule(0);
+ assertEquals("", list.getGESS());
+ }
+}
diff --git a/src/test/java/seedu/penus/model/ModuleTest.java b/src/test/java/seedu/penus/model/ModuleTest.java
new file mode 100644
index 0000000000..b9e38fe577
--- /dev/null
+++ b/src/test/java/seedu/penus/model/ModuleTest.java
@@ -0,0 +1,86 @@
+package seedu.penus.model;
+
+
+import org.junit.jupiter.api.Test;
+import seedu.penus.common.exceptions.InvalidGradeException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ModuleTest {
+ static final int YEAR_2022 = 2022;
+ static final int SEMESTER_1 = 1;
+ static final String GRADE_A = "A";
+
+ @Test
+ public void testGetCode() {
+ Module module = new Module("CS1010", YEAR_2022, SEMESTER_1);
+ assertEquals("CS1010", module.getCode());
+ }
+
+ @Test
+ public void testGetYear() {
+ Module module = new Module("CS1010", YEAR_2022, SEMESTER_1);
+ assertEquals(2022, module.getYear().intValue());
+ }
+
+ @Test
+ public void testGetSem() {
+ Module module = new Module("CS1010", YEAR_2022, SEMESTER_1);
+ assertEquals(1, module.getSem().intValue());
+ }
+
+ @Test
+ public void testMarkTaken() {
+ Module module = new Module("CS1010", YEAR_2022, SEMESTER_1);
+ module.markTaken("A");
+ assertEquals("Taken", module.getStatus());
+ assertEquals("A", module.getGrade());
+ }
+
+ @Test
+ public void testMarkUntaken() {
+ Module module = new Module("CS1010", YEAR_2022, SEMESTER_1, GRADE_A);
+ module.markUntaken();
+ assertEquals("Plan", module.getStatus());
+ assertEquals("", module.getGrade());
+ }
+
+ @Test
+ public void testGetStatus() {
+ Module modulePlan = new Module("CS1010", YEAR_2022, SEMESTER_1);
+ Module moduleTaken = new Module("CS1010", YEAR_2022, SEMESTER_1, GRADE_A);
+ assertEquals("Plan", modulePlan.getStatus());
+ assertEquals("Taken", moduleTaken.getStatus());
+ }
+
+ @Test
+ public void testGetGrade() {
+ Module modulePlan = new Module("CS1010", YEAR_2022, SEMESTER_1);
+ Module moduleTaken = new Module("CS1010", YEAR_2022, SEMESTER_1, GRADE_A);
+ assertEquals("", modulePlan.getGrade());
+ assertEquals("A", moduleTaken.getGrade());
+ }
+
+ @Test
+ public void testGetGradePoint() throws InvalidGradeException {
+ Module module = new Module("CS1010", YEAR_2022, SEMESTER_1, GRADE_A);
+ assertEquals(5.0, module.getGradePoint(), YEAR_2022);
+ }
+
+ @Test
+ public void testToString() {
+ Module modulePlan = new Module("CS1010", YEAR_2022, SEMESTER_1);
+ Module moduleTaken = new Module("CS1010", YEAR_2022, SEMESTER_1, GRADE_A);
+ assertEquals("Plan CS1010 year 2022 semester 1 ", modulePlan.toString());
+ assertEquals("Taken CS1010 year 2022 semester 1 A", moduleTaken.toString());
+ }
+
+ @Test
+ public void testEncode() {
+ Module modulePlan = new Module("CS1010", YEAR_2022, SEMESTER_1);
+ Module moduleTaken = new Module("CS1010", YEAR_2022, SEMESTER_1, GRADE_A);
+ assertEquals("Plan ### CS1010 ### 2022 ### 1", modulePlan.encode());
+ assertEquals("Taken ### CS1010 ### 2022 ### 1 ### A", moduleTaken.encode());
+ }
+
+}
diff --git a/src/test/java/seedu/penus/model/UserTest.java b/src/test/java/seedu/penus/model/UserTest.java
new file mode 100644
index 0000000000..f3ac96f951
--- /dev/null
+++ b/src/test/java/seedu/penus/model/UserTest.java
@@ -0,0 +1,53 @@
+package seedu.penus.model;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class UserTest {
+ static final String EMPTY_STRING = "";
+ public User user;
+
+ @BeforeEach
+ public void setUp() {
+ user = new User();
+ }
+ @Test
+ public void testDefaultConstructor() {
+ assertEquals(EMPTY_STRING, user.getName());
+ assertEquals(EMPTY_STRING, user.getCourse());
+ }
+
+ @Test
+ public void testOverloadedConstructor() {
+ User user = new User("John Doe", "Computer Engineering");
+ String name = "John Doe";
+ String course = "Computer Engineering";
+ assertEquals(name, user.getName());
+ assertEquals(course, user.getCourse());
+ }
+
+ @Test
+ public void testSetName() {
+ String name = "Jane Doe";
+ user.setName(name);
+ assertEquals(name, user.getName());
+ }
+
+ @Test
+ public void testSetCourse() {
+ String course = "Computer Engineering";
+ user.setCourse(course);
+ assertEquals(course, user.getCourse());
+ }
+
+ @Test
+ public void testEncode() {
+ String name = "John Doe";
+ String course = "Computer Engineering";
+ user = new User(name, course);
+ String expected = String.format("User ### %s ### %s", name, course);
+ assertEquals(expected, user.encode());
+ }
+}
diff --git a/src/test/java/seedu/penus/storage/FileManagerTest.java b/src/test/java/seedu/penus/storage/FileManagerTest.java
new file mode 100644
index 0000000000..c12f91e41c
--- /dev/null
+++ b/src/test/java/seedu/penus/storage/FileManagerTest.java
@@ -0,0 +1,23 @@
+package seedu.penus.storage;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class FileManagerTest {
+ FileStorage fileManager;
+
+ @BeforeEach
+ public void setUp() {
+ fileManager = new FileStorage();
+ }
+ //test that file exists and path is correct
+ @Test
+ public void testConstructor() {
+ assertNotNull(fileManager.filePath);
+ assertEquals("./data/penus.txt", fileManager.filePath);
+ assert fileManager.filePath.equals("./data/penus.txt") : "saved file path error";
+ }
+}
+
diff --git a/src/test/java/seedu/penus/storage/FileStorageTest.java b/src/test/java/seedu/penus/storage/FileStorageTest.java
new file mode 100644
index 0000000000..b36bef8edb
--- /dev/null
+++ b/src/test/java/seedu/penus/storage/FileStorageTest.java
@@ -0,0 +1,171 @@
+package seedu.penus.storage;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.penus.common.exceptions.DuplicateModuleException;
+import seedu.penus.common.exceptions.InvalidFormatException;
+import seedu.penus.common.exceptions.InvalidGradeException;
+import seedu.penus.common.exceptions.InvalidModuleException;
+import seedu.penus.common.exceptions.InvalidSemesterException;
+import seedu.penus.common.exceptions.InvalidYearException;
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.model.Module;
+import seedu.penus.model.ModuleList;
+import seedu.penus.model.User;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.List;
+import java.util.Scanner;
+
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class FileStorageTest {
+ static final int NUMBER_OF_MODULES = 2;
+ static final String DATA_DIRECTORY = "./data/";
+ static final String FILE_PATH = "./data/penus.txt";
+
+ FileStorage fileStorage;
+
+ @BeforeEach
+ public void setUp() {
+ fileStorage = new FileStorage();
+ }
+
+
+ @Test
+ public void testConstructor() {
+ assertEquals(FILE_PATH, fileStorage.filePath);
+ assertEquals(DATA_DIRECTORY, fileStorage.dataDirectory);
+ assertEquals(new File("./data/penus.txt"), fileStorage.file);
+ }
+
+ @Test
+ void testSave_inputModuleListAndUser_writeSuccess() throws IOException {
+ ModuleList moduleList = new ModuleList();
+ moduleList.addModule(new Module("CS1010", 1, 1, "A+"));
+ User user = new User("John", "Electrical Engineering");
+ fileStorage.save(moduleList, user);
+ Scanner scanner = new Scanner(fileStorage.file);
+ assertEquals("User ### John ### Electrical Engineering", scanner.nextLine());
+ assertEquals("Taken ### CS1010 ### 1 ### 1 ### A+", scanner.nextLine());
+ FileWriter writer = new FileWriter(fileStorage.file);
+ writer.write("");
+ writer.close();
+ }
+
+ @Test
+ void testRetrieveMods_returnsModuleListSuccess() throws IOException, PenusException {
+ FileWriter writer = new FileWriter("./data/penus.txt");
+ writer.write("User ### John ### Electrical Engineering\n" +
+ "Taken ### CS1010 ### 1 ### 1 ### A+\n" +
+ "Plan ### CS2040C ### 2 ### 1\n");
+ writer.close();
+ List moduleList = fileStorage.retrieveMods();
+ FileWriter toDelete = new FileWriter("./data/penus.txt");
+ toDelete.write("");
+ toDelete.close();
+ assertEquals("CS1010", moduleList.get(0).getCode());
+ assertEquals("CS2040C", moduleList.get(1).getCode());
+ assertEquals(NUMBER_OF_MODULES, moduleList.size());
+ }
+
+ @Test
+ void testRetrieveUser_returnsUserSuccess() throws IOException, PenusException {
+ FileWriter writer = new FileWriter("./data/penus.txt");
+ writer.write("User ### John ### Electrical Engineering\n" +
+ "Taken ### CS1010 ### 1 ### 1 ### A+\n" +
+ "Plan ### CS2040C ### 2 ### 1\n");
+ writer.close();
+ User user = fileStorage.retrieveUser();
+ FileWriter toDelete = new FileWriter("./data/penus.txt");
+ toDelete.write("");
+ toDelete.close();
+ assertEquals("John", user.getName());
+ assertEquals("Electrical Engineering", user.getCourse());
+ }
+
+ @Test
+ void testRetrieveUser_returnsUserSuccessEmptyLine() throws IOException, PenusException {
+ FileWriter writer = new FileWriter("./data/penus.txt");
+ writer.write("User ### John ### Electrical Engineering\n" +
+ "Taken ### CS1010 ### 1 ### 1 ### A+\n" +
+ " \n" +
+ "Plan ### CS2040C ### 2 ### 1\n");
+ writer.close();
+ User user = fileStorage.retrieveUser();
+ FileWriter toDelete = new FileWriter("./data/penus.txt");
+ toDelete.write("");
+ toDelete.close();
+ assertEquals("John", user.getName());
+ assertEquals("Electrical Engineering", user.getCourse());
+ }
+
+ @Test
+ void testInvalidUser_throwsException() throws PenusException, IOException {
+ FileWriter writer = new FileWriter("./data/penus.txt");
+ writer.write("User ### John1 ### Electrical Engineering\n");
+ writer.close();
+ assertThrows(InvalidFormatException.class, () -> fileStorage.retrieveUser());
+ }
+
+ @Test
+ void testInvalidCourse_throwsException() throws PenusException, IOException {
+ FileWriter writer = new FileWriter("./data/penus.txt");
+ writer.write("User ### John ### Electrical Engineering sadasd\n");
+ writer.close();
+ assertThrows(InvalidFormatException.class, () -> fileStorage.retrieveUser());
+ }
+
+ @Test
+ void testInvalidModule_throwsException() throws PenusException, IOException {
+ FileWriter writer = new FileWriter("./data/penus.txt");
+ writer.write("Taken ### CS10101 ### 1 ### 1 ### A+\n");
+ writer.close();
+ assertThrows(InvalidModuleException.class, () -> fileStorage.retrieveMods());
+ }
+
+ @Test
+ void testInvalidGrade_throwsException() throws PenusException, IOException {
+ FileWriter writer = new FileWriter("./data/penus.txt");
+ writer.write("Taken ### CS1010 ### 1 ### 1 ### 12\n");
+ writer.close();
+ assertThrows(InvalidGradeException.class, () -> fileStorage.retrieveMods());
+ }
+
+ @Test
+ void testInvalidYear_throwsException() throws PenusException, IOException {
+ FileWriter writer = new FileWriter("./data/penus.txt");
+ writer.write("Taken ### CS1010 ### 9999 ### 1 ### A+\n");
+ writer.close();
+ assertThrows(InvalidYearException.class, () -> fileStorage.retrieveMods());
+ }
+
+ @Test
+ void testInvalidSemester_throwsException() throws PenusException, IOException {
+ FileWriter writer = new FileWriter("./data/penus.txt");
+ writer.write("Taken ### CS1010 ### 1 ### 1090 ### A+\n");
+ writer.close();
+ assertThrows(InvalidSemesterException.class, () -> fileStorage.retrieveMods());
+ }
+
+ @Test
+ void testInvalidInteger_throwsException() throws PenusException, IOException {
+ FileWriter writer = new FileWriter("./data/penus.txt");
+ writer.write("Taken ### CS1010 ### 1 ### asdf ### A+\n");
+ writer.close();
+ assertThrows(InvalidFormatException.class, () -> fileStorage.retrieveMods());
+ }
+
+ @Test
+ void testDuplicateMod_throwsException() throws PenusException, IOException {
+ FileWriter writer = new FileWriter("./data/penus.txt");
+ writer.write("Taken ### CS1010 ### 1 ### 1 ### A+\n" +
+ "Plan ### CS1010 ### 2 ### 1\n");
+ writer.close();
+ assertThrows(DuplicateModuleException.class, () -> fileStorage.retrieveMods());
+ }
+}
diff --git a/src/test/java/seedu/penus/storage/ResourceManagerTest.java b/src/test/java/seedu/penus/storage/ResourceManagerTest.java
new file mode 100644
index 0000000000..c9726fcf5e
--- /dev/null
+++ b/src/test/java/seedu/penus/storage/ResourceManagerTest.java
@@ -0,0 +1,35 @@
+package seedu.penus.storage;
+
+import java.util.List;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+class ResourceManagerTest {
+ ResourceStorage resource;
+
+ @BeforeEach
+ public void setUp() {
+ resource = new ResourceStorage();
+ }
+
+ //test that file exists and path is correct
+ @Test
+ public void testConstructor() {
+ assertNotNull(resource.coreModFile);
+ assertNotNull(resource.modDetailsFile);
+ assertEquals("core-modules.txt", resource.coreModFile);
+ assert resource.coreModFile == "core-modules.txt" : "core modules file name error";
+ assertEquals("core-module-details.txt", resource.modDetailsFile);
+ assert resource.modDetailsFile == "core-module-details.txt" : "core module details file name error";
+ }
+
+ //test if getAllModuleDetails successfully converts module-details.txt into List form
+ @Test
+ public void testGetAllModuleDetails() {
+ List moduleDetailsList = resource.getAllModuleDetails();
+ assertEquals(28, moduleDetailsList.size());
+ }
+}
diff --git a/src/test/java/seedu/penus/storage/ResourceStorageTest.java b/src/test/java/seedu/penus/storage/ResourceStorageTest.java
new file mode 100644
index 0000000000..bc2614f314
--- /dev/null
+++ b/src/test/java/seedu/penus/storage/ResourceStorageTest.java
@@ -0,0 +1,81 @@
+package seedu.penus.storage;
+
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+
+class ResourceStorageTest {
+ static final int NUMBER_OF_CORE_MODS_FOR_MECH_ENG = 27;
+ static final int NUMBER_OF_CORE_MODS_FOR_ENV_ENG = 25;
+
+ public ResourceStorage storage;
+ @BeforeEach
+ public void setUp() {
+ storage = new ResourceStorage();
+ }
+
+ @Test
+ public void testGetCoreModsSuccess() {
+ HashMap> coreMods = storage.getCoreMods();
+ assertNotNull(coreMods);
+ assertTrue(coreMods.containsKey("Electrical Engineering"));
+ assertEquals(NUMBER_OF_CORE_MODS_FOR_MECH_ENG, coreMods.get("Mechanical Engineering").size());
+ assertTrue(coreMods.containsKey("Environmental Engineering"));
+ assertEquals(NUMBER_OF_CORE_MODS_FOR_ENV_ENG, coreMods.get("Environmental Engineering").size());
+ assertEquals("EG2501", coreMods.get("Civil Engineering").get(7));
+ }
+
+ @Test
+ public void testConstructor() {
+ assertEquals("core-modules.txt", storage.coreModFile);
+ assertEquals("core-module-details.txt", storage.modDetailsFile);
+ }
+
+ @Test
+ public void testGetCoreMods() {
+ HashMap> coreHashMap = storage.getCoreMods();
+ List courseNameList = new ArrayList<>(Arrays.asList("Computer Engineering",
+ "Electrical Engineering", "Biomedical Engineering", "Chemical Engineering",
+ "Civil Engineering", "Environmental Engineering",
+ "Industrial and Systems Engineering", "Mechanical Engineering"));
+ for (String courseName : coreHashMap.keySet()) {
+ assertTrue(courseNameList.contains(courseName));
+ }
+ List compareList = new ArrayList<>(Arrays.asList("ES2631", "CS1010", "GEA1000",
+ "DTK1234", "EG1311", "IE2141", "EE2211", "EG2501", "CDE2000", "PF1101",
+ "CG4002", "MA1511", "MA1512", "MA1508E", "EG2401A", "EG3611A",
+ "CG1111A", "CG2111A", "CS1231", "CG2023", "CG2027", "CG2028",
+ "CG2271", "CS2040C", "CS2113", "EE2026", "EE4204"));
+ List modCodeList = coreHashMap.get("Computer Engineering");
+ for (String modCode : modCodeList) {
+ assertTrue(compareList.contains(modCode));
+ }
+ }
+
+ @Test
+ public void testGetAllModuleDetails() {
+ List moduleDetailsList = storage.getAllModuleDetails();
+ String[] moduleDetails = moduleDetailsList.get(0);
+ List compareList = new ArrayList<>(Arrays.asList("CG1111A",
+ "Engineering Principles and Practice I", "4", "0", "0",
+ "BN2111, EE1111A, EE1111B, EE2111A", "Sem 1", "1"));
+ for (int i = 0; i < 8; ++i) {
+ assert (moduleDetails[i].equals(compareList.get(i)));
+ }
+ }
+
+
+
+
+}
diff --git a/src/test/java/seedu/penus/storage/StorageManagerTest.java b/src/test/java/seedu/penus/storage/StorageManagerTest.java
new file mode 100644
index 0000000000..06963aefb9
--- /dev/null
+++ b/src/test/java/seedu/penus/storage/StorageManagerTest.java
@@ -0,0 +1,53 @@
+package seedu.penus.storage;
+
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.penus.common.exceptions.PenusException;
+import seedu.penus.model.ModuleList;
+import seedu.penus.model.User;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+class StorageManagerTest {
+ StorageManager storageManager;
+ @BeforeEach
+ public void setUp() {
+ storageManager = new StorageManager();
+ }
+
+
+ @Test
+ public void testConstructor() {
+ assertNotNull(storageManager);
+ assertNotNull(storageManager.storage);
+ assertNotNull(storageManager.resource);
+ }
+
+ @Test
+ public void testLoadStorage() throws PenusException {
+ storageManager.storage = new FileStorage();
+ assertNotNull(storageManager.loadStorage());
+ }
+ @Test
+ public void testLoadUser() throws PenusException {
+ storageManager.storage = new FileStorage();
+ assertNotNull(storageManager.loadUser());
+ }
+ @Test
+ public void testSaveStorage() {
+ storageManager.storage = new FileStorage();
+ storageManager.saveStorage(new ModuleList(), new User());
+ }
+ @Test
+ public void testLoadCoreDetails() {
+ storageManager.resource = new ResourceStorage();
+ assertNotNull(storageManager.loadCoreDetails());
+ }
+
+ @Test
+ public void testLoadCoreModList() {
+ storageManager.resource = new ResourceStorage();
+ assertNotNull(storageManager.loadCoreModList());
+ }
+}
diff --git a/src/test/java/seedu/penus/testutil/SampleDataTest.java b/src/test/java/seedu/penus/testutil/SampleDataTest.java
new file mode 100644
index 0000000000..e1004ff7ce
--- /dev/null
+++ b/src/test/java/seedu/penus/testutil/SampleDataTest.java
@@ -0,0 +1,47 @@
+package seedu.penus.testutil;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.penus.model.ModelManager;
+import seedu.penus.testutils.SampleData;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SampleDataTest {
+ private ModelManager model;
+
+ @BeforeEach
+ public void setUp() {
+ model = SampleData.getSampleModel();
+ }
+
+ @Test
+ public void testSampleUserSuccess() {
+ assertEquals("bentohset", model.getUserName());
+ assertEquals("Computer Engineering", model.getUserCourse());
+ }
+
+ @Test
+ public void testSampleCoreModsListSuccess() {
+ assertTrue(model.getCoreModList().containsKey("Computer Engineering"));
+ assertEquals("CS2040C", model.getCoreModList().get("Computer Engineering").get(0));
+ }
+
+ @Test
+ public void testSampleModulesSuccess() {
+ assertEquals("CS2040C", model.getModule(0).getCode());
+ assertEquals("CS1231", model.getModule(1).getCode());
+ assertEquals("GESS1004", model.getModule(2).getCode());
+ assertEquals("Taken", model.getModule(2).getStatus());
+ assertEquals("Plan", model.getModule(3).getStatus());
+ assertEquals("CS2040C", model.getModuleList().getModule(0).getCode());
+ }
+
+ @Test
+ public void testGetCoreModsListFromUserCourseSuccess() {
+ assertEquals("CS2040C", model.getCoreModList().get(model.getUserCourse()).get(0));
+ }
+}
+
+
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 892cb6cae7..5df009a3dc 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,9 +1,100 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
-
-What is your name?
-Hello James Gosling
+
+ ___________________________________________________________
+
+ ___ _____ ______ ___ ___ ___ _________
+ / \/ \ / \ / \/ \ / \ / \
+ | __ \ | \/ || | | || _ |
+ | |__| | | | || | | || \ |
+ \ / | | | || | | |\ \_ /
+ / ___/___ | | | || | | | \_ \
+ | | / \ | | | || | | |/ \ \
+ |_______|/ <> _\| | | || \__/ || \_ |
+ \ /| \____| / \ |\ /| |
+ \__|__/ \______/\___/ \_____/ \__________/ \_________/
+
+ Welcome to PENUS!
+ Enter help for a list of commands or init to start
+ ___________________________________________________________
+Enter command:
+ ___________________________________________________________
+ Module has been added:
+ Plan CS2113 year 1 semester 2
+ You have 1 module(s) in your planner
+ ___________________________________________________________
+Enter command:
+ ___________________________________________________________
+ Module has been added:
+ Taken CS2040C year 2 semester 1 A
+ You have 2 module(s) in your planner
+ ___________________________________________________________
+Enter command:
+ ___________________________________________________________
+ Module has been taken:
+ Taken CS2113 year 1 semester 2 A+
+ ___________________________________________________________
+Enter command:
+ ___________________________________________________________
+ Module has been removed:
+ Taken CS2113 year 1 semester 2 A+
+ You have 1 module(s) in your planner
+ ___________________________________________________________
+Enter command:
+ ___________________________________________________________
+ Module has been removed:
+ Taken CS2040C year 2 semester 1 A
+ You have 0 module(s) in your planner
+ ___________________________________________________________
+Enter command:
+ ___________________________________________________________
+
+ Command Description
+ ------- -----------
+ clear [FILTER] Clears modules in the specified Year or Semester.
+ If [FILTER] is not specified, then all modules will cleared.
+
+ exit Exits the program.
+
+ list [FILTER] Displays a list of all modules taken or planned
+ in the specified Year or Semester.
+ If [FILTER] is not specified, then all modules will shown.
+
+ mark [MODULE CODE] g/[GRADE] Marks the module that has been cleared, while updating its grades.
+
+ plan [MODULE CODE] y/[YEAR] s/[SEMESTER] Adds a module to the planner as an untaken module.
+
+ remove [MODULECODE] Removes a module from the planner.
+
+ status Displays the status of Core Modules and MCs taken.
+
+ taken [MODULE CODE] y/[YEAR] s/[SEMESTER] g/[GRADE] Adds a module to the planner as a module you have already taken.
+
+ details [MODULE CODE] Displays the details of given module, including
+ Title, Description, Prerequisites, Module Credits
+ and if it can be SU-ed.
+
+ init n/[NAME] c/[COURSE NUMBER] Initialize User.
+
+ [COURSE NUMBER] -> [COURSE NAME]
+ 1 -> Biomedical Engineering
+ 2 -> Chemical Engineering
+ 3 -> Civil Engineering
+ 4 -> Computer Engineering
+ 5 -> Electrical Engineering
+ 6 -> Environmental Engineering
+ 7 -> Industrial and Systems Engineering
+ 8 -> Mechanical Engineering
+
+ ___________________________________________________________
+Enter command:
+ ___________________________________________________________
+ CS2113 Software Engineering & Object-Oriented Programming
+ This module introduces the necessary skills for systematic and rigorous development of software systems. It covers requirements, design, implementation, quality assurance, and project management aspects of small-to-medium size multi-person software projects. The module uses the Object Oriented Programming paradigm. Students of this module will receive hands-on practice of tools commonly used in the industry, such as test automation tools, build automation tools, and code revisioning tools will be covered.
+ Pre-Requisites: CS2040C or ((CS2030 or its equivalent) and CS2040/S)
+ MCs: 4
+ Module cannot be SU-ed.
+ ___________________________________________________________
+Enter command:
+ ___________________________________________________________
+ Exiting PENUS ...
+ Bye see you again!
+ ___________________________________________________________
diff --git a/text-ui-test/data/penus.txt b/text-ui-test/data/penus.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index f6ec2e9f95..d248e35d15 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -1 +1,8 @@
-James Gosling
\ No newline at end of file
+plan CS2113 y/1 s/2
+taken CS2040C y/2 s/1 g/A
+mark cs2113 g/A+
+remove CS2113
+remove cs2040c
+help
+details cs2113
+exit
\ 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