:bulb: **Tip:**
-A person can have any number of tags (including 0)
+Updates the student's information, given the student's label, fields to change, and updated field value
+
+Format: `update-info [index/STUDENT INDEX] [field/NEW_INFO] ...`
+
+Examples:
+* `update-info index/1 phone/12345678` Changes the phone number of the student at index 1 to `12345678`.
+* `update-info index/2 address/Block 414` Changes the address of the student at index 2 to `Block 414`.
+* `update-info index/3 name/John` Changes the name of the student at index 3 to `John`.
+
+* `field/NEW_INFO` is to be replaced by one of
+ * `name/`
+ * `phone/`
+ * `address/`
+ And their respective new values.
+* At least one updated parameter has to be present.
:information_source: **Why can't I update the School and Grade Level of a Student?**
+When your student changes schools or goes up a grade level, their curriculum will likely change. In that case, you should
+create a fresh new profile for your student, so that all the lessons, exams and homeworks from the previous grade level/school do not
+get mixed up with the new one. You may then delete the old profile.
+This is purposely done to make the information less cluttered and reduce confusion.
+
+
+:bulb: **Tip:** You can edit multiple fields in a student's profile at once by using several of the above prefixes. For Example:
+* `update-info index/1 name/John` Changes the name of the student at index 1 to `John`.
+* `update-info index/2 name/John address/Block 414` Changes the name and address of a student at index 2 to `John` and `Block 414` respectively.
+
+#### Delete a Student Profile
+
+Deletes the student's profile, given the index of the student.
+
+Format: `delete [index/STUDENT_INDEX]`
+
Examples:
-* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01`
-* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal`
+* `delete index/1` Deletes the first profile in the student list.
-### Listing all persons : `list`
+
-Shows a list of all persons in the address book.
+### Homework Commands
+#### Assign Homework to a Student
-Format: `list`
+Create a homework assignment with a deadline for one or more students.
+
+Format: `new-homework [name/STUDENT_NAME]... [homework/HOMEWORK_NAME] [deadline/DEADLINE]`
+
+* The `STUDENT_NAME` must be an existing student of the tutor.
+* You can enter multiple `name/` prefixes to assign the same homework to multiple students.
+* The `DEADLINE` must be in the format given in the support date and time formats' appendix.
+* The `DEADLINE` must be in the future.
+
+Examples:
+* `new-homework name/John homework/listening comprehension ex1 deadline/2023-05-30 2359` adds the assignment `listening comprehension ex1` to the student named `John. The deadline is 30 May 2023 at 23:59.
+* `new-homework name/Donald homework/english essay deadline/2023-05-14 2359` adds the assignment `English Essay` to the student named `Donald`. The deadline is 14 May 2023 at 23:59.
+* `new-homework name/Kai Ze name/Muhammad homework/math ex1 deadline/2023-05-23 2359` adds the assignment `math ex1` to the students named `Kai Ze` and `Muhammad`. The deadline is 23 May 2023 at 23:59.
+
+
+:bulb: **Tip:** You can use the `view-homework` command or click on the `Homework` button next to their name to view the list of homework the student currently has.
+
+:bulb: **Tip:** You can view the supported date and time formats [here](#supported-date-time-formats).
+
+:bulb: **Tip:** A student cannot have multiple homeworks with the same name,
+even if they have different deadlines.
+
+:exclamation: **Caution:** STUDENT_NAME is case-insensitive and supports partial matching.
+For example, `john` will match `John Doe` and `john doe`. You can refer to the [search by name mechanism](#search-by-name-mechanism) for more details.
+
+:exclamation: **Caution:** STUDENT_NAME should appear at least once and should not be empty.
+
+:exclamation: **Caution:** HOMEWORK_INDEX
+and DEADLINE should all only appear exactly once and should not be empty.
+
+
+
+
+#### View Student Homework
+
+Displays a list of homework with the ability to filter by student name and homework status.
+
+Format: `view-homework [name/STUDENT_NAME]... [status/STATUS]`
-### Editing a person : `edit`
+* By default, all homework will be displayed if no name or status parameter is provided.
+* To view homework for specific students, specify the name using `name/STUDENT_NAME` prefixes.
+* To view homework with a specific status, specify the status using `status/STATUS`.
+* The available status values are `completed` and `pending`.
+* It is possible to filter by both student name and status simultaneously.
-Edits an existing person in the address book.
+Examples:
+* `view-homework` displays a list of all homework.
+* `view-homework name/John` displays homework for a student named `John`.
+* `view-homework status/completed` displays all completed homework from all students.
+* `view-homework name/John status/pending` displays pending homework for a student named `John`.
+
+
+
+:exclamation: **Caution:** STUDENT_NAME is case-insensitive and supports partial matching.
+For example, `john` will match `John Doe` and `john doe`. You can refer to the [search by name mechanism](#search-by-name-mechanism) for more details.
+
+:exclamation: **Caution:** STATUS should only appear at most once.
+STUDENT_NAME can appear zero or multiple times.
+
+
+
+#### Delete Homework from a Student
+
+Deletes a homework assignment for a student.
-Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…`
+Format: `delete-homework [name/STUDENT_NAME] [index/HOMEWORK_INDEX]`
-* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …
-* At least one of the optional fields must be provided.
-* Existing values will be updated to the input values.
-* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative.
-* You can remove all the person’s tags by typing `t/` without
- specifying any tags after it.
+* The `STUDENT_NAME` must be an existing student of the tutor. Note that there can only be one student's name.
+* The `HOMEWORK_INDEX` must be the index of an existing homework assignment for the specified student.
+* A success message will be displayed if the homework assignment is successfully deleted. Otherwise, an error message will be displayed.
Examples:
-* `edit 1 p/91234567 e/johndoe@example.com` Edits the phone number and email address of the 1st person to be `91234567` and `johndoe@example.com` respectively.
-* `edit 2 n/Betsy Crower t/` Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags.
-### Locating persons by name: `find`
+* `delete-homework name/John index/1` deletes the first homework for the student named John.
+* `delete-homework name/Susan index/3` deletes the third homework for the student named Susan.
-Finds persons whose names contain any of the given keywords.
-Format: `find KEYWORD [MORE_KEYWORDS]`
-* The search is case-insensitive. e.g `hans` will match `Hans`
-* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans`
-* Only the name is searched.
-* Only full words will be matched e.g. `Han` will not match `Hans`
-* Persons matching at least one keyword will be returned (i.e. `OR` search).
- e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang`
+:exclamation: **Caution:** STUDENT_NAME is case-insensitive and supports partial matching.
+For example, `john` will match `John Doe` and `john doe`. You can refer to the [search by name mechanism](#search-by-name-mechanism) for more details.
+
+:exclamation: **Caution:** STUDENT_NAME, and HOMEWORK_INDEX should all only appear exactly once.
+
+#### Mark the Homework of a Student as Done
+
+Marks homework of a student as done.
+
+Format: `mark-homework [name/STUDENT_NAME] [index/HOMEWORK_INDEX]`
+
+* The `STUDENT_NAME` must be an existing student of the tutor.
+* The `HOMEWORK_INDEX` must be the index of an existing homework assignment for the specified student.
+* A success message will be displayed if the homework assignment is successfully deleted. Otherwise, an error message will be displayed.
Examples:
-* `find John` returns `john` and `John Doe`
-* `find alex david` returns `Alex Yeoh`, `David Li`
- 
-### Deleting a person : `delete`
+* `mark-homework name/John index/1` marks the first homework assignment for the student named John.
+* `mark-homework name/Susan index/3` marks the third homework assignment for the student named Susan.
+
+
-Deletes the specified person from the address book.
+:exclamation: **Caution:** STUDENT_NAME is case-insensitive and supports partial matching.
+For example, `john` will match `John Doe` and `john doe`. You can refer to the [search by name mechanism](#search-by-name-mechanism) for more details.
-Format: `delete INDEX`
+:exclamation: **Caution:** STUDENT_NAME, and HOMEWORK_INDEX should all only appear exactly once.
-* Deletes the person at the specified `INDEX`.
-* The index refers to the index number shown in the displayed person list.
-* The index **must be a positive integer** 1, 2, 3, …
+#### Unmark Homework of a Student as Undone
+
+Marks homework of a student as undone.
+
+Format: `unmark-homework [name/STUDENT_NAME] [index/HOMEWORK_INDEX]`
+
+* The `STUDENT_NAME` must be an existing student of the tutor.
+* The `HOMEWORK_INDEX` must be the index of an existing homework assignment for the specified student.
+* A success message will be displayed if the homework assignment is successfully deleted. Otherwise, an error message will be displayed.
Examples:
-* `list` followed by `delete 2` deletes the 2nd person in the address book.
-* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command.
-### Clearing all entries : `clear`
+* `unmark-homework name/John index/1`unmarks the first homework assignment for the student named John.
+* `unmark-homework name/Susan index/3` unmarks the third homework assignment for the student named Susan.
-Clears all entries from the address book.
+
-Format: `clear`
+:exclamation: **Caution:** STUDENT_NAME is case-insensitive and supports partial matching.
+For example, `john` will match `John Doe` and `john doe`. You can refer to the [search by name mechanism](#search-by-name-mechanism) for more details.
-### Exiting the program : `exit`
+:exclamation: **Caution:** STUDENT_NAME, and HOMEWORK_INDEX should all only appear exactly once.
-Exits the program.
+#### Update Homework of a Student
-Format: `exit`
+Updates the information on homework of a student
+
+Format: `update-homework [name/STUDENT_NAME] [index/HOMEWORK_INDEX] [homework/HOMEWORK_NAME] [deadline/DEADLINE]`
+
+* The `STUDENT_NAME` must be an existing student of the tutor. Note that there can only be one student's name.
+* The `HOMEWORK_INDEX` must be the index of an existing homework assignment for the specified student.
+* The `DEADLINE` must be in the format given in the support date and time formats' appendix.
+* The `DEADLINE` must be in the future.
+* `HOMEWORK_NAME` and `DEADLINE` are the updated values for this homework.
+* At least one of homework names and deadline must be in the command. They can't both be absent.
+* A success message will be displayed if the homework assignment is successfully deleted. Otherwise, an error message will be displayed.
+
+Examples:
+
+* `update-homework name/John index/1 homework/Math Assignment 1` updates the name of homework 1 of John to be `Math Assignment 1`.
+* `update-homework name/Susan index/3 deadline/2023-05-12 23:59` updates the deadline of homework 3 of Susan to be `2023-05-12 23:59`.
+* `update-homework name/Donald index/2 homework/Math Assignment 1 deadline/2023-05-12 23:59` updates the name of homework 2 of Donald to be `Math Assignment 1` and updates the deadline of homework 2 of Donald to be `2023-05-12 23:59`.
+
+
+:bulb: **Tip:** You can use the `view-homework` command to view the list of homework the student currently has.
+
+:bulb: **Tip:** You can view the supported date and time formats [here](#supported-date-time-formats).
-### Saving the data
+:exclamation: **Caution:** STUDENT_NAME is case-insensitive and supports partial matching.
+For example, `john` will match `John Doe` and `john doe`. You can refer to the [search by name mechanism](#search-by-name-mechanism) for more details.
-AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
+:exclamation: **Caution:** STUDENT_NAME, HOMEWORK_INDEX,
+and DEADLINE should all only appear at most once.
-### Editing the data file
+:bulb: **Tip:** A student cannot have multiple homework with the same name,
+even if they have different deadlines.
-AddressBook data are saved as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file.
+
:exclamation: **Caution:**
-If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run.
+### Lessons Commands
+
+#### Create a New Lesson Plan for an Upcoming Lesson
+
+Creates a new lesson for a given student, with a lesson title and time.
+
+Format: `new-lesson [name/STUDENT_NAME] [lesson/LESSON_TITLE] [start/START_TIME] [end/END_TIME]`
+
+* The `STUDENT_NAME` must be an existing student of the tutor.
+* `START_TIME` must be before `END_TIME`, and their difference must be at least 30 minutes and at most 3 hours.
+* `START_TIME` and `END_TIME` must be in the future.
+* A success message will be displayed if the lesson is successfully created. Otherwise, an error message will be displayed.
+
+Examples:
+* `new-lesson name/John Doe lesson/The Water Cycle start/2025-03-23 1300 end/2025-03-23 1500` creates a new lesson for the student named `John Doe` with the lesson title `The Water Cycle` starting at `23 Mar 2025 13:00` and ending at `23 Mar 2025 15:00`.
+* `new-lesson name/David Li name/John Doe lesson/Metamorphic Rocks start/2025-04-23 1300 end/2025-04-23 1500` creates a new lesson for the students named `David Li` and `John Doe` with the lesson title `Metamorphic Rocks` starting at `23 Apr 2025 13:00` and ending at `23 Apr 2025 15:00`.
+
+
+:bulb: **Tip:** You can use the `view-lesson` command to view the list of lessons the student currently has.
+
+:bulb: **Tip:** You can view the supported date and time formats [here](#supported-date-time-formats).
+
+:exclamation: **Caution:** STUDENT_NAME is case-insensitive and supports partial matching.
+For example, `john` will match `John Doe` and `john doe`. You can refer to the [search by name mechanism](#search-by-name-mechanism) for more details.
+
+:exclamation: **Caution:** STUDENT_NAME should appear exactly once.
+
+:exclamation: **Caution:** LESSON_TITLE, START_TIME, and END_TIME should all appear exactly once.
+
+:exclamation: **Caution:** A student can have multiple lessons with the same lesson title, provided that they do not overlap in time.
+
+
+:information_source: **Note:** Why do TutorPro allows adding homework and exams to multiple students in one command, but only allows adding lessons to one student at a time?
+This is because TutorPro assume all lessons you have with a student are one-to-one. Therefore, it is not possible for you to plan a single lesson with multiple students at the same time.
+However, on the other hand, it is perfectly possible for you to assign the same homework to multiple students at the same time. It is also possible for multiple student to attend to the same exam at the same time.
-### Archiving data files `[coming in v2.0]`
+#### View Lessons
+
+Displays the lessons for a given student/all students.
+
+Format: `view-lesson [name/STUDENT_NAME] [lesson/LESSON] [date/DATE] [done/DONE]`
+
+* Every parameter is optional.
+* If no parameters are specified, the lessons for all students will be displayed.
+* To view the lessons for specific students, specify the names using `name/STUDENT_NAME`s.
+* To view the lessons whose titles contain a certain keyword/phrase, specify the keyword/phrase using `lesson/LESSON`.
+* To view the lessons for a specific date, specify the date using `date/DATE`.
+* To view the lessons that have been completed, include `done/done`.
+* To view the lessons that haven't been completed, include `done/not done`.
+
+Examples:
+* `view-lesson` Displays the lesson history for all the tutor’s students.
+* `view-lesson name/John` Displays the lesson history for the student named John.
+* `view-lesson name/John lesson/Math date/2023-05-03` Displays the lessons for student John, where the lessons' titles contain the keyword "Math", on the day 2023-05-03.
+* `view-lesson done/done` Displays all lessons that'd been completed
+* `view-lesson done/not done` Displays all lessons that haven't been completed
+* `view-lesson name/John done/done` Displays all lessons that'd been completed for student John
+* `view-lesson name/John name/Bernice done/not done` Displays all lessons that haven't been completed for students John and Bernice
+
+
+
+:bulb: **Tip:** You can view the supported date formats [here](#supported-date-formats).
+
+:exclamation: **Caution:** STUDENT_NAME is case-insensitive and supports partial matching.
+For example, `john` will match `John Doe` and `john doe`. You can refer to the [search by name mechanism](#search-by-name-mechanism) for more details.
+
+:exclamation: **Caution:** LESSON, DATE, and DONE can appear at most once.
+STUDENT_NAME can appear multiple times.
+
+#### Delete a Lesson from a student
+Deletes a lesson for a given student.
+
+Format: `delete-lesson [name/STUDENT_NAME] [index/LESSON_INDEX]`
+
+* The `STUDENT_NAME` must be an existing student of the tutor. Note that there can only be one student's name.
+* The `LESSON_INDEX` must be a positive integer that is within the range of the student's lesson list.
+* A success message will be displayed if the lesson is successfully deleted. Otherwise, an error message will be displayed.
+
+Example:
+* `delete-lesson name/John Doe index/1` deletes the first lesson for the student named John Doe.
+* `delete-lesson name/Bernice Yu index/2` deletes the second lesson for the student named Bernice Yu.
+
+
+:bulb: **Tip:** You can use the `view-lesson` command to view the list of lessons the student currently has.
+
+:exclamation: **Caution:** STUDENT_NAME is case-insensitive and supports partial matching.
+For example, `john` will match `John Doe` and `john doe`. You can refer to the [search by name mechanism](#search-by-name-mechanism) for more details.
+
+:exclamation: **Caution:** STUDENT_NAME and LESSON_INDEX should appear exactly once.
+
+
+#### Update a Lesson
+Updates a lesson for a given student. This can change the lesson title, start time, and/or end time.
+
+Format: `update-lesson [name/STUDENT_NAME] [index/LESSON_INDEX] [lesson/LESSON_TITLE] [start/START_TIME] [end/END_TIME]`
+
+* The `STUDENT_NAME` must be an existing student of the tutor. Note that there can only be one student's name.
+* The `LESSON_INDEX` must be a positive integer that is within the range of the student's lesson list.
+* `LESSON_TITLE`, `START_TIME` and `END_TIME` are the updated values for this lesson. At least one of them should be present.
+* The provided `START_TIME` must be before the provided `END_TIME`, or, if the `END_TIME` is not provided, it must be before the original end time of the lesson.
+* The provided `END_TIME` must be after the provided `START_TIME`, or, if the `START_TIME` is not provided, it must be after the original start time of the lesson.
+* The updated lesson's duration must be at least 30 minutes and at most 3 hours.
+* A success message will be displayed if the lesson is successfully updated. Otherwise, an error message will be displayed.
+
+Example:
+* `update-lesson name/John Doe index/1 lesson/The Water Cycle start/2025-03-23 1300 end/2025-03-23 1500` updates the first lesson for the student named John Doe to have the lesson title `The Water Cycle`, start time `23 Mar 2025 13:00`, and end time `23 Mar 2025 15:00`.
+* `update-lesson name/Bernice Yu index/2 lesson/Photosynthesis` updates the second lesson for the student named Bernice Yu to have the lesson title `Photosynthesis`.
+* `update-lesson name/John Doe index/1 start/2025-03-23 1300` updates the first lesson for the student named John Doe to have the start time `23 Mar 2025 13:00`.
+* `update-lesson name/Bernice Yu index/2 end/2025-03-23 1500` updates the second lesson for the student named Bernice Yu to have the end time `23 Mar 2025 15:00`.
+
+
+:bulb: **Tip:** You can use the `view-lesson` command to view the list of lessons the student currently has.
+
+:bulb: **Tip:** You can view the supported date and time formats [here](#supported-date-time-formats).
+
+:exclamation: **Caution:** STUDENT_NAME is case-insensitive and supports partial matching.
+For example, `john` will match `John Doe` and `john doe`. You can refer to the [search by name mechanism](#search-by-name-mechanism) for more details.
+
+:exclamation: **Caution:** STUDENT_NAME, LESSON_INDEX should appear exactly once.
+
+:exclamation: **Caution:** LESSON_TITLE, START_TIME, and END_TIME should each appear at most once.
+
+
+
+### Exam Commands
+
+#### Add an Exam to be tracked
+
+Create an Exam for a given student(s).
+
+Format: `new-exam [name/STUDENT_NAME].. [exam/EXAM_NAME] [start/START_TIME]
+[end/END_TIME] [weightage/WEIGHTAGE] [grade/GRADE]`
+
+* One or more `STUDENT_NAME` prefixes must be provided. Multiple `STUDENT_NAME` will assign the exam to multiple students.
+* `GRADE` and `WEIGHTAGE` are optional.
+* The format of `GRADE` should be `grade/ACTUAL_SCORE/TOTAL_SCORE`
+* `GRADE` can only be saved if the exam is already completed.
+* `WEIGHTAGE` should be entered as a percentage out of 100 (with/without the % symbol).
+
+Examples:
+* `new-exam name/John Doe exam/Math MYE start/2023-05-21 12:00 end/2023-05-21 14:00`
+* `new-exam name/John Doe name/Faye Doe exam/Science MYE start/2023-05-22 12:00 end/2023-05-22 14:00`
+
+:bulb: **Tip:** You can view the supported date and time formats [here](#supported-date-time-formats).
+
+:exclamation: **Caution:** STUDENT_NAME is case-insensitive and supports partial matching.
+For example, `john` will match `John Doe` and `john doe`.
+You can refer to the [search by name mechanism](#search-by-name-mechanism) for more details.
+
+:exclamation: **Caution:** STUDENT_NAME should appear at least once.
-_Details coming soon ..._
+:exclamation: **Caution:** EXAM_NAME, START_TIME, and END_TIME should appear exactly once.
+
+#### Remove an exam
+
+Deletes an exam of a student.
+
+Format: `delete-exam [name/STUDENT_NAME] [index/EXAM_INDEX]`
+
+* Removes an exam that TutorPro is currently tracking.
+* At least one student name must be provided.
+* `EXAM_INDEX` is in reference to the indexing of the exams listed when invoking the `view-exam` command on a
+ student.
+* There can be multiple `STUDENT_NAME`s provided to this command, and each name provided will attempt to match with only
+ one student. eg. `delete-exam name/John name/Faye index/1` will attempt to match each name to a student being tracked,
+ and will result in exams of index '1' of students 'John' and 'Faye' being removed from TutorPro.
+
+Examples:
+* `delete-exam name/John Doe index/1` - This command will remove student John's 1st indexed exam.
+
+:bulb: **Tip:** You can use the `view-exam` command to view the list of exams the student currently has.
+
+:exclamation: **Caution:** STUDENT_NAME is case-insensitive and supports partial matching.
+For example, `john` will match `John Doe` and `john doe`.
+You can refer to the [search by name mechanism](#search-by-name-mechanism) for more details.
+
+:exclamation: **Caution:** STUDENT_NAME should appear at least once.
+
+#### View exams tracked by TutorPro
+
+Displays exams stored in TutorPro, with the option to filter based on Exam date, Student name or past/upcoming status.
+
+Format: `view-exam [name/STUDENT_NAME].. [date/DATE] [exam/NAME_OF_EXAM] [done/IS_DONE]`
+
+* Lists exams TutorPro is currently tracking, while filtering for the specified predicates
+* All predicates are optional, leaving all parameters blank will list all currently tracked exams
+* Field `IS_DONE` can be used with values `done` to show completed exams, or `not done` to show upcoming exams.
+* There can be multiple `STUDENT_NAME` prefixes provided to this command, and each name provided will attempt to match with only
+ one student. eg. `view-exam name/John name/Faye` will attempt to match each name to a student being tracked, and will
+ result in exams of students 'John' and 'Faye' being listed.
+
+
+Examples:
+* `view-exam` - Lists all exams currently being tracked by TutorPro
+* `view-exam name/John date/2023-05-01 exam/MYE done/` - List exams attributed to student 'John' on date '2023-05-01'
+ with description 'MYE' which are undone.
+
+
+
+:bulb: **Tip:** You can view the supported date formats [here](#supported-date-formats).
+
+:exclamation: **Caution:** STUDENT_NAME is case-insensitive and supports partial matching.
+For example, `john` will match `John Doe` and `john doe`.
+You can refer to the [search by name mechanism](#search-by-name-mechanism) for more details.
+
+:exclamation: **Caution:** The only parameter allowed to have more than one value is `STUDENT_NAME`.
+
+#### Edit exam details
+
+Updates an exam's information.
+
+Format: `update-exam [name/STUDENT_NAME] [index/EXAM_INDEX] [exam/NEW_EXAM_NAME] [start/START_TIME]
+[end/END_TIME] [weightage/WEIGHTAGE] [grade/GRADE]`
+
+* Updates the details of an exam tracked by TutorPro.
+* `NEW_EXAM_NAME`, `START_TIME`, `END_TIME`, `WEIGHTAGE` and `GRADE` are the the updated values for this exam and are optional.
+* Of the optional fields, at least one must be provided in order to update the exam.
+* `GRADE` can only be updated after the exam is already completed.
+* `EXAM_INDEX` is in reference to the indexing of the exams listed when invoking the `view-exam` command on a
+ student.
+
+Examples:
+* `update-exam name/John index/1 grade/20/25` -updates the first exam (index when `view-exam` is invoked with student
+ name) grade to `20/25`.
+
+:bulb: **Tip:** You can use the `view-exam` command to view the list of exams the student currently has.
+
+:exclamation: **Caution:** STUDENT_NAME is case-insensitive and supports partial matching.
+For example, `john` will match `John Doe` and `john doe`.
+You can refer to the [search by name mechanism](#search-by-name-mechanism) for more details.
+
+:exclamation: **Caution:** STUDENT_NAME should appear exactly once and should not be empty.
+
+
+
+
+### Global Commands
+
+#### Get Help for TutorPro
+
+Shows the link for TutorPro support and documentation.
+
+Format: `help`
+
+* Opens the help window where you can copy the link to TutorPro's support site.
+
+Example:
+* `help` Opens the help window.
+
+#### Clear All TutorPro Data
+
+Deletes all information stored in TutorPro.
+
+Format: `clear`
+
+Example:
+* `clear` Deletes all student, homework, lesson and exam data in TutorPro.
+
+:exclamation: **Caution:** Clear is an irreversible command. Any data you lose cannot be recovered anymore. Please use with caution.
+
+
+#### Exit TutorPro
+
+Closes TutorPro.
+
+Format: `exit`
+
+* This command is just a quick shortcut for keyboard-savvy users. Closing TutorPro with the (X) icon also safely quits TutorPro, without losing any data.
+
+Example:
+* `exit` Safely closes TutorPro.
+
+
+
+## Unique Mechanisms
+
+### Search by Name Mechanism
+
+* TutorPro uses students' Names as primary keys to identify students.
+* Most commands in TutorPro allow you to search for a student by name, rather than by index, which is more intuitive for the user and eliminates the need to remember the index of the student.
+* Therefore, duplicate names aren't allowed. Names that are substrings of other names or vice versa aren't allowed. For example, `John Doe` and `John` are not allowed. If you have students with the exact name, say `John Doe`, you can add a number to the end of the name to differentiate them. For example, `John Doe 1` and `John Doe 2`.
+* The search by name mechanism is case-insensitive, meaning that the search will be case-insensitive. For Example, `john doe` and `John Doe` will be treated as the same name.
+* Partial names can be used as well. For example, `doe` will return all students with the name `John Doe` and `Jane Doe`.
+
+### Schedule Clash Detection Mechanism
+
+#### Schedule Clash Detection Mechanism when Adding a New Lesson
+
+##### With respect to existing lessons
+* TutorPro will detect if there is a clash between the new lesson and existing lessons of all students.
+* Since TutorPro is meant for one tutor, it is assumed that the tutor will not be teaching two lessons at the same time.
+* When adding a new lesson, TutorPro will check if the lesson clashes with any other lessons of all students. For example:
+ * Running command `new-lesson name/John Doe lesson/Math Lesson start/2023-05-21 12:00 end/2023-05-21 14:00` will add a new lesson for `John Doe` on `2023-05-21` from `12:00` to `14:00`.
+ * If you then run command `new-lesson name/John Doe lesson/Science Lesson start/2023-05-21 13:00 end/2023-05-21 15:00`, which adds a new lesson for `John Doe` on `2023-05-21` from `13:00` to `15:00`, TutorPro will detect that there is a clash in the schedule and will not add the lesson for `John Doe` as a student can't have two lessons at the same time.
+* We also Assume that tutors will only teach one student at a time. Therefore, if multiple students have lessons even with the same at the same time, TutorPro will detect that there is a clash in the schedule and will not add the lesson for the student. For example:
+ * Running command `new-lesson name/John Doe lesson/Math Lesson start/2023-05-21 12:00 end/2023-05-21 14:00` will add a new lesson for `John Doe` on `2023-05-21` from `12:00` to `14:00`.
+ * If you then run command `new-lesson name/Irfan Ibrahim lesson/Math Lesson start/2023-05-21 12:00 end/2023-05-21 14:00`, which adds a new lesson for `Irfan Ibrahim` on `2023-05-21` from `12:00` to `14:00`, TutorPro will detect that there is a clash in the schedule and will not add the lesson for `Irfan Ibrahim` as a tutor can't teach two students at the same time.
+
+##### With respect to existing exams
+* TutorPro will detect if there is a clash between the new lesson and existing exams of the particular student.
+* Since the timing of the exam is determined by the school of the student, TutorPro will prioritize the timing of the exam over the new lesson.
+* We assume that a student will not have an exam and a lesson at the same time. For example:
+ * Run command `new-exam name/John Doe exam/Math Exam start/2023-05-21 13:00 end/2023-05-21 15:00`, which adds a new exam for `John Doe` on `2023-05-21` from `13:00` to `15:00`,
+ * If you then run command `new-lesson name/John Doe lesson/Math Lesson start/2023-05-21 12:00 end/2023-05-21 14:00`, which will add a new lesson for `John Doe` on `2023-05-21` from `12:00` to `14:00`, TutorPro will detect that there is a clash in the schedule and will not add the lesson for `John Doe`, as a student can't have an exam and a lesson at the same time.
+
+#### Schedule Clash Detection Mechanism when Adding a New Exam
+
+##### With respect to existing lessons
+* TutorPro will detect if there is a clash between the new exam and existing lessons of the particular student.
+* Since the timing of the exam is determined by the school of the student, TutorPro will prioritize the timing of the new exam over the existing lessons.
+* We assume that a student will not have an exam and a lesson at the same time. For example:
+ * Run command `new-lesson name/John Doe lesson/Math Lesson start/2023-05-21 12:00 end/2023-05-21 14:00` will add a new lesson for `John Doe` on `2023-05-21` from `12:00` to `14:00`.
+ * If you then run command `new-exam name/John Doe exam/Science Exam start/2023-05-21 13:00 end/2023-05-21 15:00`, which adds a new exam for `John Doe` on `2023-05-21` from `13:00` to `15:00`, unlike the previous section, TutorPro will allow you to add the exam for `John Doe` as the exam timing is more important than the lesson timing.
+ * However, TutorPro will detect that the clash in the schedule and will prompt you to update the clashed lesson's timing.
+
+##### With respect to existing exams
+* TutorPro will detect if there is a clash in the schedule of a student when adding a new exam.
+* We assume that a student will not have two exams at the same time.
+* Unlike adding a new lesson, when adding a new exam, TutorPro will check if the exam clashes with any other exams of the same student only. (note the difference between adding a new lesson and adding a new exam). For example:
+ * Run command `new-exam name/John Doe exam/English Exam start/2023-05-21 12:00 end/2023-05-21 14:00` will add a new exam for `John Doe` on `2023-05-21` from `12:00` to `14:00`.
+ * If you run command `new-exam name/John Doe exam/Math Exam start/2023-05-21 13:00 end/2023-05-21 14:00`, which adds a new exam for `John Doe` on `2023-05-21` from `13:00` to `14:00`, TutorPro will detect that there is a clash in the schedule and will not add the exam for `John Doe` as a student can't have two exams at the same time.
+
+
+
+### Duplicate Detection Mechanism
+TutorPro will detect duplicate homeworks,
+lessons and exams and will not add them to the student's list of homeworks, lessons and exams respectively.
+However,
+you may have noticed that duplicate homework name is not allowed but duplicate lesson name and exam name is allowed.
+This is a carefully crafted feature that will be explained in the following section.
+
+#### Duplicate Homework Detection
+* TutorPro identifies homework by its name, since we assume that a student can have multiple homeworks with the same deadline.
+* Since name is the primary key for homework, duplicate homework names are not allowed. For example, if you have a homework named `Math Homework` and you try to add another homework with the same name even with a different deadline, TutorPro will detect that there is a duplicate homework and will not add the homework.
+* If you have homeworks with the same name, we encourage you to add a number to the end of the name to differentiate them. For example, `Math Homework 1` and `Math Homework 2`.
+
+#### Duplicate Lesson Detection
+* TutorPro identifies a lesson by its start time and end time, since we assume that a student can have multiple lessons with the same name, but they can't have two lessons at the same time.
+* Since start time and end time is the primary key for lessons, lessons that `clash` with other lessons aren't allowed. (See [Schedule Clash Detection Mechanism when Adding a New Lesson](#schedule-clash-detection-mechanism-when-adding-a-new-lesson) for more details)
+* However, adding lessons with the duplicate name but without `clashes` with other lessons are allowed. For example:
+ * if you have a lesson named `Math Lesson` for `John Doe` on `2023-05-21` from `12:00` to `14:00`
+ * you then try to add another lesson with the same name but on `2023-05-24` from `13:00` to `15:00`, TutorPro will not detect that there is a duplicate lesson and will add the lesson.
+
+
+
+
+#### Duplicate Exam Detection
+* TutorPro identifies an exam by its start time and end time, since we assume that a student can have multiple exams with the same name, but they can't have two exams at the same time.
+* Since start time and end time is the primary key for exams, exams that `clash` with other exams aren't allowed. (See [Schedule Clash Detection Mechanism when Adding a New Exam](#schedule-clash-detection-mechanism-when-adding-a-new-exam) for more details)
+* However, adding exams with the duplicate name but without `clashes` with other exams are allowed. For example:
+ * if you have an exam named `Math Exam` for `John Doe` on `2023-05-21` from `12:00` to `14:00`
+ * you then try to add another exam with the same name but on `2023-05-24` from `13:00` to `15:00`, TutorPro will not detect that there is a duplicate exam and will add the exam.
+
+### Storage Mechanism
+:biohazard: This section is for advanced users who are interested in learning about the storage mechanism of TutorPro. If you are new to TutorPro, we recommend that you skip this section and do not edit the JSON file directly.
+
+* TutorPro stores all the data in a JSON file.
+* The JSON file is located in the `data` folder of the TutorPro home folder.
+* If it is your first time using TutorPro, the TutorPro home folder will be created in the same directory as the TutorPro JAR file and a sample student list will be created for your to quickly get started.
+* We discourage you from editing the JSON file directly as the content you edited may not be in the correct format and may not be able to be parsed by TutorPro.
+* If the storage file is corrupted, TutorPro will create a new storage file and an empty student list.
+
+:warning: **Warning**
+If what you edit in the JSON file is in the correct format but violates the constraint of TutorPro, TutorPro can parse the data file but may not be able to function as expected. For example, if you edit the end time of an exam to be earlier than the start time of the exam.
+Therefore, we strongly discourage you from editing the JSON file directly unless you are absolutely sure of what you are doing.
--------------------------------------------------------------------------------------------------------------------
+
+
## FAQ
-**Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder.
+**Q**: How can I transfer my data to another computer in TutorPro?
+**A**: You can transfer your data to another computer by installing TutorPro on the new computer and replacing its empty data file with the one that contains the data from your previous TutorPro home folder. Refer to [Storage Mechanism](#storage-mechanism) for more details.
+
+**Q**: My students' full information is not shown on the student list.
+How can I view the full information of a student?
+
+**A**: You can view the full information of a student by running command `view-student [index/INDEX]`.
+For example, if you want to view the full information of the student at index 1,
+you can run command `view-student index/1`.
+Alternatively, you can also view with the profile button on the student card.
--------------------------------------------------------------------------------------------------------------------
-## Command summary
-
-Action | Format, Examples
---------|------------------
-**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…`
e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague`
-**Clear** | `clear`
-**Delete** | `delete INDEX`
e.g., `delete 3`
-**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…`
e.g.,`edit 2 n/James Lee e/jameslee@example.com`
-**Find** | `find KEYWORD [MORE_KEYWORDS]`
e.g., `find James Jake`
-**List** | `list`
-**Help** | `help`
+
+
+## Summary
+
+### List of Commands
+
+| Action | Command Format | Example |
+|:-----------------------------|------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------|
+| Create new student profile | `new-student [name/STUDENT_NAME] [address/STUDENT_ADDRESS] [phone/PHONE] [email/EMAIL] [school/SCHOOL] [level/GRADE_LEVEL]` | `new-student name/John Doe address/21 Prince George’s Park email/jdoe@gmail.com phone/12345678 school/ACJC level/sec8` |
+| List all students | `list` | `list` |
+| Update student information | `update-info [index/INDEX] [name/STUDENT_NAME] [field/NEW_INFO]...` | `update-info index/1 name/John address/Block 123 #12-34` |
+| Delete student profile | `delete [index/STUDENT_INDEX]` | `delete index/1` |
+| View Profile | `view-profile [name/STUDENTS_NAME]` | `view-profile name/John` |
+| Assign homework to a student | `new-homework [name/STUDENT_NAME]... [homework/HOMEWORK_NAME] [deadline/DEADLINE]` | `new-homework name/John homework/listening comprehension ex1 deadline/02-12-2023 23:59` |
+| View student's homework | `view-homework [name/STUDENT_NAME]... [status/STATUS]` | `view-homework name/John status/pending` |
+| Delete student's homework | `delete-homework [name/STUDENT_NAME] [index/HOMEWORK_INDEX]` | `delete-homework name/John index/1` |
+| Mark homework as done | `mark-homework [name/STUDENT_NAME] [index/HOMEWORK_INDEX]` | `mark-homework name/John index/1` |
+| Unmark homework as undone | `unmark-homework [name/STUDENT_NAME] [index/HOMEWORK_INDEX]` | `unmark-homework name/John index/1` |
+| Update student's homework | `update-homework [name/STUDENT_NAME] [index/HOMEWORK_INDEX] [homework/HOMEWORK_NAME] [deadline/DEADLINE]` | `update-homework name/John index/1 homework/Math Assignment 1` |
+| Create new lesson plan | `new-lesson [name/STUDENT_NAME] [lesson/LESSON_TITLE] [start/START_TIME] [end/END_TIME]` | `new-lesson name/John Doe lesson/The Water Cycle start/25-03-23-1300 end/25-03-23-1500` |
+| View lessons history | `view-lesson [name/STUDENT_NAME] [subject/SUBJECT] [date/DATE] [done/DONE]` | `view-lesson name/John` |
+| Delete a lesson | `delete-lesson [name/STUDENT_NAME] [index/LESSON_INDEX]` | `delete-lesson name/John Doe index/1` |
+| Update a lesson | `update-lesson [name/STUDENT_NAME] [index/LESSON_INDEX] [lesson/LESSON_TITLE] [start/START_TIME] [end/END_TIME]` | `update-lesson name/John Doe index/1 lesson/The Water Cycle start/2025-03-23 1300 end/2025-03-23 1500` |
+| Add an exam | `new-exam [name/STUDENT_NAME]... [exam/EXAM_NAME] [start/START_TIME] [end/END_TIME] [weightage/WEIGHTAGE] [grade/GRADE]` | `new-exam name/John Doe exam/Math MYE start/2023-05-21 12:00 end/2023-05-21 14:00` |
+| Remove an exam | `delete-exam [name/STUDENT_NAME]... [index/INDEX_OF_EXAM]` | `delete-exam name/John Doe index/1` |
+| Update an exam | `update-exam [name/STUDENT_NAME] [index/INDEX_OF_EXAM] [exam/NEW_EXAM_NAME] [start/START_TIME] [end/END_TIME] [grade/GRADE]` | `update-exam name/John Doe index/1 exam/Math MYE` |
+| View exams | `view-exam [name/STUDENT_NAME]... [date/DATE] [exam/EXAM_NAME] [done/DONE_STATUS]` | `view-exam name/John Doe date/2023-05-01 exam/MYE done/` |
+| Get help for TutorPro | `help` | `help` |
+| Exit TutorPro | `exit` | `exit` |
+
+
+
+### List of Prefixes
+
+| Prefix | Meaning | Usage | Example |
+|:-------------|-------------|-----------------------------------------------------|-----------------------------------|
+| `name/` | Name | Student name | `name/John Doe` |
+| `phone/` | Phone | Phone number of a Student | `phone/12345678` |
+| `email/` | Email | Email address of a Student | `email/johndoe@gmail.com` |
+| `address/` | Address | Home address of a Student | `address/21 Prince George's Park` |
+| `level/` | Grade Level | Grade level of a Student | `level/sec8` |
+| `school/` | School | School name of a Student | `school/ACJC` |
+| `tag/` | Tag | Tag on a Student | `tag/favorite` |
+| `homework/` | Homework | name of Homework assigned to a Student | `homework/Math Assignment` |
+| `deadline/` | Deadline | Due date | `deadline/02-12-2023 2359` |
+| `exam/` | Exam | Exam name | `exam/Math MYE` |
+| `status/` | Status | Indicates whether a homework is completed | `status/pending` |
+| `index/` | Index | Index of a student/homework/lesson/exam | `index/1` |
+| `lesson/` | Lesson | Lesson title | `lesson/The Water Cycle` |
+| `start/` | Start Time | Start time of a lesson/exam | `start/2025-03-23 1300` |
+| `end/` | End Time | End time of a lesson/exam | `end/2025-03-23 1500` |
+| `date/` | Date | Date of a lesson/exam | `date/2023-03-29` |
+| `done/` | Done | indicates if a lesson/exam is past the current time | `done/done` |
+
+
+
+### Supported date-time formats
+* `MMM dd yyyy HHmm`
+* `MMM dd yyyy HH:mm`
+* `yyyy-MM-dd'T'HH:mm `
+* `dd/MM/yyyy HHmm`
+* `dd/MM/yyyy HH:mm`
+* `yyyy/MM/dd HHmm`
+* `yyyy/MM/dd HH:mm `
+* `yyyy/MM/dd'T'HHmm`
+* `yyyy/MM/dd'T'HH:mm `
+* `yyyy-MM-dd HHmm`
+* `yyyy-MM-dd HH:mm `
+* `dd MMM yyyy HHmm`
+* `dd MMM yyyy HH:mm `
+* `MMM dd, yyyy HHmm`
+* `MMM dd, yyyy HH:mm `
+
+### Supported date formats
+* `MMM dd yyyy`
+* `yyyy-MM-dd`
+* `dd/MM/yyyy`
+* `yyyy/MM/dd`
+* `dd MMM yyyy`
+* `MMM dd, yyyy`
+* `dd-mm-yyyy`
diff --git a/docs/_config.yml b/docs/_config.yml
index 6bd245d8f4e..7ff28f42471 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -1,4 +1,4 @@
-title: "AB-3"
+title: "TutorPro"
theme: minima
header_pages:
@@ -8,7 +8,7 @@ header_pages:
markdown: kramdown
-repository: "se-edu/addressbook-level3"
+repository: "AY2223S2-CS2103T-W13-4/tp"
github_icon: "images/github-icon.png"
plugins:
diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss
index 0d3f6e80ced..9cc4e2c2386 100644
--- a/docs/_sass/minima/_base.scss
+++ b/docs/_sass/minima/_base.scss
@@ -288,7 +288,7 @@ table {
text-align: center;
}
.site-header:before {
- content: "AB-3";
+ content: "TutorPro";
font-size: 32px;
}
}
diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml
index ef81d18c337..8ecd8898c14 100644
--- a/docs/diagrams/ArchitectureSequenceDiagram.puml
+++ b/docs/diagrams/ArchitectureSequenceDiagram.puml
@@ -10,9 +10,10 @@ Participant ":Storage" as storage STORAGE_COLOR
user -[USER_COLOR]> ui : "delete 1"
activate ui UI_COLOR
-ui -[UI_COLOR]> logic : execute("delete 1")
+ui -[UI_COLOR]> logic : execute("delete index/1")
activate logic LOGIC_COLOR
+
logic -[LOGIC_COLOR]> model : deletePerson(p)
activate model MODEL_COLOR
diff --git a/docs/diagrams/ClickButtonActivityDiagram.puml b/docs/diagrams/ClickButtonActivityDiagram.puml
new file mode 100644
index 00000000000..fa262c8d6fe
--- /dev/null
+++ b/docs/diagrams/ClickButtonActivityDiagram.puml
@@ -0,0 +1,29 @@
+@startuml
+'https://plantuml.com/activity-diagram-beta
+
+start
+
+:User clicks a button;
+:Student Card creates the Header Bar;
+:Detailed Info Section sets the Header Bar;
+
+if () then ([ProfileButton])
+:Detailed Info Section creates Profile Content;
+:Detailed Info Section sets the Profile Content;
+
+else() then ([HomeworkButton, LessonsButton, ExamsButton])
+if () then ([no homework, lessons or exams])
+:Detailed Info Section creates Empty Content;
+:Detailed Info Section sets the Empty Content;
+else ([Student has homework])
+:Detailed Info Section gets information about homework, lessons or exams;
+:Detailed Info Section creates Filled Content;
+:Detailed Info Section sets the Filled Content;
+endif
+
+endif
+
+:Detailed Info Section sets the Detailed Content;
+stop
+
+@enduml
diff --git a/docs/diagrams/CreateLessonAD.puml b/docs/diagrams/CreateLessonAD.puml
new file mode 100644
index 00000000000..3ba2dd6b08a
--- /dev/null
+++ b/docs/diagrams/CreateLessonAD.puml
@@ -0,0 +1,12 @@
+@startuml
+'https://plantuml.com/activity-diagram-beta
+
+start
+
+:User inputs new-lesson command;
+:Instantiates a CreateLessonCommand;
+:Creates a Lesson object according to user inputs;
+:Adds the lesson to the specified Student's list of lessons;
+:Update the UI to inform user the Lesson is successfully added;
+
+@enduml
diff --git a/docs/diagrams/CreateLessonSequenceDiagram.puml b/docs/diagrams/CreateLessonSequenceDiagram.puml
new file mode 100644
index 00000000000..086b8e4cda4
--- /dev/null
+++ b/docs/diagrams/CreateLessonSequenceDiagram.puml
@@ -0,0 +1,89 @@
+@startuml
+!include style.puml
+
+box Logic LOGIC_COLOR_T1
+participant ":Logic Manager" as lm LOGIC_COLOR
+participant ":AddressBookParser" as abp LOGIC_COLOR
+participant ":CreateLessonCommandParser" as clp LOGIC_COLOR
+participant "c:CreateLessonCommand" as clc LOGIC_COLOR
+participant ":CommandResult" as cr LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Student" as s MODEL_COLOR
+participant ":UniqueLessonList" as ll MODEL_COLOR
+participant ":Lesson" as l MODEL_COLOR
+end box
+
+[-> lm : execute("new-lesson\nname/John\nlesson/Math Lesson\nstart/2023-05-01 1200\nend/2023-05-01 1400")
+activate lm
+
+lm -> abp : parseCommand("new-lesson\nname/John\nlesson/Math Lesson\nstart/2023-05-01 1200\nend/2023-05-01 1400")
+activate abp
+
+create clp
+abp -> clp
+activate clp
+
+clp --> abp
+deactivate clp
+
+abp -> clp : parse("new-lesson\nname/John\nlesson/Math Lesson\nstart/2023-05-01 1200\nend/2023-05-01 1400")
+activate clp
+
+create clc
+clp -> clc
+activate clc
+
+clc --> clp : c
+deactivate clc
+
+clp --> abp : c
+deactivate clp
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+clp -[hidden]-> abp
+destroy clp
+
+abp --> lm : c
+deactivate abp
+
+lm -> clc : execute()
+activate clc
+
+create l
+clc -> l
+activate l
+
+l --> clc : l
+deactivate l
+
+clc -> s : addLesson(l)
+activate s
+
+s -> ll : addLesson(l)
+activate ll
+
+ll --> s
+deactivate ll
+
+s --> clc
+deactivate s
+
+create cr
+clc -> cr
+activate cr
+
+cr --> clc
+deactivate cr
+
+clc --> lm : result
+deactivate clc
+
+[<--lm
+
+
+
+
+
+
+@enduml
diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml
index 1dc2311b245..5f2730322fe 100644
--- a/docs/diagrams/DeleteSequenceDiagram.puml
+++ b/docs/diagrams/DeleteSequenceDiagram.puml
@@ -13,10 +13,10 @@ box Model MODEL_COLOR_T1
participant ":Model" as Model MODEL_COLOR
end box
-[-> LogicManager : execute("delete 1")
+[-> LogicManager : execute("delete index/1")
activate LogicManager
-LogicManager -> AddressBookParser : parseCommand("delete 1")
+LogicManager -> AddressBookParser : parseCommand("delete index/1")
activate AddressBookParser
create DeleteCommandParser
diff --git a/docs/diagrams/ExamsClickSequenceDiagram.puml b/docs/diagrams/ExamsClickSequenceDiagram.puml
new file mode 100644
index 00000000000..fa2776df1d0
--- /dev/null
+++ b/docs/diagrams/ExamsClickSequenceDiagram.puml
@@ -0,0 +1,71 @@
+@startuml
+!include style.puml
+
+box Ui UI_COLOR_T1
+participant ":StudentLisCard" as StudentListCard UI_COLOR
+participant ":MainWindow" as MainWindow UI_COLOR
+participant ":DetailedInfoSection" as DetailedInfoSection UI_COLOR
+participant ":FilledExamsContent" as FilledExamsContent UI_COLOR
+participant ":EmptyExamsContent" as EmptyExamsContent UI_COLOR
+end box
+
+[-> StudentListCard : handleViewLessonsButtonClick()
+activate StudentListCard
+
+StudentListCard -> MainWindow : setDetailedHeaderBar(firstName)
+activate MainWindow
+
+MainWindow -> DetailedInfoSection : setDetailedHeaderBar(firstName)
+activate DetailedInfoSection
+
+DetailedInfoSection -> DetailedInfoSection : setHeaderBar(firstName)
+activate DetailedInfoSection
+
+
+DetailedInfoSection --> DetailedInfoSection
+deactivate DetailedInfoSection
+
+DetailedInfoSection --> MainWindow
+deactivate DetailedInfoSection
+
+MainWindow --> StudentListCard
+deactivate MainWindow
+
+alt student has lessons
+ create FilledExamsContent
+ StudentListCard -> FilledExamsContent : new FilledExamsContent(student)
+ activate FilledExamsContent
+
+ return FilledExamsContent
+
+else student has no lessons
+ create EmptyExamsContent
+ StudentListCard -> EmptyExamsContent : new EmptyExamsContent(student)
+ activate EmptyExamsContent
+
+ return EmptyExamsContent
+
+end
+
+StudentListCard -> MainWindow : setDetailedContent()
+activate MainWindow
+
+MainWindow -> DetailedInfoSection : setDetailedContent()
+activate DetailedInfoSection
+
+DetailedInfoSection -> DetailedInfoSection : setContent()
+activate DetailedInfoSection
+
+DetailedInfoSection --> DetailedInfoSection
+deactivate DetailedInfoSection
+
+DetailedInfoSection --> MainWindow
+deactivate DetailedInfoSection
+
+MainWindow --> StudentListCard
+deactivate MainWindow
+
+[<-- StudentListCard
+deactivate StudentListCard
+
+@enduml
diff --git a/docs/diagrams/HomeworkClickSequenceDiagram.puml b/docs/diagrams/HomeworkClickSequenceDiagram.puml
new file mode 100644
index 00000000000..d84a812d314
--- /dev/null
+++ b/docs/diagrams/HomeworkClickSequenceDiagram.puml
@@ -0,0 +1,69 @@
+@startuml
+!include style.puml
+
+box Ui UI_COLOR_T1
+participant ":StudentLisCard" as StudentListCard UI_COLOR
+participant ":MainWindow" as MainWindow UI_COLOR
+participant ":DetailedInfoSection" as DetailedInfoSection UI_COLOR
+participant ":FilledHomeworkContent" as FilledHomeworkContent UI_COLOR
+participant ":EmptyHomeworkContent" as EmptyHomeworkContent UI_COLOR
+end box
+
+
+[-> StudentListCard : handleViewHomeworkButtonClick()
+activate StudentListCard
+
+StudentListCard -> MainWindow : setDetailedHeaderBar(firstName)
+activate MainWindow
+
+MainWindow -> DetailedInfoSection : setDetailedHeaderBar(firstName)
+activate DetailedInfoSection
+
+DetailedInfoSection -> DetailedInfoSection : setHeaderBar(firstName)
+activate DetailedInfoSection
+
+DetailedInfoSection --> DetailedInfoSection
+deactivate DetailedInfoSection
+
+DetailedInfoSection --> MainWindow
+deactivate DetailedInfoSection
+
+MainWindow --> StudentListCard
+deactivate MainWindow
+
+alt student has homework
+ create FilledHomeworkContent
+ StudentListCard -> FilledHomeworkContent : new FilledHomeworkContent(student)
+ activate FilledHomeworkContent
+ return FilledHomeworkContent
+
+else student has no homework
+ create EmptyHomeworkContent
+ StudentListCard -> EmptyHomeworkContent : new EmptyHomeworkContent(student)
+ activate EmptyHomeworkContent
+
+ return EmptyHomeworkContent
+end
+
+StudentListCard -> MainWindow : setDetailedContent()
+activate MainWindow
+
+MainWindow -> DetailedInfoSection : setDetailedContent()
+activate DetailedInfoSection
+
+DetailedInfoSection -> DetailedInfoSection : setContent()
+activate DetailedInfoSection
+
+DetailedInfoSection --> DetailedInfoSection
+deactivate DetailedInfoSection
+
+DetailedInfoSection --> MainWindow
+deactivate DetailedInfoSection
+
+MainWindow --> StudentListCard
+deactivate MainWindow
+
+[<-- StudentListCard
+deactivate StudentListCard
+
+@enduml
diff --git a/docs/diagrams/LessonsClickSequenceDiagram.puml b/docs/diagrams/LessonsClickSequenceDiagram.puml
new file mode 100644
index 00000000000..126ac035d8a
--- /dev/null
+++ b/docs/diagrams/LessonsClickSequenceDiagram.puml
@@ -0,0 +1,71 @@
+@startuml
+!include style.puml
+
+box Ui UI_COLOR_T1
+participant ":StudentLisCard" as StudentListCard UI_COLOR
+participant ":MainWindow" as MainWindow UI_COLOR
+participant ":DetailedInfoSection" as DetailedInfoSection UI_COLOR
+participant ":FilledLessonsContent" as FilledLessonsContent UI_COLOR
+participant ":EmptyLessonsContent" as EmptyLessonsContent UI_COLOR
+end box
+
+[-> StudentListCard : handleViewLessonsButtonClick()
+activate StudentListCard
+
+
+StudentListCard -> MainWindow : setDetailedHeaderBar(firstName)
+activate MainWindow
+
+MainWindow -> DetailedInfoSection : setDetailedHeaderBar(firstName)
+activate DetailedInfoSection
+
+DetailedInfoSection -> DetailedInfoSection : setHeaderBar(firstName)
+activate DetailedInfoSection
+
+DetailedInfoSection --> DetailedInfoSection
+deactivate DetailedInfoSection
+
+DetailedInfoSection --> MainWindow
+deactivate DetailedInfoSection
+
+MainWindow --> StudentListCard
+deactivate MainWindow
+
+alt student has lessons
+ create FilledLessonsContent
+ StudentListCard -> FilledLessonsContent : new FilledLessonsContent(student)
+ activate FilledLessonsContent
+
+ return FilledLessonsContent
+
+else student has no lessons
+ create EmptyLessonsContent
+ StudentListCard -> EmptyLessonsContent : new EmptyLessonsContent(student)
+ activate EmptyLessonsContent
+
+ return EmptyLessonsContent
+
+end
+
+StudentListCard -> MainWindow : setDetailedContent()
+activate MainWindow
+
+MainWindow -> DetailedInfoSection : setDetailedContent()
+activate DetailedInfoSection
+
+DetailedInfoSection -> DetailedInfoSection : setContent()
+activate DetailedInfoSection
+
+DetailedInfoSection --> DetailedInfoSection
+deactivate DetailedInfoSection
+
+DetailedInfoSection --> MainWindow
+deactivate DetailedInfoSection
+
+MainWindow --> StudentListCard
+deactivate MainWindow
+
+[<-- StudentListCard
+deactivate StudentListCard
+
+@enduml
diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml
index 4439108973a..625712f66de 100644
--- a/docs/diagrams/ModelClassDiagram.puml
+++ b/docs/diagrams/ModelClassDiagram.puml
@@ -6,45 +6,67 @@ skinparam classBackgroundColor MODEL_COLOR
Package Model <
>{
Class "<>\nReadOnlyAddressBook" as ReadOnlyAddressBook
+Class "<>\nModel" as model
Class "<>\nReadOnlyUserPrefs" as ReadOnlyUserPrefs
-Class "<>\nModel" as Model
Class AddressBook
Class ModelManager
Class UserPrefs
-Class UniquePersonList
-Class Person
+Class UniqueStudentList
+Class Student
Class Address
Class Email
Class Name
Class Phone
Class Tag
+Class Homework
+Class Exam
+Class Lesson
-}
+Class UniqueHomeworkList
+Class UniqueLessonsList
+Class UniqueExamList
+
+}
Class HiddenOutside #FFFFFF
-HiddenOutside ..> Model
+HiddenOutside .down.|> model
+ModelManager .up.|> model
AddressBook .up.|> ReadOnlyAddressBook
-
-ModelManager .up.|> Model
-Model .right.> ReadOnlyUserPrefs
-Model .left.> ReadOnlyAddressBook
+model .right.> ReadOnlyUserPrefs
+model .left.> ReadOnlyAddressBook
ModelManager -left-> "1" AddressBook
ModelManager -right-> "1" UserPrefs
UserPrefs .up.|> ReadOnlyUserPrefs
-AddressBook *--> "1" UniquePersonList
-UniquePersonList --> "~* all" Person
-Person *--> Name
-Person *--> Phone
-Person *--> Email
-Person *--> Address
-Person *--> "*" Tag
+
+
+AddressBook *--> "1" UniqueStudentList
+UniqueStudentList --> "~* all" Student
+Student *--> "1" Name
+Student *--> "1" Phone
+Student *--> "1" Email
+Student *--> "1" Address
+Student *--> "*" Tag
+
+Student *--> "1" UniqueHomeworkList
+UniqueHomeworkList *--> "*" Homework
+
+Student *--> "1" UniqueLessonsList
+UniqueLessonsList *--> "*" Lesson
+
+Student *--> "1" UniqueExamList
+UniqueExamList *--> "*" Exam
+
Name -[hidden]right-> Phone
-Phone -[hidden]right-> Address
-Address -[hidden]right-> Email
+Phone -[hidden]right-> Email
+Email -[hidden]right-> Address
+
+ModelManager -->"~* filtered" Student
+
-ModelManager -->"~* filtered" Person
@enduml
+
+
diff --git a/docs/diagrams/ProfileClickSequenceDiagram.puml b/docs/diagrams/ProfileClickSequenceDiagram.puml
new file mode 100644
index 00000000000..8792f75bcae
--- /dev/null
+++ b/docs/diagrams/ProfileClickSequenceDiagram.puml
@@ -0,0 +1,60 @@
+@startuml
+!include style.puml
+
+box Ui UI_COLOR_T1
+participant ":StudentLisCard" as StudentListCard UI_COLOR
+participant ":MainWindow" as MainWindow UI_COLOR
+participant ":DetailedInfoSection" as DetailedInfoSection UI_COLOR
+participant ":ProfileContent" as ProfileContent UI_COLOR
+end box
+
+[-> StudentListCard : handleViewProfileButtonClick()
+activate StudentListCard
+
+StudentListCard -> MainWindow : setDetailedHeaderBar(firstName)
+activate MainWindow
+
+MainWindow -> DetailedInfoSection : setDetailedHeaderBar(firstName)
+activate DetailedInfoSection
+
+DetailedInfoSection -> DetailedInfoSection : setHearderBar(firstName)
+activate DetailedInfoSection
+
+DetailedInfoSection --> DetailedInfoSection
+deactivate DetailedInfoSection
+
+DetailedInfoSection --> MainWindow
+deactivate DetailedInfoSection
+
+MainWindow --> StudentListCard
+deactivate MainWindow
+
+create ProfileContent
+StudentListCard -> ProfileContent : new ProfileContent(student)
+activate ProfileContent
+
+ProfileContent -> StudentListCard
+deactivate ProfileContent
+
+StudentListCard -> MainWindow : setDetailedContent(ProfileContent)
+activate MainWindow
+
+MainWindow -> DetailedInfoSection : setDetailedContent(ProfileContent)
+activate DetailedInfoSection
+
+DetailedInfoSection -> DetailedInfoSection : setContent(ProfileContent)
+activate DetailedInfoSection
+
+DetailedInfoSection --> DetailedInfoSection
+deactivate DetailedInfoSection
+
+DetailedInfoSection --> MainWindow
+deactivate DetailedInfoSection
+
+MainWindow --> StudentListCard
+deactivate MainWindow
+
+[<-- StudentListCard
+deactivate StudentListCard
+
+@enduml
diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml
index 760305e0e58..daefcedd5eb 100644
--- a/docs/diagrams/StorageClassDiagram.puml
+++ b/docs/diagrams/StorageClassDiagram.puml
@@ -18,7 +18,7 @@ package "AddressBook Storage" #F4F6F6{
Class "<>\nAddressBookStorage" as AddressBookStorage
Class JsonAddressBookStorage
Class JsonSerializableAddressBook
-Class JsonAdaptedPerson
+Class JsonAdaptedStudent
Class JsonAdaptedTag
}
@@ -37,7 +37,7 @@ Storage -right-|> AddressBookStorage
JsonUserPrefsStorage .up.|> UserPrefsStorage
JsonAddressBookStorage .up.|> AddressBookStorage
JsonAddressBookStorage ..> JsonSerializableAddressBook
-JsonSerializableAddressBook --> "*" JsonAdaptedPerson
-JsonAdaptedPerson --> "*" JsonAdaptedTag
+JsonSerializableAddressBook --> "*" JsonAdaptedStudent
+JsonAdaptedStudent --> "*" JsonAdaptedTag
@enduml
diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml
index 95473d5aa19..ba72bd0c7bb 100644
--- a/docs/diagrams/UiClassDiagram.puml
+++ b/docs/diagrams/UiClassDiagram.puml
@@ -11,10 +11,11 @@ Class UiManager
Class MainWindow
Class HelpWindow
Class ResultDisplay
-Class PersonListPanel
-Class PersonCard
+Class StudentListPanel
+Class StudentCard
Class StatusBarFooter
Class CommandBox
+Class DetailedInfoSection
}
package Model <> {
@@ -32,26 +33,30 @@ UiManager .left.|> Ui
UiManager -down-> "1" MainWindow
MainWindow *-down-> "1" CommandBox
MainWindow *-down-> "1" ResultDisplay
-MainWindow *-down-> "1" PersonListPanel
+MainWindow *-down-> "1" StudentListPanel
MainWindow *-down-> "1" StatusBarFooter
+MainWindow *-down-> "1" DetailedInfoSection
MainWindow --> "0..1" HelpWindow
-PersonListPanel -down-> "*" PersonCard
+StudentListPanel -down-> "*" StudentCard
MainWindow -left-|> UiPart
ResultDisplay --|> UiPart
CommandBox --|> UiPart
-PersonListPanel --|> UiPart
-PersonCard --|> UiPart
+StudentListPanel --|> UiPart
+StudentCard --|> UiPart
StatusBarFooter --|> UiPart
+DetailedInfoSection --|> UiPart
HelpWindow --|> UiPart
-PersonCard ..> Model
+StudentCard ..> Model
+DetailedInfoSection ..> Model
+DetailedInfoSection <.. StudentCard
UiManager -right-> Logic
MainWindow -left-> Logic
-PersonListPanel -[hidden]left- HelpWindow
+StudentListPanel -[hidden]left- HelpWindow
HelpWindow -[hidden]left- CommandBox
CommandBox -[hidden]left- ResultDisplay
ResultDisplay -[hidden]left- StatusBarFooter
diff --git a/docs/diagrams/UiDetailedInfoSectionDiagram.puml b/docs/diagrams/UiDetailedInfoSectionDiagram.puml
new file mode 100644
index 00000000000..b5080ce69ba
--- /dev/null
+++ b/docs/diagrams/UiDetailedInfoSectionDiagram.puml
@@ -0,0 +1,55 @@
+@startuml
+!include style.puml
+skinparam arrowThickness 1.1
+skinparam arrowColor UI_COLOR_T4
+skinparam classBackgroundColor UI_COLOR
+
+package UI <>{
+
+Class "{abstract}\nUiPart" as UiPart
+
+Class DetailedInfoSection
+Class DetailedContent
+Class HeaderBar
+
+Class WelcomeContent
+Class ProfileContent
+Class "{abstract}\nGeneralHomework" as GeneralHomework
+Class "{abstract}\nGeneralLessons" as GeneralLessons
+Class "{abstract}\nGeneralExams" as GeneralExams
+
+Class EmptyHomework
+Class EmptyLessons
+Class EmptyExams
+Class FilledHomework
+Class FilledLessons
+Class FilledExams
+}
+
+package Model <> {
+Class HiddenModel #FFFFFF
+}
+
+Class HiddenOutside #FFFFFF
+
+DetailedInfoSection *-down-> "1" HeaderBar
+DetailedInfoSection *-down-> "1" DetailedContent
+
+DetailedInfoSection --|> UiPart
+HeaderBar --|> UiPart
+DetailedContent --|> UiPart
+
+DetailedInfoSection ..> Model
+WelcomeContent -down-|> DetailedContent
+ProfileContent -down-|> DetailedContent
+GeneralHomework -up-|> DetailedContent
+GeneralLessons -up-|> DetailedContent
+GeneralExams -up-|> DetailedContent
+EmptyExams -up-|> GeneralExams
+EmptyHomework -down-|> GeneralHomework
+EmptyLessons -up-|> GeneralLessons
+FilledExams -up-|> GeneralExams
+FilledHomework -down-|> GeneralHomework
+FilledLessons -up-|> GeneralLessons
+
+@enduml
diff --git a/docs/diagrams/UiFilledExamsContent.puml b/docs/diagrams/UiFilledExamsContent.puml
new file mode 100644
index 00000000000..74f7a94831c
--- /dev/null
+++ b/docs/diagrams/UiFilledExamsContent.puml
@@ -0,0 +1,61 @@
+@startuml
+!include style.puml
+skinparam arrowThickness 1.1
+skinparam arrowColor UI_COLOR_T4
+skinparam classBackgroundColor UI_COLOR
+
+package UI <>{
+Class "<>\nUi" as Ui
+Class "{abstract}\nUiPart" as UiPart
+Class UiManager
+Class MainWindow
+
+Class DetailedInfoSection
+Class DetailedContent
+Class HeaderBar
+
+Class GeneralExamsContent
+Class FilledExamsContent
+Class PastExamsListPanel
+Class UpcomingExamsListPanel
+Class ExamCard
+}
+
+package Model <> {
+Class HiddenModel #FFFFFF
+}
+
+package Logic <> {
+Class HiddenLogic #FFFFFF
+}
+
+Class HiddenOutside #FFFFFF
+HiddenOutside ..> Ui
+
+
+UiManager .left.|> Ui
+UiManager -down-> "1" MainWindow
+MainWindow *-down-> "1" DetailedInfoSection
+DetailedInfoSection *-down-> "1" HeaderBar
+DetailedInfoSection *-down-> "1" DetailedContent
+FilledExamsContent *-down-> "1" PastExamsListPanel
+FilledExamsContent *-down-> "1" UpcomingExamsListPanel
+PastExamsListPanel -down-> "*" ExamCard
+UpcomingExamsListPanel -down-> "*" ExamCard
+
+MainWindow -left-|> UiPart
+DetailedInfoSection --|> UiPart
+HeaderBar --|> UiPart
+DetailedContent --|> UiPart
+PastExamsListPanel --|> UiPart
+UpcomingExamsListPanel --|> UiPart
+ExamCard --|> UiPart
+
+DetailedInfoSection ..> Model
+UiManager -right-> Logic
+MainWindow -left-> Logic
+GeneralExamsContent -up-|> DetailedContent
+FilledExamsContent -up-|> GeneralExamsContent
+
+MainWindow -[hidden]-|> UiPart
+@enduml
diff --git a/docs/diagrams/UiFilledHomeworkContent.puml b/docs/diagrams/UiFilledHomeworkContent.puml
new file mode 100644
index 00000000000..13ffe4f4227
--- /dev/null
+++ b/docs/diagrams/UiFilledHomeworkContent.puml
@@ -0,0 +1,60 @@
+@startuml
+!include style.puml
+skinparam arrowThickness 1.1
+skinparam arrowColor UI_COLOR_T4
+skinparam classBackgroundColor UI_COLOR
+
+package UI <>{
+Class "<>\nUi" as Ui
+Class "{abstract}\nUiPart" as UiPart
+Class UiManager
+Class MainWindow
+
+Class DetailedInfoSection
+Class DetailedContent
+Class HeaderBar
+
+Class GeneralHomeworkContent
+Class FilledHomeworkContent
+Class HomeworkListPanel
+Class HomeworkCard
+Class HomeworkPieChart
+}
+
+package Model <> {
+Class HiddenModel #FFFFFF
+}
+
+package Logic <> {
+Class HiddenLogic #FFFFFF
+}
+
+Class HiddenOutside #FFFFFF
+HiddenOutside ..> Ui
+
+
+UiManager .left.|> Ui
+UiManager -down-> "1" MainWindow
+MainWindow *-down-> "1" DetailedInfoSection
+DetailedInfoSection *-down-> "1" HeaderBar
+DetailedInfoSection *-down-> "1" DetailedContent
+FilledHomeworkContent *-down-> "1" HomeworkListPanel
+FilledHomeworkContent *-down-> "1" HomeworkPieChart
+HomeworkListPanel -down-> "*" HomeworkCard
+
+MainWindow -left-|> UiPart
+DetailedInfoSection --|> UiPart
+HeaderBar --|> UiPart
+DetailedContent --|> UiPart
+HomeworkListPanel --|> UiPart
+HomeworkCard --|> UiPart
+HomeworkPieChart --|> UiPart
+
+DetailedInfoSection ..> Model
+UiManager -right-> Logic
+MainWindow -left-> Logic
+GeneralHomeworkContent -up-|> DetailedContent
+FilledHomeworkContent -up-|> GeneralHomeworkContent
+
+MainWindow -[hidden]-|> UiPart
+@enduml
diff --git a/docs/diagrams/UiFilledLessonsContent.puml b/docs/diagrams/UiFilledLessonsContent.puml
new file mode 100644
index 00000000000..97cf8f30831
--- /dev/null
+++ b/docs/diagrams/UiFilledLessonsContent.puml
@@ -0,0 +1,61 @@
+@startuml
+!include style.puml
+skinparam arrowThickness 1.1
+skinparam arrowColor UI_COLOR_T4
+skinparam classBackgroundColor UI_COLOR
+
+package UI <>{
+Class "<>\nUi" as Ui
+Class "{abstract}\nUiPart" as UiPart
+Class UiManager
+Class MainWindow
+
+Class DetailedInfoSection
+Class DetailedContent
+Class HeaderBar
+
+Class GeneralLessonsContent
+Class FilledLessonsContent
+Class PastLessonsListPanel
+Class UpcomingLessonsListPanel
+Class LessonsCard
+}
+
+package Model <> {
+Class HiddenModel #FFFFFF
+}
+
+package Logic <> {
+Class HiddenLogic #FFFFFF
+}
+
+Class HiddenOutside #FFFFFF
+HiddenOutside ..> Ui
+
+
+UiManager .left.|> Ui
+UiManager -down-> "1" MainWindow
+MainWindow *-down-> "1" DetailedInfoSection
+DetailedInfoSection *-down-> "1" HeaderBar
+DetailedInfoSection *-down-> "1" DetailedContent
+FilledLessonsContent *-down-> "1" PastLessonsListPanel
+FilledLessonsContent *-down-> "1" UpcomingLessonsListPanel
+PastLessonsListPanel -down-> "*" LessonsCard
+UpcomingLessonsListPanel -down-> "*" LessonsCard
+
+MainWindow -left-|> UiPart
+DetailedInfoSection --|> UiPart
+HeaderBar --|> UiPart
+DetailedContent --|> UiPart
+PastLessonsListPanel --|> UiPart
+UpcomingLessonsListPanel --|> UiPart
+LessonsCard --|> UiPart
+
+DetailedInfoSection ..> Model
+UiManager -right-> Logic
+MainWindow -left-> Logic
+GeneralLessonsContent -up-|> DetailedContent
+FilledLessonsContent -up-|> GeneralLessonsContent
+
+MainWindow -[hidden]-|> UiPart
+@enduml
diff --git a/docs/diagrams/UiProfileContent.puml b/docs/diagrams/UiProfileContent.puml
new file mode 100644
index 00000000000..1bb7fa30365
--- /dev/null
+++ b/docs/diagrams/UiProfileContent.puml
@@ -0,0 +1,56 @@
+@startuml
+!include style.puml
+skinparam arrowThickness 1.1
+skinparam arrowColor UI_COLOR_T4
+skinparam classBackgroundColor UI_COLOR
+
+package UI <>{
+Class "<>\nUi" as Ui
+Class "{abstract}\nUiPart" as UiPart
+Class UiManager
+Class MainWindow
+
+Class DetailedInfoSection
+Class DetailedContent
+Class HeaderBar
+
+Class ProfileContent
+}
+
+package Model <> {
+Class Name MODEL_COLOR
+Class Email MODEL_COLOR
+Class Phone MODEL_COLOR
+Class Address MODEL_COLOR
+}
+
+package Logic <> {
+Class HiddenLogic #FFFFFF
+}
+
+Class HiddenOutside #FFFFFF
+HiddenOutside ..> Ui
+
+
+UiManager .left.|> Ui
+UiManager -down-> "1" MainWindow
+MainWindow *-down-> "1" DetailedInfoSection
+DetailedInfoSection *-down-> "1" HeaderBar
+DetailedInfoSection *-down-> "1" DetailedContent
+ProfileContent *-down-> "1" Name
+ProfileContent *-down-> "1" Email
+ProfileContent *-down-> "1" Phone
+ProfileContent *-down-> "1" Address
+
+MainWindow -left-|> UiPart
+DetailedInfoSection --|> UiPart
+HeaderBar --|> UiPart
+DetailedContent --|> UiPart
+
+DetailedInfoSection ..> Model
+UiManager -right-> Logic
+MainWindow -left-> Logic
+ProfileContent -up-|> DetailedContent
+
+MainWindow -[hidden]-|> UiPart
+@enduml
diff --git a/docs/diagrams/UiQuickAccessButton.png b/docs/diagrams/UiQuickAccessButton.png
new file mode 100644
index 00000000000..bd07edc8e52
Binary files /dev/null and b/docs/diagrams/UiQuickAccessButton.png differ
diff --git a/docs/diagrams/UiQuickAccessButton.puml b/docs/diagrams/UiQuickAccessButton.puml
new file mode 100644
index 00000000000..72aa90f4f23
--- /dev/null
+++ b/docs/diagrams/UiQuickAccessButton.puml
@@ -0,0 +1,35 @@
+@startuml
+!include style.puml
+skinparam arrowThickness 1.1
+skinparam arrowColor UI_COLOR_T4
+skinparam classBackgroundColor UI_COLOR
+
+package UI <>{
+Class "{abstract}\nUiPart" as UiPart
+Class StudentListPanel
+Class StudentCard
+Class ProfileButton
+Class HomeworkButton
+Class LessonsButton
+Class ExamsButton
+}
+
+package Model <> {
+Class HiddenModel #FFFFFF
+}
+
+StudentCard *-down-> "1" ProfileButton
+StudentCard *-down-> "1" HomeworkButton
+StudentCard *-down-> "1" LessonsButton
+StudentCard *-down-> "1" ExamsButton
+
+StudentListPanel -down-> "*" StudentCard
+StudentListPanel --|> UiPart
+StudentCard --|> UiPart
+ProfileButton --|> UiPart
+HomeworkButton --|> UiPart
+LessonsButton --|> UiPart
+ExamsButton --|> UiPart
+
+StudentCard ..> Model
+@enduml
diff --git a/docs/diagrams/tracing/LogicSequenceDiagram.puml b/docs/diagrams/tracing/LogicSequenceDiagram.puml
index fdcbe1c0ccc..56fc24eadcd 100644
--- a/docs/diagrams/tracing/LogicSequenceDiagram.puml
+++ b/docs/diagrams/tracing/LogicSequenceDiagram.puml
@@ -13,7 +13,7 @@ create ecp
abp -> ecp
abp -> ecp ++: parse(arguments)
create ec
-ecp -> ec ++: index, editPersonDescriptor
+ecp -> ec ++: index, editStudentDescriptor
ec --> ecp --
ecp --> abp --: command
abp --> logic --: command
diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png
index 2f1346869d0..22fbc2ae28a 100644
Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ
diff --git a/docs/images/ClickButtonActivityDiagram.png b/docs/images/ClickButtonActivityDiagram.png
new file mode 100644
index 00000000000..8516b62c943
Binary files /dev/null and b/docs/images/ClickButtonActivityDiagram.png differ
diff --git a/docs/images/CreateLessonAD.png b/docs/images/CreateLessonAD.png
new file mode 100644
index 00000000000..927d1a8326d
Binary files /dev/null and b/docs/images/CreateLessonAD.png differ
diff --git a/docs/images/CreateLessonSequenceDiagram.png b/docs/images/CreateLessonSequenceDiagram.png
new file mode 100644
index 00000000000..ce45d15b2dd
Binary files /dev/null and b/docs/images/CreateLessonSequenceDiagram.png differ
diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png
index fa327b39618..85bb79db9b5 100644
Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ
diff --git a/docs/images/Exams.jpg b/docs/images/Exams.jpg
new file mode 100644
index 00000000000..4a8ccdd70f2
Binary files /dev/null and b/docs/images/Exams.jpg differ
diff --git a/docs/images/ExamsClickSequenceDiagram.png b/docs/images/ExamsClickSequenceDiagram.png
new file mode 100644
index 00000000000..40868f235e8
Binary files /dev/null and b/docs/images/ExamsClickSequenceDiagram.png differ
diff --git a/docs/images/GUI.jpg b/docs/images/GUI.jpg
new file mode 100644
index 00000000000..91f17cd5890
Binary files /dev/null and b/docs/images/GUI.jpg differ
diff --git a/docs/images/Homework.png b/docs/images/Homework.png
new file mode 100644
index 00000000000..6464d69470a
Binary files /dev/null and b/docs/images/Homework.png differ
diff --git a/docs/images/HomeworkClickSequenceDiagram.png b/docs/images/HomeworkClickSequenceDiagram.png
new file mode 100644
index 00000000000..d59694300cb
Binary files /dev/null and b/docs/images/HomeworkClickSequenceDiagram.png differ
diff --git a/docs/images/Lessons.jpg b/docs/images/Lessons.jpg
new file mode 100644
index 00000000000..4c400824878
Binary files /dev/null and b/docs/images/Lessons.jpg differ
diff --git a/docs/images/LessonsClickSequenceDiagram.png b/docs/images/LessonsClickSequenceDiagram.png
new file mode 100644
index 00000000000..90a2af539ca
Binary files /dev/null and b/docs/images/LessonsClickSequenceDiagram.png differ
diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png
index 04070af60d8..c0eb994fbaf 100644
Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ
diff --git a/docs/images/Profile.jpg b/docs/images/Profile.jpg
new file mode 100644
index 00000000000..2d6ba191561
Binary files /dev/null and b/docs/images/Profile.jpg differ
diff --git a/docs/images/ProfileClickSequenceDiagram.png b/docs/images/ProfileClickSequenceDiagram.png
new file mode 100644
index 00000000000..a8dfcb85114
Binary files /dev/null and b/docs/images/ProfileClickSequenceDiagram.png differ
diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png
index 2533a5c1af0..a965cff6c11 100644
Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ
diff --git a/docs/images/Thumbnail.png b/docs/images/Thumbnail.png
new file mode 100644
index 00000000000..1e54156c1d8
Binary files /dev/null and b/docs/images/Thumbnail.png differ
diff --git a/docs/images/Ui.png b/docs/images/Ui.png
index 5bd77847aa2..be7a87169cc 100644
Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ
diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png
index 785e04dbab4..56f15f3bba2 100644
Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ
diff --git a/docs/images/UiDetailedInfoSectionDiagram.png b/docs/images/UiDetailedInfoSectionDiagram.png
new file mode 100644
index 00000000000..af448da26b3
Binary files /dev/null and b/docs/images/UiDetailedInfoSectionDiagram.png differ
diff --git a/docs/images/UiFilledExamsContent.png b/docs/images/UiFilledExamsContent.png
new file mode 100644
index 00000000000..37175fadff2
Binary files /dev/null and b/docs/images/UiFilledExamsContent.png differ
diff --git a/docs/images/UiFilledHomeworkContent.png b/docs/images/UiFilledHomeworkContent.png
new file mode 100644
index 00000000000..9f3ee02e3c2
Binary files /dev/null and b/docs/images/UiFilledHomeworkContent.png differ
diff --git a/docs/images/UiFilledLessonsContent.png b/docs/images/UiFilledLessonsContent.png
new file mode 100644
index 00000000000..5b724717cec
Binary files /dev/null and b/docs/images/UiFilledLessonsContent.png differ
diff --git a/docs/images/UiProfileContent.png b/docs/images/UiProfileContent.png
new file mode 100644
index 00000000000..3faf37612f8
Binary files /dev/null and b/docs/images/UiProfileContent.png differ
diff --git a/docs/images/UiQuickAccessButton.png b/docs/images/UiQuickAccessButton.png
new file mode 100644
index 00000000000..633a7401e71
Binary files /dev/null and b/docs/images/UiQuickAccessButton.png differ
diff --git a/docs/images/delete-homework.jpg b/docs/images/delete-homework.jpg
new file mode 100644
index 00000000000..aacc0385748
Binary files /dev/null and b/docs/images/delete-homework.jpg differ
diff --git a/docs/images/delete-lesson.jpg b/docs/images/delete-lesson.jpg
new file mode 100644
index 00000000000..fccba34a462
Binary files /dev/null and b/docs/images/delete-lesson.jpg differ
diff --git a/docs/images/delete.png b/docs/images/delete.png
new file mode 100644
index 00000000000..f9471a951a9
Binary files /dev/null and b/docs/images/delete.png differ
diff --git a/docs/images/entireList1.jpg b/docs/images/entireList1.jpg
new file mode 100644
index 00000000000..9fc7ff60121
Binary files /dev/null and b/docs/images/entireList1.jpg differ
diff --git a/docs/images/entireList2.jpg b/docs/images/entireList2.jpg
new file mode 100644
index 00000000000..87cdaf21aa2
Binary files /dev/null and b/docs/images/entireList2.jpg differ
diff --git a/docs/images/entireList3.jpg b/docs/images/entireList3.jpg
new file mode 100644
index 00000000000..cdfc5f51119
Binary files /dev/null and b/docs/images/entireList3.jpg differ
diff --git a/docs/images/fahim-tazz.png b/docs/images/fahim-tazz.png
new file mode 100644
index 00000000000..ba30d7f71b1
Binary files /dev/null and b/docs/images/fahim-tazz.png differ
diff --git a/docs/images/mark-homework.jpg b/docs/images/mark-homework.jpg
new file mode 100644
index 00000000000..99709b2e10f
Binary files /dev/null and b/docs/images/mark-homework.jpg differ
diff --git a/docs/images/nbqian.png b/docs/images/nbqian.png
new file mode 100644
index 00000000000..a5a7a0895f9
Binary files /dev/null and b/docs/images/nbqian.png differ
diff --git a/docs/images/new-homework.jpg b/docs/images/new-homework.jpg
new file mode 100644
index 00000000000..2ea84e6c040
Binary files /dev/null and b/docs/images/new-homework.jpg differ
diff --git a/docs/images/new-lesson.jpg b/docs/images/new-lesson.jpg
new file mode 100644
index 00000000000..b35e8303ff2
Binary files /dev/null and b/docs/images/new-lesson.jpg differ
diff --git a/docs/images/new-student.png b/docs/images/new-student.png
new file mode 100644
index 00000000000..dc5c65b1730
Binary files /dev/null and b/docs/images/new-student.png differ
diff --git a/docs/images/szejiancheng.png b/docs/images/szejiancheng.png
new file mode 100644
index 00000000000..31b23c11fce
Binary files /dev/null and b/docs/images/szejiancheng.png differ
diff --git a/docs/images/unmark-homework.jpg b/docs/images/unmark-homework.jpg
new file mode 100644
index 00000000000..a06a540a49a
Binary files /dev/null and b/docs/images/unmark-homework.jpg differ
diff --git a/docs/images/update-homework.jpg b/docs/images/update-homework.jpg
new file mode 100644
index 00000000000..1e6e49ceb20
Binary files /dev/null and b/docs/images/update-homework.jpg differ
diff --git a/docs/images/update-info.png b/docs/images/update-info.png
new file mode 100644
index 00000000000..02365a8b9b0
Binary files /dev/null and b/docs/images/update-info.png differ
diff --git a/docs/images/update-lesson.jpg b/docs/images/update-lesson.jpg
new file mode 100644
index 00000000000..0a49a8070bf
Binary files /dev/null and b/docs/images/update-lesson.jpg differ
diff --git a/docs/images/view-exam.png b/docs/images/view-exam.png
new file mode 100644
index 00000000000..439698d1372
Binary files /dev/null and b/docs/images/view-exam.png differ
diff --git a/docs/images/view-homework.jpg b/docs/images/view-homework.jpg
new file mode 100644
index 00000000000..23f6be4e449
Binary files /dev/null and b/docs/images/view-homework.jpg differ
diff --git a/docs/images/view-lesson.jpg b/docs/images/view-lesson.jpg
new file mode 100644
index 00000000000..a3dc6c14b65
Binary files /dev/null and b/docs/images/view-lesson.jpg differ
diff --git a/docs/images/view-profile.png b/docs/images/view-profile.png
new file mode 100644
index 00000000000..4934b990163
Binary files /dev/null and b/docs/images/view-profile.png differ
diff --git a/docs/images/yufannnn.png b/docs/images/yufannnn.png
new file mode 100644
index 00000000000..ef9567c17dd
Binary files /dev/null and b/docs/images/yufannnn.png differ
diff --git a/docs/img.png b/docs/img.png
new file mode 100644
index 00000000000..439698d1372
Binary files /dev/null and b/docs/img.png differ
diff --git a/docs/index.md b/docs/index.md
index 7601dbaad0d..fdf07d1d1e1 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,18 +1,18 @@
---
layout: page
-title: AddressBook Level-3
+title: TutorPro
---
-[](https://github.com/se-edu/addressbook-level3/actions)
-[](https://codecov.io/gh/se-edu/addressbook-level3)
+[](https://github.com/AY2223S2-CS2103T-W13-4/tp/actions/workflows/gradle.yml)

-**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface).
-
-* If you are interested in using AddressBook, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start).
-* If you are interested about developing AddressBook, the [**Developer Guide**](DeveloperGuide.html) is a good place to start.
+
+* TutorPro is a desktop application for managing contacts of students and classes, optimised for use via a Command Line Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI).
+* For the detailed documentation of this project, see the **[TutorPro Website](https://ay2223s2-cs2103t-w13-4.github.io/tp/)**.
+* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info.
+* This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org).
**Acknowledgements**
diff --git a/docs/team/fahim-tazz.md b/docs/team/fahim-tazz.md
new file mode 100644
index 00000000000..5d8b15a1a0f
--- /dev/null
+++ b/docs/team/fahim-tazz.md
@@ -0,0 +1,47 @@
+---
+layout: page
+title: Muhammad Fahim Tajwar's Project Portfolio Page
+---
+
+### Project: TutorPro
+
+TutorPro is a desktop app designed to help private tutors manage their student information effectively.
+With TutorPro, tutors can easily keep track of their students' details, lesson plans, homeworks, and exams, all in one place.
+TutorPro uses a combination of GUI and CLI to provide an aesthetically-pleasing UI for viewing student's information, while promising the
+speed and control of a CLI. Whether you're managing a handful of students or hundreds,
+
+TutorPro can help you streamline your workflow and make your tutoring experience much more efficient.
+
+Given below are my contributions to the project.
+
+
+* **New Feature**: Track School name and Grade level in Student's profile.
+ - What it does: Allows user to add an optional School name and Grade level.
+ - Justification: This feature enables tutors to tag students with their School name and Grade Level, for easy reference on the student list.
+ - Highlights: This feature can allow future enhancements for batch processing, such as adding a homework to all students of a particular grade level.
+
+* **Code contributed**: [Reposense Link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=fahim-tazz&breakdown=true)
+
+
+* **Project management**:
+ * Managed release v1.3
+ * Renamed instances of AB-3 to TutorPro in Github pages.
+ * Defined Use Cases
+ * Designed a Product pitch, with a formal Business Plan (for CS2101).
+ * Create PR to the upstream Repo
+
+* **Enhancements to new Features**:
+ * Added unit testing for most of the newly created Parser and Command classes.
+
+* **Contributions to the UG**:
+ * Created new User Guide for v1.2.
+ * Finalized and Polished User Guide for v1.3 and v1.4.
+
+* **Contributions to the DG**:
+ * User Stories
+ * Non-Functional Requirements
+ * Use cases
+
+* **Community**:
+ * PRs reviewed (with non-trivial review comments): 10
+ * PRs authored : 26
diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md
deleted file mode 100644
index 773a07794e2..00000000000
--- a/docs/team/johndoe.md
+++ /dev/null
@@ -1,46 +0,0 @@
----
-layout: page
-title: John Doe's Project Portfolio Page
----
-
-### Project: AddressBook Level 3
-
-AddressBook - Level 3 is a desktop address book application used for teaching Software Engineering principles. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
-
-Given below are my contributions to the project.
-
-* **New Feature**: Added the ability to undo/redo previous commands.
- * What it does: allows the user to undo all previous commands one at a time. Preceding undo commands can be reversed by using the redo command.
- * Justification: This feature improves the product significantly because a user can make mistakes in commands and the app should provide a convenient way to rectify them.
- * Highlights: This enhancement affects existing commands and commands to be added in future. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing commands.
- * Credits: *{mention here if you reused any code/ideas from elsewhere or if a third-party library is heavily used in the feature so that a reader can make a more accurate judgement of how much effort went into the feature}*
-
-* **New Feature**: Added a history command that allows the user to navigate to previous commands using up/down keys.
-
-* **Code contributed**: [RepoSense link]()
-
-* **Project management**:
- * Managed releases `v1.3` - `v1.5rc` (3 releases) on GitHub
-
-* **Enhancements to existing features**:
- * Updated the GUI color scheme (Pull requests [\#33](), [\#34]())
- * Wrote additional tests for existing features to increase coverage from 88% to 92% (Pull requests [\#36](), [\#38]())
-
-* **Documentation**:
- * User Guide:
- * Added documentation for the features `delete` and `find` [\#72]()
- * Did cosmetic tweaks to existing documentation of features `clear`, `exit`: [\#74]()
- * Developer Guide:
- * Added implementation details of the `delete` feature.
-
-* **Community**:
- * PRs reviewed (with non-trivial review comments): [\#12](), [\#32](), [\#19](), [\#42]()
- * Contributed to forum discussions (examples: [1](), [2](), [3](), [4]())
- * Reported bugs and suggestions for other teams in the class (examples: [1](), [2](), [3]())
- * Some parts of the history feature I added was adopted by several other class mates ([1](), [2]())
-
-* **Tools**:
- * Integrated a third party library (Natty) to the project ([\#42]())
- * Integrated a new Github plugin (CircleCI) to the team repo
-
-* _{you can add/remove categories in the list above}_
diff --git a/docs/team/nbqian.md b/docs/team/nbqian.md
new file mode 100644
index 00000000000..0d6cf343799
--- /dev/null
+++ b/docs/team/nbqian.md
@@ -0,0 +1,60 @@
+---
+layout: page
+title: Niu Boqian's Project Portfolio Page
+---
+
+### Project: TutorPro
+
+TutorPro is a desktop app designed to help private tutors manage their student information effectively. With TutorPro, tutors can easily keep track of their students' addresses, contact details, lessons, homework, and progress, all in one place. Its ability to keep track of a student's lessons and exams makes it especially useful for tutors to plan their lessons to prepare his/her students for their exams.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Lesson Feature and Lesson Related commands (new-lesson, view-lesson, delete-lesson, update-lesson)
+ * What it does: Allows the user to add, view, delete, and update lessons for a student.
+ * Justification: This feature improves the product significantly because a tutor can manage the lessons of a student more efficiently.
+ * Highlights:
+ * The commands are simple to use. The view-lesson command is especially flexible as the user can filter by date, name of student, subject, or any combination of these.
+ * I created a storage for the lesson list, so that now each json file of a student will have a lesson list.
+ * I created relevant helper classes to help with the implementation of the lesson feature. This include various predicate and exception classes.
+ * I create test classes for various classes in the lesson feature.
+ * Credits: N/A
+
+* **New Feature**: Exam Related Commands (view-exam, delete-exam, update-exam)
+ * What it does: Allows the user to view, delete, and update exams for a student.
+ * Justification: This feature improves the product significantly because a tutor can be aware of the exams of a student and can plan the lessons accordingly.
+ * Highlights:
+ * The commands are simple to use. The view-exam command is especially flexible as the user can filter by date, name of student, exam name, or any combination of these.
+ * I created a storage for the exam list, so that now each json file of a student will have a exam list.
+ * I created relevant helper classes to help with the implementation of the exam feature. This include various predicate and exception classes.
+ * Credits: N/A
+
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=nbqian&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+
+* **Project management**:
+ * Created and assigned issues to team members on GitHub and kept track of their progress
+ * Created labels and categorized issues on GitHub
+* **Enhancements to existing features**:
+ * Changed the find, edit, delete command such that they now require the user to use prefixes to specify the fields to search for, edit, or delete
+ * Changed the add and edit command such that when a new student is created or an existing student is modified, his/her name cannot be part of any existing students' names, and vice versa.
+ * Added lesson unique lists in the application, added, and updated relevant methods in a logical model, storage, and other classes ot fit the change.
+* **Documentation**:
+ * User Guide:
+ * Added documentation for the features `new-lesson`, `view-lesson`, `delete-lesson`, `update-lesson`, `new-exam`, `view-exam`, `delete-exam`, `update-exam`: [#184](https://github.com/AY2223S2-CS2103T-W13-4/tp/pull/184)
+ * Added Glossary, List of Commands, and List of Prefixes as 3 separate tables: [#117](https://github.com/AY2223S2-CS2103T-W13-4/tp/pull/117)
+ * Developer Guide:
+ * Added implementation for `CreateLessonCommand` with Sequence Diagram and Activity Diagram [#89](https://github.com/AY2223S2-CS2103T-W13-4/tp/pull/89)
+ * Added Glossary and changed the table of contents [#198](https://github.com/AY2223S2-CS2103T-W13-4/tp/pull/198)
+
+* **Community**:
+ * Reported bugs and suggestions for other teams in the class:
+ [#1](https://github.com/NBQian/ped/issues/1)
+ [#2](https://github.com/NBQian/ped/issues/2)
+ [#3](https://github.com/NBQian/ped/issues/3)
+ [#4](https://github.com/NBQian/ped/issues/4)
+ [#5](https://github.com/NBQian/ped/issues/5)
+ [#6](https://github.com/NBQian/ped/issues/6)
+ [#7](https://github.com/NBQian/ped/issues/7)
+ [#8](https://github.com/NBQian/ped/issues/8)
+* **Tools**:
+* Use PlantUML to add more UML diagrams in the developer guide.
diff --git a/docs/team/szejiancheng.md b/docs/team/szejiancheng.md
new file mode 100644
index 00000000000..c51a579f8d0
--- /dev/null
+++ b/docs/team/szejiancheng.md
@@ -0,0 +1,42 @@
+---
+layout: page
+title: Sze Jian Cheng's Project Portfolio Page
+---
+
+### Project: TutorPro
+
+TutorPro is a desktop app designed to help private tutors manage their student information effectively. With TutorPro, tutors can easily keep track of their students' addresses, contact details, lessons, homework, and progress, all in one place. This app is optimised for use via a Graphical User Interface (GUI), allowing tutors to interact with the app using easy-to-understand buttons and menus. However, TutorPro also provides a Command Line Interface (CLI) for those who prefer a faster way of getting things done. Whether you're managing a handful of students or hundreds, TutorPro can help you streamline your workflow and make your tutoring experience more efficient.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Adds the ability to track exams for a particular student.
+ - What it does: Allows user to add, manipulate, and delete exams of a particular student, including details such as
+ the name of the exam, its start time, its end time, weightage, and grade.
+ - Justification: This feature enables tutors to effectively track of upcoming and completed exams.
+ - Highlights: This feature required additional classes for parsing and data storage that needed to be
+ implemented in order to function with the existing project structure.
+ (see [exam parsers](https://github.com/AY2223S2-CS2103T-W13-4/tp/tree/master/src/main/java/seedu/address/logic/parser/exam) and
+ [exam commands](https://github.com/AY2223S2-CS2103T-W13-4/tp/tree/master/src/main/java/seedu/address/logic/commands/exam).)
+ - Credits: @nbqian for providing the basic skeleton for similar student attributes (see lesson-related classes)
+
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=szejiancheng&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+
+* **Project management**:
+ * Managed releases `v1.2`-`v1.4` on Github
+ * Bug reporting for all versions (4 releases)
+
+* **Documentation**:
+ * User Guide:
+ * Authored exam-related commands in user guide
+ * Developer Guide:
+ * Brainstormed user stories and use-cases during conceptualization phase.
+ * UML diagrams:
+ * contributed to tweaking of existing UML diagrams using PlantUML
+
+* **Community**:
+ * PRs reviewed : 7
+ * PRs authored : 23
+
+* **Others**:
+ * Created professional demo video with voiceover for product presentation using Davinci Resolve
diff --git a/docs/team/yufannnn.md b/docs/team/yufannnn.md
new file mode 100644
index 00000000000..82cb29dd3d1
--- /dev/null
+++ b/docs/team/yufannnn.md
@@ -0,0 +1,74 @@
+---
+layout: page
+title: Zhu Yufan's Project Portfolio Page
+---
+
+### Project: TutorPro
+
+TutorPro is a desktop app designed to help private tutors manage their student information effectively.
+Offers both a Command Line Interface (CLI) and a Graphical User Interface (GUI) for managing student information.
+The CLI provides a fast and efficient way to keep track of students' details, homework, lessons, and exams,
+while the GUI offers an intuitive visual interface.
+Whether tutoring a few or many students, TutorPro can streamline workflow.
+
+Given below are my contributions to the project.
+
+* **Idea**: Came up with the idea of enhancing AB3 to a student management application for private tutors.
+
+* **New Feature**: Homework Feature and Six New Homework Related commands
+ - what it does: The Homework Feature and Homework related commands are designed to help tutors manage their student homework tasks more efficiently. It allows tutors to create, view, update, and delete homework tasks using a set of commands. The new commands include: new-homework, view-homework, delete-homework, make-homework, unmark-homework, and update-homework commands.
+ - Justification: The Homework Feature is aimed at addressing the common problem of managing multiple homework tasks for private tutors. With this feature, users can easily keep track of their students' assignments and deadlines, prioritize their work, and avoid missing important submission dates.
+ - Highlights: The Homework Feature is easy to use and can be accessed through a set of simple commands. The feature helps tutors stay organized and on top of the students' homework tasks, reducing the risk of missing important deadlines.
+ - Credit: N/A
+
+* **New Feature**: Redesigned GUI and Detailed Information Section
+ - what it does: The new feature redesigns the UI of the application and adds a Detailed Information Section that provides more specific information about each student. The Detailed Information Section includes a Profile Page, a Homework Page, a Lesson Page, and an Exam Page. Each page is accessible through quick access buttons on the student card.
+ - Justification: The redesigned UI and Detailed Information Section are aimed at improving the user experience of the application, making the application more aesthetically appealing. It provides a more user-friendly interface for tutors to manage their students' information and allows for more efficient navigation between different types of information.
+ - Highlights: The Detailed Information Section provides more specific information about each student, including their personal information, homework assignments, past and upcoming lessons, and past and upcoming exams.
+ - Credit: N/A
+
+* **New Feature**: New Feature: Quick Access Button on Each Student Card
+ - What it does: The Quick Access Button is a new feature that provides a more user-friendly interface for tutors to access student-specific information. It adds four buttons on each student card, including profile, homework, lessons, and exams. By clicking on any of these buttons, the user can quickly access the corresponding information for the selected student.
+ - Justification: The Quick Access Button feature is aimed at improving the user experience for tutors who need to manage multiple students' profiles, homework, lessons, and exams. It helps to streamline the process of accessing student-specific information and eliminates the need for users to search through long lists to find the information they need.
+ - Highlights: The Quick Access Button is easy to use and provides users with quick access to the information they need. The feature is intuitive and visually appealing, making it easy for users to navigate through the app and access the relevant information.
+ - Credit: N/A
+
+* **Code contributed**: [RepoSense Link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=Yufannnn&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=false&tabOpen=true&tabType=zoom&zA=Yufannnn&zR=AY2223S2-CS2103T-W13-4%2Ftp%5Bmaster%5D&zACS=247.67299412915852&zS=2023-02-17&zFS=&zU=2023-04-05&zMG=false&zFTF=commit&zFGS=groupByRepos&zFR=false)
+
+* **Project management**:
+ * Set up Team Repo
+ * Updated Workflow: [#1](https://github.com/AY2223S2-CS2103T-W13-4/tp/pull/1)
+ * Created and assigned issues to team members on GitHub and kept track of their progress
+ * Created labels and categorized issues on GitHub
+ * Created and manages milestones on GitHub, changed their due dates, and closed them to wrap-up.
+ * Managed releases `v1.3.Trail` on GitHub ([Link to v1.3 trail release](https://github.com/AY2223S2-CS2103T-W13-4/tp/releases/tag/v1.3.trial))
+
+* **Enhancements to existing features**:
+ * Refactor the Person model to a Student model and added relevant methods in a logic model, storage and other class to fit the change: [#1](https://github.com/AY2223S2-CS2103T-W13-4/tp/pull/1)
+ * Added homework unique lists in the application, added, and updated relevant methods in logic, model, storage and other class to fit the changes: [#13](https://github.com/AY2223S2-CS2103T-W13-4/tp/pull/13)
+ * Redesign the GUI to make it more aesthetically appealing and add more sections on the GUI: [#56](https://github.com/AY2223S2-CS2103T-W13-4/tp/pull/13)
+
+* **Documentation**:
+ * User Guide:
+ * Updated the `Quick Start` section and update TOC: [#170](https://github.com/AY2223S2-CS2103T-W13-4/tp/pull/170)
+ * Added documentation for the new GUI I redesigned: [#124](https://github.com/AY2223S2-CS2103T-W13-4/tp/pull/124)
+ * Added documentation for the new six homework commands I created: [#170](https://github.com/AY2223S2-CS2103T-W13-4/tp/pull/170)
+ * Added documentation for the Unique Mechanism Section I created: [#186](https://github.com/AY2223S2-CS2103T-W13-4/tp/pull/186), [#193](https://github.com/AY2223S2-CS2103T-W13-4/tp/pull/193)
+ * Developer Guide:
+ * Added implementation for `Detailed Information Section and Quick Access Button`: [#80](https://github.com/AY2223S2-CS2103T-W13-4/tp/pull/80), [#81](https://github.com/AY2223S2-CS2103T-W13-4/tp/pull/81)
+ * Updated `UI component Section`:[#80](https://github.com/AY2223S2-CS2103T-W13-4/tp/pull/80), [#81](https://github.com/AY2223S2-CS2103T-W13-4/tp/pull/81)
+* **Community**:
+ * PRs reviewed (with non-trivial review comments):
+ [#44](https://github.com/AY2223S2-CS2103T-W13-4/tp/pull/44),
+ [#70](https://github.com/AY2223S2-CS2103T-W13-4/tp/pull/70).
+ * Reported bugs and suggestions for other teams in the class:
+ [#106](https://github.com/AY2223S2-CS2103T-W10-3/tp/issues/106),
+ [#116](https://github.com/AY2223S2-CS2103T-W10-3/tp/issues/116),
+ [#118](https://github.com/AY2223S2-CS2103T-W10-3/tp/issues/118),
+ [#120](https://github.com/AY2223S2-CS2103T-W10-3/tp/issues/120),
+ [#123](https://github.com/AY2223S2-CS2103T-W10-3/tp/issues/123),
+ [#131](https://github.com/AY2223S2-CS2103T-W10-3/tp/issues/131).
+ * Some manual testing was done to ensure that the UI worked fine.
+* **Tools**:
+ * Use JavaFX and Scene Builder to modify the UI.
+ * Use PlantUML to add more UML diagrams in the developer guide.
diff --git a/docs/tutorials/AddRemark.md b/docs/tutorials/AddRemark.md
index 880c701042f..98c0fd08d15 100644
--- a/docs/tutorials/AddRemark.md
+++ b/docs/tutorials/AddRemark.md
@@ -229,7 +229,7 @@ Now that we have all the information that we need, let’s lay the groundwork fo
### Add a new `Remark` class
-Create a new `Remark` in `seedu.address.model.person`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code.
+Create a new `Remark` in `seedu.address.model.student`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code.
A copy-paste and search-replace later, you should have something like [this](https://github.com/se-edu/addressbook-level3/commit/4516e099699baa9e2d51801bd26f016d812dedcc#diff-41bb13c581e280c686198251ad6cc337cd5e27032772f06ed9bf7f1440995ece). Note how `Remark` has no constrains and thus does not require input
validation.
@@ -242,7 +242,7 @@ Let’s change `RemarkCommand` and `RemarkCommandParser` to use the new `Remark`
Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each person.
-Simply add the following to [`seedu.address.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688).
+Simply add the following to [`seedu.address.ui.student.StudentCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688).
**`PersonCard.java`:**
diff --git a/docs/tutorials/RemovingFields.md b/docs/tutorials/RemovingFields.md
index f29169bc924..1f8f05e80f6 100644
--- a/docs/tutorials/RemovingFields.md
+++ b/docs/tutorials/RemovingFields.md
@@ -28,7 +28,7 @@ IntelliJ IDEA provides a refactoring tool that can identify *most* parts of a re
### Assisted refactoring
-The `address` field in `Person` is actually an instance of the `seedu.address.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu.
+The `address` field in `Person` is actually an instance of the `seedu.address.model.student.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu.
* :bulb: To make things simpler, you can unselect the options `Search in comments and strings` and `Search for text occurrences`

diff --git a/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md
index 4fb62a83ef6..211a206c9a7 100644
--- a/docs/tutorials/TracingCode.md
+++ b/docs/tutorials/TracingCode.md
@@ -190,7 +190,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
public CommandResult execute(Model model) throws CommandException {
...
Person personToEdit = lastShownList.get(index.getZeroBased());
- Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor);
+ Person editedPerson = createEditedPerson(personToEdit, editStudentDescriptor);
if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) {
throw new CommandException(MESSAGE_DUPLICATE_PERSON);
}
diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java
index 4133aaa0151..4d6bbc493f1 100644
--- a/src/main/java/seedu/address/MainApp.java
+++ b/src/main/java/seedu/address/MainApp.java
@@ -48,7 +48,7 @@ public class MainApp extends Application {
@Override
public void init() throws Exception {
- logger.info("=============================[ Initializing AddressBook ]===========================");
+ logger.info("=============================[ Initializing TutorPro ]===========================");
super.init();
AppParameters appParameters = AppParameters.parse(getParameters());
@@ -79,14 +79,14 @@ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) {
try {
addressBookOptional = storage.readAddressBook();
if (!addressBookOptional.isPresent()) {
- logger.info("Data file not found. Will be starting with a sample AddressBook");
+ logger.info("Data file not found. Will be starting with a sample TutorPro");
}
initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook);
} catch (DataConversionException e) {
- logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook");
+ logger.warning("Data file not in the correct format. Will be starting with an empty TutorPro");
initialData = new AddressBook();
} catch (IOException e) {
- logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook");
+ logger.warning("Problem while reading from the file. Will be starting with an empty TutorPro");
initialData = new AddressBook();
}
@@ -151,7 +151,7 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) {
+ "Using default user prefs");
initializedPrefs = new UserPrefs();
} catch (IOException e) {
- logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook");
+ logger.warning("Problem while reading from the file. Will be starting with an empty TutorPro");
initializedPrefs = new UserPrefs();
}
@@ -167,13 +167,13 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) {
@Override
public void start(Stage primaryStage) {
- logger.info("Starting AddressBook " + MainApp.VERSION);
+ logger.info("Starting TutorPro " + MainApp.VERSION);
ui.start(primaryStage);
}
@Override
public void stop() {
- logger.info("============================ [ Stopping Address Book ] =============================");
+ logger.info("============================ [ Stopping TutorPro ] =============================");
try {
storage.saveUserPrefs(model.getUserPrefs());
} catch (IOException e) {
diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java
index 1deb3a1e469..917beb41471 100644
--- a/src/main/java/seedu/address/commons/core/Messages.java
+++ b/src/main/java/seedu/address/commons/core/Messages.java
@@ -4,10 +4,94 @@
* Container for user visible messages.
*/
public class Messages {
-
public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command";
public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s";
- public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid";
- public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!";
-
+ public static final String MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX = "The student index provided is invalid";
+ public static final String MESSAGE_STUDENTS_LISTED_OVERVIEW = "%d students listed!\n%s";
+ public static final String MESSAGE_HOMEWORK_ADDED_SUCCESS = "New homework added:\n%s\n"
+ + "To the following students:\n%s";
+ public static final String MESSAGE_HOMEWORK_LISTED_OVERVIEW = "%d homework from %d student listed:\n%s";
+ public static final String MESSAGE_ALL_HOMEWORK_LISTED_OVERVIEW = "%d homework listed:\n%s";
+ public static final String MESSAGE_NO_HOMEWORK_FOUND = "No homework found";
+ public static final String MESSAGE_INVALID_HOMEWORK_DISPLAYED_INDEX = "The homework index provided is invalid";
+ public static final String MESSAGE_HOMEWORK_DELETED_SUCCESS = "Homework : %s. %s\n"
+ + "Deleted from the student %s\n";
+ public static final String MESSAGE_LESSON_ADDED_SUCCESS = "New lesson added: \n%s \n"
+ + "To the following students: \n%s";
+ public static final String MESSAGE_HOMEWORK_ALREADY_MARKED_AS_DONE =
+ "Homework %s\nof student %s is already marked as done\n";
+ public static final String MESSAGE_HOMEWORK_MARKED_AS_DONE = "Homework %s of\nstudent %s is marked as done\n";
+ public static final String MESSAGE_HOMEWORK_MARKED_AS_UNDONE = "Homework %s of\nstudent %s is marked as undone\n";
+ public static final String MESSAGE_HOMEWORK_ALREADY_MARKED_AS_UNDONE =
+ "Homework %s of\bstudent %s is already marked as undone\n";
+ public static final String MESSAGE_INVALID_STUDENT_NAME = "No student found!\n";
+ public static final String MESSAGE_NO_LESSON_FOUND = "No lesson is found!";
+ public static final String MESSAGE_ALL_LESSONS_LISTED_OVERVIEW = "%d lessons from all students listed:\n%s";
+ public static final String MESSAGE_LESSONS_LISTED_OVERVIEW = "%d lessons from %d students listed: \n%s";
+ public static final String MESSAGE_LESSON_DELETED_SUCCESS = "Lesson: %s, %s\n"
+ + "Deleted from the student %s\n";
+ public static final String MESSAGE_INVALID_LESSON_DISPLAYED_INDEX = "The lesson index provided is invalid";
+ public static final String MESSAGE_INVALID_EXAM_DISPLAYED_INDEX = "The exam index provided is invalid";
+ public static final String MESSAGE_EXAM_DELETED_SUCCESS = "Exam: %s, %s\n"
+ + "Deleted from the student %s\n";
+ public static final String MESSAGE_EXAM_ADDED_SUCCESS = "New exam added: \n%s \n"
+ + "To the following students: \n%s";
+ public static final String MESSAGE_EXAMS_LISTED_OVERVIEW = "%d exams from %d students listed: \n%s";
+ public static final String MESSAGE_ALL_EXAMS_LISTED_OVERVIEW = "%d exams from all students listed:\n%s";
+ public static final String MESSAGE_NO_EXAM_FOUND = "No exam is found!";
+ public static final String MESSAGE_EXAM_NOT_COMPLETED = "Exam is not yet completed, a grade cannot be assigned!";
+ public static final String MESSAGE_EXAM_UPDATED_SUCCESS = "Exam %s of student %s is updated to:\n"
+ + "Exam name: %s\n"
+ + "Start Time: %s\n"
+ + "End Time: %s\n"
+ + "Weightage: %s" + "%%" + "\n"
+ + "Grade: %s\n";
+ public static final String MESSAGE_HOMEWORK_UPDATED_SUCCESS = "Homework %s of student %s is updated to:\n"
+ + "Homework name: %s\n"
+ + "Deadline: %s\n";
+ public static final String MESSAGE_LESSON_UPDATED_SUCCESS = "Lesson %s of student %s is updated to:\n"
+ + "Lesson name: %s\n"
+ + "Start Time: %s\n"
+ + "End Time: %s\n";
+ public static final String MESSAGE_HAS_DUPLICATE_NAMES = "Duplicate names detected for **%s**."
+ + "\nPlease enter full name(s)";
+ public static final String MESSAGE_RESULT_IN_DUPLICATE = "The result of the command will result in duplicate "
+ + "%s.\nPlease check the name(s) entered";
+ public static final String MESSAGE_NO_SUCH_STUDENT = "No student found: **%s**.\nPlease check the name entered";
+ public static final String MESSAGE_INVALID_LESSON_TIME = "Start time cannot be after end time";
+ public static final String MESSAGE_INVALID_LESSON_DURATION =
+ "The lesson duration is too short(< 30 min)/long(> 3 hours)";
+ public static final String MESSAGE_INVALID_EXAM_TIME = "Exam start time cannot be after exam end time";
+ public static final String MESSAGE_INVALID_EXAM_DURATION =
+ "The exam duration is too short(< 30 min)/long(> 3 hours)";
+ public static final String MESSAGE_INVALID_DONE_INPUT = "Invalid input for done/ field. Accepted inputs:"
+ + "done, not done";
+ public static final String MESSAGE_DEADLINE_IN_PAST = "Deadline cannot be in the past!";
+ public static final String MESSAGE_ONLY_ONE_STUDENT = "Only one student name is allowed!";
+ public static final String MESSAGE_EMPTY_STUDENT = "Student name cannot be empty!";
+ public static final String MESSAGE_ONLY_ONE_HOMEWORK = "Only one homework name is allowed!";
+ public static final String MESSAGE_EMPTY_HOMEWORK = "Homework name cannot be empty!";
+ public static final String MESSAGE_ONLY_ONE_INDEX = "Only one index is allowed!";
+ public static final String MESSAGE_EMPTY_INDEX = "Index cannot be empty!";
+ public static final String MESSAGE_ONLY_ONE_DEADLINE = "Only one deadline is allowed!";
+ public static final String MESSAGE_EMPTY_DEADLINE = "Deadline cannot be empty!";
+ public static final Object MESSAGE_ONLY_ONE_STATUS = "Only one status is allowed!";
+ public static final Object MESSAGE_EMPTY_STATUS = "Status cannot be empty!";
+ public static final String MESSAGE_CONTAIN_STUDENT_NAME = "There is at least one existing student "
+ + "whose name contains \"%s\".";
+ public static final String MESSAGE_EXTENDED_STUDENT_NAME = "There is at least one existing student "
+ + "whose name is contained in \"%s\".";
+ public static final String MESSAGE_CONFLICTING_LESSON_TIME = "You already have a lesson during this time!";
+ public static final String MESSAGE_CONFLICTING_EXAM_TIME = "This student has an exam during this time!";
+ public static final String MESSAGE_ONLY_ONE_LESSON = "Only one lesson title is allowed";
+ public static final String MESSAGE_ONLY_ONE_DATE = "Only one lesson date is allowed";
+ public static final String MESSAGE_ONLY_ONE_STARTTIME = "Only one start time is allowed";
+ public static final String MESSAGE_ONLY_ONE_ENDTIME = "Only one end time is allowed";
+ public static final String MESSAGE_ONLY_ONE_EMAIL = "Only one email is allowed";
+ public static final String MESSAGE_ONLY_ONE_ADDRESS = "Only one address is allowed";
+ public static final String MESSAGE_ONLY_ONE_PHONE = "Only one phone number is allowed";
+ public static final String MESSAGE_ONLY_ONE_DONE = "Only one DONE keyword is allowed";
+ public static final String MESSAGE_ONLY_ONE_GRADE = "Only one exam grade is allowed";
+ public static final String MESSAGE_ONLY_ONE_WEIGHTAGE = "Only one exam weightage is allowed";
+ public static final String MESSAGE_ONLY_ONE_EXAM = "Only one exam title is allowed";
}
diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java
index 92cd8fa605a..641d315e68d 100644
--- a/src/main/java/seedu/address/logic/Logic.java
+++ b/src/main/java/seedu/address/logic/Logic.java
@@ -8,7 +8,7 @@
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Person;
+import seedu.address.model.student.Student;
/**
* API of the Logic component
@@ -31,7 +31,7 @@ public interface Logic {
ReadOnlyAddressBook getAddressBook();
/** Returns an unmodifiable view of the filtered list of persons */
- ObservableList getFilteredPersonList();
+ ObservableList getFilteredPersonList();
/**
* Returns the user prefs' address book file path.
diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java
index 9d9c6d15bdc..644df39fd74 100644
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ b/src/main/java/seedu/address/logic/LogicManager.java
@@ -14,7 +14,7 @@
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.Model;
import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Person;
+import seedu.address.model.student.Student;
import seedu.address.storage.Storage;
/**
@@ -60,8 +60,8 @@ public ReadOnlyAddressBook getAddressBook() {
}
@Override
- public ObservableList getFilteredPersonList() {
- return model.getFilteredPersonList();
+ public ObservableList getFilteredPersonList() {
+ return model.getFilteredStudentList();
}
@Override
diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java
index 71656d7c5c8..50518cf93ab 100644
--- a/src/main/java/seedu/address/logic/commands/AddCommand.java
+++ b/src/main/java/seedu/address/logic/commands/AddCommand.java
@@ -3,47 +3,52 @@
import static java.util.Objects.requireNonNull;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GRADELEVEL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SCHOOL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+import seedu.address.commons.core.Messages;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
-import seedu.address.model.person.Person;
+import seedu.address.model.student.Student;
/**
- * Adds a person to the address book.
+ * Adds a student to the address book.
*/
public class AddCommand extends Command {
- public static final String COMMAND_WORD = "add";
+ public static final String COMMAND_WORD = "new-student";
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. "
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a student to the address book. "
+ "Parameters: "
+ PREFIX_NAME + "NAME "
+ PREFIX_PHONE + "PHONE "
+ PREFIX_EMAIL + "EMAIL "
+ PREFIX_ADDRESS + "ADDRESS "
+ + "[" + PREFIX_SCHOOL + "SCHOOL] "
+ + "[" + PREFIX_GRADELEVEL + "GRADE LEVEL] "
+ "[" + PREFIX_TAG + "TAG]...\n"
+ "Example: " + COMMAND_WORD + " "
+ PREFIX_NAME + "John Doe "
+ PREFIX_PHONE + "98765432 "
+ PREFIX_EMAIL + "johnd@example.com "
+ PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 "
- + PREFIX_TAG + "friends "
- + PREFIX_TAG + "owesMoney";
+ + PREFIX_SCHOOL + "Anglo Chinese "
+ + PREFIX_GRADELEVEL + "Secondary 3 ";
- public static final String MESSAGE_SUCCESS = "New person added: %1$s";
- public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book";
+ public static final String MESSAGE_SUCCESS = "New student added: %1$s";
+ public static final String MESSAGE_DUPLICATE_STUDENT = "This student already exists in the address book";
- private final Person toAdd;
+ private final Student toAdd;
/**
- * Creates an AddCommand to add the specified {@code Person}
+ * Creates an AddCommand to add the specified {@code Student}
*/
- public AddCommand(Person person) {
- requireNonNull(person);
- toAdd = person;
+ public AddCommand(Student student) {
+ requireNonNull(student);
+ toAdd = student;
}
@Override
@@ -51,7 +56,17 @@ public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
if (model.hasPerson(toAdd)) {
- throw new CommandException(MESSAGE_DUPLICATE_PERSON);
+ throw new CommandException(MESSAGE_DUPLICATE_STUDENT);
+ }
+
+ if (model.hasDuplicateNameAdd(toAdd.getName().toString())) {
+ throw new CommandException(String.format(Messages.MESSAGE_CONTAIN_STUDENT_NAME,
+ toAdd.getName().toString()));
+ }
+
+ if (model.hasExtendedName(toAdd.getName().toString())) {
+ throw new CommandException(String.format(Messages.MESSAGE_EXTENDED_STUDENT_NAME,
+ toAdd.getName().toString()));
}
model.addPerson(toAdd);
diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java
index 9c86b1fa6e4..8291af5857b 100644
--- a/src/main/java/seedu/address/logic/commands/ClearCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java
@@ -11,7 +11,7 @@
public class ClearCommand extends Command {
public static final String COMMAND_WORD = "clear";
- public static final String MESSAGE_SUCCESS = "Address book has been cleared!";
+ public static final String MESSAGE_SUCCESS = "TutorPro has been cleared!";
@Override
diff --git a/src/main/java/seedu/address/logic/commands/CommandUtil.java b/src/main/java/seedu/address/logic/commands/CommandUtil.java
new file mode 100644
index 00000000000..b291ad2030c
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/CommandUtil.java
@@ -0,0 +1,51 @@
+package seedu.address.logic.commands;
+
+import java.util.List;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+
+/**
+ * Contains utility methods used for commands.
+ */
+public class CommandUtil {
+
+ /**
+ * Checks if the student name exists in the address book.
+ *
+ * @param model {@code Model} which the command should operate on.
+ * @throws CommandException if the student name does not exist in the address book.
+ */
+ public static void handleNonExistName(Model model, List names) throws CommandException {
+ StringBuilder nonExistNames = new StringBuilder();
+ for (String name : names) {
+ if (model.noSuchStudent(name)) {
+ nonExistNames.append(name).append(", ");
+ }
+ }
+ if (nonExistNames.length() != 0) {
+ nonExistNames = new StringBuilder(nonExistNames.substring(0, nonExistNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_NO_SUCH_STUDENT, nonExistNames));
+ }
+ }
+
+ /**
+ * Checks if the student name exists in the address book.
+ *
+ * @param model {@code Model} which the command should operate on.
+ * @throws CommandException if the student name does not exist in the address book.
+ */
+ public static void handleDuplicateName(Model model, List names) throws CommandException {
+ StringBuilder dupNames = new StringBuilder();
+ for (String name : names) {
+ if (model.hasDuplicateName(name)) {
+ dupNames.append(name).append(", ");
+ }
+ }
+ if (dupNames.length() != 0) {
+ dupNames = new StringBuilder(dupNames.substring(0, dupNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_HAS_DUPLICATE_NAMES, dupNames));
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
index 02fd256acba..3402c8d81e2 100644
--- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java
+++ b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
@@ -8,7 +8,7 @@
import seedu.address.commons.core.index.Index;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
-import seedu.address.model.person.Person;
+import seedu.address.model.student.Student;
/**
* Deletes a person identified using it's displayed index from the address book.
@@ -18,11 +18,11 @@ public class DeleteCommand extends Command {
public static final String COMMAND_WORD = "delete";
public static final String MESSAGE_USAGE = COMMAND_WORD
- + ": Deletes the person identified by the index number used in the displayed person list.\n"
- + "Parameters: INDEX (must be a positive integer)\n"
- + "Example: " + COMMAND_WORD + " 1";
+ + ": Deletes the student identified by the index number used in the displayed student list.\n"
+ + "Parameters: index/INDEX (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " index/1";
- public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s";
+ public static final String MESSAGE_DELETE_STUDENT_SUCCESS = "Deleted Person: %1$s";
private final Index targetIndex;
@@ -33,15 +33,19 @@ public DeleteCommand(Index targetIndex) {
@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
- List lastShownList = model.getFilteredPersonList();
+ List lastShownList = model.getFilteredStudentList();
+
+ if (lastShownList.isEmpty()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ }
if (targetIndex.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
}
- Person personToDelete = lastShownList.get(targetIndex.getZeroBased());
+ Student personToDelete = lastShownList.get(targetIndex.getZeroBased());
model.deletePerson(personToDelete);
- return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete));
+ return new CommandResult(String.format(MESSAGE_DELETE_STUDENT_SUCCESS, personToDelete));
}
@Override
diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java
index 7e36114902f..d5d5b4c9898 100644
--- a/src/main/java/seedu/address/logic/commands/EditCommand.java
+++ b/src/main/java/seedu/address/logic/commands/EditCommand.java
@@ -3,10 +3,11 @@
import static java.util.Objects.requireNonNull;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
import java.util.Collections;
import java.util.HashSet;
@@ -19,11 +20,11 @@
import seedu.address.commons.util.CollectionUtil;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
-import seedu.address.model.person.Address;
-import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
+import seedu.address.model.student.Address;
+import seedu.address.model.student.Email;
+import seedu.address.model.student.Name;
+import seedu.address.model.student.Phone;
+import seedu.address.model.student.Student;
import seedu.address.model.tag.Tag;
/**
@@ -31,18 +32,19 @@
*/
public class EditCommand extends Command {
- public static final String COMMAND_WORD = "edit";
+ public static final String COMMAND_WORD = "update-info";
public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified "
+ "by the index number used in the displayed person list. "
+ "Existing values will be overwritten by the input values.\n"
- + "Parameters: INDEX (must be a positive integer) "
+ + "Parameters: "
+ + PREFIX_INDEX + "INDEX (must be a positive integer) "
+ "[" + PREFIX_NAME + "NAME] "
+ "[" + PREFIX_PHONE + "PHONE] "
+ "[" + PREFIX_EMAIL + "EMAIL] "
+ "[" + PREFIX_ADDRESS + "ADDRESS] "
+ "[" + PREFIX_TAG + "TAG]...\n"
- + "Example: " + COMMAND_WORD + " 1 "
+ + "Example: " + COMMAND_WORD + " index/1 "
+ PREFIX_PHONE + "91234567 "
+ PREFIX_EMAIL + "johndoe@example.com";
@@ -51,55 +53,68 @@ public class EditCommand extends Command {
public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book.";
private final Index index;
- private final EditPersonDescriptor editPersonDescriptor;
+ private final EditStudentDescriptor editStudentDescriptor;
/**
* @param index of the person in the filtered person list to edit
- * @param editPersonDescriptor details to edit the person with
+ * @param editStudentDescriptor details to edit the person with
*/
- public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) {
+ public EditCommand(Index index, EditStudentDescriptor editStudentDescriptor) {
requireNonNull(index);
- requireNonNull(editPersonDescriptor);
+ requireNonNull(editStudentDescriptor);
this.index = index;
- this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor);
+ this.editStudentDescriptor = new EditStudentDescriptor(editStudentDescriptor);
}
@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
- List lastShownList = model.getFilteredPersonList();
+ List lastShownList = model.getFilteredStudentList();
if (index.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
}
- Person personToEdit = lastShownList.get(index.getZeroBased());
- Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor);
+ Student personToEdit = lastShownList.get(index.getZeroBased());
+ Student editedPerson = createEditedPerson(personToEdit, editStudentDescriptor);
+ String newName = editedPerson.getName().toString();
+ if (!newName.equals(personToEdit.getName().toString())) {
+ if (model.hasDuplicateNameEdit(newName, index.getZeroBased())) {
+ throw new CommandException(String.format(Messages.MESSAGE_CONTAIN_STUDENT_NAME,
+ newName));
+ }
+
+ if (model.hasExtendedNameEdit(newName, index.getZeroBased())) {
+ throw new CommandException(String.format(Messages.MESSAGE_EXTENDED_STUDENT_NAME,
+ newName));
+ }
+ }
- if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) {
+ if (!personToEdit.isSameStudent(editedPerson) && model.hasPerson(editedPerson)) {
throw new CommandException(MESSAGE_DUPLICATE_PERSON);
}
- model.setPerson(personToEdit, editedPerson);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ model.setStudent(personToEdit, editedPerson);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson));
}
/**
* Creates and returns a {@code Person} with the details of {@code personToEdit}
- * edited with {@code editPersonDescriptor}.
+ * edited with {@code editStudentDescriptor}.
*/
- private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) {
+ private static Student createEditedPerson(Student personToEdit, EditStudentDescriptor editStudentDescriptor) {
assert personToEdit != null;
- Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName());
- Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone());
- Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail());
- Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress());
- Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags());
+ Name updatedName = editStudentDescriptor.getName().orElse(personToEdit.getName());
+ Phone updatedPhone = editStudentDescriptor.getPhone().orElse(personToEdit.getPhone());
+ Email updatedEmail = editStudentDescriptor.getEmail().orElse(personToEdit.getEmail());
+ Address updatedAddress = editStudentDescriptor.getAddress().orElse(personToEdit.getAddress());
+ Set updatedTags = editStudentDescriptor.getTags().orElse(personToEdit.getTags());
- return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags);
+ return new Student(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags,
+ personToEdit.getHomeworkList(), personToEdit.getLessonsList(), personToEdit.getExamsList());
}
@Override
@@ -117,27 +132,27 @@ public boolean equals(Object other) {
// state check
EditCommand e = (EditCommand) other;
return index.equals(e.index)
- && editPersonDescriptor.equals(e.editPersonDescriptor);
+ && editStudentDescriptor.equals(e.editStudentDescriptor);
}
/**
* Stores the details to edit the person with. Each non-empty field value will replace the
* corresponding field value of the person.
*/
- public static class EditPersonDescriptor {
+ public static class EditStudentDescriptor {
private Name name;
private Phone phone;
private Email email;
private Address address;
private Set tags;
- public EditPersonDescriptor() {}
+ public EditStudentDescriptor() {}
/**
* Copy constructor.
* A defensive copy of {@code tags} is used internally.
*/
- public EditPersonDescriptor(EditPersonDescriptor toCopy) {
+ public EditStudentDescriptor(EditStudentDescriptor toCopy) {
setName(toCopy.name);
setPhone(toCopy.phone);
setEmail(toCopy.email);
@@ -209,12 +224,12 @@ public boolean equals(Object other) {
}
// instanceof handles nulls
- if (!(other instanceof EditPersonDescriptor)) {
+ if (!(other instanceof EditStudentDescriptor)) {
return false;
}
// state check
- EditPersonDescriptor e = (EditPersonDescriptor) other;
+ EditStudentDescriptor e = (EditStudentDescriptor) other;
return getName().equals(e.getName())
&& getPhone().equals(e.getPhone())
diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java
index 3dd85a8ba90..fc1184f8f9d 100644
--- a/src/main/java/seedu/address/logic/commands/ExitCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java
@@ -9,7 +9,7 @@ public class ExitCommand extends Command {
public static final String COMMAND_WORD = "exit";
- public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ...";
+ public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Tutor Pro as requested ...";
@Override
public CommandResult execute(Model model) {
diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java
index d6b19b0a0de..a1ba93eaffc 100644
--- a/src/main/java/seedu/address/logic/commands/FindCommand.java
+++ b/src/main/java/seedu/address/logic/commands/FindCommand.java
@@ -4,7 +4,7 @@
import seedu.address.commons.core.Messages;
import seedu.address.model.Model;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import seedu.address.model.student.NamePredicate;
/**
* Finds and lists all persons in address book whose name contains any of the argument keywords.
@@ -14,23 +14,23 @@ public class FindCommand extends Command {
public static final String COMMAND_WORD = "find";
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of "
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all students whose names contain any of "
+ "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n"
- + "Parameters: KEYWORD [MORE_KEYWORDS]...\n"
- + "Example: " + COMMAND_WORD + " alice bob charlie";
+ + "Parameters: name/NAME [MORE_NAMES]...\n"
+ + "Example: " + COMMAND_WORD + " name/alice name/bob name/charlie";
- private final NameContainsKeywordsPredicate predicate;
+ private final NamePredicate predicate;
- public FindCommand(NameContainsKeywordsPredicate predicate) {
+ public FindCommand(NamePredicate predicate) {
this.predicate = predicate;
}
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
- model.updateFilteredPersonList(predicate);
+ model.updateFilteredStudentList(predicate);
return new CommandResult(
- String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size()));
+ String.format(Messages.MESSAGE_STUDENTS_LISTED_OVERVIEW, model.getFilteredStudentList().size(), ""));
}
@Override
diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java
index 84be6ad2596..e2c74ebe3e3 100644
--- a/src/main/java/seedu/address/logic/commands/ListCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ListCommand.java
@@ -1,7 +1,7 @@
package seedu.address.logic.commands;
import static java.util.Objects.requireNonNull;
-import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
import seedu.address.model.Model;
@@ -12,13 +12,13 @@ public class ListCommand extends Command {
public static final String COMMAND_WORD = "list";
- public static final String MESSAGE_SUCCESS = "Listed all persons";
+ public static final String MESSAGE_SUCCESS = "Listed all students";
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
return new CommandResult(MESSAGE_SUCCESS);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/ViewProfileCommand.java b/src/main/java/seedu/address/logic/commands/ViewProfileCommand.java
new file mode 100644
index 00000000000..7e86345e601
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ViewProfileCommand.java
@@ -0,0 +1,94 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.student.Student;
+import seedu.address.model.tag.Tag;
+
+/**
+ * Prints student profiles with or without name-matching
+ */
+public class ViewProfileCommand extends Command {
+ public static final String COMMAND_WORD = "view-profile";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Prints the profile of the student that matches "
+ + "the specified name.\n"
+ + "Parameters: \n"
+ + PREFIX_NAME + "STUDENT_NAME \n"
+ + "Example: " + COMMAND_WORD + " " + PREFIX_NAME + "John Doe";
+ private static final Predicate SHOW_ALL_STUDENTS = student -> true;
+ private static final String SEPERATOR = "--------------------------------------------------\n";
+
+ private final List names;
+ private final Predicate namePredicate;
+
+ /**
+ * public constructor for a ViewProfileCommand
+ * @param names
+ * @param namePredicate
+ */
+ public ViewProfileCommand(List names, Predicate namePredicate) {
+ this.names = names;
+ this.namePredicate = namePredicate;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+
+ StringBuilder nonExistNames = new StringBuilder();
+ for (String name : names) {
+ if (model.noSuchStudent(name)) {
+ nonExistNames.append(name).append(", ");
+ }
+ }
+ if (nonExistNames.length() != 0) {
+ nonExistNames = new StringBuilder(nonExistNames.substring(0, nonExistNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_NO_SUCH_STUDENT, nonExistNames));
+ }
+ StringBuilder dupNames = new StringBuilder();
+ for (String name : names) {
+ if (model.hasDuplicateName(name)) {
+ dupNames.append(name).append(", ");
+ }
+ }
+ if (dupNames.length() != 0) {
+ dupNames = new StringBuilder(dupNames.substring(0, dupNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_HAS_DUPLICATE_NAMES, dupNames));
+ }
+ model.updateFilteredStudentList(namePredicate);
+
+ List studentList = model.getFilteredStudentList();
+
+ int numberOfStudents = studentList.size();
+ StringBuilder sb = new StringBuilder();
+ sb.append(SEPERATOR);
+
+ // Loop through each student and add their lesson to the string builder
+ for (Student student : studentList) {
+ sb.append(student.getName().fullName).append(":\n");
+ sb.append("Phone: ").append(student.getPhone()).append("\n");
+ sb.append("Address: ").append(student.getAddress().toString()).append("\n");
+ sb.append("Email: ").append(student.getEmail()).append("\n");
+ sb.append("Tags: ");
+ for (Tag tag : student.getTags()) {
+ sb.append(tag);
+ }
+ sb.append("\n");
+
+ sb.append(SEPERATOR);
+ }
+
+ return new CommandResult(
+ String.format(Messages.MESSAGE_STUDENTS_LISTED_OVERVIEW, numberOfStudents, sb));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/exam/CreateExamCommand.java b/src/main/java/seedu/address/logic/commands/exam/CreateExamCommand.java
new file mode 100644
index 00000000000..e81abf38082
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/exam/CreateExamCommand.java
@@ -0,0 +1,139 @@
+package seedu.address.logic.commands.exam;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ENDTIME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EXAM;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STARTTIME;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.student.Exam;
+import seedu.address.model.student.Grade;
+import seedu.address.model.student.NamePredicate;
+import seedu.address.model.student.Student;
+
+/**
+ * Adds an exam to a student.
+ */
+public class CreateExamCommand extends Command {
+
+ public static final String COMMAND_WORD = "new-exam";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an exam to students.\n"
+ + "Parameters: "
+ + PREFIX_NAME + "STUDENT_NAME "
+ + PREFIX_EXAM + "EXAM_NAME "
+ + PREFIX_STARTTIME + "Start time "
+ + PREFIX_ENDTIME + "End time\n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_NAME + "John Doe "
+ + PREFIX_EXAM + "Math MYE "
+ + PREFIX_STARTTIME + "2023-05-21 12:00 "
+ + PREFIX_ENDTIME + "2023-05-21 14:00";
+
+
+ private final String examDescription;
+ private final LocalDateTime startTime;
+ private final LocalDateTime endTime;
+ private final NamePredicate predicate;
+ private final List names;
+ private final Double weightage;
+ private final Grade grade;
+
+
+ /**
+ * Creates a CreateExamCommand to add the specified exam to the specified student.
+ */
+ public CreateExamCommand(List names, NamePredicate predicate, String examDescription,
+ LocalDateTime startTime, LocalDateTime endTime, Double weightage, Grade grade) {
+ requireNonNull(predicate);
+ requireNonNull(examDescription);
+ requireNonNull(startTime);
+
+ this.examDescription = examDescription;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.predicate = predicate;
+ this.names = names;
+ this.weightage = weightage;
+ this.grade = grade;
+
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ StringBuilder lessonClashMessage = new StringBuilder("\n");
+ requireNonNull(model);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+
+ StringBuilder nonExistNames = new StringBuilder();
+ for (String name : names) {
+ if (model.noSuchStudent(name)) {
+ nonExistNames.append(name).append(", ");
+ }
+ }
+ if (nonExistNames.length() != 0) {
+ nonExistNames = new StringBuilder(nonExistNames.substring(0, nonExistNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_NO_SUCH_STUDENT, nonExistNames));
+ }
+ StringBuilder dupNames = new StringBuilder();
+ for (String name : names) {
+ if (model.hasDuplicateName(name)) {
+ dupNames.append(name).append(", ");
+ }
+ }
+ if (dupNames.length() != 0) {
+ dupNames = new StringBuilder(dupNames.substring(0, dupNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_HAS_DUPLICATE_NAMES, dupNames));
+ }
+ model.updateFilteredStudentList(predicate);
+
+ List studentList = model.getFilteredStudentList();
+
+ if (endTime.isAfter(LocalDateTime.now()) && grade != null) {
+ throw new CommandException(Messages.MESSAGE_EXAM_NOT_COMPLETED);
+ }
+
+ Exam exam = new Exam(examDescription, startTime, endTime, weightage, grade);
+
+ try {
+ for (Student student : studentList) {
+ if (student.hasLessonAtSameTime(exam)) {
+ lessonClashMessage.append("*******WARNING*******\n").append(student.getName().fullName)
+ .append(" has a lesson at the same time as the exam\n Consider rescheduling clashing lesson");
+ }
+ student.addExam(exam);
+ }
+ } catch (Exception e) {
+ throw new CommandException(e.getMessage());
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (Student student : studentList) {
+ sb.append(student.getName().fullName);
+ sb.append("\n");
+ }
+
+ return new CommandResult(
+ String.format(Messages.MESSAGE_EXAM_ADDED_SUCCESS, exam, sb) + lessonClashMessage);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof CreateExamCommand // instanceof handles nulls
+ && predicate.equals(((CreateExamCommand) other).predicate)
+ && startTime.equals(((CreateExamCommand) other).startTime)
+ && endTime.equals(((CreateExamCommand) other).endTime)
+ && examDescription.equals(((CreateExamCommand) other).examDescription));
+ }
+}
+
diff --git a/src/main/java/seedu/address/logic/commands/exam/DeleteExamCommand.java b/src/main/java/seedu/address/logic/commands/exam/DeleteExamCommand.java
new file mode 100644
index 00000000000..f22c0f6c939
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/exam/DeleteExamCommand.java
@@ -0,0 +1,109 @@
+package seedu.address.logic.commands.exam;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.util.List;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.student.NamePredicate;
+import seedu.address.model.student.Student;
+
+/**
+ * Deletes an exam from a student.
+ */
+public class DeleteExamCommand extends Command {
+
+ public static final String COMMAND_WORD = "delete-exam";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deletes an exam from a student.\n"
+ + "Parameters: "
+ + PREFIX_NAME + "STUDENT_NAME "
+ + PREFIX_INDEX + "INDEX\n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_NAME + "John Doe "
+ + PREFIX_INDEX + "1";
+
+ private final NamePredicate predicate;
+ private final Index targetIndex;
+ private final List names;
+
+
+ /**
+ * Creates a DeleteExamCommand to delete the specified exam from the specified student.
+ */
+ public DeleteExamCommand(List inputNames, NamePredicate predicate, Index targetIndex) {
+ requireNonNull(predicate);
+ requireNonNull(targetIndex);
+
+ this.names = inputNames;
+ this.predicate = predicate;
+ this.targetIndex = targetIndex;
+
+ }
+
+ /**
+ * Executes the command and returns the result message.
+ *
+ * @param model {@code Model} which the command should operate on.
+ * @return feedback message of the operation result for display
+ * @throws CommandException if the command's preconditions are not met
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+
+ StringBuilder nonExistNames = new StringBuilder();
+ for (String name : names) {
+ if (model.noSuchStudent(name)) {
+ nonExistNames.append(name).append(", ");
+ }
+ }
+ if (nonExistNames.length() != 0) {
+ nonExistNames = new StringBuilder(nonExistNames.substring(0, nonExistNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_NO_SUCH_STUDENT, nonExistNames));
+ }
+ StringBuilder dupNames = new StringBuilder();
+ for (String name : names) {
+ if (model.hasDuplicateName(name)) {
+ dupNames.append(name).append(", ");
+ }
+ }
+ if (dupNames.length() != 0) {
+ dupNames = new StringBuilder(dupNames.substring(0, dupNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_HAS_DUPLICATE_NAMES, dupNames));
+ }
+ model.updateFilteredStudentList(predicate);
+
+ List studentList = model.getFilteredStudentList();
+
+ StringBuilder sb = new StringBuilder();
+ for (Student student : studentList) {
+ try {
+ sb.append(String.format(Messages.MESSAGE_EXAM_DELETED_SUCCESS, targetIndex.getOneBased(),
+ student.getExam(targetIndex).toString(), student.getName().toString()));
+ sb.append("\n");
+ student.deleteExam(targetIndex);
+ } catch (IndexOutOfBoundsException e) {
+ throw new CommandException(Messages.MESSAGE_INVALID_EXAM_DISPLAYED_INDEX);
+ }
+ }
+
+ return new CommandResult(sb.toString());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof DeleteExamCommand // instanceof handles nulls
+ && predicate.equals(((DeleteExamCommand) other).predicate));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/exam/UpdateExamCommand.java b/src/main/java/seedu/address/logic/commands/exam/UpdateExamCommand.java
new file mode 100644
index 00000000000..039c95df267
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/exam/UpdateExamCommand.java
@@ -0,0 +1,150 @@
+package seedu.address.logic.commands.exam;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ENDTIME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EXAM;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GRADE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STARTTIME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_WEIGHT;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Optional;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.student.Exam;
+import seedu.address.model.student.Grade;
+import seedu.address.model.student.NamePredicate;
+import seedu.address.model.student.Student;
+
+/**
+ * Updates the information of an existing exam.
+ */
+public class UpdateExamCommand extends Command {
+
+ public static final String COMMAND_WORD = "update-exam";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Update the information of an existing exam.\n"
+ + "Parameters: "
+ + PREFIX_NAME + "STUDENT_NAME "
+ + PREFIX_INDEX + "EXAM_INDEX "
+ + PREFIX_EXAM + "EXAM_NAME "
+ + PREFIX_STARTTIME + "START TIME"
+ + PREFIX_ENDTIME + "END TIME"
+ + PREFIX_WEIGHT + "WEIGHTAGE"
+ + PREFIX_GRADE + "GRADE\n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_NAME + "John Doe "
+ + PREFIX_INDEX + "1 "
+ + PREFIX_EXAM + "Math Exam ";
+
+ private final Index index;
+ private final Optional examName;
+ private final Optional startTime;
+ private final Optional endTime;
+ private final Optional weightage;
+ private final Optional grade;
+
+ private final NamePredicate predicate;
+ private final List names;
+
+ /**
+ * Creates an UpdateExamCommand to update the specified exam of the specified student.
+ */
+ public UpdateExamCommand(List names, Index index, NamePredicate predicate,
+ Optional examName, Optional startTime,
+ Optional endTime, Optional weightage,
+ Optional grade) {
+ requireNonNull(predicate);
+ requireNonNull(index);
+
+ this.index = index;
+ this.predicate = predicate;
+ this.examName = examName;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.names = names;
+ this.weightage = weightage;
+ this.grade = grade;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+
+ StringBuilder nonExistNames = new StringBuilder();
+ for (String name : names) {
+ if (model.noSuchStudent(name)) {
+ nonExistNames.append(name).append(", ");
+ }
+ }
+ if (nonExistNames.length() != 0) {
+ nonExistNames = new StringBuilder(nonExistNames.substring(0, nonExistNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_NO_SUCH_STUDENT, nonExistNames));
+ }
+ StringBuilder dupNames = new StringBuilder();
+ for (String name : names) {
+ if (model.hasDuplicateName(name)) {
+ dupNames.append(name).append(", ");
+ }
+ }
+ if (dupNames.length() != 0) {
+ dupNames = new StringBuilder(dupNames.substring(0, dupNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_HAS_DUPLICATE_NAMES, dupNames));
+ }
+ model.updateFilteredStudentList(predicate);
+ List studentList = model.getFilteredStudentList();
+
+ Student student = studentList.get(0);
+ Exam examToUpdate;
+ try {
+ examToUpdate = student.getExam(index);
+ } catch (IndexOutOfBoundsException e) {
+ throw new CommandException(Messages.MESSAGE_INVALID_EXAM_DISPLAYED_INDEX);
+ }
+
+ String newExamName = this.examName.orElse(examToUpdate.getDescription());
+ LocalDateTime newStartTime = this.startTime.orElse(examToUpdate.getStartTime());
+ LocalDateTime newEndTime = this.endTime.orElse(examToUpdate.getEndTime());
+ if (newStartTime.isAfter(newEndTime)) {
+ throw new CommandException(Messages.MESSAGE_INVALID_EXAM_TIME);
+ }
+ if (newEndTime.isAfter(LocalDateTime.now()) && grade.isPresent()) {
+ throw new CommandException(Messages.MESSAGE_EXAM_NOT_COMPLETED);
+ }
+ Double newWeightage = this.weightage.orElse(examToUpdate.getWeightage());
+ Grade newGrade = this.grade.orElse(examToUpdate.getGrade());
+ Exam newExam = new Exam(newExamName, newStartTime, newEndTime, newWeightage, newGrade);
+
+ try {
+ student.setExam(index.getZeroBased(), newExam);
+ } catch (Exception e) {
+ throw new CommandException(e.getMessage());
+ }
+ model.updateFilteredStudentList(s -> s == student);
+ return new CommandResult(
+ String.format(Messages.MESSAGE_EXAM_UPDATED_SUCCESS, index.getOneBased(),
+ student.getName().getFirstName(),
+ newExamName, newStartTime, newEndTime, newWeightage, newGrade));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof UpdateExamCommand // instanceof handles nulls
+ && predicate.equals(((UpdateExamCommand) other).predicate)
+ && index == ((UpdateExamCommand) other).index
+ && examName.equals(((UpdateExamCommand) other).examName)
+ && startTime.equals(((UpdateExamCommand) other).startTime)
+ && endTime.equals((((UpdateExamCommand) other).endTime)));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/exam/ViewExamCommand.java b/src/main/java/seedu/address/logic/commands/exam/ViewExamCommand.java
new file mode 100644
index 00000000000..a2002dc6d50
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/exam/ViewExamCommand.java
@@ -0,0 +1,174 @@
+package seedu.address.logic.commands.exam;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EXAM;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.student.Exam;
+import seedu.address.model.student.Student;
+
+/**
+ * Finds and lists all exams in address book whose name contains any of the argument keywords.
+ * Keyword matching is case insensitive.
+ */
+public class ViewExamCommand extends Command {
+
+ public static final String COMMAND_WORD = "view-exam";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all exams filtered by\n"
+ + "* name of student (case-insensitive) and/or\n"
+ + "* date and/or\n"
+ + "* exam name and/or\n"
+ + "* whether it is done\n"
+ + "and displays them as a list with index numbers.\n"
+ + "Parameters: "
+ + PREFIX_NAME + "STUDENT_NAME "
+ + PREFIX_DATE + "DATE "
+ + PREFIX_EXAM + "EXAM NAME "
+ + PREFIX_DONE + "COMPLETED KEYWORD\n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_NAME + "John Doe "
+ + PREFIX_DATE + "2023-05-21 "
+ + PREFIX_EXAM + "Math MYE "
+ + PREFIX_DONE + "done(or not done)";
+ private static final Predicate SHOW_ALL_EXAMS = exam -> true;
+ private final Predicate namePredicate;
+ private final Predicate examDatePredicate;
+ private final Predicate examNamePredicate;
+ private final Predicate donePredicate;
+ private final boolean defaultPredicateFlag;
+ private final List names;
+
+
+ /**
+ * Overloaded constructor for ViewExamCommand.
+ *
+ * @param namePredicate Predicate to filter students by name.
+ * @param examNamePredicate Predicate to filter exams by exam name.
+ * @param donePredicate Predicate to filter exams by whether it is done.
+ * @param defaultPredicateFlag Flag to indicate if the default predicate is used.
+ */
+ public ViewExamCommand(List names, Predicate namePredicate, Predicate examNamePredicate,
+ Predicate donePredicate, boolean defaultPredicateFlag) {
+ this.namePredicate = namePredicate;
+ this.examDatePredicate = SHOW_ALL_EXAMS;
+ this.defaultPredicateFlag = defaultPredicateFlag;
+ this.examNamePredicate = examNamePredicate;
+ this.donePredicate = donePredicate;
+ this.names = names;
+ }
+
+ /**
+ * Overloaded constructor for ViewExamCommand.
+ *
+ * @param namePredicate Predicate to filter students by name.
+ * @param examDatePredicate Predicate to filter exams by date.
+ * @param examNamePredicate Predicate to filter exams by exam name.
+ * @param donePredicate Predicate to filter exams by whether it is done.
+ * @param defaultPredicateFlag Flag to indicate if the default predicate is used.
+ */
+ public ViewExamCommand(List names, Predicate namePredicate, Predicate examDatePredicate,
+ Predicate examNamePredicate, Predicate donePredicate,
+ boolean defaultPredicateFlag) {
+ this.namePredicate = namePredicate;
+ this.examDatePredicate = examDatePredicate;
+ this.defaultPredicateFlag = defaultPredicateFlag;
+ this.examNamePredicate = examNamePredicate;
+ this.donePredicate = donePredicate;
+ this.names = names;
+ }
+
+ /**
+ * Executes the view-exam command and returns the result message.
+ *
+ * @param model {@code Model} which the command should operate on.
+ * @return feedback message of the operation result for display.
+ * @throws CommandException if an error occurs during command execution.
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+
+ StringBuilder nonExistNames = new StringBuilder();
+ for (String name : names) {
+ if (model.noSuchStudent(name)) {
+ nonExistNames.append(name).append(", ");
+ }
+ }
+ if (nonExistNames.length() != 0) {
+ nonExistNames = new StringBuilder(nonExistNames.substring(0, nonExistNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_NO_SUCH_STUDENT, nonExistNames));
+ }
+ StringBuilder dupNames = new StringBuilder();
+ for (String name : names) {
+ if (model.hasDuplicateName(name)) {
+ dupNames.append(name).append(", ");
+ }
+ }
+ if (dupNames.length() != 0) {
+ dupNames = new StringBuilder(dupNames.substring(0, dupNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_HAS_DUPLICATE_NAMES, dupNames));
+ }
+ model.updateFilteredStudentList(namePredicate);
+
+ List studentList = model.getFilteredStudentList();
+
+ int numberOfStudents = studentList.size();
+ int numOfLessons = 0;
+ StringBuilder sb = new StringBuilder();
+ sb.append("--------------------------------------------------\n");
+
+ // Loop through each student and add their lesson to the string builder
+ for (Student student : studentList) {
+ List examList = student.getFilteredExamList(examDatePredicate, examNamePredicate,
+ donePredicate);
+ if (!examList.isEmpty()) {
+ sb.append(student.getName().fullName).append(":\n");
+
+ numOfLessons += examList.size();
+
+ for (int i = 0; i < examList.size(); i++) {
+ sb.append(i + 1).append(". ").append(examList.get(i)).append("\n");
+ }
+
+ sb.append("--------------------------------------------------\n");
+ }
+ }
+
+ // If no homework is found, throw an exception
+ if (numOfLessons == 0) {
+ throw new CommandException(Messages.MESSAGE_NO_EXAM_FOUND);
+ }
+
+ // If the default predicate is used, display a different message
+ if (defaultPredicateFlag) {
+ return new CommandResult(
+ String.format(Messages.MESSAGE_ALL_EXAMS_LISTED_OVERVIEW, numOfLessons, sb));
+ } else {
+ return new CommandResult(
+ String.format(Messages.MESSAGE_EXAMS_LISTED_OVERVIEW, numOfLessons, numberOfStudents, sb));
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof ViewExamCommand // instanceof handles nulls
+ && namePredicate.equals(((ViewExamCommand) other).namePredicate)
+ && examDatePredicate.equals(((ViewExamCommand) other).examDatePredicate)
+ && defaultPredicateFlag == ((ViewExamCommand) other).defaultPredicateFlag);
+ }
+}
+
diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java
index a16bd14f2cd..f44c0c19186 100644
--- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java
+++ b/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java
@@ -1,5 +1,7 @@
package seedu.address.logic.commands.exceptions;
+import seedu.address.logic.commands.Command;
+
/**
* Represents an error which occurs during execution of a {@link Command}.
*/
diff --git a/src/main/java/seedu/address/logic/commands/homework/CreateHomeworkCommand.java b/src/main/java/seedu/address/logic/commands/homework/CreateHomeworkCommand.java
new file mode 100644
index 00000000000..4190d675e8b
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/homework/CreateHomeworkCommand.java
@@ -0,0 +1,120 @@
+package seedu.address.logic.commands.homework;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.commands.CommandUtil.handleDuplicateName;
+import static seedu.address.logic.commands.CommandUtil.handleNonExistName;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_HOMEWORK;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.student.Homework;
+import seedu.address.model.student.NamePredicate;
+import seedu.address.model.student.Student;
+import seedu.address.model.student.exceptions.DuplicateEntryException;
+
+/**
+ * Adds an assignment to a student.
+ */
+public class CreateHomeworkCommand extends Command {
+ public static final String COMMAND_WORD = "new-homework";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an assignment to students.\n"
+ + "Parameters: "
+ + PREFIX_NAME + "STUDENT_NAME "
+ + PREFIX_HOMEWORK + "HOMEWORK_NAME "
+ + PREFIX_DEADLINE + "DEADLINE\n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_NAME + "John Doe "
+ + PREFIX_HOMEWORK + "Math Homework "
+ + PREFIX_DEADLINE + "2023-03-01T12:00";
+
+ private final String homeworkName;
+ private final LocalDateTime deadline;
+ private final NamePredicate predicate;
+ private final List names;
+
+ /**
+ * Creates a CreateHomeworkCommand to add the specified assignment to the specified student.
+ */
+ public CreateHomeworkCommand(List names, NamePredicate predicate, String homeworkName,
+ LocalDateTime deadline) {
+ requireNonNull(homeworkName);
+ requireNonNull(deadline);
+ requireNonNull(predicate);
+
+ this.homeworkName = homeworkName;
+ this.deadline = deadline;
+ this.predicate = predicate;
+ this.names = names;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+
+ handleNonExistName(model, names);
+ handleDuplicateName(model, names);
+ model.updateFilteredStudentList(predicate);
+
+ List studentList = model.getFilteredStudentList();
+ Homework homework = new Homework(homeworkName, deadline);
+ addHomework(studentList, homework);
+ String message = formatMessage(studentList, homework);
+
+ return new CommandResult(
+ String.format(Messages.MESSAGE_HOMEWORK_ADDED_SUCCESS, homework, message));
+ }
+
+ /**
+ * Adds the homework to the students.
+ *
+ * @param studentList The list of students to add the homework to.
+ * @param homework The homework to be added.
+ * @throws DuplicateEntryException If the homework already exists in the student.
+ * @throws CommandException If the homework already exists in the student.
+ */
+ public void addHomework(List studentList, Homework homework)
+ throws DuplicateEntryException, CommandException {
+ try {
+ for (Student student : studentList) {
+ student.addHomework(homework);
+ }
+ } catch (DuplicateEntryException e) {
+ throw new CommandException(String.format(Messages.MESSAGE_RESULT_IN_DUPLICATE, "homework"));
+ }
+ }
+
+ /**
+ * Formats the message to be displayed to the user.
+ *
+ * @param studentList The list of students to add the homework to.
+ * @param homework The homework to be added.
+ * @return The formatted message.
+ */
+ public String formatMessage(List studentList, Homework homework) {
+ StringBuilder sb = new StringBuilder();
+ for (Student student : studentList) {
+ sb.append(student.getName().fullName);
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof CreateHomeworkCommand // instanceof handles nulls
+ && predicate.equals(((CreateHomeworkCommand) other).predicate)
+ && homeworkName.equals(((CreateHomeworkCommand) other).homeworkName)
+ && deadline.equals(((CreateHomeworkCommand) other).deadline));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/homework/DeleteHomeworkCommand.java b/src/main/java/seedu/address/logic/commands/homework/DeleteHomeworkCommand.java
new file mode 100644
index 00000000000..5c274b40944
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/homework/DeleteHomeworkCommand.java
@@ -0,0 +1,110 @@
+package seedu.address.logic.commands.homework;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.commands.CommandUtil.handleDuplicateName;
+import static seedu.address.logic.commands.CommandUtil.handleNonExistName;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.util.List;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.student.NamePredicate;
+import seedu.address.model.student.Student;
+
+/**
+ * Deletes an assignment from a student.
+ */
+public class DeleteHomeworkCommand extends Command {
+ public static final String COMMAND_WORD = "delete-homework";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deletes an assignment from a student.\n"
+ + "Parameters: "
+ + PREFIX_NAME + "STUDENT_NAME "
+ + PREFIX_INDEX + "INDEX\n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_NAME + "John Doe "
+ + PREFIX_INDEX + "1";
+
+ private final NamePredicate predicate;
+ private final Index targetIndex;
+ private final List names;
+ /**
+ * Creates a DeleteHomeworkCommand to delete the specified assignment from the specified student.
+ */
+ public DeleteHomeworkCommand(List names, NamePredicate predicate, Index targetIndex) {
+ requireNonNull(predicate);
+ requireNonNull(targetIndex);
+
+ this.predicate = predicate;
+ this.targetIndex = targetIndex;
+ this.names = names;
+ }
+
+ /**
+ * Executes the command and returns the result message.
+ *
+ * @param model {@code Model} which the command should operate on.
+ * @return feedback message of the operation result for display
+ * @throws CommandException if the command's preconditions are not met
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+
+ handleNonExistName(model, names);
+ handleDuplicateName(model, names);
+ model.updateFilteredStudentList(predicate);
+
+ List studentList = model.getFilteredStudentList();
+
+ String message = formatMessage(studentList);
+ return new CommandResult(message);
+ }
+
+ /**
+ * Formats the string to be displayed.
+ *
+ * @param sb StringBuilder to be formatted.
+ * @param student Student to be formatted.
+ * @throws CommandException if the command's preconditions are not met
+ */
+ public void removeHomework(StringBuilder sb, Student student) throws CommandException {
+ try {
+ sb.append(String.format(Messages.MESSAGE_HOMEWORK_DELETED_SUCCESS, targetIndex.getOneBased(),
+ student.getHomework(targetIndex).toString(), student.getName().toString()));
+ sb.append("\n");
+ student.deleteHomework(targetIndex);
+ } catch (IndexOutOfBoundsException e) {
+ throw new CommandException(Messages.MESSAGE_INVALID_HOMEWORK_DISPLAYED_INDEX);
+ }
+ }
+
+ /**
+ * Formats the string to be displayed.
+ *
+ * @param studentList List of students to be formatted.
+ * @return String to be displayed.
+ * @throws CommandException if the command's preconditions are not met
+ */
+ public String formatMessage(List studentList) throws CommandException {
+ StringBuilder sb = new StringBuilder();
+ for (Student student : studentList) {
+ removeHomework(sb, student);
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof DeleteHomeworkCommand // instanceof handles nulls
+ && predicate.equals(((DeleteHomeworkCommand) other).predicate)); // state check
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/homework/MarkHomeworkAsDoneCommand.java b/src/main/java/seedu/address/logic/commands/homework/MarkHomeworkAsDoneCommand.java
new file mode 100644
index 00000000000..73ddf5634ae
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/homework/MarkHomeworkAsDoneCommand.java
@@ -0,0 +1,133 @@
+package seedu.address.logic.commands.homework;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.commands.CommandUtil.handleDuplicateName;
+import static seedu.address.logic.commands.CommandUtil.handleNonExistName;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.util.List;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.student.Homework;
+import seedu.address.model.student.NamePredicate;
+import seedu.address.model.student.Student;
+
+/**
+ * Marks an assignment as done for a student.
+ */
+public class MarkHomeworkAsDoneCommand extends Command {
+ public static final String COMMAND_WORD = "mark-homework";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Marks an assignment as done for a student.\n"
+ + "Parameters: "
+ + PREFIX_NAME + "STUDENT_NAME "
+ + PREFIX_INDEX + "INDEX\n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_NAME + "John Doe "
+ + PREFIX_INDEX + "1";
+
+ private final NamePredicate predicate;
+ private final Index targetIndex;
+ private final List names;
+
+ /**
+ * Creates a MarkHomeworkAsDoneCommand to mark the specified assignment as done for the specified student.
+ */
+ public MarkHomeworkAsDoneCommand(List names, NamePredicate predicate, Index targetIndex) {
+ requireNonNull(predicate);
+ requireNonNull(targetIndex);
+
+ this.predicate = predicate;
+ this.targetIndex = targetIndex;
+ this.names = names;
+ }
+
+ /**
+ * Executes the command and returns the result message.
+ *
+ * @param model {@code Model} which the command should operate on.
+ * @return feedback message of the operation result for display
+ * @throws CommandException if the command's preconditions are not met
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+
+ handleNonExistName(model, names);
+ handleDuplicateName(model, names);
+ model.updateFilteredStudentList(predicate);
+
+ List studentList = model.getFilteredStudentList();
+ String message = formatString(studentList);
+
+ return new CommandResult(message);
+ }
+
+ /**
+ * Formats the string to be displayed to the user.
+ *
+ * @param studentList List of students to be displayed.
+ * @return String to be displayed to the user.
+ * @throws CommandException if the command's preconditions are not met
+ */
+ public String formatString(List studentList) throws CommandException {
+ if (studentList.isEmpty()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_NAME);
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (Student student : studentList) {
+ markHomework(sb, student);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Marks the homework as done for the student.
+ *
+ * @param sb String builder to be displayed to the user.
+ * @param student Student to be marked as done.
+ * @throws CommandException if the command's preconditions are not met
+ */
+ public void markHomework(StringBuilder sb, Student student) throws CommandException {
+ try {
+ Homework homeworkToMarkAsDone = student.getHomework(targetIndex);
+ differentiateHomework(sb, homeworkToMarkAsDone, student);
+ sb.append("\n");
+ } catch (IndexOutOfBoundsException e) {
+ throw new CommandException(Messages.MESSAGE_INVALID_HOMEWORK_DISPLAYED_INDEX);
+ }
+ }
+
+ /**
+ * Differentiates between the homework being marked as done and the homework already being marked as done.
+ *
+ * @param sb String builder to be displayed to the user.
+ * @param homeworkToMarkAsDone Homework to be marked as done.
+ * @param student Student to be marked as done.
+ */
+ public void differentiateHomework(StringBuilder sb, Homework homeworkToMarkAsDone, Student student) {
+ if (homeworkToMarkAsDone.isCompleted()) {
+ sb.append(String.format(Messages.MESSAGE_HOMEWORK_ALREADY_MARKED_AS_DONE,
+ homeworkToMarkAsDone.getDescription(), student.getName().toString()));
+ } else {
+ sb.append(String.format(Messages.MESSAGE_HOMEWORK_MARKED_AS_DONE,
+ homeworkToMarkAsDone.getDescription(), student.getName().toString()));
+ student.markHomeworkAsDone(targetIndex);
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof MarkHomeworkAsDoneCommand // instanceof handles nulls
+ && predicate.equals(((MarkHomeworkAsDoneCommand) other).predicate)); // state check
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/homework/MarkHomeworkAsUndoCommand.java b/src/main/java/seedu/address/logic/commands/homework/MarkHomeworkAsUndoCommand.java
new file mode 100644
index 00000000000..37809b52b41
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/homework/MarkHomeworkAsUndoCommand.java
@@ -0,0 +1,133 @@
+package seedu.address.logic.commands.homework;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.commands.CommandUtil.handleDuplicateName;
+import static seedu.address.logic.commands.CommandUtil.handleNonExistName;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.util.List;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.student.Homework;
+import seedu.address.model.student.NamePredicate;
+import seedu.address.model.student.Student;
+
+/**
+ * Marks a homework as undone for a student.
+ */
+public class MarkHomeworkAsUndoCommand extends Command {
+ public static final String COMMAND_WORD = "unmark-homework";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Marks a homework as undone for a student.\n"
+ + "Parameters: "
+ + PREFIX_NAME + "STUDENT_NAME "
+ + PREFIX_INDEX + "INDEX\n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_NAME + "John Doe "
+ + PREFIX_INDEX + "1";
+ private final NamePredicate predicate;
+ private final Index targetIndex;
+ private final List names;
+
+ /**
+ * Creates a MarkHomeworkAsUndoCommand to mark the specified homework as undone for the specified student.
+ */
+ public MarkHomeworkAsUndoCommand(List names, NamePredicate predicate, Index targetIndex) {
+ requireNonNull(predicate);
+ requireNonNull(targetIndex);
+
+ this.predicate = predicate;
+ this.targetIndex = targetIndex;
+ this.names = names;
+ }
+
+ /**
+ * Executes the command and returns the result message.
+ *
+ * @param model {@code Model} which the command should operate on.
+ * @return feedback message of the operation result for display
+ * @throws CommandException if the command's preconditions are not met
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+
+ handleNonExistName(model, names);
+ handleDuplicateName(model, names);
+ model.updateFilteredStudentList(predicate);
+
+ List studentList = model.getFilteredStudentList();
+ String message = formatString(studentList);
+
+ return new CommandResult(message);
+ }
+
+ /**
+ * Formats the string to be returned by the command.
+ *
+ * @param studentList List of students to be formatted.
+ * @return String to be returned by the command.
+ * @throws CommandException if the command's preconditions are not met
+ */
+ public String formatString(List studentList) throws CommandException {
+ if (studentList.isEmpty()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_NAME);
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (Student student : studentList) {
+ unmarkHomework(sb, student);
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Marks the homework as undone for the specified student.
+ *
+ * @param sb StringBuilder to append the result message.
+ * @param student Student to be marked as undone.
+ * @throws CommandException if the command's preconditions are not met
+ */
+ public void unmarkHomework(StringBuilder sb, Student student) throws CommandException {
+ try {
+ Homework homeworkToMarkAsUndone = student.getHomework(targetIndex);
+ differentiateHomework(sb, homeworkToMarkAsUndone, student);
+ } catch (IndexOutOfBoundsException e) {
+ throw new CommandException(Messages.MESSAGE_INVALID_HOMEWORK_DISPLAYED_INDEX);
+ }
+ }
+
+ /**
+ * Differentiates between marking a homework as undone for a student who has not completed the homework
+ * and marking a homework as undone for a student who has completed the homework.
+ *
+ * @param sb StringBuilder to append the result message.
+ * @param homeworkToMarkAsUndone Homework to be marked as undone.
+ * @param student Student to be marked as undone.
+ */
+ public void differentiateHomework(StringBuilder sb, Homework homeworkToMarkAsUndone, Student student) {
+ if (!homeworkToMarkAsUndone.isCompleted()) {
+ sb.append(String.format(Messages.MESSAGE_HOMEWORK_ALREADY_MARKED_AS_UNDONE,
+ homeworkToMarkAsUndone.getDescription(), student.getName().toString()));
+ } else {
+ sb.append(String.format(Messages.MESSAGE_HOMEWORK_MARKED_AS_UNDONE,
+ homeworkToMarkAsUndone.getDescription(), student.getName().toString()));
+ student.markHomeworkAsUndone(targetIndex);
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof MarkHomeworkAsUndoCommand // instanceof handles nulls
+ && predicate.equals(((MarkHomeworkAsUndoCommand) other).predicate)); // state check
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/homework/UpdateHomeworkCommand.java b/src/main/java/seedu/address/logic/commands/homework/UpdateHomeworkCommand.java
new file mode 100644
index 00000000000..c6730931686
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/homework/UpdateHomeworkCommand.java
@@ -0,0 +1,129 @@
+package seedu.address.logic.commands.homework;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.commands.CommandUtil.handleDuplicateName;
+import static seedu.address.logic.commands.CommandUtil.handleNonExistName;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_HOMEWORK;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.Optional;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.student.Homework;
+import seedu.address.model.student.NamePredicate;
+import seedu.address.model.student.Student;
+import seedu.address.model.student.exceptions.DuplicateEntryException;
+
+/**
+ * Update the information of an existing homework.
+ */
+public class UpdateHomeworkCommand extends Command {
+ public static final String COMMAND_WORD = "update-homework";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Update the information of an existing homework.\n"
+ + "Parameters: "
+ + PREFIX_NAME + "STUDENT_NAME "
+ + PREFIX_INDEX + "HOMEWORK_INDEX "
+ + PREFIX_HOMEWORK + "HOMEWORK_NAME "
+ + PREFIX_DEADLINE + "DEADLINE\n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_NAME + "John Doe "
+ + PREFIX_INDEX + "1 "
+ + PREFIX_HOMEWORK + "Math Homework ";
+ private static final DateTimeFormatter PRINT_FORMATTER = DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm");
+ private final Index index;
+ private final Optional homeworkName;
+ private final Optional deadline;
+ private final NamePredicate predicate;
+ private final List names;
+
+ /**
+ * Creates an UpdateHomeworkCommand to update the information of an existing homework.
+ *
+ * @param index of the homework in the filtered homework list to update
+ * @param predicate of the student to update the homework
+ * @param homeworkName of the homework to be updated to
+ * @param deadline of the homework to be updated to
+ */
+ public UpdateHomeworkCommand(List names, Index index, NamePredicate predicate,
+ Optional homeworkName, Optional deadline) {
+ requireNonNull(predicate);
+ requireNonNull(index);
+
+ this.index = index;
+ this.predicate = predicate;
+ this.homeworkName = homeworkName;
+ this.deadline = deadline;
+ this.names = names;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+
+ handleNonExistName(model, names);
+ handleDuplicateName(model, names);
+ model.updateFilteredStudentList(predicate);
+
+ List studentList = model.getFilteredStudentList();
+ Student student = studentList.get(0);
+
+ try {
+ Homework homeworkToUpdate = student.getHomework(index);
+ String newHomeworkName = this.homeworkName.orElse(homeworkToUpdate.getDescription());
+ LocalDateTime newDeadline = this.deadline.orElse(homeworkToUpdate.getDeadline());
+ Homework newHomework = new Homework(newHomeworkName, newDeadline);
+ if (homeworkToUpdate.isCompleted()) {
+ newHomework.markAsDone();
+ }
+ updateHomework(student, homeworkToUpdate, newHomework);
+
+ return new CommandResult(
+ String.format(Messages.MESSAGE_HOMEWORK_UPDATED_SUCCESS, index.getOneBased(),
+ student.getName().getFirstName(), newHomeworkName, newDeadline.format(PRINT_FORMATTER)));
+ } catch (IndexOutOfBoundsException e) {
+ throw new CommandException(Messages.MESSAGE_INVALID_HOMEWORK_DISPLAYED_INDEX);
+ }
+ }
+
+ /**
+ * Updates the homework of the student.
+ *
+ * @param student to update the homework
+ * @param homeworkToUpdate to be updated
+ * @param newHomework to be updated to
+ * @throws DuplicateEntryException if the homework to be updated to already exists
+ * @throws CommandException if the homework to be updated does not exist
+ */
+ public void updateHomework(Student student, Homework homeworkToUpdate, Homework newHomework)
+ throws DuplicateEntryException, CommandException {
+ try {
+ student.setHomework(homeworkToUpdate, newHomework);
+ } catch (IndexOutOfBoundsException e) {
+ throw new CommandException(Messages.MESSAGE_INVALID_HOMEWORK_DISPLAYED_INDEX);
+ } catch (DuplicateEntryException e) {
+ throw new CommandException(String.format(Messages.MESSAGE_RESULT_IN_DUPLICATE, "homework"));
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof UpdateHomeworkCommand // instanceof handles nulls
+ && predicate.equals(((UpdateHomeworkCommand) other).predicate)
+ && index == ((UpdateHomeworkCommand) other).index
+ && homeworkName.equals(((UpdateHomeworkCommand) other).homeworkName)
+ && deadline.equals(((UpdateHomeworkCommand) other).deadline));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/homework/ViewHomeworkCommand.java b/src/main/java/seedu/address/logic/commands/homework/ViewHomeworkCommand.java
new file mode 100644
index 00000000000..04b0d653343
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/homework/ViewHomeworkCommand.java
@@ -0,0 +1,145 @@
+package seedu.address.logic.commands.homework;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.commands.CommandUtil.handleDuplicateName;
+import static seedu.address.logic.commands.CommandUtil.handleNonExistName;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STATUS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.student.Homework;
+import seedu.address.model.student.Student;
+
+/**
+ * Finds and lists all homework in the homework tracker that match the given name and status keywords.
+ * Displays a list of homework with the ability to filter by student name and homework status.
+ * Keyword matching is case-insensitive.
+ */
+public class ViewHomeworkCommand extends Command {
+ public static final String COMMAND_WORD = "view-homework";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all homework that match the specified "
+ + "name and status keywords (case-insensitive) and displays them as a list with index numbers.\n"
+ + "Parameters: "
+ + PREFIX_NAME + "STUDENT_NAME "
+ + PREFIX_STATUS + "STATUS\n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_NAME + "John Doe "
+ + PREFIX_STATUS + "pending";
+ private static final String SEPERATOR = "--------------------------------------------------\n";
+ private static final String DOT = ". ";
+ private static final String LINE_BREAK = "\n";
+ private static final String NAME_LABEL = "%s:\n";
+ private static final Predicate SHOW_ALL_HOMEWORK = homework -> true;
+ private final Predicate namePredicate;
+ private final Predicate homeworkStatusPredicate;
+ private final boolean defaultPredicateFlag;
+ private final List names;
+
+
+ /**
+ * Overloaded constructor for ViewHomeworkCommand.
+ *
+ * @param namePredicate Predicate to filter students by name.
+ */
+ public ViewHomeworkCommand(List names, Predicate namePredicate, boolean defaultPredicateFlag) {
+ this.namePredicate = namePredicate;
+ this.homeworkStatusPredicate = SHOW_ALL_HOMEWORK;
+ this.defaultPredicateFlag = defaultPredicateFlag;
+ this.names = names;
+ }
+
+ /**
+ * Overloaded constructor for ViewHomeworkCommand.
+ *
+ * @param namePredicate Predicate to filter students by name.
+ * @param homeworkStatusPredicate Predicate to filter homework by status.
+ */
+ public ViewHomeworkCommand(List names, Predicate namePredicate,
+ Predicate homeworkStatusPredicate, boolean defaultPredicateFlag) {
+ this.namePredicate = namePredicate;
+ this.homeworkStatusPredicate = homeworkStatusPredicate;
+ this.defaultPredicateFlag = defaultPredicateFlag;
+ this.names = names;
+ }
+
+ /**
+ * Executes the view-homework command and returns the result message.
+ *
+ * @param model {@code Model} which the command should operate on.
+ * @return feedback message of the operation result for display
+ * @throws CommandException if an error occurs during command execution.
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+
+ handleNonExistName(model, names);
+ handleDuplicateName(model, names);
+ model.updateFilteredStudentList(namePredicate);
+
+ List studentList = model.getFilteredStudentList();
+
+ int numberOfHomework = 0;
+ StringBuilder sb = new StringBuilder();
+
+ for (Student student : studentList) {
+ List homeworkList = student.getFilteredHomeworkList(homeworkStatusPredicate);
+ if (!homeworkList.isEmpty()) {
+ sb.append(SEPERATOR);
+ sb.append(String.format(NAME_LABEL, student.getName()));
+ numberOfHomework += homeworkList.size();
+ formatHomeworkList(homeworkList, sb);
+ }
+ }
+
+ handleNoHomework(numberOfHomework);
+ return new CommandResult(
+ String.format(Messages.MESSAGE_ALL_HOMEWORK_LISTED_OVERVIEW, numberOfHomework, sb.toString()));
+ }
+
+ /**
+ * Formats the homework list into a string.
+ *
+ * @param homeworkList List of homework to be formatted.
+ * @param sb StringBuilder to append the formatted homework list to.
+ */
+ public void formatHomeworkList(List homeworkList, StringBuilder sb) {
+ for (int i = 0; i < homeworkList.size(); i++) {
+ sb.append(i + 1);
+ sb.append(DOT);
+ sb.append(homeworkList.get(i).toString());
+ sb.append(LINE_BREAK);
+ }
+ }
+
+ /**
+ * Handles the case where no homework is found.
+ *
+ * @param numberOfHomework Number of homework found.
+ * @throws CommandException If no homework is found.
+ */
+ public void handleNoHomework(int numberOfHomework) throws CommandException {
+ // If no homework is found, throw an exception
+ if (numberOfHomework == 0) {
+ throw new CommandException(Messages.MESSAGE_NO_HOMEWORK_FOUND);
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof ViewHomeworkCommand // instanceof handles nulls
+ && namePredicate.equals(((ViewHomeworkCommand) other).namePredicate)
+ && homeworkStatusPredicate.equals(((ViewHomeworkCommand) other).homeworkStatusPredicate)
+ && defaultPredicateFlag == ((ViewHomeworkCommand) other).defaultPredicateFlag); // state check
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/lesson/CreateLessonCommand.java b/src/main/java/seedu/address/logic/commands/lesson/CreateLessonCommand.java
new file mode 100644
index 00000000000..649c58c7664
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/lesson/CreateLessonCommand.java
@@ -0,0 +1,145 @@
+package seedu.address.logic.commands.lesson;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ENDTIME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_LESSON;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STARTTIME;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.List;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.student.Lesson;
+import seedu.address.model.student.NamePredicate;
+import seedu.address.model.student.Student;
+import seedu.address.model.student.exceptions.ConflictingLessonsException;
+
+/**
+ * Adds a lesson to a student.
+ */
+public class CreateLessonCommand extends Command {
+
+ public static final String COMMAND_WORD = "new-lesson";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a lesson to students.\n"
+ + "Parameters: "
+ + PREFIX_NAME + "STUDENT_NAME "
+ + PREFIX_LESSON + "LESSON_NAME "
+ + PREFIX_STARTTIME + "Start time "
+ + PREFIX_ENDTIME + "End time\n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_NAME + "John Doe "
+ + PREFIX_LESSON + "Math Lesson "
+ + PREFIX_STARTTIME + "2023-05-21 12:00 "
+ + PREFIX_ENDTIME + "2023-05-21 14:00";
+
+ public static final String MESSAGE_DATE = "endTime must be after startTime, both in the format YYYY-MM-DD HH:mm";
+
+ private final String lessonName;
+ private final LocalDateTime startTime;
+ private final LocalDateTime endTime;
+ private final NamePredicate predicate;
+ private final List names;
+
+ /**
+ * Creates a CreateLessonCommand to add the specified {@code Lesson}
+ */
+ public CreateLessonCommand(List names, NamePredicate predicate, String lessonName, LocalDateTime startTime,
+ LocalDateTime endTime) {
+ requireNonNull(lessonName);
+ requireNonNull(startTime);
+ requireNonNull(endTime);
+ requireNonNull(predicate);
+
+ this.lessonName = lessonName;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.predicate = predicate;
+ this.names = names;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+
+ StringBuilder nonExistNames = new StringBuilder();
+ for (String name : names) {
+ if (model.noSuchStudent(name)) {
+ nonExistNames.append(name).append(", ");
+ }
+ }
+ if (nonExistNames.length() != 0) {
+ nonExistNames = new StringBuilder(nonExistNames.substring(0, nonExistNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_NO_SUCH_STUDENT, nonExistNames));
+ }
+ StringBuilder dupNames = new StringBuilder();
+ for (String name : names) {
+ if (model.hasDuplicateName(name)) {
+ dupNames.append(name).append(", ");
+ }
+ }
+ if (dupNames.length() != 0) {
+ dupNames = new StringBuilder(dupNames.substring(0, dupNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_HAS_DUPLICATE_NAMES, dupNames));
+ }
+
+ Lesson lesson = new Lesson(lessonName, startTime, endTime);
+
+ if (model.hasConflictingLessonTime(lesson)) {
+ throw new CommandException(Messages.MESSAGE_CONFLICTING_LESSON_TIME);
+ }
+
+ model.updateFilteredStudentList(predicate);
+
+ List studentList = model.getFilteredStudentList();
+
+ if (startTime.isBefore(LocalDateTime.now())) {
+ throw new CommandException("start time must be in the future.");
+ }
+
+ if (startTime.isAfter(endTime)) {
+ throw new CommandException(Messages.MESSAGE_INVALID_EXAM_TIME);
+ }
+
+ if (Duration.between(startTime, endTime).toMinutes() < 30 || Duration.between(startTime,
+ endTime).toHours() > 3) {
+ throw new CommandException(Messages.MESSAGE_INVALID_LESSON_DURATION);
+ }
+
+ try {
+ for (Student student : studentList) {
+ student.addLesson(lesson);
+ }
+ } catch (ConflictingLessonsException e) {
+ throw new CommandException(e.getMessage());
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (Student student : studentList) {
+ sb.append(student.getName().fullName);
+ sb.append("\n");
+ }
+
+ return new CommandResult(
+ String.format(Messages.MESSAGE_LESSON_ADDED_SUCCESS, lesson, sb));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof CreateLessonCommand // instanceof handles nulls
+ && predicate.equals(((CreateLessonCommand) other).predicate)
+ && lessonName.equals(((CreateLessonCommand) other).lessonName)
+ && startTime.equals(((CreateLessonCommand) other).startTime)
+ && endTime.equals(((CreateLessonCommand) other).endTime));
+ }
+}
+
diff --git a/src/main/java/seedu/address/logic/commands/lesson/DeleteLessonCommand.java b/src/main/java/seedu/address/logic/commands/lesson/DeleteLessonCommand.java
new file mode 100644
index 00000000000..d2988919203
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/lesson/DeleteLessonCommand.java
@@ -0,0 +1,110 @@
+package seedu.address.logic.commands.lesson;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.util.List;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.student.NamePredicate;
+import seedu.address.model.student.Student;
+
+/**
+ * Deletes a lesson from a student.
+ */
+public class DeleteLessonCommand extends Command {
+
+ public static final String COMMAND_WORD = "delete-lesson";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deletes a lesson from a student.\n"
+ + "Parameters: "
+ + PREFIX_NAME + "STUDENT_NAME "
+ + PREFIX_INDEX + "INDEX\n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_NAME + "John Doe "
+ + PREFIX_INDEX + "1";
+
+ private final NamePredicate predicate;
+ private final Index targetIndex;
+ private final List names;
+
+
+ /**
+ * Creates a DeleteLessonCommand to delete the specified lesson from the specified student.
+ */
+ public DeleteLessonCommand(List inputNames, NamePredicate predicate, Index targetIndex) {
+ requireNonNull(predicate);
+ requireNonNull(targetIndex);
+
+ this.names = inputNames;
+ this.predicate = predicate;
+ this.targetIndex = targetIndex;
+
+ }
+
+ /**
+ * Executes the command and returns the result message.
+ *
+ * @param model {@code Model} which the command should operate on.
+ * @return feedback message of the operation result for display
+ * @throws CommandException if the command's preconditions are not met
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+
+ StringBuilder nonExistNames = new StringBuilder();
+ for (String name : names) {
+ if (model.noSuchStudent(name)) {
+ nonExistNames.append(name).append(", ");
+ }
+ }
+ if (nonExistNames.length() != 0) {
+ nonExistNames = new StringBuilder(nonExistNames.substring(0, nonExistNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_NO_SUCH_STUDENT, nonExistNames));
+ }
+ StringBuilder dupNames = new StringBuilder();
+ for (String name : names) {
+ if (model.hasDuplicateName(name)) {
+ dupNames.append(name).append(", ");
+ }
+ }
+ if (dupNames.length() != 0) {
+ dupNames = new StringBuilder(dupNames.substring(0, dupNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_HAS_DUPLICATE_NAMES, dupNames));
+ }
+ model.updateFilteredStudentList(predicate);
+
+ List studentList = model.getFilteredStudentList();
+
+ StringBuilder sb = new StringBuilder();
+ for (Student student : studentList) {
+ try {
+ sb.append(String.format(Messages.MESSAGE_LESSON_DELETED_SUCCESS, targetIndex.getOneBased(),
+ student.getLesson(targetIndex).toString(), student.getName().toString()));
+ sb.append("\n");
+ student.deleteLesson(targetIndex);
+ } catch (IndexOutOfBoundsException e) {
+ throw new CommandException(Messages.MESSAGE_INVALID_LESSON_DISPLAYED_INDEX);
+ }
+ }
+
+ return new CommandResult(sb.toString());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof DeleteLessonCommand // instanceof handles nulls
+ && predicate.equals(((DeleteLessonCommand) other).predicate));
+ }
+}
+
diff --git a/src/main/java/seedu/address/logic/commands/lesson/UpdateLessonCommand.java b/src/main/java/seedu/address/logic/commands/lesson/UpdateLessonCommand.java
new file mode 100644
index 00000000000..f210605fe26
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/lesson/UpdateLessonCommand.java
@@ -0,0 +1,151 @@
+package seedu.address.logic.commands.lesson;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ENDTIME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_LESSON;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STARTTIME;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Optional;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.student.Lesson;
+import seedu.address.model.student.NamePredicate;
+import seedu.address.model.student.Student;
+
+/**
+ * Updates the information of an existing lesson.
+ */
+public class UpdateLessonCommand extends Command {
+ public static final String COMMAND_WORD = "update-lesson";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Update the information of an existing lesson.\n"
+ + "Parameters: "
+ + PREFIX_NAME + "STUDENT_NAME "
+ + PREFIX_INDEX + "LESSON_INDEX "
+ + PREFIX_LESSON + "HOMEWORK_NAME "
+ + PREFIX_STARTTIME + "START TIME"
+ + PREFIX_ENDTIME + "END TIME\n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_NAME + "John Doe "
+ + PREFIX_INDEX + "1 "
+ + PREFIX_LESSON + "Math Lesson ";
+
+ private final Index index;
+ private final Optional lessonName;
+ private final Optional startTime;
+ private final Optional endTime;
+ private final NamePredicate predicate;
+ private final List names;
+
+ /**
+ * Creates a UpdateLessonCommand to update the specified {@code Lesson}
+ */
+ public UpdateLessonCommand(List names, Index index, NamePredicate predicate,
+ Optional lessonName, Optional startTime,
+ Optional endTime) {
+ requireNonNull(predicate);
+ requireNonNull(index);
+
+ this.index = index;
+ this.predicate = predicate;
+ this.lessonName = lessonName;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.names = names;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+
+ StringBuilder nonExistNames = new StringBuilder();
+ for (String name : names) {
+ if (model.noSuchStudent(name)) {
+ nonExistNames.append(name).append(", ");
+ }
+ }
+ if (nonExistNames.length() != 0) {
+ nonExistNames = new StringBuilder(nonExistNames.substring(0, nonExistNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_NO_SUCH_STUDENT, nonExistNames));
+ }
+ StringBuilder dupNames = new StringBuilder();
+ for (String name : names) {
+ if (model.hasDuplicateName(name)) {
+ dupNames.append(name).append(", ");
+ }
+ }
+ if (dupNames.length() != 0) {
+ dupNames = new StringBuilder(dupNames.substring(0, dupNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_HAS_DUPLICATE_NAMES, dupNames));
+ }
+ model.updateFilteredStudentList(predicate);
+ List studentList = model.getFilteredStudentList();
+
+ Student student = studentList.get(0);
+ Lesson lessonToUpdate;
+ try {
+ lessonToUpdate = student.getLesson(index);
+ } catch (IndexOutOfBoundsException e) {
+ throw new CommandException(Messages.MESSAGE_INVALID_LESSON_DISPLAYED_INDEX);
+ }
+
+ String newLessonName = this.lessonName.orElse(lessonToUpdate.getTitle());
+ LocalDateTime newStartTime = this.startTime.orElse(lessonToUpdate.getStartTime());
+ LocalDateTime newEndTime = this.endTime.orElse(lessonToUpdate.getEndTime());
+ if (newStartTime.isAfter(newEndTime)) {
+ throw new CommandException(Messages.MESSAGE_INVALID_LESSON_TIME);
+ }
+
+ if (Duration.between(newStartTime, newEndTime).toMinutes() < 30 || Duration.between(newStartTime,
+ newEndTime).toHours() > 3) {
+ throw new CommandException(Messages.MESSAGE_INVALID_LESSON_DURATION);
+ }
+
+ Lesson newLesson = new Lesson(newLessonName, newStartTime, newEndTime);
+ model.updateFilteredStudentList(s -> s != student);
+
+ if (model.hasConflictingLessonTime(newLesson)) {
+ throw new CommandException(Messages.MESSAGE_CONFLICTING_LESSON_TIME);
+ }
+
+ if (model.hasConflictingExamTime(newLesson)) {
+ throw new CommandException(Messages.MESSAGE_CONFLICTING_EXAM_TIME);
+ }
+
+ try {
+ student.setLesson(index.getZeroBased(), newLesson);
+ } catch (Exception e) {
+ throw new CommandException(e.getMessage());
+ }
+ model.updateFilteredStudentList(s -> s == student);
+ return new CommandResult(
+ String.format(Messages.MESSAGE_LESSON_UPDATED_SUCCESS, index.getOneBased(),
+ student.getName().getFirstName(),
+ newLessonName, newStartTime, newEndTime));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof UpdateLessonCommand // instanceof handles nulls
+ && predicate.equals(((UpdateLessonCommand) other).predicate)
+ && index.equals(((UpdateLessonCommand) other).index)
+ && lessonName.equals(((UpdateLessonCommand) other).lessonName)
+ && startTime.orElse(LocalDateTime.parse("2090-04-10T08:30:00")).isEqual((((UpdateLessonCommand) other))
+ .startTime.orElse(LocalDateTime.parse("2090-04-10T08:30:00")))
+ && endTime.orElse(LocalDateTime.parse("2090-04-10T08:30:00")).isEqual((((UpdateLessonCommand) other))
+ .endTime.orElse(LocalDateTime.parse("2090-04-10T08:30:00"))));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/lesson/ViewLessonCommand.java b/src/main/java/seedu/address/logic/commands/lesson/ViewLessonCommand.java
new file mode 100644
index 00000000000..c1cdd2411cf
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/lesson/ViewLessonCommand.java
@@ -0,0 +1,171 @@
+package seedu.address.logic.commands.lesson;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_LESSON;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.student.Lesson;
+import seedu.address.model.student.Student;
+
+/**
+ * Finds and lists all lessons in address book whose name contains any of the argument keywords.
+ * Keyword matching is case insensitive.
+ */
+public class ViewLessonCommand extends Command {
+ public static final String COMMAND_WORD = "view-lesson";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all lessons filtered by\n"
+ + "* name of student (case-insensitive) and/or\n"
+ + "* date and/or\n"
+ + "* lesson title and/or\n"
+ + "* whether it is done\n"
+ + "and displays them as a list with index numbers.\n"
+ + "Parameters: "
+ + PREFIX_NAME + "STUDENT_NAME "
+ + PREFIX_DATE + "DATE "
+ + PREFIX_LESSON + "LESSON "
+ + PREFIX_DONE + "COMPLETED KEYWORD\n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_NAME + "John Doe "
+ + PREFIX_DATE + "2023-05-21 "
+ + PREFIX_LESSON + "Math "
+ + PREFIX_DONE + "done(or not done)";
+ private static final String SEPERATOR = "--------------------------------------------------\n";
+ private static final Predicate SHOW_ALL_LESSONS = lesson -> true;
+
+ private final Predicate namePredicate;
+ private final Predicate lessonDatePredicate;
+ private final Predicate subjectPredicate;
+ private final Predicate donePredicate;
+ private final boolean defaultPredicateFlag;
+ private final List names;
+
+
+ /**
+ * Overloaded constructor for ViewLessonCommand.
+ *
+ * @param namePredicate Predicate to filter students by name.
+ * @param defaultPredicateFlag Flag to indicate if the default predicate is used.
+ */
+ public ViewLessonCommand(List names, Predicate namePredicate, Predicate subjectPredicate,
+ Predicate donePredicate, boolean defaultPredicateFlag) {
+ this.names = names;
+ this.namePredicate = namePredicate;
+ this.lessonDatePredicate = SHOW_ALL_LESSONS;
+ this.defaultPredicateFlag = defaultPredicateFlag;
+ this.subjectPredicate = subjectPredicate;
+ this.donePredicate = donePredicate;
+ }
+
+ /**
+ * Overloaded constructor for ViewLessonCommand.
+ *
+ * @param namePredicate Predicate to filter students by name.
+ * @param lessonDatePredicate Predicate to filter lessons by date.
+ * @param defaultPredicateFlag Flag to indicate if the default predicate is used.
+ */
+ public ViewLessonCommand(List names, Predicate namePredicate,
+ Predicate lessonDatePredicate, Predicate subjectPredicate,
+ Predicate donePredicate,
+ boolean defaultPredicateFlag) {
+ this.names = names;
+ this.namePredicate = namePredicate;
+ this.lessonDatePredicate = lessonDatePredicate;
+ this.defaultPredicateFlag = defaultPredicateFlag;
+ this.subjectPredicate = subjectPredicate;
+ this.donePredicate = donePredicate;
+ }
+
+ /**
+ * Executes the view-lesson command and returns the result message.
+ *
+ * @param model {@code Model} which the command should operate on.
+ * @return feedback message of the operation result for display
+ * @throws CommandException if an error occurs during command execution.
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+
+ StringBuilder nonExistNames = new StringBuilder();
+ for (String name : names) {
+ if (model.noSuchStudent(name)) {
+ nonExistNames.append(name).append(", ");
+ }
+ }
+ if (nonExistNames.length() != 0) {
+ nonExistNames = new StringBuilder(nonExistNames.substring(0, nonExistNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_NO_SUCH_STUDENT, nonExistNames));
+ }
+ StringBuilder dupNames = new StringBuilder();
+ for (String name : names) {
+ if (model.hasDuplicateName(name)) {
+ dupNames.append(name).append(", ");
+ }
+ }
+ if (dupNames.length() != 0) {
+ dupNames = new StringBuilder(dupNames.substring(0, dupNames.length() - 2));
+ throw new CommandException(String.format(Messages.MESSAGE_HAS_DUPLICATE_NAMES, dupNames));
+ }
+ model.updateFilteredStudentList(namePredicate);
+
+ List studentList = model.getFilteredStudentList();
+
+ int numberOfStudents = studentList.size();
+ int numOfLessons = 0;
+ StringBuilder sb = new StringBuilder();
+ sb.append(SEPERATOR);
+
+ // Loop through each student and add their lesson to the string builder
+ for (Student student : studentList) {
+ List lessonList = student.getFilteredLessonsList(lessonDatePredicate, subjectPredicate,
+ donePredicate);
+ if (!lessonList.isEmpty()) {
+ sb.append(student.getName().fullName).append(":\n");
+
+ numOfLessons += lessonList.size();
+
+ for (int i = 0; i < lessonList.size(); i++) {
+ sb.append(i + 1).append(". ").append(lessonList.get(i)).append("\n");
+ }
+
+ sb.append(SEPERATOR);
+ }
+ }
+
+ // If no lessons are found, throw an exception
+ if (numOfLessons == 0) {
+ throw new CommandException(Messages.MESSAGE_NO_LESSON_FOUND);
+ }
+
+ // If the default predicate is used, display a different message
+ if (defaultPredicateFlag) {
+ return new CommandResult(
+ String.format(Messages.MESSAGE_ALL_LESSONS_LISTED_OVERVIEW, numOfLessons, sb));
+ } else {
+ return new CommandResult(
+ String.format(Messages.MESSAGE_LESSONS_LISTED_OVERVIEW, numOfLessons, numberOfStudents, sb));
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof ViewLessonCommand // instanceof handles nulls
+ && namePredicate.equals(((ViewLessonCommand) other).namePredicate)
+ && lessonDatePredicate.equals(((ViewLessonCommand) other).lessonDatePredicate)
+ && defaultPredicateFlag == ((ViewLessonCommand) other).defaultPredicateFlag);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
index 3b8bfa035e8..33c2b86f429 100644
--- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
@@ -3,20 +3,27 @@
import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GRADELEVEL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SCHOOL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneAddress;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneEmail;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOnePhone;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNUllName;
+import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
import seedu.address.logic.commands.AddCommand;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.Address;
-import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
+import seedu.address.model.student.Address;
+import seedu.address.model.student.Email;
+import seedu.address.model.student.Name;
+import seedu.address.model.student.Phone;
+import seedu.address.model.student.Student;
import seedu.address.model.tag.Tag;
/**
@@ -31,20 +38,40 @@ public class AddCommandParser implements Parser {
*/
public AddCommand parse(String args) throws ParseException {
ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL,
+ PREFIX_ADDRESS, PREFIX_TAG, PREFIX_GRADELEVEL,
+ PREFIX_SCHOOL);
if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL)
|| !argMultimap.getPreamble().isEmpty()) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
}
+ checkUniqueNotNUllName(argMultimap);
+ checkMaxOneAddress(argMultimap);
+ checkMaxOnePhone(argMultimap);
+ checkMaxOneEmail(argMultimap);
+
Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get());
Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get());
Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get());
Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get());
Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));
- Person person = new Person(name, phone, email, address, tagList);
+ //Add school Tag if sc/ present
+ Optional schoolOptional = argMultimap.getValue(PREFIX_SCHOOL);
+ if (!schoolOptional.isEmpty()) {
+ Tag school = ParserUtil.parseTag(schoolOptional.get());
+ tagList.add(school);
+ }
+ //Add grade level Tag if lv/ present
+ Optional gradeLevelOptional = argMultimap.getValue(PREFIX_GRADELEVEL);
+ if (!gradeLevelOptional.isEmpty()) {
+ Tag gradeLevel = ParserUtil.parseTag(gradeLevelOptional.get());
+ tagList.add(gradeLevel);
+ }
+
+ Student person = new Student(name, phone, email, address, tagList);
return new AddCommand(person);
}
diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
index 1e466792b46..ac94af2d011 100644
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
@@ -15,7 +15,36 @@
import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.ViewProfileCommand;
+import seedu.address.logic.commands.exam.CreateExamCommand;
+import seedu.address.logic.commands.exam.DeleteExamCommand;
+import seedu.address.logic.commands.exam.UpdateExamCommand;
+import seedu.address.logic.commands.exam.ViewExamCommand;
+import seedu.address.logic.commands.homework.CreateHomeworkCommand;
+import seedu.address.logic.commands.homework.DeleteHomeworkCommand;
+import seedu.address.logic.commands.homework.MarkHomeworkAsDoneCommand;
+import seedu.address.logic.commands.homework.MarkHomeworkAsUndoCommand;
+import seedu.address.logic.commands.homework.UpdateHomeworkCommand;
+import seedu.address.logic.commands.homework.ViewHomeworkCommand;
+import seedu.address.logic.commands.lesson.CreateLessonCommand;
+import seedu.address.logic.commands.lesson.DeleteLessonCommand;
+import seedu.address.logic.commands.lesson.UpdateLessonCommand;
+import seedu.address.logic.commands.lesson.ViewLessonCommand;
+import seedu.address.logic.parser.exam.CreateExamCommandParser;
+import seedu.address.logic.parser.exam.DeleteExamCommandParser;
+import seedu.address.logic.parser.exam.UpdateExamCommandParser;
+import seedu.address.logic.parser.exam.ViewExamCommandParser;
import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.logic.parser.homework.CreateHomeworkCommandParser;
+import seedu.address.logic.parser.homework.DeleteHomeworkCommandParser;
+import seedu.address.logic.parser.homework.MarkHomeworkAsDoneCommandParser;
+import seedu.address.logic.parser.homework.MarkHomeworkAsUndoCommandParser;
+import seedu.address.logic.parser.homework.UpdateHomeworkCommandParser;
+import seedu.address.logic.parser.homework.ViewHomeworkCommandParser;
+import seedu.address.logic.parser.lesson.CreateLessonCommandParser;
+import seedu.address.logic.parser.lesson.DeleteLessonCommandParser;
+import seedu.address.logic.parser.lesson.UpdateLessonCommandParser;
+import seedu.address.logic.parser.lesson.ViewLessonCommandParser;
/**
* Parses user input.
@@ -68,9 +97,56 @@ public Command parseCommand(String userInput) throws ParseException {
case HelpCommand.COMMAND_WORD:
return new HelpCommand();
+ // Homework commands
+ case CreateHomeworkCommand.COMMAND_WORD:
+ return new CreateHomeworkCommandParser().parse(arguments);
+
+ case ViewHomeworkCommand.COMMAND_WORD:
+ return new ViewHomeworkCommandParser().parse(arguments);
+
+ case MarkHomeworkAsUndoCommand.COMMAND_WORD:
+ return new MarkHomeworkAsUndoCommandParser().parse(arguments);
+
+ case DeleteHomeworkCommand.COMMAND_WORD:
+ return new DeleteHomeworkCommandParser().parse(arguments);
+
+ case MarkHomeworkAsDoneCommand.COMMAND_WORD:
+ return new MarkHomeworkAsDoneCommandParser().parse(arguments);
+
+ case UpdateHomeworkCommand.COMMAND_WORD:
+ return new UpdateHomeworkCommandParser().parse(arguments);
+
+ // Lesson commands
+ case ViewLessonCommand.COMMAND_WORD:
+ return new ViewLessonCommandParser().parse(arguments);
+
+ case CreateLessonCommand.COMMAND_WORD:
+ return new CreateLessonCommandParser().parse(arguments);
+
+ case DeleteLessonCommand.COMMAND_WORD:
+ return new DeleteLessonCommandParser().parse(arguments);
+
+ // Exam commands
+ case CreateExamCommand.COMMAND_WORD:
+ return new CreateExamCommandParser().parse(arguments);
+
+ case ViewExamCommand.COMMAND_WORD:
+ return new ViewExamCommandParser().parse(arguments);
+
+ case DeleteExamCommand.COMMAND_WORD:
+ return new DeleteExamCommandParser().parse(arguments);
+
+ case UpdateLessonCommand.COMMAND_WORD:
+ return new UpdateLessonCommandParser().parse(arguments);
+
+ case UpdateExamCommand.COMMAND_WORD:
+ return new UpdateExamCommandParser().parse(arguments);
+
+ case ViewProfileCommand.COMMAND_WORD:
+ return new ViewProfileCommandParser().parse(arguments);
+
default:
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
}
}
-
}
diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java
index 75b1a9bf119..b33b0b1dd40 100644
--- a/src/main/java/seedu/address/logic/parser/CliSyntax.java
+++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java
@@ -6,10 +6,25 @@
public class CliSyntax {
/* Prefix definitions */
- public static final Prefix PREFIX_NAME = new Prefix("n/");
- public static final Prefix PREFIX_PHONE = new Prefix("p/");
- public static final Prefix PREFIX_EMAIL = new Prefix("e/");
- public static final Prefix PREFIX_ADDRESS = new Prefix("a/");
- public static final Prefix PREFIX_TAG = new Prefix("t/");
+ public static final Prefix PREFIX_NAME = new Prefix("name/");
+ public static final Prefix PREFIX_PHONE = new Prefix("phone/");
+ public static final Prefix PREFIX_EMAIL = new Prefix("email/");
+ public static final Prefix PREFIX_ADDRESS = new Prefix("address/");
+ public static final Prefix PREFIX_GRADELEVEL = new Prefix("level/");
+ public static final Prefix PREFIX_SCHOOL = new Prefix("school/");
+ public static final Prefix PREFIX_TAG = new Prefix("tag/");
+ public static final Prefix PREFIX_HOMEWORK = new Prefix("homework/");
+ public static final Prefix PREFIX_DEADLINE = new Prefix("deadline/");
+ public static final Prefix PREFIX_EXAM = new Prefix("exam/");
+ public static final Prefix PREFIX_WEIGHT = new Prefix("weightage/");
+ public static final Prefix PREFIX_GRADE = new Prefix("grade/");
+ public static final Prefix PREFIX_STATUS = new Prefix("status/");
+ public static final Prefix PREFIX_INDEX = new Prefix("index/");
+
+ public static final Prefix PREFIX_LESSON = new Prefix("lesson/");
+ public static final Prefix PREFIX_STARTTIME = new Prefix("start/");
+ public static final Prefix PREFIX_ENDTIME = new Prefix("end/");
+ public static final Prefix PREFIX_DATE = new Prefix("date/");
+ public static final Prefix PREFIX_DONE = new Prefix("done/");
}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
index 522b93081cc..e956f24df66 100644
--- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
@@ -1,6 +1,11 @@
package seedu.address.logic.parser;
+import static java.util.Objects.requireNonNull;
import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNullIndex;
+
+import java.util.stream.Stream;
import seedu.address.commons.core.index.Index;
import seedu.address.logic.commands.DeleteCommand;
@@ -17,13 +22,31 @@ public class DeleteCommandParser implements Parser {
* @throws ParseException if the user input does not conform the expected format
*/
public DeleteCommand parse(String args) throws ParseException {
- try {
- Index index = ParserUtil.parseIndex(args);
- return new DeleteCommand(index);
- } catch (ParseException pe) {
- throw new ParseException(
- String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe);
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_INDEX);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_INDEX)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ DeleteCommand.MESSAGE_USAGE));
}
+
+ checkUniqueNotNullIndex(argMultimap);
+
+ Index index = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_INDEX).get());
+ return new DeleteCommand(index);
+ }
+
+ /**
+ * Returns true if all prefixes are present in the given {@code ArgumentMultimap}.
+ *
+ * @param argumentMultimap the argument multimap to check for prefixes.
+ * @param prefixes the prefixes to be checked.
+ * @return true if all prefixes are present in the given {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
}
}
diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
index 845644b7dea..dc59d698037 100644
--- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
@@ -4,18 +4,25 @@
import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneAddress;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneEmail;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOnePhone;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNUllName;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNullIndex;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
+import java.util.stream.Stream;
import seedu.address.commons.core.index.Index;
import seedu.address.logic.commands.EditCommand;
-import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
+import seedu.address.logic.commands.EditCommand.EditStudentDescriptor;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.tag.Tag;
@@ -32,36 +39,43 @@ public class EditCommandParser implements Parser {
public EditCommand parse(String args) throws ParseException {
requireNonNull(args);
ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
+ ArgumentTokenizer.tokenize(args, PREFIX_INDEX, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS,
+ PREFIX_TAG);
- Index index;
-
- try {
- index = ParserUtil.parseIndex(argMultimap.getPreamble());
- } catch (ParseException pe) {
- throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe);
+ if (!arePrefixesPresent(argMultimap, PREFIX_INDEX)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ EditCommand.MESSAGE_USAGE));
}
- EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
+ checkMaxOneAddress(argMultimap);
+ checkMaxOneEmail(argMultimap);
+ checkMaxOnePhone(argMultimap);
+ checkUniqueNotNullIndex(argMultimap);
+
+ Index index = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_INDEX).get());
+
+ EditCommand.EditStudentDescriptor editStudentDescriptor = new EditStudentDescriptor();
if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
- editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()));
+ checkUniqueNotNUllName(argMultimap);
+ editStudentDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()));
}
if (argMultimap.getValue(PREFIX_PHONE).isPresent()) {
- editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()));
+ editStudentDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()));
}
if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
- editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()));
+ editStudentDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()));
}
if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
- editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()));
+ editStudentDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()));
}
- parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags);
+ parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editStudentDescriptor::setTags);
- if (!editPersonDescriptor.isAnyFieldEdited()) {
+ if (!editStudentDescriptor.isAnyFieldEdited()) {
throw new ParseException(EditCommand.MESSAGE_NOT_EDITED);
}
- return new EditCommand(index, editPersonDescriptor);
+ return new EditCommand(index, editStudentDescriptor);
}
/**
@@ -79,4 +93,15 @@ private Optional> parseTagsForEdit(Collection tags) throws Pars
return Optional.of(ParserUtil.parseTags(tagSet));
}
+ /**
+ * Returns true if all prefixes are present in the given {@code ArgumentMultimap}.
+ *
+ * @param argumentMultimap the argument multimap to check for prefixes.
+ * @param prefixes the prefixes to be checked.
+ * @return true if all prefixes are present in the given {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+
}
diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java
index 4fb71f23103..7396eb95878 100644
--- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/FindCommandParser.java
@@ -1,12 +1,18 @@
package seedu.address.logic.parser;
+import static java.util.Objects.requireNonNull;
import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
-import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
import seedu.address.logic.commands.FindCommand;
+import seedu.address.logic.commands.exam.ViewExamCommand;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import seedu.address.model.student.NamePredicate;
+import seedu.address.model.student.Student;
/**
* Parses input arguments and creates a new FindCommand object
@@ -19,15 +25,38 @@ public class FindCommandParser implements Parser {
* @throws ParseException if the user input does not conform the expected format
*/
public FindCommand parse(String args) throws ParseException {
- String trimmedArgs = args.trim();
- if (trimmedArgs.isEmpty()) {
- throw new ParseException(
- String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME) || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
}
- String[] nameKeywords = trimmedArgs.split("\\s+");
+ Predicate namePredicate;
+ List nameKeywords = argMultimap.getAllValues(PREFIX_NAME);
+ // for all the names, trim the name and only take the first word
+ for (int i = 0; i < nameKeywords.size(); i++) {
+ String name = nameKeywords.get(i);
+ name = name.trim();
+ if (name.trim().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ViewExamCommand.MESSAGE_USAGE));
+ }
+ nameKeywords.set(i, name);
+ }
+ return new FindCommand(new NamePredicate(nameKeywords));
+ }
- return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
+ /**
+ * Returns true if all prefixes are present in the given {@code ArgumentMultimap}.
+ *
+ * @param argumentMultimap the argument multimap to check for prefixes.
+ * @param prefixes the prefixes to be checked.
+ * @return true if all prefixes are present in the given {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
}
}
diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java
index b117acb9c55..d301713498f 100644
--- a/src/main/java/seedu/address/logic/parser/ParserUtil.java
+++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java
@@ -1,27 +1,93 @@
package seedu.address.logic.parser;
import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ENDTIME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EXAM;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GRADE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_HOMEWORK;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_LESSON;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STARTTIME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STATUS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_WEIGHT;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
+import seedu.address.commons.core.Messages;
import seedu.address.commons.core.index.Index;
import seedu.address.commons.util.StringUtil;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.Address;
-import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
-import seedu.address.model.person.Phone;
+import seedu.address.model.student.Address;
+import seedu.address.model.student.Email;
+import seedu.address.model.student.Grade;
+import seedu.address.model.student.Homework;
+import seedu.address.model.student.Name;
+import seedu.address.model.student.Phone;
import seedu.address.model.tag.Tag;
/**
* Contains utility methods used for parsing strings in the various *Parser classes.
*/
public class ParserUtil {
-
public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer.";
+ //@@author Yufannn-reused
+ //Reused from https://github.com/RussellDash332/ip/blob/master/src/main/java/stashy/parser/Parser.java
+ //with minor modification, it is a pretty good way to organise and extend the acceptable date format.
+ private static final String[] ACCEPTABLE_DATETIME_FORMATS = {
+ "MMM dd yyyy HHmm", "MMM dd yyyy HH:mm",
+ "yyyy-MM-dd'T'HH:mm", "dd/MM/yyyy HHmm",
+ "dd/MM/yyyy HH:mm", "yyyy/MM/dd HHmm",
+ "yyyy/MM/dd HH:mm", "yyyy/MM/dd'T'HHmm",
+ "yyyy/MM/dd'T'HH:mm", "yyyy-MM-dd HHmm",
+ "yyyy-MM-dd HH:mm", "dd MMM yyyy HHmm",
+ "dd MMM yyyy HH:mm", "MMM dd, yyyy HHmm",
+ "MMM dd, yyyy HH:mm"
+ };
+ //@@author
+
+ //@@author NBQian-reused
+ //Reused from https://github.com/Yufannnn/ip/blob/master/src/main/java/duke/parser/TimeHandler.java
+ //with minor modification, it is a pretty good way to organise and extend the acceptable date format.
+ private static final String[] ACCEPTABLE_DATE_FORMATS = {
+ "MMM dd yyyy", "yyyy-MM-dd", "dd/MM/yyyy", "yyyy/MM/dd",
+ "dd MMM yyyy", "MMM dd, yyyy", "dd-mm-yyyy"
+ };
+ //@@author
+
+ /**
+ * Parses a string to a LocalDateTime object using the acceptable date time formats defined
+ *
+ * @param date The date string to be parsed
+ * @return The parsed LocalDate object
+ * @throws ParseException if the string does not match any supported date formats
+ */
+ public static LocalDate parseDate(String date) throws ParseException {
+ for (String dateFormat : ACCEPTABLE_DATE_FORMATS) {
+ try {
+ return LocalDate.parse(date, DateTimeFormatter.ofPattern(dateFormat));
+ } catch (Exception e) {
+ // Go to the next dateFormat
+ }
+ }
+ throw new ParseException("Invalid date format. Please use one of the following formats:\n"
+ + String.join("\n", ACCEPTABLE_DATE_FORMATS));
+ }
+
/**
* Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be
* trimmed.
@@ -107,6 +173,9 @@ public static Tag parseTag(String tag) throws ParseException {
if (!Tag.isValidTagName(trimmedTag)) {
throw new ParseException(Tag.MESSAGE_CONSTRAINTS);
}
+ if (!Tag.isValidTagLength(trimmedTag)) {
+ throw new ParseException(Tag.MESSAGE_TAG_LENGTH);
+ }
return new Tag(trimmedTag);
}
@@ -121,4 +190,389 @@ public static Set parseTags(Collection tags) throws ParseException
}
return tagSet;
}
+
+ //@@author Yufannnn-reused
+ //Reused from https://github.com/wweqg/ip/blob/master/src/main/java/duke/parser/Parser.java
+ //with minor modification, it is a pretty clean and concise regular expression for general instructions
+ /**
+ * Parses a string to a LocalDateTime object using the acceptable date time formats defined
+ * in {@link #ACCEPTABLE_DATETIME_FORMATS}.
+ *
+ * @param date The date string to be parsed
+ * @return The parsed LocalDateTime object
+ * @throws ParseException if the date string does not match any of the acceptable date time formats
+ */
+ public static LocalDateTime parseDeadline(String date) throws ParseException {
+ for (String dateTimeFormat : ACCEPTABLE_DATETIME_FORMATS) {
+ try {
+ return LocalDateTime.parse(date,
+ DateTimeFormatter.ofPattern(dateTimeFormat));
+ } catch (Exception e) {
+ // Go to the next dateTimeFormat
+ }
+ }
+
+ throw new ParseException("Invalid date format. Please use one of the following formats:\n"
+ + String.join("\n", ACCEPTABLE_DATETIME_FORMATS));
+ }
+
+ /**
+ * Parses a string to a LocalDateTime object using the acceptable date time formats defined
+ * in {@link #ACCEPTABLE_DATETIME_FORMATS}.
+ *
+ * @param date The date string to be parsed
+ * @return The parsed LocalDateTime object
+ * @throws ParseException if the date string does not match any of the acceptable date time formats
+ */
+ public static LocalDateTime parseStartTime(String date) throws ParseException {
+ for (String dateTimeFormat : ACCEPTABLE_DATETIME_FORMATS) {
+ try {
+ return LocalDateTime.parse(date,
+ DateTimeFormatter.ofPattern(dateTimeFormat));
+ } catch (Exception e) {
+ // Go to the next dateTimeFormat
+ }
+ }
+
+ throw new ParseException("Invalid date format. Please use one of the following formats:\n"
+ + String.join("\n", ACCEPTABLE_DATETIME_FORMATS));
+ }
+ /**
+ * Parses a string to a LocalDateTime object using the acceptable date time formats defined
+ * in {@link #ACCEPTABLE_DATETIME_FORMATS}.
+ *
+ * @param date The date string to be parsed
+ * @return The parsed LocalDateTime object
+ * @throws ParseException if the date string does not match any of the acceptable date time formats
+ */
+ public static LocalDateTime parseEndTime(String date) throws ParseException {
+ for (String dateTimeFormat : ACCEPTABLE_DATETIME_FORMATS) {
+ try {
+ return LocalDateTime.parse(date,
+ DateTimeFormatter.ofPattern(dateTimeFormat));
+ } catch (Exception e) {
+ // Go to the next dateTimeFormat
+ }
+ }
+
+ throw new ParseException("Invalid date format. Please use one of the following formats:\n"
+ + String.join("\n", ACCEPTABLE_DATETIME_FORMATS));
+ }
+
+ /**
+ * Parses a string to a LocalDateTime object using the acceptable date time formats defined
+ *
+ * @param status The status string to be parsed
+ * @return The parsed Boolean object
+ * @throws ParseException if the status string does not match any of the acceptable status
+ */
+ public static Boolean parseStatus(String status) throws ParseException {
+ if (status.equalsIgnoreCase(Homework.Status.COMPLETED.toString())) {
+ return true;
+ } else if (status.equalsIgnoreCase(Homework.Status.PENDING.toString())) {
+ return false;
+ } else {
+ throw new ParseException("Invalid status. Please use either 'completed' or 'pending'.");
+ }
+ }
+
+ /**
+ * Checks if the deadline is unique and not null.
+ *
+ * @param argMultimap the argument multimap to check for deadline.
+ * @throws ParseException if the deadline is not unique or null.
+ */
+ public static void checkUniqueNotNUllDeadline(ArgumentMultimap argMultimap) throws ParseException {
+ // only one deadline keyword is allowed
+ if (argMultimap.getAllValues(PREFIX_DEADLINE).size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_ONLY_ONE_DEADLINE));
+ }
+ // it cannot be empty
+ if (argMultimap.getValue(PREFIX_DEADLINE).isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_EMPTY_DEADLINE));
+ }
+ }
+
+ /**
+ * Checks if the name is unique and not null.
+ *
+ * @param argMultimap the argument multimap to check for name.
+ * @throws ParseException if the name is not unique or null.
+ */
+ public static void checkUniqueNotNUllName(ArgumentMultimap argMultimap) throws ParseException {
+ // only one name keyword is allowed
+ if (argMultimap.getAllValues(PREFIX_NAME).size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_ONLY_ONE_STUDENT));
+ }
+ // it cannot be empty
+ if (argMultimap.getValue(PREFIX_NAME).isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_EMPTY_STUDENT));
+ }
+ }
+
+ /**
+ * Checks if the homework name is unique and not null.
+ *
+ * @param argMultimap the argument multimap to check for homework name.
+ * @throws ParseException if the homework name is not unique or null.
+ */
+ public static void checkUniqueNotNUllHomework(ArgumentMultimap argMultimap) throws ParseException {
+ // only one homework keyword is allowed
+ if (argMultimap.getAllValues(PREFIX_HOMEWORK).size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_ONLY_ONE_HOMEWORK));
+ }
+ // it cannot be empty
+ if (argMultimap.getValue(PREFIX_HOMEWORK).isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_EMPTY_HOMEWORK));
+ }
+ }
+
+ /**
+ * Checks if the index is unique and not null.
+ *
+ * @param argMultimap the argument multimap to check for index.
+ * @throws ParseException if the index is not unique or null.
+ */
+ public static void checkUniqueNotNullIndex(ArgumentMultimap argMultimap) throws ParseException {
+ // only one index keyword is allowed
+ if (argMultimap.getAllValues(PREFIX_INDEX).size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_ONLY_ONE_INDEX));
+ }
+ // it cannot be empty
+ if (argMultimap.getValue(PREFIX_INDEX).isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_EMPTY_INDEX));
+ }
+ }
+ /**
+ * Checks if at most one lesson prefix is present.
+ * @param argumentMultimap the argument multimap to check for lesson.
+ * @throws ParseException if more than one lesson prefix is present.
+ */
+ public static void checkMaxOneLesson(ArgumentMultimap argumentMultimap) throws ParseException {
+ if (argumentMultimap.getAllValues(PREFIX_LESSON).size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_ONLY_ONE_LESSON));
+ }
+ }
+ /**
+ * Checks if at most one student name prefix is present.
+ * @param argumentMultimap the argument multimap to check for name.
+ * @throws ParseException if more than one name prefix is present.
+ */
+ public static void checkMaxOneEmail(ArgumentMultimap argumentMultimap) throws ParseException {
+ if (argumentMultimap.getAllValues(PREFIX_EMAIL).size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_ONLY_ONE_EMAIL));
+ }
+ }
+ /**
+ * Checks if at most one start time prefix is present.
+ * @param argumentMultimap the argument multimap to check for start time.
+ * @throws ParseException if more than one start time prefix is present.
+ */
+ public static void checkMaxOneStartTime(ArgumentMultimap argumentMultimap) throws ParseException {
+ if (argumentMultimap.getAllValues(PREFIX_STARTTIME).size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_ONLY_ONE_STARTTIME));
+ }
+ }
+ /**
+ * Checks if at most one end time prefix is present.
+ * @param argumentMultimap the argument multimap to check for end time.
+ * @throws ParseException if more than one end time prefix is present.
+ */
+ public static void checkMaxOneEndTime(ArgumentMultimap argumentMultimap) throws ParseException {
+ if (argumentMultimap.getAllValues(PREFIX_ENDTIME).size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_ONLY_ONE_ENDTIME));
+ }
+ }
+ /**
+ * Checks if at most one address prefix is present.
+ * @param argumentMultimap the argument multimap to check for address.
+ * @throws ParseException if more than one address prefix is present.
+ */
+ public static void checkMaxOneAddress(ArgumentMultimap argumentMultimap) throws ParseException {
+ if (argumentMultimap.getAllValues(PREFIX_ADDRESS).size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_ONLY_ONE_ADDRESS));
+ }
+ }
+ /**
+ * Checks if at most one phone prefix is present.
+ * @param argumentMultimap the argument multimap to check for phone.
+ * @throws ParseException if more than one phone prefix is present.
+ */
+ public static void checkMaxOnePhone(ArgumentMultimap argumentMultimap) throws ParseException {
+ if (argumentMultimap.getAllValues(PREFIX_PHONE).size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_ONLY_ONE_PHONE));
+ }
+ }
+ /**
+ * Checks if at most one done prefix is present.
+ * @param argumentMultimap the argument multimap to check for done.
+ * @throws ParseException if more than one done prefix is present.
+ */
+ public static void checkMaxOneDone(ArgumentMultimap argumentMultimap) throws ParseException {
+ if (argumentMultimap.getAllValues(PREFIX_DONE).size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_ONLY_ONE_DONE));
+ }
+ }
+ /**
+ * Checks if at most one date prefix is present.
+ * @param argumentMultimap the argument multimap to check for date.
+ * @throws ParseException if more than one date prefix is present.
+ */
+ public static void checkMaxOneDate(ArgumentMultimap argumentMultimap) throws ParseException {
+ if (argumentMultimap.getAllValues(PREFIX_DATE).size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_ONLY_ONE_DATE));
+ }
+ }
+ /**
+ * Checks if at most one grade prefix is present.
+ * @param argumentMultimap the argument multimap to check for grade.
+ * @throws ParseException if more than one grade prefix is present.
+ */
+ public static void checkMaxOneGrade(ArgumentMultimap argumentMultimap) throws ParseException {
+ if (argumentMultimap.getAllValues(PREFIX_GRADE).size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_ONLY_ONE_GRADE));
+ }
+ }
+ /**
+ * Checks if at most one weight prefix is present.
+ * @param argumentMultimap the argument multimap to check for weight.
+ * @throws ParseException if more than one weight prefix is present.
+ */
+ public static void checkMaxOneWeight(ArgumentMultimap argumentMultimap) throws ParseException {
+ if (argumentMultimap.getAllValues(PREFIX_WEIGHT).size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_ONLY_ONE_WEIGHTAGE));
+ }
+ }
+ /**
+ * Checks if at most one exam prefix is present.
+ * @param argumentMultimap the argument multimap to check for exam.
+ * @throws ParseException if more than one exam prefix is present.
+ */
+ public static void checkMaxOneExam(ArgumentMultimap argumentMultimap) throws ParseException {
+ if (argumentMultimap.getAllValues(PREFIX_EXAM).size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_ONLY_ONE_EXAM));
+ }
+ }
+
+
+ /**
+ * Checks if the index is unique and not null.
+ *
+ * @param argMultimap the argument multimap to check for index.
+ * @throws ParseException if the index is not unique or null.
+ */
+ public static void checkUniqueNotNullStatus(ArgumentMultimap argMultimap) throws ParseException {
+ // only one index keyword is allowed
+ if (argMultimap.getAllValues(PREFIX_STATUS).size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_ONLY_ONE_STATUS));
+ }
+ // it cannot be empty
+ if (argMultimap.getValue(PREFIX_STATUS).isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_EMPTY_STATUS));
+ }
+ }
+
+ /**
+ * Checks if the index is unique and not null.
+ *
+ * @param argMultimap the argument multimap to check for index.
+ * @throws ParseException if the index is not unique or null.
+ */
+ public static void checkUniqueHomework(ArgumentMultimap argMultimap) throws ParseException {
+ // only one homework keyword is allowed
+ if (argMultimap.getAllValues(PREFIX_HOMEWORK).size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_ONLY_ONE_HOMEWORK));
+ }
+ }
+
+ /**
+ * Checks if the homework name is not null.
+ *
+ * @param homework the homework name to check for null.
+ * @throws ParseException if the homework name is null.
+ */
+ public static void checkNotNullHomework(String homework) throws ParseException {
+ // it cannot be empty
+ if (homework.isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_EMPTY_HOMEWORK));
+ }
+ }
+
+ /**
+ * Checks if the name is not null.
+ *
+ * @param names the name to check for null.
+ * @throws ParseException if the name is null.
+ */
+ public static void checkNotNullNames(List names) throws ParseException {
+ for (String name : names) {
+ if (name.isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_EMPTY_STUDENT));
+ }
+ }
+ }
+
+ /**
+ * parses a string and returns a double representing the percentage weightage
+ * @param weight string to parse
+ * @return Double percentage
+ * @throws ParseException
+ */
+ public static double parseWeightage(String weight) throws ParseException {
+ if (!weight.matches("^[0-9]+(?:\\.[0-9]+)?%?$")) {
+ throw new ParseException("Weightage is in an invalid format!");
+ }
+ weight = weight.replace("%", ""); //removes % sign if it exists
+ Double res = null;
+ try {
+ res = Double.parseDouble(weight);
+ } catch (NumberFormatException e) {
+ throw new ParseException("unexpected error occurred when parsing weightage", e);
+ }
+ return res;
+ }
+
+ /**
+ * parses a string and returns a Grade object representing it
+ * @param grade string to parse
+ * @return Grade representationn
+ * @throws ParseException
+ */
+ public static Grade parseGrade(String grade) throws ParseException {
+ if (!grade.matches("^[0-9]+/[0-9]+$")) {
+ throw new ParseException("Grade is in an invalid format!");
+ }
+ Grade res;
+ try {
+ res = new Grade(Double.parseDouble(grade.split("/")[0]), Double.parseDouble(grade.split("/")[1]));
+ } catch (IllegalArgumentException e) {
+ throw new ParseException(e.getMessage());
+ }
+ return res;
+ }
+
}
diff --git a/src/main/java/seedu/address/logic/parser/ViewProfileCommandParser.java b/src/main/java/seedu/address/logic/parser/ViewProfileCommandParser.java
new file mode 100644
index 00000000000..808cef9257d
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/ViewProfileCommandParser.java
@@ -0,0 +1,61 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.ViewProfileCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.student.NamePredicate;
+import seedu.address.model.student.Student;
+
+
+
+/**
+ * Parses input arguments and creates a new ViewProfileCommand object
+ */
+public class ViewProfileCommandParser implements Parser {
+ @Override
+ public Command parse(String args) throws ParseException {
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME);
+
+ if (!argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ViewProfileCommand.MESSAGE_USAGE));
+ }
+
+ Predicate namePredicate;
+ List nameList = new ArrayList<>();
+
+ // If name is present, create a predicate to filter by name
+ if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
+
+ List nameKeywords = argMultimap.getAllValues(PREFIX_NAME);
+ // for all the names, trim the name and only take the first word
+ for (int i = 0; i < nameKeywords.size(); i++) {
+ String name = nameKeywords.get(i);
+ name = name.trim();
+ if (name.trim().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ViewProfileCommand.MESSAGE_USAGE));
+ }
+ nameKeywords.set(i, name);
+ }
+ nameList = nameKeywords;
+ namePredicate = new NamePredicate(nameKeywords);
+ } else {
+ namePredicate = PREDICATE_SHOW_ALL_STUDENTS;
+ }
+
+
+ return new ViewProfileCommand(nameList, namePredicate);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/exam/CreateExamCommandParser.java b/src/main/java/seedu/address/logic/parser/exam/CreateExamCommandParser.java
new file mode 100644
index 00000000000..e7559562a9a
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/exam/CreateExamCommandParser.java
@@ -0,0 +1,104 @@
+package seedu.address.logic.parser.exam;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ENDTIME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EXAM;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GRADE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STARTTIME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_WEIGHT;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneEndTime;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneExam;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneGrade;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneStartTime;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneWeight;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import seedu.address.logic.commands.exam.CreateExamCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.student.Grade;
+import seedu.address.model.student.NamePredicate;
+
+/**
+ * Parses input arguments and creates a new CreateHomeworkExam object
+ */
+public class CreateExamCommandParser implements Parser {
+ private List names = new ArrayList<>();
+ /**
+ * Parses the given {@code String} of arguments in the context of the CreateExamCommand
+ * and returns a CreateExamCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public CreateExamCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_EXAM, PREFIX_STARTTIME, PREFIX_ENDTIME,
+ PREFIX_WEIGHT, PREFIX_GRADE);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_EXAM, PREFIX_STARTTIME, PREFIX_ENDTIME)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ CreateExamCommand.MESSAGE_USAGE));
+ }
+
+ checkMaxOneExam(argMultimap);
+ checkMaxOneStartTime(argMultimap);
+ checkMaxOneEndTime(argMultimap);
+ checkMaxOneWeight(argMultimap);
+ checkMaxOneGrade(argMultimap);
+
+ String examDescription = argMultimap.getValue(PREFIX_EXAM).get();
+ if (examDescription.trim().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ CreateExamCommand.MESSAGE_USAGE));
+ }
+ LocalDateTime startTime = ParserUtil.parseStartTime(argMultimap.getValue(PREFIX_STARTTIME).get());
+ LocalDateTime endTime = ParserUtil.parseEndTime(argMultimap.getValue(PREFIX_ENDTIME).get());
+ List nameKeywords = argMultimap.getAllValues(PREFIX_NAME);
+
+ Double weightage = null;
+ //if the weightage of an exam is provided, store the weight
+ if (argMultimap.getValue(PREFIX_WEIGHT).isPresent()) {
+ weightage = ParserUtil.parseWeightage(argMultimap.getValue(PREFIX_WEIGHT).get());
+ }
+
+ Grade grade = null;
+ //if the grade of an exam is provided, store the grade
+ if (argMultimap.getValue(PREFIX_GRADE).isPresent()) {
+ grade = ParserUtil.parseGrade(argMultimap.getValue(PREFIX_GRADE).get());
+ }
+
+ // for all the names, trim the name and only take the first word
+ for (int i = 0; i < nameKeywords.size(); i++) {
+ String name = nameKeywords.get(i);
+ name = name.trim();
+ if (name.trim().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ CreateExamCommand.MESSAGE_USAGE));
+ }
+ nameKeywords.set(i, name);
+ }
+ names = nameKeywords;
+
+ return new CreateExamCommand(names, new NamePredicate(nameKeywords),
+ examDescription, startTime, endTime, weightage, grade);
+ }
+
+ /**
+ * Returns true if all prefixes are present in the given {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/exam/DeleteExamCommandParser.java b/src/main/java/seedu/address/logic/parser/exam/DeleteExamCommandParser.java
new file mode 100644
index 00000000000..138aa51b27d
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/exam/DeleteExamCommandParser.java
@@ -0,0 +1,73 @@
+package seedu.address.logic.parser.exam;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNUllName;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNullIndex;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.exam.DeleteExamCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.student.NamePredicate;
+
+/**
+ * Parses input arguments and creates a new DeleteExamCommand object
+ */
+public class DeleteExamCommandParser implements Parser {
+ private List inputNames = new ArrayList<>();
+ /**
+ * Parses the given {@code String} of arguments in the context of the DeleteExamCommand
+ * and returns an DeleteExamCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public DeleteExamCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_INDEX);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_INDEX)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ DeleteExamCommand.MESSAGE_USAGE));
+ }
+
+ checkUniqueNotNullIndex(argMultimap);
+ checkUniqueNotNUllName(argMultimap);
+
+ List nameKeywords = argMultimap.getAllValues(PREFIX_NAME);
+ // for all the names, trim the name and only take the first word
+ for (int i = 0; i < nameKeywords.size(); i++) {
+ String name = nameKeywords.get(i);
+ name = name.trim();
+ if (name.trim().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ DeleteExamCommand.MESSAGE_USAGE));
+ }
+ nameKeywords.set(i, name);
+ }
+ inputNames = nameKeywords;
+
+ Index index = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_INDEX).get());
+ return new DeleteExamCommand(inputNames, new NamePredicate(nameKeywords), index);
+ }
+
+ /**
+ * Returns true if all prefixes are present in the given {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/exam/UpdateExamCommandParser.java b/src/main/java/seedu/address/logic/parser/exam/UpdateExamCommandParser.java
new file mode 100644
index 00000000000..db85b5089d9
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/exam/UpdateExamCommandParser.java
@@ -0,0 +1,134 @@
+package seedu.address.logic.parser.exam;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ENDTIME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EXAM;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GRADE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STARTTIME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_WEIGHT;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneEndTime;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneExam;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneGrade;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneStartTime;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneWeight;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNUllName;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNullIndex;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.exam.UpdateExamCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.student.Grade;
+import seedu.address.model.student.NamePredicate;
+
+/**
+ * Parses input arguments and creates a new UpdateExamCommand object
+ */
+public class UpdateExamCommandParser implements Parser {
+ private List names = new ArrayList<>();
+ /**
+ * Parses the given {@code String} of arguments in the context of the UpdateExamCommand
+ * and returns an UpdateExamCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public UpdateExamCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_INDEX, PREFIX_EXAM, PREFIX_STARTTIME,
+ PREFIX_ENDTIME, PREFIX_WEIGHT, PREFIX_GRADE);
+
+ if ((!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_INDEX, PREFIX_EXAM)
+ && !arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_INDEX, PREFIX_STARTTIME)
+ && !arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_INDEX, PREFIX_ENDTIME)
+ && !arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_INDEX, PREFIX_WEIGHT)
+ && !arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_INDEX, PREFIX_GRADE))
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ UpdateExamCommand.MESSAGE_USAGE));
+ }
+
+ checkMaxOneExam(argMultimap);
+ checkMaxOneStartTime(argMultimap);
+ checkMaxOneEndTime(argMultimap);
+ checkMaxOneWeight(argMultimap);
+ checkMaxOneGrade(argMultimap);
+ checkUniqueNotNUllName(argMultimap);
+ checkUniqueNotNullIndex(argMultimap);
+
+ Index index = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_INDEX).get());
+ List nameKeywords = argMultimap.getAllValues(PREFIX_NAME);
+ // for all the names, trim the name and only take the first word
+ for (int i = 0; i < nameKeywords.size(); i++) {
+ String name = nameKeywords.get(i);
+ name = name.trim();
+ if (name.trim().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ UpdateExamCommand.MESSAGE_USAGE));
+ }
+ nameKeywords.set(i, name);
+ }
+ names = nameKeywords;
+
+ if (nameKeywords.size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ "Only one name is allowed for update exam command."));
+ }
+
+ // if exam name is not present, set it to null, else parse it
+ Optional examName = Optional.empty();
+ if (argMultimap.getValue(PREFIX_EXAM).isPresent()) {
+ examName = Optional.of(argMultimap.getValue(PREFIX_EXAM).get());
+ }
+
+ // if start time is not present, set it to null, else parse it
+ Optional startTime = Optional.empty();
+ if (argMultimap.getValue(PREFIX_STARTTIME).isPresent()) {
+ startTime = Optional.of(ParserUtil.parseStartTime(argMultimap.getValue(PREFIX_STARTTIME).get()));
+ }
+
+ // if end time is not present, set it to null, else parse it
+ Optional endTime = Optional.empty();
+ if (argMultimap.getValue(PREFIX_ENDTIME).isPresent()) {
+ endTime = Optional.of(ParserUtil.parseEndTime(argMultimap.getValue(PREFIX_ENDTIME).get()));
+ }
+
+ Optional weight = Optional.empty();
+ if (argMultimap.getValue(PREFIX_WEIGHT).isPresent()) {
+ weight = Optional.of(ParserUtil.parseWeightage(argMultimap.getValue(PREFIX_WEIGHT).get()));
+ }
+
+ Optional grade = Optional.empty();
+ if (argMultimap.getValue(PREFIX_GRADE).isPresent()) {
+ grade = Optional.of(ParserUtil.parseGrade(argMultimap.getValue(PREFIX_GRADE).get()));
+ }
+
+ return new UpdateExamCommand(names, index, new NamePredicate(nameKeywords),
+ examName, startTime, endTime, weight, grade);
+ }
+
+ /**
+ * Returns true if all prefixes are present in the given {@code ArgumentMultimap}.
+ *
+ * @param argumentMultimap the argument multimap to check for prefixes.
+ * @param prefixes the prefixes to be checked.
+ * @return true if all prefixes are present in the given {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/exam/ViewExamCommandParser.java b/src/main/java/seedu/address/logic/parser/exam/ViewExamCommandParser.java
new file mode 100644
index 00000000000..440ab39623c
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/exam/ViewExamCommandParser.java
@@ -0,0 +1,110 @@
+package seedu.address.logic.parser.exam;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EXAM;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneDate;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneDone;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneExam;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNUllName;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+
+import seedu.address.logic.commands.exam.ViewExamCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.student.Exam;
+import seedu.address.model.student.ExamDatePredicate;
+import seedu.address.model.student.ExamDonePredicate;
+import seedu.address.model.student.ExamPredicate;
+import seedu.address.model.student.NamePredicate;
+import seedu.address.model.student.Student;
+
+/**
+ * Parses input arguments and creates a new ViewExamCommand object
+ */
+public class ViewExamCommandParser implements Parser {
+ /**
+ * Parses the given {@code String} of arguments in the context of the ViewExamCommand
+ * and returns an ViewExamCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public ViewExamCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_DATE, PREFIX_EXAM,
+ PREFIX_DONE);
+
+ if (!argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ViewExamCommand.MESSAGE_USAGE));
+ }
+
+ Predicate namePredicate;
+ Predicate examPredicate = exam -> true;
+ Predicate donePredicate = exam -> true;
+ boolean defaultPredicateFlag;
+ List nameList = new ArrayList<>();
+
+ // If name is present, create a predicate to filter by name
+ if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
+ checkUniqueNotNUllName(argMultimap);
+ List nameKeywords = argMultimap.getAllValues(PREFIX_NAME);
+ // for all the names, trim the name and only take the first word
+ for (int i = 0; i < nameKeywords.size(); i++) {
+ String name = nameKeywords.get(i);
+ name = name.trim();
+ if (name.trim().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ViewExamCommand.MESSAGE_USAGE));
+ }
+ nameKeywords.set(i, name);
+ }
+ nameList = nameKeywords;
+ namePredicate = new NamePredicate(nameKeywords);
+ defaultPredicateFlag = false;
+ } else {
+ namePredicate = PREDICATE_SHOW_ALL_STUDENTS;
+ defaultPredicateFlag = true;
+ }
+
+ if (argMultimap.getValue(PREFIX_EXAM).isPresent()) {
+ checkMaxOneExam(argMultimap);
+ String exam = argMultimap.getValue(PREFIX_EXAM).get();
+ examPredicate = new ExamPredicate(exam);
+ }
+
+ if (argMultimap.getValue(PREFIX_DONE).isPresent()) {
+ checkMaxOneDone(argMultimap);
+ String done = argMultimap.getValue(PREFIX_DONE).get();
+ if (!done.equals("done") && !done.equals("not done")) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ViewExamCommand.MESSAGE_USAGE));
+ }
+ donePredicate = new ExamDonePredicate(done);
+ }
+
+ // If date is present, create a predicate to filter by date
+ if (argMultimap.getValue(PREFIX_DATE).isPresent()) {
+ checkMaxOneDate(argMultimap);
+ String date = argMultimap.getValue(PREFIX_DATE).get();
+ LocalDate targetDate = ParserUtil.parseDate(date);
+ ExamDatePredicate datePredicate = new ExamDatePredicate(targetDate);
+ return new ViewExamCommand(nameList, namePredicate, datePredicate, examPredicate, donePredicate,
+ defaultPredicateFlag);
+ } else {
+ return new ViewExamCommand(nameList, namePredicate, examPredicate, donePredicate, defaultPredicateFlag);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/homework/CreateHomeworkCommandParser.java b/src/main/java/seedu/address/logic/parser/homework/CreateHomeworkCommandParser.java
new file mode 100644
index 00000000000..2f7476d2b18
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/homework/CreateHomeworkCommandParser.java
@@ -0,0 +1,76 @@
+package seedu.address.logic.parser.homework;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_HOMEWORK;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.ParserUtil.checkNotNullNames;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNUllDeadline;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNUllHomework;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.stream.Stream;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.logic.commands.homework.CreateHomeworkCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.student.NamePredicate;
+
+/**
+ * Parses input arguments and creates a new CreateHomeworkCommand object
+ */
+public class CreateHomeworkCommandParser implements Parser {
+ /**
+ * Parses the given {@code String} of arguments in the context of the CreateHomeworkCommand
+ * and returns a CreateHomeworkCommand object for execution.
+ *
+ * @param args the user input to be parsed.
+ * @return CreateHomeworkCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public CreateHomeworkCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_HOMEWORK, PREFIX_DEADLINE);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_HOMEWORK, PREFIX_DEADLINE)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ CreateHomeworkCommand.MESSAGE_USAGE));
+ }
+
+ List nameKeywords = argMultimap.getAllValues(PREFIX_NAME);
+
+ checkNotNullNames(nameKeywords);
+ checkUniqueNotNUllHomework(argMultimap);
+ checkUniqueNotNUllDeadline(argMultimap);
+
+ String homeworkName = argMultimap.getValue(PREFIX_HOMEWORK).get();
+ LocalDateTime deadline = ParserUtil.parseDeadline(argMultimap.getValue(PREFIX_DEADLINE).get());
+ if (deadline.isBefore(LocalDateTime.now())) {
+ throw new ParseException(Messages.MESSAGE_DEADLINE_IN_PAST);
+ }
+
+ return new CreateHomeworkCommand(nameKeywords, new NamePredicate(nameKeywords),
+ homeworkName, deadline);
+ }
+
+ /**
+ * Returns true if all prefixes are present in the given {@code ArgumentMultimap}.
+ *
+ * @param argumentMultimap the argument multimap to check for prefixes.
+ * @param prefixes the prefixes to be checked.
+ * @return true if all prefixes are present in the given {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/homework/DeleteHomeworkCommandParser.java b/src/main/java/seedu/address/logic/parser/homework/DeleteHomeworkCommandParser.java
new file mode 100644
index 00000000000..98cfae441f4
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/homework/DeleteHomeworkCommandParser.java
@@ -0,0 +1,69 @@
+package seedu.address.logic.parser.homework;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNUllName;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNullIndex;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.homework.DeleteHomeworkCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.student.NamePredicate;
+
+/**
+ * Parses input arguments and creates a new CreateHomeworkCommand object
+ */
+public class DeleteHomeworkCommandParser implements Parser {
+ private List names = new ArrayList<>();
+ /**
+ * Parses the given {@code String} of arguments in the context of the CreateHomeworkCommand
+ * and returns a CreateHomeworkCommand object for execution.
+ *
+ * @param args the user input arguments to be parsed.
+ * @return CreateHomeworkCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public DeleteHomeworkCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_INDEX);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_INDEX)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ DeleteHomeworkCommand.MESSAGE_USAGE));
+ }
+
+ checkUniqueNotNUllName(argMultimap);
+ checkUniqueNotNullIndex(argMultimap);
+
+ List nameKeywords = argMultimap.getAllValues(PREFIX_NAME);
+ names = nameKeywords;
+ Index index = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_INDEX).get());
+
+ return new DeleteHomeworkCommand(names, new NamePredicate(nameKeywords), index);
+ }
+
+ /**
+ * Returns true if all prefixes are present in the given {@code ArgumentMultimap}.
+ *
+ * @param argumentMultimap the argument multimap to be checked.
+ * @param prefixes the prefixes to be checked.
+ * @return true if all prefixes are present in the given {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/homework/MarkHomeworkAsDoneCommandParser.java b/src/main/java/seedu/address/logic/parser/homework/MarkHomeworkAsDoneCommandParser.java
new file mode 100644
index 00000000000..d5241e6438e
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/homework/MarkHomeworkAsDoneCommandParser.java
@@ -0,0 +1,69 @@
+package seedu.address.logic.parser.homework;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNUllName;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNullIndex;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.homework.MarkHomeworkAsDoneCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.student.NamePredicate;
+
+/**
+ * Parses input arguments and creates a new MarkHomeworkAsDoneCommand object
+ */
+public class MarkHomeworkAsDoneCommandParser implements Parser {
+ private List names = new ArrayList<>();
+ /**
+ * Parses the given {@code String} of arguments in the context of the MarkHomeworkAsDoneCommand
+ * and returns a MarkHomeworkAsDoneCommand object for execution.
+ *
+ * @param args the user input to be parsed.
+ * @return MarkHomeworkAsDoneCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public MarkHomeworkAsDoneCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_INDEX);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_INDEX)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ MarkHomeworkAsDoneCommand.MESSAGE_USAGE));
+ }
+
+ checkUniqueNotNUllName(argMultimap);
+ checkUniqueNotNullIndex(argMultimap);
+
+ List nameKeywords = argMultimap.getAllValues(PREFIX_NAME);
+ names = nameKeywords;
+ Index index = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_INDEX).get());
+
+ return new MarkHomeworkAsDoneCommand(names, new NamePredicate(nameKeywords), index);
+ }
+
+ /**
+ * Returns true if all prefixes are present in the given {@code ArgumentMultimap}.
+ *
+ * @param argumentMultimap the argument multimap to check for prefixes.
+ * @param prefixes the prefixes to be checked.
+ * @return true if all prefixes are present in the given {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/homework/MarkHomeworkAsUndoCommandParser.java b/src/main/java/seedu/address/logic/parser/homework/MarkHomeworkAsUndoCommandParser.java
new file mode 100644
index 00000000000..01151aecc38
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/homework/MarkHomeworkAsUndoCommandParser.java
@@ -0,0 +1,69 @@
+package seedu.address.logic.parser.homework;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNUllName;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNullIndex;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.homework.MarkHomeworkAsUndoCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.student.NamePredicate;
+
+/**
+ * Parses input arguments and creates a new MarkHomeworkAsDoneCommand object
+ */
+public class MarkHomeworkAsUndoCommandParser implements Parser {
+ private List names = new ArrayList<>();
+ /**
+ * Parses the given {@code String} of arguments in the context of the MarkHomeworkAsDoneCommand
+ * and returns a MarkHomeworkAsDoneCommand object for execution.
+ *
+ * @param args the user input to be parsed.
+ * @return MarkHomeworkAsUndoCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public MarkHomeworkAsUndoCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_INDEX);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_INDEX)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ MarkHomeworkAsUndoCommand.MESSAGE_USAGE));
+ }
+
+ checkUniqueNotNUllName(argMultimap);
+ checkUniqueNotNullIndex(argMultimap);
+
+ List nameKeywords = argMultimap.getAllValues(PREFIX_NAME);
+ names = nameKeywords;
+ Index index = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_INDEX).get());
+
+ return new MarkHomeworkAsUndoCommand(names, new NamePredicate(nameKeywords), index);
+ }
+
+ /**
+ * Returns true if all prefixes are present in the given {@code ArgumentMultimap}.
+ *
+ * @param argumentMultimap the argument multimap to check for prefixes.
+ * @param prefixes the prefixes to be checked.
+ * @return true if all prefixes are present in the given {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/homework/UpdateHomeworkCommandParser.java b/src/main/java/seedu/address/logic/parser/homework/UpdateHomeworkCommandParser.java
new file mode 100644
index 00000000000..4f8179dd942
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/homework/UpdateHomeworkCommandParser.java
@@ -0,0 +1,91 @@
+package seedu.address.logic.parser.homework;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_HOMEWORK;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.ParserUtil.checkNotNullHomework;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueHomework;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNUllDeadline;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNUllName;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNullIndex;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.homework.UpdateHomeworkCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.student.NamePredicate;
+
+/**
+ * An UpdateHomeworkCommandParser that parses input arguments and creates a new UpdateHomeworkCommand object
+ */
+public class UpdateHomeworkCommandParser implements Parser {
+ private List names = new ArrayList<>();
+ /**
+ * Parses the given {@code String} of arguments in the context of the UpdateHomeworkCommand
+ * and returns an UpdateHomeworkCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public UpdateHomeworkCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_INDEX, PREFIX_HOMEWORK, PREFIX_DEADLINE);
+
+ if ((!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_INDEX, PREFIX_HOMEWORK)
+ && !arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_INDEX, PREFIX_DEADLINE))
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ UpdateHomeworkCommand.MESSAGE_USAGE));
+ }
+
+ checkUniqueNotNUllName(argMultimap);
+ checkUniqueNotNullIndex(argMultimap);
+
+ List nameKeywords = argMultimap.getAllValues(PREFIX_NAME);
+ names = nameKeywords;
+ Index index = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_INDEX).get());
+
+ // if homework name is not present, set it to null, else parse it
+ Optional homeworkName = Optional.empty();
+ if (argMultimap.getValue(PREFIX_HOMEWORK).isPresent()) {
+ checkUniqueHomework(argMultimap);
+ homeworkName = Optional.of(argMultimap.getValue(PREFIX_HOMEWORK).get());
+ checkNotNullHomework(homeworkName.get());
+ }
+
+ // if deadline is not present, set it to null, else parse it
+ Optional deadline = Optional.empty();
+ if (argMultimap.getValue(PREFIX_DEADLINE).isPresent()) {
+ checkUniqueNotNUllDeadline(argMultimap);
+ deadline = Optional.of(ParserUtil.parseDeadline(argMultimap.getValue(PREFIX_DEADLINE).get()));
+ }
+
+ return new UpdateHomeworkCommand(names, index, new NamePredicate(nameKeywords),
+ homeworkName, deadline);
+ }
+
+ /**
+ * Returns true if all prefixes are present in the given {@code ArgumentMultimap}.
+ *
+ * @param argumentMultimap the argument multimap to check for prefixes.
+ * @param prefixes the prefixes to be checked.
+ * @return true if all prefixes are present in the given {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/homework/ViewHomeworkCommandParser.java b/src/main/java/seedu/address/logic/parser/homework/ViewHomeworkCommandParser.java
new file mode 100644
index 00000000000..5145f7c140a
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/homework/ViewHomeworkCommandParser.java
@@ -0,0 +1,92 @@
+package seedu.address.logic.parser.homework;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STATUS;
+import static seedu.address.logic.parser.ParserUtil.checkNotNullNames;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNullStatus;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+
+import seedu.address.logic.commands.homework.ViewHomeworkCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.student.HomeworkIsCompletePredicate;
+import seedu.address.model.student.NamePredicate;
+import seedu.address.model.student.Student;
+
+/**
+ * Parses input arguments and creates a new ViewHomeworkCommand object
+ */
+public class ViewHomeworkCommandParser implements Parser {
+ private List names = new ArrayList<>();
+
+ /**
+ * Checks if the list of strings contains an empty string.
+ *
+ * @param list the list of strings to be checked.
+ * @return true if the list does not contain an empty string, false otherwise.
+ */
+ private boolean checkEmptyString(List list) {
+ for (String s : list) {
+ if (s.isEmpty()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the ViewHomeworkCommand
+ * and returns a ViewHomeworkCommand object for execution.
+ *
+ * @param args the user input to be parsed into a ViewHomeworkCommand object.
+ * @return a ViewHomeworkCommand object.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public ViewHomeworkCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_STATUS);
+
+ if (!argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ViewHomeworkCommand.MESSAGE_USAGE));
+ }
+
+ Predicate namePredicate;
+ boolean defaultPredicateFlag;
+
+ // If name is present, create a predicate to filter by name
+ if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
+ List nameKeywords = argMultimap.getAllValues(PREFIX_NAME);
+ checkNotNullNames(nameKeywords);
+
+ names = nameKeywords;
+ namePredicate = new NamePredicate(nameKeywords);
+ defaultPredicateFlag = false;
+ } else {
+ namePredicate = PREDICATE_SHOW_ALL_STUDENTS;
+ defaultPredicateFlag = true;
+ }
+
+ // If status is present, create a predicate to filter by status
+ if (argMultimap.getValue(PREFIX_STATUS).isPresent()) {
+ checkUniqueNotNullStatus(argMultimap);
+
+ String status = argMultimap.getValue(PREFIX_STATUS).get();
+ boolean isCompleted = ParserUtil.parseStatus(status);
+ HomeworkIsCompletePredicate statusPredicate = new HomeworkIsCompletePredicate(isCompleted);
+ return new ViewHomeworkCommand(names, namePredicate, statusPredicate, defaultPredicateFlag);
+ } else {
+ return new ViewHomeworkCommand(names, namePredicate, defaultPredicateFlag);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/lesson/CreateLessonCommandParser.java b/src/main/java/seedu/address/logic/parser/lesson/CreateLessonCommandParser.java
new file mode 100644
index 00000000000..28b51545eb4
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/lesson/CreateLessonCommandParser.java
@@ -0,0 +1,90 @@
+package seedu.address.logic.parser.lesson;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ENDTIME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_LESSON;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STARTTIME;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneEndTime;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneLesson;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneStartTime;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNUllName;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import seedu.address.logic.commands.lesson.CreateLessonCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.student.NamePredicate;
+
+/**
+ * Parses input arguments and creates a new CreateLessonCommand object
+ */
+public class CreateLessonCommandParser implements Parser {
+ private List names = new ArrayList<>();
+ /**
+ * Parses the given {@code String} of arguments in the context of the CreateLessonCommand
+ * and returns an CreateLessonCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public CreateLessonCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_LESSON, PREFIX_STARTTIME, PREFIX_ENDTIME);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_LESSON, PREFIX_STARTTIME, PREFIX_ENDTIME)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ CreateLessonCommand.MESSAGE_USAGE));
+ }
+ checkUniqueNotNUllName(argMultimap);
+ checkMaxOneStartTime(argMultimap);
+ checkMaxOneEndTime(argMultimap);
+ checkMaxOneLesson(argMultimap);
+
+ String lessonName = argMultimap.getValue(PREFIX_LESSON).get();
+ if (lessonName.trim().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ CreateLessonCommand.MESSAGE_USAGE));
+ }
+ LocalDateTime startTime = ParserUtil.parseDeadline(argMultimap.getValue(PREFIX_STARTTIME).get());
+ LocalDateTime endTime = ParserUtil.parseDeadline(argMultimap.getValue(PREFIX_ENDTIME).get());
+ List nameKeywords = argMultimap.getAllValues(PREFIX_NAME);
+
+ if (endTime.isBefore(startTime)) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, CreateLessonCommand.MESSAGE_DATE));
+ }
+
+ // for all the names, trim the name and only take the first word
+ for (int i = 0; i < nameKeywords.size(); i++) {
+ String name = nameKeywords.get(i);
+ if (name.trim().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ CreateLessonCommand.MESSAGE_USAGE));
+ }
+ name = name.trim();
+ nameKeywords.set(i, name);
+ }
+ names = nameKeywords;
+
+ return new CreateLessonCommand(names, new NamePredicate(nameKeywords),
+ lessonName, startTime, endTime);
+ }
+
+ /**
+ * Returns true if all prefixes are present in the given {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/lesson/DeleteLessonCommandParser.java b/src/main/java/seedu/address/logic/parser/lesson/DeleteLessonCommandParser.java
new file mode 100644
index 00000000000..742382a7b02
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/lesson/DeleteLessonCommandParser.java
@@ -0,0 +1,73 @@
+package seedu.address.logic.parser.lesson;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNUllName;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNullIndex;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.lesson.DeleteLessonCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.student.NamePredicate;
+
+/**
+ * Parses input arguments and creates a new DeleteLessonCommand object
+ */
+public class DeleteLessonCommandParser implements Parser {
+ private List inputNames = new ArrayList<>();
+ /**
+ * Parses the given {@code String} of arguments in the context of the DeleteLessonCommand
+ * and returns a DeleteLessonCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public DeleteLessonCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_INDEX);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_INDEX)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ DeleteLessonCommand.MESSAGE_USAGE));
+ }
+
+ checkUniqueNotNullIndex(argMultimap);
+ checkUniqueNotNUllName(argMultimap);
+
+ List nameKeywords = argMultimap.getAllValues(PREFIX_NAME);
+ // for all the names, trim the name and only take the first word
+ for (int i = 0; i < nameKeywords.size(); i++) {
+ String name = nameKeywords.get(i);
+ name = name.trim();
+ if (name.trim().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ DeleteLessonCommand.MESSAGE_USAGE));
+ }
+ nameKeywords.set(i, name);
+ }
+ inputNames = nameKeywords;
+
+ Index index = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_INDEX).get());
+ return new DeleteLessonCommand(inputNames, new NamePredicate(nameKeywords), index);
+ }
+
+ /**
+ * Returns true if all prefixes are present in the given {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/lesson/UpdateLessonCommandParser.java b/src/main/java/seedu/address/logic/parser/lesson/UpdateLessonCommandParser.java
new file mode 100644
index 00000000000..8a75d79e5f6
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/lesson/UpdateLessonCommandParser.java
@@ -0,0 +1,115 @@
+package seedu.address.logic.parser.lesson;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ENDTIME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_LESSON;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STARTTIME;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneEndTime;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneLesson;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneStartTime;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNUllName;
+import static seedu.address.logic.parser.ParserUtil.checkUniqueNotNullIndex;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.lesson.UpdateLessonCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.student.NamePredicate;
+
+/**
+ * Parses input arguments and creates a new UpdateLessonCommand object
+ */
+public class UpdateLessonCommandParser implements Parser {
+ private List names = new ArrayList<>();
+ /**
+ * Parses the given {@code String} of arguments in the context of the UpdateLessonCommand
+ * and returns an UpdateLessonCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public UpdateLessonCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_INDEX, PREFIX_LESSON, PREFIX_STARTTIME,
+ PREFIX_ENDTIME);
+
+ if ((!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_INDEX, PREFIX_LESSON)
+ && !arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_INDEX, PREFIX_STARTTIME)
+ && !arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_INDEX, PREFIX_ENDTIME))
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ UpdateLessonCommand.MESSAGE_USAGE));
+ }
+
+ checkUniqueNotNUllName(argMultimap);
+ checkUniqueNotNullIndex(argMultimap);
+ checkMaxOneStartTime(argMultimap);
+ checkMaxOneEndTime(argMultimap);
+ checkMaxOneLesson(argMultimap);
+
+ Index index = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_INDEX).get());
+ List nameKeywords = argMultimap.getAllValues(PREFIX_NAME);
+ // for all the names, trim the name and only take the first word
+ for (int i = 0; i < nameKeywords.size(); i++) {
+ String name = nameKeywords.get(i);
+ name = name.trim();
+ if (name.trim().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ UpdateLessonCommand.MESSAGE_USAGE));
+ }
+ nameKeywords.set(i, name);
+ }
+ names = nameKeywords;
+
+ if (nameKeywords.size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ "Only one name is allowed for update lesson command."));
+ }
+
+ // if lesson is not present, set it to null, else parse it
+ Optional lessonName = Optional.empty();
+ if (argMultimap.getValue(PREFIX_LESSON).isPresent()) {
+ lessonName = Optional.of(argMultimap.getValue(PREFIX_LESSON).get());
+ }
+
+ // if start time is not present, set it to null, else parse it
+ Optional startTime = Optional.empty();
+ if (argMultimap.getValue(PREFIX_STARTTIME).isPresent()) {
+ startTime = Optional.of(ParserUtil.parseStartTime(argMultimap.getValue(PREFIX_STARTTIME).get()));
+ }
+
+ // if end time is not present, set it to null, else parse it
+ Optional endTime = Optional.empty();
+ if (argMultimap.getValue(PREFIX_ENDTIME).isPresent()) {
+ endTime = Optional.of(ParserUtil.parseEndTime(argMultimap.getValue(PREFIX_ENDTIME).get()));
+ }
+
+ return new UpdateLessonCommand(names, index, new NamePredicate(nameKeywords),
+ lessonName, startTime, endTime);
+ }
+
+ /**
+ * Returns true if all prefixes are present in the given {@code ArgumentMultimap}.
+ *
+ * @param argumentMultimap the argument multimap to check for prefixes.
+ * @param prefixes the prefixes to be checked.
+ * @return true if all prefixes are present in the given {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/lesson/ViewLessonCommandParser.java b/src/main/java/seedu/address/logic/parser/lesson/ViewLessonCommandParser.java
new file mode 100644
index 00000000000..8df86b8cea8
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/lesson/ViewLessonCommandParser.java
@@ -0,0 +1,117 @@
+package seedu.address.logic.parser.lesson;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_LESSON;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneDate;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneDone;
+import static seedu.address.logic.parser.ParserUtil.checkMaxOneLesson;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+
+import seedu.address.logic.commands.lesson.ViewLessonCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.student.Lesson;
+import seedu.address.model.student.LessonBelongsToDatePredicate;
+import seedu.address.model.student.LessonDonePredicate;
+import seedu.address.model.student.LessonSubjectPredicate;
+import seedu.address.model.student.NamePredicate;
+import seedu.address.model.student.Student;
+
+/**
+ * Parses input arguments and creates a new ViewLessonCommand object
+ */
+public class ViewLessonCommandParser implements Parser {
+ /**
+ * Parses the given {@code String} of arguments in the context of the ViewLessonCommand
+ * and returns an ViewLessonCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public ViewLessonCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_DATE, PREFIX_LESSON,
+ PREFIX_DONE);
+
+ if (!argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ViewLessonCommand.MESSAGE_USAGE));
+ }
+
+ Predicate namePredicate;
+ Predicate lessonPredicate = lesson -> true;
+ Predicate donePredicate = lesson -> true;
+ List nameList = new ArrayList<>();
+ boolean defaultPredicateFlag;
+
+ // If name is present, create a predicate to filter by name
+ if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
+
+ List nameKeywords = argMultimap.getAllValues(PREFIX_NAME);
+ // for all the names, trim the name and only take the first word
+ for (int i = 0; i < nameKeywords.size(); i++) {
+ String name = nameKeywords.get(i);
+ name = name.trim();
+ if (name.trim().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ViewLessonCommand.MESSAGE_USAGE));
+ }
+ nameKeywords.set(i, name);
+ }
+ nameList = nameKeywords;
+ namePredicate = new NamePredicate(nameKeywords);
+ defaultPredicateFlag = false;
+ } else {
+ namePredicate = PREDICATE_SHOW_ALL_STUDENTS;
+ defaultPredicateFlag = true;
+ }
+
+ if (argMultimap.getValue(PREFIX_LESSON).isPresent()) {
+ checkMaxOneLesson(argMultimap);
+ String lessonSubject = argMultimap.getValue(PREFIX_LESSON).get();
+ lessonPredicate = new LessonSubjectPredicate(lessonSubject);
+ }
+
+ if (argMultimap.getValue(PREFIX_DONE).isPresent()) {
+ checkMaxOneDone(argMultimap);
+ String done = argMultimap.getValue(PREFIX_DONE).get();
+ if (!done.equals("done") && !done.equals("not done")) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ViewLessonCommand.MESSAGE_USAGE));
+ }
+ donePredicate = new LessonDonePredicate(done);
+ }
+
+ // If date is present, create a predicate to filter by status
+ if (argMultimap.getValue(PREFIX_DATE).isPresent()) {
+ checkMaxOneDate(argMultimap);
+ String date = argMultimap.getValue(PREFIX_DATE).get();
+ LocalDate targetDate = ParserUtil.parseDate(date);
+ LessonBelongsToDatePredicate datePredicate = new LessonBelongsToDatePredicate(targetDate);
+ return new ViewLessonCommand(nameList, namePredicate, datePredicate, lessonPredicate, donePredicate,
+ defaultPredicateFlag);
+ } else {
+ return new ViewLessonCommand(nameList, namePredicate, lessonPredicate, donePredicate,
+ defaultPredicateFlag);
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof ViewLessonCommandParser // instanceof handles nulls
+ && this.equals(other)); // state check
+ }
+}
diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java
index 1a943a0781a..1e6b8b6a66a 100644
--- a/src/main/java/seedu/address/model/AddressBook.java
+++ b/src/main/java/seedu/address/model/AddressBook.java
@@ -5,8 +5,8 @@
import java.util.List;
import javafx.collections.ObservableList;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.UniquePersonList;
+import seedu.address.model.student.Student;
+import seedu.address.model.student.UniqueStudentList;
/**
* Wraps all data at the address-book level
@@ -14,7 +14,7 @@
*/
public class AddressBook implements ReadOnlyAddressBook {
- private final UniquePersonList persons;
+ private final UniqueStudentList students;
/*
* The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication
@@ -24,7 +24,7 @@ public class AddressBook implements ReadOnlyAddressBook {
* among constructors.
*/
{
- persons = new UniquePersonList();
+ students = new UniqueStudentList();
}
public AddressBook() {}
@@ -43,8 +43,8 @@ public AddressBook(ReadOnlyAddressBook toBeCopied) {
* Replaces the contents of the person list with {@code persons}.
* {@code persons} must not contain duplicate persons.
*/
- public void setPersons(List persons) {
- this.persons.setPersons(persons);
+ public void setStudents(List students) {
+ this.students.setStudents(students);
}
/**
@@ -53,7 +53,7 @@ public void setPersons(List persons) {
public void resetData(ReadOnlyAddressBook newData) {
requireNonNull(newData);
- setPersons(newData.getPersonList());
+ setStudents(newData.getPersonList());
}
//// person-level operations
@@ -61,17 +61,17 @@ public void resetData(ReadOnlyAddressBook newData) {
/**
* Returns true if a person with the same identity as {@code person} exists in the address book.
*/
- public boolean hasPerson(Person person) {
+ public boolean hasPerson(Student person) {
requireNonNull(person);
- return persons.contains(person);
+ return students.contains(person);
}
/**
* Adds a person to the address book.
* The person must not already exist in the address book.
*/
- public void addPerson(Person p) {
- persons.add(p);
+ public void addStudent(Student p) {
+ students.add(p);
}
/**
@@ -79,42 +79,42 @@ public void addPerson(Person p) {
* {@code target} must exist in the address book.
* The person identity of {@code editedPerson} must not be the same as another existing person in the address book.
*/
- public void setPerson(Person target, Person editedPerson) {
+ public void setPerson(Student target, Student editedPerson) {
requireNonNull(editedPerson);
- persons.setPerson(target, editedPerson);
+ students.setPerson(target, editedPerson);
}
/**
* Removes {@code key} from this {@code AddressBook}.
* {@code key} must exist in the address book.
*/
- public void removePerson(Person key) {
- persons.remove(key);
+ public void removePerson(Student key) {
+ students.remove(key);
}
//// util methods
@Override
public String toString() {
- return persons.asUnmodifiableObservableList().size() + " persons";
+ return students.asUnmodifiableObservableList().size() + " persons";
// TODO: refine later
}
@Override
- public ObservableList getPersonList() {
- return persons.asUnmodifiableObservableList();
+ public ObservableList getPersonList() {
+ return students.asUnmodifiableObservableList();
}
@Override
public boolean equals(Object other) {
return other == this // short circuit if same object
|| (other instanceof AddressBook // instanceof handles nulls
- && persons.equals(((AddressBook) other).persons));
+ && students.equals(((AddressBook) other).students));
}
@Override
public int hashCode() {
- return persons.hashCode();
+ return students.hashCode();
}
}
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
index d54df471c1f..eeabee09e53 100644
--- a/src/main/java/seedu/address/model/Model.java
+++ b/src/main/java/seedu/address/model/Model.java
@@ -5,14 +5,15 @@
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
-import seedu.address.model.person.Person;
+import seedu.address.model.student.Lesson;
+import seedu.address.model.student.Student;
/**
- * The API of the Model component.
+ * The API of the Model component. Contains the data of the application in-memory.
*/
public interface Model {
/** {@code Predicate} that always evaluate to true */
- Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true;
+ Predicate PREDICATE_SHOW_ALL_STUDENTS = unused -> true;
/**
* Replaces user prefs data with the data in {@code userPrefs}.
@@ -55,33 +56,43 @@ public interface Model {
/**
* Returns true if a person with the same identity as {@code person} exists in the address book.
*/
- boolean hasPerson(Person person);
+ boolean hasPerson(Student person);
/**
* Deletes the given person.
* The person must exist in the address book.
*/
- void deletePerson(Person target);
+ void deletePerson(Student target);
/**
* Adds the given person.
* {@code person} must not already exist in the address book.
*/
- void addPerson(Person person);
+ void addPerson(Student person);
/**
* Replaces the given person {@code target} with {@code editedPerson}.
* {@code target} must exist in the address book.
* The person identity of {@code editedPerson} must not be the same as another existing person in the address book.
*/
- void setPerson(Person target, Person editedPerson);
+ void setStudent(Student target, Student editedPerson);
/** Returns an unmodifiable view of the filtered person list */
- ObservableList getFilteredPersonList();
+ ObservableList getFilteredStudentList();
/**
* Updates the filter of the filtered person list to filter by the given {@code predicate}.
* @throws NullPointerException if {@code predicate} is null.
*/
- void updateFilteredPersonList(Predicate predicate);
+ void updateFilteredStudentList(Predicate predicate);
+
+ boolean hasDuplicateName(String name);
+ boolean hasDuplicateNameEdit(String name, Integer index);
+ boolean hasExtendedName(String name);
+ boolean hasExtendedNameEdit(String name, Integer index);
+ boolean noSuchStudent(String name);
+ boolean hasDuplicateNameAdd(String toString);
+ boolean hasConflictingLessonTime(Lesson lesson);
+
+ boolean hasConflictingExamTime(Lesson lesson);
}
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
index 86c1df298d7..a4e760ec699 100644
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ b/src/main/java/seedu/address/model/ModelManager.java
@@ -11,7 +11,8 @@
import javafx.collections.transformation.FilteredList;
import seedu.address.commons.core.GuiSettings;
import seedu.address.commons.core.LogsCenter;
-import seedu.address.model.person.Person;
+import seedu.address.model.student.Lesson;
+import seedu.address.model.student.Student;
/**
* Represents the in-memory model of the address book data.
@@ -21,7 +22,7 @@ public class ModelManager implements Model {
private final AddressBook addressBook;
private final UserPrefs userPrefs;
- private final FilteredList filteredPersons;
+ private final FilteredList filteredPersons;
/**
* Initializes a ModelManager with the given addressBook and userPrefs.
@@ -88,24 +89,24 @@ public ReadOnlyAddressBook getAddressBook() {
}
@Override
- public boolean hasPerson(Person person) {
+ public boolean hasPerson(Student person) {
requireNonNull(person);
return addressBook.hasPerson(person);
}
@Override
- public void deletePerson(Person target) {
+ public void deletePerson(Student target) {
addressBook.removePerson(target);
}
@Override
- public void addPerson(Person person) {
- addressBook.addPerson(person);
- updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ public void addPerson(Student person) {
+ addressBook.addStudent(person);
+ updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
}
@Override
- public void setPerson(Person target, Person editedPerson) {
+ public void setStudent(Student target, Student editedPerson) {
requireAllNonNull(target, editedPerson);
addressBook.setPerson(target, editedPerson);
@@ -118,12 +119,12 @@ public void setPerson(Person target, Person editedPerson) {
* {@code versionedAddressBook}
*/
@Override
- public ObservableList getFilteredPersonList() {
+ public ObservableList getFilteredStudentList() {
return filteredPersons;
}
@Override
- public void updateFilteredPersonList(Predicate predicate) {
+ public void updateFilteredStudentList(Predicate predicate) {
requireNonNull(predicate);
filteredPersons.setPredicate(predicate);
}
@@ -147,4 +148,94 @@ public boolean equals(Object obj) {
&& filteredPersons.equals(other.filteredPersons);
}
+ /**
+ * Returns true if the model has a student whose name is part of the input name.
+ */
+ @Override
+ public boolean hasDuplicateName(String name) {
+ int count = 0;
+ for (Student s : filteredPersons) {
+ if (s.getName().toString().toLowerCase().contains(name.toLowerCase())) {
+ count++;
+ }
+ }
+ return count >= 2;
+ }
+
+ @Override
+ public boolean hasDuplicateNameAdd(String name) {
+ int count = 0;
+ for (Student s : filteredPersons) {
+ if (s.getName().toString().toLowerCase().contains(name.toLowerCase())) {
+ count++;
+ }
+ }
+ return count >= 1;
+ }
+
+ @Override
+ public boolean hasDuplicateNameEdit(String name, Integer index) {
+ int count = 0;
+ for (int i = 0; i < filteredPersons.size(); i++) {
+ if (filteredPersons.get(i).getName().toString().toLowerCase().contains(name.toLowerCase()) && i != index) {
+ count++;
+ }
+ }
+ return count >= 1;
+ }
+
+ /**
+ * Returns true if the model has a student whose name is part of the input name.
+ */
+ @Override
+ public boolean hasExtendedName(String name) {
+ int count = 0;
+ for (Student s : filteredPersons) {
+ if (name.toLowerCase().contains(s.getName().toString().toLowerCase())) {
+ count++;
+ }
+ }
+ return count >= 1;
+ }
+
+ @Override
+ public boolean hasExtendedNameEdit(String name, Integer index) {
+ int count = 0;
+ for (int i = 0; i < filteredPersons.size(); i++) {
+ if (name.toLowerCase().contains(filteredPersons.get(i).getName().toString().toLowerCase()) && i != index) {
+ count++;
+ }
+ }
+ return count >= 1;
+ }
+
+ @Override
+ public boolean noSuchStudent(String name) {
+ for (Student s : filteredPersons) {
+ if (s.getName().toString().toLowerCase().contains(name.toLowerCase())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean hasConflictingLessonTime(Lesson lesson) {
+ for (Student s : filteredPersons) {
+ if (s.hasConflictingLessonTime(lesson)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean hasConflictingExamTime(Lesson lesson) {
+ for (Student s : filteredPersons) {
+ if (s.hasConflictingExamTime(lesson)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
index 6ddc2cd9a29..e1c7f661f4d 100644
--- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
+++ b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
@@ -1,7 +1,7 @@
package seedu.address.model;
import javafx.collections.ObservableList;
-import seedu.address.model.person.Person;
+import seedu.address.model.student.Student;
/**
* Unmodifiable view of an address book
@@ -12,6 +12,6 @@ public interface ReadOnlyAddressBook {
* Returns an unmodifiable view of the persons list.
* This list will not contain any duplicate persons.
*/
- ObservableList getPersonList();
+ ObservableList getPersonList();
}
diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java
deleted file mode 100644
index 8ff1d83fe89..00000000000
--- a/src/main/java/seedu/address/model/person/Person.java
+++ /dev/null
@@ -1,123 +0,0 @@
-package seedu.address.model.person;
-
-import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-
-import seedu.address.model.tag.Tag;
-
-/**
- * Represents a Person in the address book.
- * Guarantees: details are present and not null, field values are validated, immutable.
- */
-public class Person {
-
- // Identity fields
- private final Name name;
- private final Phone phone;
- private final Email email;
-
- // Data fields
- private final Address address;
- private final Set tags = new HashSet<>();
-
- /**
- * Every field must be present and not null.
- */
- public Person(Name name, Phone phone, Email email, Address address, Set tags) {
- requireAllNonNull(name, phone, email, address, tags);
- this.name = name;
- this.phone = phone;
- this.email = email;
- this.address = address;
- this.tags.addAll(tags);
- }
-
- public Name getName() {
- return name;
- }
-
- public Phone getPhone() {
- return phone;
- }
-
- public Email getEmail() {
- return email;
- }
-
- public Address getAddress() {
- return address;
- }
-
- /**
- * Returns an immutable tag set, which throws {@code UnsupportedOperationException}
- * if modification is attempted.
- */
- public Set