:information_source: **Note:** If a command fails its execution, it will not call `Model#commitAddressBook()`, so the address book state will not be saved into the `addressBookStateList`.
-
+
:information_source: **Note:** If the `currentStatePointer` is at index 0 (the initial state), there are no earlier states to restore. `Model#canUndoTutorTrack()` checks this and returns an error if undo is not possible.
-Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state.
+Step 4. The user now decides that adding the student was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoTutorTrack()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous TutorTrack state, and restores the previous state.

-
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather
+
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial TutorTrack state, then there are no previous states to restore. The `undo` command uses `Model#canUndoTutorTrack()` to check if this is the case. If so, it will return an error to the user rather
than attempting to perform the undo.
@@ -206,17 +206,17 @@ Similarly, how an undo operation goes through the `Model` component is shown bel

-The `redo` command does the opposite — it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state.
+The `redo` command reverses the undo. It calls `Model#redoTutorTrack()`, shifting the `currentStatePointer` one step forward to restore the next state.
-
:information_source: **Note:** If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone AddressBook states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
+
:information_source: **Note:** If the `currentStatePointer` is already at the latest state (i.e., index `tutorTrackStateList.size() - 1`), `redo` is not possible. `Model#canRedoTutorTrack()` handles this check.
-Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged.
+Step 5. The user executes a non-mutating command like `list`. Such commands do not modify the state and do not trigger any undo/redo behavior. The `tutorTrackStateList` remains unchanged.

-Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …` command. This is the behavior that most modern desktop applications follow.
+Step 6. The user executes `clear`. Since `clear` calls `Model#commitTutorTrack()` and the `currentStatePointer` is not at the latest state, all states after the pointer are purged — making redo impossible. This mirrors typical desktop application behavior.

@@ -224,26 +224,19 @@ The following activity diagram summarizes what happens when a user executes a ne
-#### Design considerations:
+#### Design considerations
**Aspect: How undo & redo executes:**
-* **Alternative 1 (current choice):** Saves the entire address book.
+* **Alternative 1 (current choice):** Saves the entire addressbook.
* Pros: Easy to implement.
* Cons: May have performance issues in terms of memory usage.
* **Alternative 2:** Individual command knows how to undo/redo by
itself.
- * Pros: Will use less memory (e.g. for `delete`, just save the person being deleted).
+ * Pros: Will use less memory (e.g. for `delete_student`, just save the student being deleted).
* Cons: We must ensure that the implementation of each individual command are correct.
-_{more aspects and alternatives to be added}_
-
-### \[Proposed\] Data archiving
-
-_{Explain here how the data archiving feature will be implemented}_
-
-
--------------------------------------------------------------------------------------------------------------------
## **Documentation, logging, testing, configuration, dev-ops**
@@ -262,121 +255,439 @@ _{Explain here how the data archiving feature will be implemented}_
**Target user profile**:
-* has a need to manage a significant number of contacts
+* is a Singaporean freelance tutor, who teaches small groups or individual students
+* has a need to manage a significant number of students, with various lessons and assignments
* prefer desktop apps over other types
* can type fast
* prefers typing to mouse interactions
* is reasonably comfortable using CLI apps
-**Value proposition**: manage contacts faster than a typical mouse/GUI driven app
+**Value proposition**: Quickly organise student contact details, track lesson schedules, log student progress all in one app
### User stories
Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*`
-| Priority | As a … | I want to … | So that I can… |
-| -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- |
-| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App |
-| `* * *` | user | add a new person | |
-| `* * *` | user | delete a person | remove entries that I no longer need |
-| `* * *` | user | find a person by name | locate details of persons without having to go through the entire list |
-| `* *` | user | hide private contact details | minimize chance of someone else seeing them by accident |
-| `*` | user with many persons in the address book | sort persons by name | locate a person easily |
+| Priority | As a … | I want to … | So that I can… |
+|----------|-----------------|----------------------------------------------------------------|-------------------------------------------------------------------------------------------|
+| `* * *` | tutor | add a new student | keep track of their details |
+| `* * *` | tutor | add lessons for each student | make a lesson plan suited for my students |
+| `* * *` | tutor | view lessons for each student | have an overview of the students' learning curriculum |
+| `* * *` | tutor | assign tasks to students | track student's workload and assignments |
+| `* * *` | tutor | view list of all students | view all students that I am teaching |
+| `* * *` | tutor | delete a student | remove students that I am no longer tutoring |
+| `* * *` | tutor | track completion status of assignments | know if my students have completed them |
+| `* *` | tutor | reschedule lessons | make changes to lesson plans to better fit mine or my student's schedule |
+| `* *` | tutor | mark lessons as complete | review session history and track my students' lesson progress |
+| `* *` | tutor | set personalized reminders for students | address individual needs effectively |
+| `* *` | tutor | set reminders for my own tasks | keep up with what I need to do |
+| `* *` | user | add custom tags to contacts | organise my contacts better |
+| `* *` | tutor | filter students by status (e.g. active or inactive) | manage my student's long-term engagements |
+| `* *` | tutor | filter students by keywords and tags | easily find a student |
+| `* *` | tutor | search for a student by name | quickly find their records |
+| `* *` | tutor | update student details | keep their information accurate |
+| `* *` | new user | view help documentation | understand how to interact with the application effectively |
+| `* *` | long-time user | add shortcuts to commands | personalize my use of the app |
+| `*` | tutor | export schedules to my personal calendar | manage the tutoring schedule with my other commitments |
+| `*` | new user | import data from a spreadsheet | start to keep track of my students |
+| `*` | first time user | use commands with contextual help | learn proper command syntax and options without having to refer to external documentation |
+| `*` | tutor | get a timeline overview of all events within a period of time | view the overall structure of the schedule for said period of time |
+
+# **Use Cases for TutorTrack**
+
+## Use Case 1: Add Student
+
+**System**: TutorTrack\
+**Actor**: Tutor\
+**Use Case**: UC01 - Add Student
+
+### Main Success Scenario (MSS)
+
+1. Tutor enters a command to add a student, including name, phone number, address, and email. Subject(s) may be optionally included.
+2. TutorTrack validates the input and ensures the student does not already exist.
+3. TutorTrack saves the student information and confirms the addition.
+
+ **Use case ends.**
+
+### Extensions
+
+- **1a**: Tutor enters an invalid phone number format.
+ - 1a1: TutorTrack warns the tutor about the invalid phone number.
+ - 1a2: TutorTrack prompts the tutor to enter the command again.
+ - Use case resumes from step 1.
+
+- **1b**: Tutor enters an invalid email format.
+ - 1b1: TutorTrack warns the tutor about the invalid email.
+ - 1b2: TutorTrack prompts the tutor to enter the command again.
+ - Use case resumes from step 1.
+
+- **1c**: Tutor tries to add a student that already exists (same name and phone number).
+ - 1c1: TutorTrack informs the tutor that the student already exists.
+ - **Use case ends.**
+
+- **1d**: Tutor enters invalid subject format.
+ - 1d1: TutorTrack warns the tutor about the invalid subject.
+ - 1d2: TutorTrack prompts the tutor to enter the command again.
+ - Use case resumes from step 1.
+
+---
+
+## Use Case 2: View Student List
+
+**System**: TutorTrack\
+**Actor**: Tutor\
+**Use Case**: UC02 - View Student List
+
+### Main Success Scenario (MSS)
+
+1. Tutor enters the command to view all students.
+2. TutorTrack retrieves and displays all registered students.
+
+ **Use case ends.**
+
+### Extensions
+
+- **1a**: No students are found in the system.
+ - 1a1: TutorTrack informs the tutor that no students are available.
+ - **Use case ends.**
+
+---
+
+## Use Case 3: Delete Student
+
+**System**: TutorTrack\
+**Actor**: Tutor\
+**Use Case**: UC03 - Delete Student
+
+### Main Success Scenario (MSS)
+
+1. Tutor enters the command to delete a student using the student index.
+2. TutorTrack validates the index and removes the student.
+3. TutorTrack confirms the deletion.
+
+ **Use case ends.**
+
+### Extensions
+
+- **1a**: The entered student index is out of bound.
+ - 1a1: TutorTrack warns the tutor that the index is not valid.
+ - **Use case ends.**
+
+---
+
+## Use Case 4: Add Lesson
+
+**System**: TutorTrack\
+**Actor**: Tutor\
+**Use Case**: UC04 - Add Lesson
+
+### Main Success Scenario (MSS)
+
+1. Tutor enters a command to add a lesson, including student name, date, time, and subject.
+2. TutorTrack validates the input and ensures no duplicate lesson exists at the same time.
+3. TutorTrack saves the lesson and confirms the addition.
+
+ **Use case ends.**
+
+### Extensions
+
+- **1a**: Student not found.
+ - 1a1: TutorTrack informs the tutor that the student does not exist.
+ - **Use case ends.**
+
+- **1b**: Tutor enters an invalid date or time format.
+ - 1b1: TutorTrack warns the tutor about the incorrect format.
+ - 1b2: TutorTrack prompts the tutor to enter the command again.
+ - Use case resumes from step 1.
+
+- **1c**: A duplicate lesson exists for the same student at the same time.
+ - 1c1: TutorTrack warns the tutor about the duplicate lesson.
+ - **Use case ends.**
+
+---
+
+## Use Case 5: View Lessons
+
+**System**: TutorTrack\
+**Actor**: Tutor\
+**Use Case**: UC05 - View Lessons
-*{More to be added}*
+### Main Success Scenario (MSS)
-### Use cases
+1. Tutor enters the command to view all lessons, optionally with keywords of student names.
+2. TutorTrack retrieves and displays all lessons.
-(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise)
+ **Use case ends.**
-**Use case: Delete a person**
+### Extensions
+
+- **1a**: No student matches the keywords.
+ - 1a1: TutorTrack informs the tutor that no lessons are available.
+ - **Use case ends.**
+
+---
-**MSS**
+## Use Case 6: Create Assignment to a Student
-1. User requests to list persons
-2. AddressBook shows a list of persons
-3. User requests to delete a specific person in the list
-4. AddressBook deletes the person
+**System**: TutorTrack\
+**Actor**: Tutor\
+**Use Case**: UC06 - Create Assignment
- Use case ends.
+### Main Success Scenario (MSS)
-**Extensions**
+1. Tutor enters a command to create an assignment, including the student's index, assignment name, and due date.
+2. TutorTrack validates the input and ensures the assignment does not already exist for the student.
+3. TutorTrack saves the assignment and confirms the addition.
-* 2a. The list is empty.
+ **Use case ends.**
- Use case ends.
+### Extensions
-* 3a. The given index is invalid.
+- **1a**: Student index is out of bounds.
+ - 1a1: TutorTrack informs the tutor that the student was not found.
+ - **Use case ends.**
+
+- **1b**: Tutor enters an invalid due date (e.g., past date).
+ - 1b1: TutorTrack warns the tutor about the invalid date.
+ - 1b2: TutorTrack prompts the tutor to enter the command again.
+ - Use case resumes from step 1.
+
+- **1c**: Assignment already exists for the student.
+ - 1c1: TutorTrack warns the tutor about the duplicate assignment.
+ - **Use case ends.**
+
+- **1d**: Tutor enters a blank assignment name.
+ - 1d1: TutorTrack warns the tutor about the blank assignment name.
+ - 1d2: TutorTrack prompts the tutor to enter the command again.
+ - **Use case ends.**
+
+---
- * 3a1. AddressBook shows an error message.
+## Use Case 7: Track Assignment Completion
- Use case resumes at step 2.
+**System**: TutorTrack\
+**Actor**: Tutor\
+**Use Case**: UC07 - Toggle Assignment Completion Status
-*{More to be added}*
+### Main Success Scenario (MSS)
-### Non-Functional Requirements
+1. Tutor enters the command to mark/unmark an assignment as completed/incomplete.
+2. TutorTrack updates the assignment status and confirms completion.
+
+ **Use case ends.**
+
+### Extensions
+
+- **1a**: Student index out of bound.
+ - 1a1: TutorTrack informs the tutor that the index is out of bound.
+ - 1a2: TutorTrack prompts the tutor to enter the command again.
+ - Use case resumes from step 1.
+
+- **1b**: Assignment not found.
+ - 1b1: TutorTrack informs the tutor that the assignment does not exist.
+ - **Use case ends.**
+
+- **1c**: Tutor enters a blank assignment name.
+ - 1c1: TutorTrack warns the tutor about the assignment name is missing.
+ - 1c2: TutorTrack prompts the tutor to enter the command again.
+ - **Use case ends.**
+
+- **1d**: Assignment already in the target (e.g., tutor tries to mark completed when already completed).
+ - 1d1: TutorTrack informs the tutor that the assignment is already in the target state.
+ - **Use case ends.**
+
+---
+
+## **Non-Functional Requirements**
1. Should work on any _mainstream OS_ as long as it has Java `17` or above installed.
-2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage.
+2. Should be able to hold up to 1000 students without a noticeable sluggishness in performance for typical usage.
3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
+4. Any command should respond in 10 seconds or less for up to 100 students.
+5. System should be usable by a user who is comfortable with typing and using a CLI.
+6. System should be able to store data for at least 1 year without data loss.
+7. Project is expected to adhere to the [Java Code Quality Guide](https://se-education.org/guides/contributing/javaCodeQualityGuide.html).
+8. Project is expected to adhere to a schedule that delivers a feature set every week.
+9. This project is not expected to connect to the internet or any external services (email, cloud storage, telegram, etc).
-*{More to be added}*
+---
-### Glossary
+## **Glossary**
-* **Mainstream OS**: Windows, Linux, Unix, MacOS
-* **Private contact detail**: A contact detail that is not meant to be shared with others
+* **Mainstream OS**: Windows, Linux, Unix, macOS
+* **Tutor**: A person who does free-lance tutoring
+* **Student**: A person that is being or has been tutored by the current user of the application
+* **Spreadsheet**: An Excel spreadsheet
+* **Assignment**: A homework assignment or task that has been given by the tutor to the student
--------------------------------------------------------------------------------------------------------------------
-## **Appendix: Instructions for manual testing**
+## **Appendix: Effort**
-Given below are instructions to test the app manually.
+**Team size**: 5
-
:information_source: **Note:** These instructions only provide a starting point for testers to work on;
-testers are expected to do more *exploratory* testing.
+**Difficulty level**: Moderate to High
-
+**Challenges faced**:
-### Launch and shutdown
+1. **Complexity of Features**: Implementing features such as lessons and assignments required a deep understanding of the existing codebase and careful planning to ensure seamless integration.
+2. **Data Management**: Handling multiple entity types (students, lessons, assignments) added complexity to the data management and storage components.
+3. **User Interface**: Ensuring the CLI-based interface remains user-friendly while accommodating new features was challenging.
+4. **Testing**: Writing comprehensive tests for new features required significant effort.
-1. Initial launch
+**Effort required**:
- 1. Download the jar file and copy into an empty folder
+- **Planning and Design**: Approximately 20 hours were spent on planning and designing the new features and their integration with the existing system.
+- **Implementation**: Around 80 hours were dedicated to coding, debugging, and refining the new features.
+- **Testing**: About 30 hours were spent on writing and executing test cases to ensure the reliability of the new features.
+- **Documentation**: Approximately 10 hours were used to update the Developer Guide and User Guide to reflect the new features and changes.
- 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
+**Achievements**:
-1. Saving window preferences
+- Successfully implemented the lessons and assignments feature, allowing users to add lessons and assignments to students with ease.
+- Enhanced data management capabilities to handle multiple entity types efficiently.
+- Improved the user interface to provide better feedback and usability.
+- Maintained high code quality and adherence to coding standards throughout the project.
- 1. Resize the window to an optimum size. Move the window to a different location. Close the window.
+**Reuse**:
- 1. Re-launch the app by double-clicking the jar file.
- Expected: The most recent window size and location is retained.
+- The lessons/assignments feature was inspired by similar implementations in other projects, but our implementation was tailored to fit the specific needs of TutorTrack.
+- Some utility functions and classes were reused from the AddressBook-Level3 project, with minimal modifications to suit our requirements.
-1. _{ more test cases … }_
+---
-### Deleting a person
+## **Appendix: Instructions for manual testing**
-1. Deleting a person while all persons are being shown
+### Launch and Shutdown Testing
- 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list.
+1. **First-time launch**
+ - Delete any existing **`data/TutorTrack.json`** file
+ - Launch the application via **`java -jar tutortrack.jar`**
+ - *Expected*: Loads with sample data, creates new data file
+2. **Persisting window preferences**
+ - Resize and reposition the window
+ - Close and relaunch the application
+ - *Expected*: Retains previous window size and position
- 1. Test case: `delete 1`
- Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated.
+---
- 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.
+### Student Management Testing
+
+1. **Adding a student**
+ - Test case: **`add_student n/John Doe p/98765432 e/john@email.com a/123 Street s/Math`**
+ - *Expected*: Student added with proper formatting (name in Title Case)
+ - Test invalid:
+ - **`add_student n/John@Doe...`** (special characters)
+ - **`add_student n/John p/abc...`** (invalid phone)
+ - *Expected*: Clear error messages for each invalid field
+2. **Deleting a student**
+ - First list students: **`list_students`**
+ - Then: **`delete_student 1`**
+ - *Expected*: First student removed, confirmation shown
+ - Test invalid:
+ - **`delete_student 0`**
+ - **`delete_student 999`** (non-existent index)
+ - *Expected*: Appropriate index errors
- 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
- Expected: Similar to previous.
+---
-1. _{ more test cases … }_
+### Lesson Management Testing
+
+1. **Adding a lesson**
+ - Prerequisite: At least one student exists
+ - Test case: **`add_lesson n/John Doe d/01-09-2025 t/14:00 s/Math`**
+ - *Expected*: Lesson added to student
+ - Test conflicts:
+ - Same time for different students
+ - Invalid dates (past dates, 31-04-2025)
+ - *Expected*: Clear time conflict/validation errors
+2. **Editing a lesson**
+ - First list lessons: **`list_lessons`**
+ - Then: **`edit_lesson 1 t/15:00`**
+ - *Expected*: Lesson time updated
+ - Test invalid:
+ - Overlapping times
+ - Invalid date formats
+ - *Expected*: Appropriate error messages
-### Saving data
+---
+
+### Assignment Management Testing
+
+1. **Creating an assignment**
+ - Prerequisite: At least one student exists
+ - Test case: **`add_assignment 1 as/MathHomework d/01-09-2025`**
+ - *Expected*: Assignment added with future date
+ - Test invalid:
+ - Past dates
+ - Duplicate assignment names
+ - *Expected*: Validation errors
+2. **Marking assignments**
+ - **`mark_assignment 1 as/MathHomework`**
+ - *Expected*: Assignment marked complete (visual indicator)
+ - **`unmark_assignment 1 as/MathHomework`**
+ - *Expected*: Assignment marked incomplete
-1. Dealing with missing/corrupted data files
+---
- 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_
+### Data Persistence Testing
+
+1. **Corrupted data file**
+ - Manually edit **`data/TutorTrack.json`** to:
+ - Remove closing brackets
+ - Add invalid field values
+ - Launch application
+ - *Expected*: Creates new empty data file, logs error
+2. **Missing data file**
+ - Delete **`data/TutorTrack.json`**
+ - Launch application
+ - *Expected*: Creates new file with sample data
+3. **Data integrity**
+ - Perform series of add/edit/delete operations
+ - Close and reopen application
+ - *Expected*: All changes persist correctly
+
+---
+
+### Edge Case Testing
+
+1. **Mass data operations**
+ - Add 50+ students via script
+ - *Expected*: No performance lag in commands
+2. **Special characters**
+ - Test names with apostrophes: **`n/O'Connor`**
+ - *Expected*: Handled properly (may need validation adjustment)
+3. **Timezone testing**
+ - Change system timezone
+ - Test date-sensitive commands
+ - *Expected*: Consistent behavior across timezones
+
+---
+
+### Verification Steps
+
+For each test case:
+
+1. Check command output for success/error messages
+2. Verify UI updates match expected state
+3. For data operations, restart app to verify persistence
+4. Check **`logs/TutorTrack.log`** for any unexpected errors
+
+**Tip**: Use the **`clear`** command between test scenarios to reset state.
+
+---
-1. _{ more test cases … }_
+## **Planned Enhancements**
+
+1. **Undo/Redo Feature**: Allow users to undo their previous commands. It improves the users experience by providing a way to recover from mistakes.
+2. **Enhanced Error Messages**: Improve error messages to be more specific and actionable. For example, instead of showing "Operation failed!", the message could indicate the exact reason for the failure, such as "The student 'John Doe' could not be added because the name already exists."
+2. **Data Validation**: Implement more robust data validation to prevent invalid inputs from being processed. For instance, validate email formats, phone numbers, and date formats before saving them.
+3. **Batch Operations**: Add support for batch operations, such as adding multiple students or assignments at once, to improve efficiency for users managing large datasets.
+4. **Improved Search Functionality**: Enhance the search feature to support more complex queries, such as searching by multiple criteria (e.g., name, subject, and status) simultaneously.
+5. **Customizable Commands**: Enable users to customize commands to their own liking and preferences, to cater to tutors who like shorter commands to have a more efficient workflow.
+6. **Customizable Reminders**: Enable users to set customizable reminders for lessons and assignments, with options for recurring reminders and notifications.
+7. **Input Working Hours**: Enable the tutors to input their working hours so to restrict the timings of the lessons to within their comfortable timings.
+8. **Performance Optimization**: Optimize the application's performance to handle larger datasets more efficiently, ensuring smooth operation even with thousands of entries.
+9. **Enhanced Reporting**: Add reporting features to generate summaries and insights, such as student progress reports, lesson attendance, and assignment completion rates.
+10. **Integration with Calendars**: Allow users to integrate their lesson schedules with external calendar applications (e.g., Google Calendar) for better schedule management.
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index 27c2d1cf16c..b4e47c7a276 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -3,40 +3,60 @@ layout: page
title: User Guide
---
-AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps.
+TutorTrack is a **desktop application designed for Singaporean freelance tutors** to efficiently manage their students, lessons, and assignments. It combines the speed of a **Command Line Interface (CLI)** with the convenience of a **Graphical User Interface (GUI)**, making it ideal for tutors who prefer typing over mouse interactions. If you can type fast, TutorTrack will help you manage your tutoring tasks faster than traditional GUI apps.
+
+TutorTrack uses a **dual-list system** to manage students and lessons. Users can easily modify the student list and lesson list, allowing for quick access to student information and lesson schedules with their unique indexes. The application also supports assignment management, enabling tutors to keep track of their students' assignments and their completion status uniquely identified by the name of the assignment. Users can easily toggle between the lists with simple CLI-based commands.
+
+---
* Table of Contents
{:toc}
---------------------------------------------------------------------------------------------------------------------
+---
-## Quick start
+## Target Users
-1. Ensure you have Java `17` or above installed in your Computer.
- **Mac users:** Ensure you have the precise JDK version prescribed [here](https://se-education.org/guides/tutorials/javaInstallationMac.html).
+Singaporean freelance tutors who:
-1. Download the latest `.jar` file from [here](https://github.com/se-edu/addressbook-level3/releases).
+- Juggle multiple students/lessons and need centralized tracking
+- Prefer keyboard-driven efficiency over mouse navigation
+- Want lightweight but capable task management
-1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook.
+## Assumptions about Users
-1. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar addressbook.jar` command to run the application.
- A GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
- 
+- Users are familiar with basic CLI commands.
+- Users have a basic understanding of file management (e.g., creating folders, moving files).
+- Users are comfortable with Java-based applications.
-1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
- Some example commands you can try:
+--------------------------------------------------------------------------------------------------------------------
+
+## Quick start
- * `list` : Lists all contacts.
- * `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` : Adds a contact named `John Doe` to the Address Book.
+1. **Ensure Java 17 or above is installed** on your computer.
+ **Mac users:** Follow the installation guide [here](https://se-education.org/guides/tutorials/javaInstallationMac.html).
- * `delete 3` : Deletes the 3rd contact shown in the current list.
+1. Download the latest `.jar` file from the [releases page](https://github.com/AY2425S2-CS2103T-T13-4/tp/releases).
- * `clear` : Deletes all contacts.
+1. Copy the `.jar` file to the folder you want to use as the _home folder_ for your TutorTrack.
+
+1. Open a command terminal, navigate to the folder containing the `.jar` file using the `cd` command, and run the application with:
+ ```bash
+ java -jar tutortrack.jar
+ ```
+ A GUI similar to the below should appear in a few seconds, preloaded with sample data.
+ 
- * `exit` : Exits the app.
+1. Type commands in the command box and press `Enter` to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
+ - `help`: Opens the help window.
+ - `list_students`: Lists all students.
+ - `list_lessons n/Jackie` : Lists all lessons under a student or all lessons in the data
+ - `clear`: Deletes all students.
+ - `find_student Bernice`: Finds student containing Bernice.
+ - `exit`: Exits the app.
+ More commands listed below.
-1. Refer to the [Features](#features) below for details of each command.
+1. Refer to the [Features](#features) section below for detailed instructions on each command.
--------------------------------------------------------------------------------------------------------------------
@@ -46,155 +66,460 @@ AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized fo
**:information_source: Notes about the command format:**
-* Words in `UPPER_CASE` are the parameters to be supplied by the user.
- e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`.
+- **Parameters in `UPPER_CASE`** are to be supplied by the user. Example: In `add_student n/STUDENT_NAME`, `STUDENT_NAME` can be replaced with `John Doe`.
+
+- **Optional fields** are enclosed in square brackets `[]`. Example: `add_student n/NAME p/PHONE e/EMAIL a/ADDRESS [s/SUBJECT]` can be used as `add_student n/John Doe p/91234567 e/ johndoe@gmail.com a/ 311, Clementi Ave 2, #02-25 s/Math` or `add_student n/John Doe p/91234567 e/ johndoe@gmail.com a/ 311, Clementi Ave 2, #02-25`.
+
+- **Multiple uses** of a field are indicated by `…`.
+
+ Example: `[s/SUBJECT]…` can be used as `s/Math`, `s/Math s/Science`, or omitted entirely.
-* Items in square brackets are optional.
- e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`.
+- **Parameters can be in any order**.
-* Items with `…` after them can be used multiple times including zero times.
- e.g. `[t/TAG]…` can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc.
+ Example: `n/STUDENT_NAME p/PHONE_NUMBER` is equivalent to `p/PHONE_NUMBER n/STUDENT_NAME`.
-* Parameters can be in any order.
- e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable.
+- **Extraneous parameters** for commands like `help`, `list_students`, `exit`, and `clear` will be ignored.
-* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
- e.g. if the command specifies `help 123`, it will be interpreted as `help`.
+ Example: `help 123` is interpreted as `help`.
-* If you are using a PDF version of this document, be careful when copying and pasting commands that span multiple lines as space characters surrounding line-breaks may be omitted when copied over to the application.
+- **Use of indexes**: To facilitate ease of typing, lessons and students are referred as indexes in the current view.
+
+ For example, if you are viewing the student list and you want to delete the 2nd student, you can type `delete_student 2` to delete the 2nd student in the list. The same applies for lessons.
+
+ - Depends on the filtered view of each list, each student/lesson may have different index.
+
+ - Avoid accessing lessons through index while viewing the student list and vice versa. It is recommended to use the `list_students` or `list_lessons` command to view the respective lists before using the index.
+
+- If you are using a PDF version of this document, be careful when copying and pasting commands that span multiple lines as space characters surrounding line-breaks may be omitted when copied over to the application.
-### Viewing help : `help`
+### General features
-Shows a message explaning how to access the help page.
+#### Viewing help : `help`
+
+Shows a message explaining how to access the help page.
+
+**Format:**
+
+`help`

-Format: `help`
+#### Clearing all entries : `clear`
+
+Clears all entries from both the student list and the lesson list.
+
+
:exclamation: **Caution:**
+There is no confirmation for this command and the data will be cleared immediately after execution. Use at your own risk.
+
+
+**Format:**
+
+`clear`
+
+#### Exiting the program : `exit`
+
+Exits the program.
+
+**Format:**
+
+`exit`
+
+#### Saving the data
-### Adding a person: `add`
+TutorTrack data is saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
-Adds a person to the address book.
+#### Editing the data file
-Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…`
+TutorTrack data is saved automatically as a JSON file `[JAR file location]/data/TutorTrack.json`. Advanced users are welcome to update data directly by editing that data file.
-
:bulb: **Tip:**
-A person can have any number of tags (including 0)
+
:exclamation: **Caution:**
+If your changes to the data file makes its format invalid, TutorTrack will discard all data and start with an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it.
+Furthermore, certain edits can cause the TutorTrack to behave in unexpected ways (e.g., if a value entered is outside of the acceptable range). Therefore, edit the data file only if you are confident that you can update it correctly.
-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`
+### Managing students
-### Listing all persons : `list`
+TutorTrack allows for easy management of students, including adding, editing, deleting, and finding students. You can also view all students in the list.
-Shows a list of all persons in the address book.
+The student list is displayed on default when the application is opened. The student list shows the name, phone number, email, address, subjects and assignments of each student.
-Format: `list`
+#### Adding a student: `add_student`
-### Editing a person : `edit`
+Adds a student to the student list, with their name, phone number, address, email and subjects.
+You can add multiple subjects by using the subject prefix `s/` for each subject.
-Edits an existing person in the address book.
+**Format:**
-Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…`
+`add_student n/STUDENT_NAME p/PHONE_NUMBER a/ADDRESS e/EMAIL [s/SUBJECTS]…`
-* 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.
+- `STUDENT_NAME` is the student's name displayed on the student list.
+ - Alphanumerical characters and spaces (e.g., **`Alex Yeoh`**, **`John Doe 2`**)
+ - Auto-converted to Title Case (e.g., **`alex yeoh`** → **`Alex Yeoh`**)
+ - Special constructs (**`d/o`**, **`s/o`**) and Symbols/hyphens (**`-`**, **`'`**) or numerals (**`0-9`**) not allowed
+- `PHONE_NUMBER` is the student's contact number
+ - Has to be 8 digits
+- `ADDRESS` is the student's postal address
+- `EMAIL` is the student's email
+ - Must be a valid email address (details in the app)
+- `SUBJECT` is the subject the student is being taught
+ - Can have any number of subjects
+ - Alphanumeric characters allowed, case-insensitive and automatically converts to Title Case
-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.
+**Examples**:
+* `add_student n/John Doe p/98765432 e/johndoe@email.com a/311, Clementi Ave 2, #02-25 s/Math`
+* `add_student n/Mary Jane p/12345678 e/maryjane@email.com a/Blk 47 Tampines Street 20, #17-35 s/Math s/Science`
-### Locating persons by name: `find`
+#### Editing a student : `edit_student`
-Finds persons whose names contain any of the given keywords.
+Edits an existing student in the student list.
-Format: `find KEYWORD [MORE_KEYWORDS]`
+**Format:**
-* 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`
+`edit_student STUDENT_INDEX [n/STUDENT_NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS]`
-Examples:
-* `find John` returns `john` and `John Doe`
-* `find alex david` returns `Alex Yeoh`, `David Li`
- 
+- `STUDENT_NAME` is the student's name displayed on the student list.
+ - Alphabetic characters and spaces (e.g., **`Alex Yeoh`**)
+ - Auto-converted to Title Case (e.g., **`alex yeoh`** → **`Alex Yeoh`**)
+ - Special constructs (**`d/o`**, **`s/o`**) and Symbols/hyphens (**`-`**, **`'`**) or numerals (**`0-9`**) not allowed
+- `PHONE_NUMBER` is the student's contact number
+ - Has to be 8 digits
+- `ADDRESS` is the student's postal address
+- `EMAIL` is the student's email
+ - Must be a valid email address (details in the app)
+- Subjects cannot be edited at this stage.
+ - To edit the subjects, you can delete the student and add a new one with the updated subjects.
-### Deleting a person : `delete`
+**Examples**:
+* `edit_student 1 p/91234567 e/johndoe@example.com`
Edits the phone number and email address of the 1st student to be `91234567` and `johndoe@example.com` respectively.
-Deletes the specified person from the address book.
+#### Deleting a student : `delete_student`
-Format: `delete INDEX`
+Deletes the specified student from the student list.
-* 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, …
+
:bulb: **Note** This command can only be executed when viewing the student list (e.g. `list_students`)
-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.
+**Format:**
-### Clearing all entries : `clear`
+`delete_student STUDENT_INDEX`
-Clears all entries from the address book.
+* `STUDENT_INDEX` corresponds to the index of the student as displayed in the student list
+ * Has to be a **positive integer**
+
+**Example**:
+* `list_students` followed by `delete_student 2` deletes the 2nd student in the student list.
-Format: `clear`
+#### Listing all students : `list_students`
-### Exiting the program : `exit`
+Switch to a view that shows all students in the student list.
-Exits the program.
+**Format:**
-Format: `exit`
+`list_students`
-### Saving the data
+#### Locating students by name: `find_student`
-AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
+Finds students whose names contain any of the given keywords.
-### Editing the data file
+
:information_source: **Note**: Partial-word matching (e.g., searching for "Sre" to match "Sree Haridos") is currently not supported but may be added in a future update.
-AddressBook data are saved automatically as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file.
+**Format:**
-
: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. Hence, it is recommended to take a backup of the file before editing it.
-Furthermore, certain edits can cause the AddressBook to behave in unexpected ways (e.g., if a value entered is outside of the acceptable range). Therefore, edit the data file only if you are confident that you can update it correctly.
-
+`find_student KEYWORD [MORE_KEYWORDS...]`
+
+* `STUDENT_NAME` is the name of the student.
+ * It must be a valid name of a student in the student list.
+ * Search is case-insensitive. e.g `hans` will match `Hans`
+ * Only matches complete words in the name (e.g., `Alex` matches `Alex Yeoh` but not `Alexander`).
+ * The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans`
+ * Only the name is searched.
+ * Students matching at least one keyword will be returned.
+ e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang`
+
+**Examples**:
+* `find_student John` returns `john` and `John Doe`
+* `find_student alex david` returns `Alex Yeoh`, `David Li`
+
+### Managing lessons
+
+TutorTrack allows for easy management of lessons, including adding, editing, deleting, and listing lessons. You can also view all lessons in the list.
+
+The lesson list is displayed when you type `list_lessons` in the command box. The lesson list shows the name of the student, date, time and subject of each lesson.
+
+
+
+#### Adding a lesson: `add_lesson`
+
+Adds a lesson to the lesson list.
+
+
:information_source: **Group Lessons Note**: Currently, TutorTrack doesn't support group lessons where multiple students share the same time slot. Each lesson time must be unique to the tutor's schedule. We're considering adding group lesson support in a future update.
+
+**Format:**
+
+`add_lesson n/STUDENT_NAME s/SUBJECT d/DATE t/TIME`
+
+
:bulb: **Note:** Student names will be automatically matched in a case-insensitive manner and stored in Title Case.
+
+* `STUDENT_NAME` is the name of the student.
+ * It must be a valid name of a student in the student list.
+ * Names are case-insensitive
+ * **`alice chan`** matches **`Alice Chan`**
+* `SUBJECT` is the subject of the lesson.
+ * It must be a valid subject of the student in the student list.
+* `DATE` is the date of the lesson.
+ * It must be in the format `d-M-yyyy` and must be in the future.
+* `TIME` is the time of the lesson.
+ * It must be in the format `HH:mm`.
+ * Two lessons cannot be scheduled at the same time (e.g. 15:00 and 15:01 are considered different)
+
+**Example:**
+* `add_lesson n/Alice Chan d/17-09-2025 t/15:00 s/Math`
+* `add_lesson n/Bob Lee d/17-09-2025 t/16:00 s/Math` (different time slot)
+
+#### Editing a lesson: `edit_lesson`
+
+Edits the details of an existing lesson
+* Edit individual details or edit multiple together.
+* Student names in lessons will be automatically converted to Title Case when edited
+
+**Format:**
+
+`edit_lesson LESSON_INDEX [n/STUDENT_NAME] [d/DATE] [t/TIME] [s/SUBJECT]`
+
+* `LESSON_INDEX` corresponds to the index of the lesson on the displayed lesson list
+ * Has to be a **positive integer**
+* `STUDENT_NAME` is the name of the student.
+ * It must be a valid name of a student in the student list.
+ * Names are case-insensitive
+ * **`alice chan`** matches **`Alice Chan`**
+* `SUBJECT` is the subject of the lesson.
+ * It must be a valid subject of the student in the student list.
+* `DATE` is the date of the lesson.
+ * It must be in the format `d-M-yyyy` and must be in the future.
+* `TIME` is the time of the lesson.
+ * It must be in the format `HH:mm`.
+ * Two lessons cannot be scheduled at the same time (e.g. 15:00 and 15:01 are considered different)
+
+**Examples**:
+* `edit_lesson 1 d/16-02-2026`
+* `edit_lesson 2 n/Jone King t/16:00 d/18-9-2025 s/Math`
+
+#### Deleting a lesson: `delete_lesson`
+
+Deletes the specified lesson from the lesson list.
-### Archiving data files `[coming in v2.0]`
+**Format:**
-_Details coming soon ..._
+`delete_lesson LESSON_INDEX`
+
+* `LESSON_INDEX` corresponds to the index of the lesson on the displayed lesson list.
+ * Has to be a **positive integer**.
+
+**Examples:**
+* `list_lessons` followed by `delete_lesson 2` deletes the 2nd lesson in the lesson list.
+* `list_lessons n/John Lee` followed by `delete_lesson 1` deletes the 1st lesson in the lesson list.
+
+#### Listing lessons : `list_lessons`
+
+Shows a list of all lessons under a student in the lesson list.
If no student is specified, shows all lessons in the list.
+
+**Format:**
+
+`list_lessons [n/STUDENT_NAME]`
+
+* `STUDENT_NAME` is as displayed on the student list (case-insensitive)
+
+**Example:**
+* `list_lessons n/John Lee`
+* `list_lessons`
+
+### Managing assignments
+
+TutorTrack allows for easy management of assignments, including adding, deleting, marking and unmarking assignments. You can also view all assignments in the student list under each student entry.
+
+#### Adding an assignment: `add_assignment`
+
+Adds an assignment to a student in the student list
+
+**Format:**
+
+`add_assignment STUDENT_INDEX as/ASSIGNMENT_NAME d/DATE`
+
+- `STUDENT_INDEX` corresponds to the student to which the assignment belongs in the displayed list.
+ - Has to be a **positive integer**
+- `ASSIGNMENT_NAME` is the name of the assignment to add.
+ - The name of the assignment must be unique within the student.
+ - Alphanumeric characters allowed (e.g., **`Math Exercise 1`**)
+ - Automatically converted to Title Case (e.g., **`math exercise`** → **`Math Exercise`**)
+- `DATE` is the due date of the assignment.
+ - The date must be in the format `d-M-yyyy` (e.g., `27-09-2026` or `1-1-2026`).
+ - The date must be in the future (i.e., not in the past).
+
+**Example:**
+* `add_assignment 2 as/Science 101 d/27-09-2026`
+
+#### Deleting an assignment: `delete_assignment`
+
+Deletes the assignment identified by the index number of the student and the assignment name.
The assignment will be removed from the student's list of assignments.
+
+**Format:**
+
+`delete_assignment STUDENT_INDEX as/ASSIGNMENT_NAME`
+
+- `INDEX` corresponds to the student to which the assignment belongs in the displayed list.
+ - Has to be a **positive integer**
+- `ASSIGNMENT_NAME` is the name of the assignment to add.
+ - The name of the assignment must be unique within the student.
+ - Alphanumeric characters allowed (e.g., **`Math Exercise 1`**)
+ - Automatically converted to Title Case (e.g., **`math exercise`** → **`Math Exercise`**)
+
+**Example:**
+- `delete_assignment 1 as/Assignment 1` deletes the assignment named `Assignment 1` for the first student in the list.
+
+#### Editing an assignment: `edit_assignment`
+
+Edits an assignment for a student in the student list
+
+**Format:**
+
+`edit assignment STUDENT_INDEX as/ASSIGNMENT_NAME [nas/NEW_ASSIGNMENT_NAME] [d/DATE]`
+
+- `STUDENT_INDEX` corresponds to the student to which the assignment belongs in the displayed list.
+ - Has to be a **positive integer**
+- `ASSIGNMENT_NAME` is the name of the assignment to add.
+ - The name of the assignment must be unique within the student.
+ - Alphanumeric characters allowed (e.g., **`Math Exercise 1`**)
+ - Automatically converted to Title Case (e.g., **`math exercise`** → **`Math Exercise`**)
+- `NEW_ASSIGNMENT_NAME` is the new name of the assignment to be changed to.
+ - Follows as above
+- `DATE` is the due date of the assignment.
+ - The date must be in the format `d-M-yyyy` (e.g., `27-09-2026` or `1-1-2026`).
+ - The date must be in the future (i.e., not in the past).
+
+**Example:**
+* `edit_assignment 1 as/Science nas/Math` changes the name of the assignment of the first student in the display student list from `Science` to `Math`.
+* `edit_assignment 1 as/Science d/12-12-2025` changes the date of the assignment named "Science" of the first student in the display student list.
+* `edit_assignment 1 as/Science nas/Math d/12-12-2025` combination of the two example above.
+
+#### Marking an assignment: `mark_assignment`
+
+Marks an assignment of a student as complete.
Complete assignments are denoted by a green colour.
+
+**Format:**
+
+`mark_assignment STUDENT_INDEX as/ASSIGNMENT_NAME`
+- `STUDENT_INDEX` corresponds to the student to which the assignment belongs in the displayed list.
+ - Has to be a **positive integer**
+- `ASSIGNMENT_NAME` is the name of the assignment to add.
+ - The name of the assignment must be unique within the student.
+ - Alphanumeric characters allowed (e.g., **`Math Exercise 1`**)
+ - Automatically converted to Title Case (e.g., **`math exercise`** → **`Math Exercise`**)
+
+**Example:**
+
+- `mark_assignment 1 as/Assignment 1` marks the assignment named "Assignment 1" of the first student in the list as completed.
+
+#### Unmarking an Assignment: `unmark_assignment`
+
+Marks an assignment of a student as incomplete.
Incomplete assignments are denoted by a red colour.
+
+**Format:**
+
+`unmark_assignment STUDENT_INDEX as/ASSIGNMENT_NAME`
+
+- `STUDENT_INDEX` corresponds to the student to which the assignment belongs in the displayed list.
+ - Has to be a **positive integer**
+- `ASSIGNMENT_NAME` is the name of the assignment to add.
+ - The name of the assignment must be unique within the student.
+ - Alphanumeric characters allowed (e.g., **`Math Exercise 1`**)
+ - Automatically converted to Title Case (e.g., **`math exercise`** → **`Math Exercise`**)
+
+**Example:**
+- `unmark_assignment 1 as/Assignment 1` unmarks the first assignment in the list, setting it to incomplete.
--------------------------------------------------------------------------------------------------------------------
## 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 do I transfer my data to another computer?
+
+**A:**
+Install the app on the new computer and replace the empty data file with the one from your previous TutorTrack folder.
+
+
+**Q:** Why can't I add students with identical names?
+
+**A:** TutorTrack prevents students with identical names to:
+
+1. **Prevent Confusion**
+ - Ensures each student has a unique identity in your records
+ - Eliminates ambiguity when scheduling lessons or assignments
+2. **Maintain Data Accuracy**
+ - Guarantees commands like **`delete_student`** or **`edit_student`** affect the correct individual
+ - Prevents accidental merging of different students' records
+3. **Optimize Workflow**
+ - Makes student selection faster when managing lessons/assignments
+ - Reduces cognitive load when scanning your student list
+
+**Q:** What if I need to add students with similar names?
+
+**A:** The system will accept names that: Differ by at least one character (e.g., "John Doe" vs "Jon Doe")
+
+
:bulb: **Tip:** For students who share names naturally, consider adding identifiers: middle initials (e.g., "John A Doe" vs "John B Doe"), numerals (e.g., "John Doe 1" vs "John Doe 2")
--------------------------------------------------------------------------------------------------------------------
## Known issues
-1. **When using multiple screens**, if you move the application to a secondary screen, and later switch to using only the primary screen, the GUI will open off-screen. The remedy is to delete the `preferences.json` file created by the application before running the application again.
-2. **If you minimize the Help Window** and then run the `help` command (or use the `Help` menu, or the keyboard shortcut `F1`) again, the original Help Window will remain minimized, and no new Help Window will appear. The remedy is to manually restore the minimized Help Window.
+1. **Multiple Screens Issue:** If you move the app to a secondary screen and later switch to a single screen, the GUI may open off-screen. To fix, delete the `preferences.json` file before running the app again.
+2. **Help Window Issue:** Minimizing the Help Window and running `help` again may not open a new window. Restore the minimized window manually.
--------------------------------------------------------------------------------------------------------------------
## 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`
+### 👤 Student Management
+
+| Action | Format, Examples |
+|--------------------|------------------|
+| **Add Student** | `add_student n/NAME p/PHONE e/EMAIL a/ADDRESS s/SUBJECT`
e.g., `add_student n/John Doe p/91234567 e/john@example.com a/123 Street s/Math` |
+| **Edit Student** | `edit_student INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS]`
e.g., `edit_student 1 p/98765432` |
+| **Delete Student** | `delete_student INDEX`
e.g., `delete_student 3` |
+| **Find Student** | `find_student KEYWORD [MORE_KEYWORDS]`
e.g., find_student James Jake
+| **List Students** | `list_students` |
+
+
+### 📚 Lesson Management
+
+| Action | Format, Examples |
+|--------------------|------------------|
+| **Add Lesson** | `add_lesson n/STUDENT_NAME d/DATE t/TIME s/SUBJECT`
e.g., `add_lesson n/Jack d/16-10-2025 t/15:00 s/CS2103T` |
+| **Edit Lesson** | `edit_lesson INDEX [n/STUDENT_NAME] [d/DATE] [t/TIME] [s/SUBJECT]`
e.g., `edit_lesson 1 d/20-10-2025 t/14:00` |
+| **Delete Lesson** | `delete_lesson INDEX`
e.g., `delete_lesson 1` |
+| **List Lessons** | `list_lessons [n/STUDENT_NAME]`
e.g., `list_lessons`, `list_lessons n/Sally Mood` |
+
+
+### 📝 Assignment Management
+
+| Action | Format, Examples |
+|------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| **Add Assignment** | `add_assignment STUDENT_INDEX as/ASSIGNMENT d/DATE`
e.g., `add_assignment 1 as/Math Homework d/23-07-2025` |
+| **Edit Assignment** | `edit_assignment STUDENT_INDEX as/ASSIGNMENT_NAME [nas/NEW_NAME] [d/NEW_DATE]`
e.g., `edit_assignment 1 as/Assignment1 nas/UpdatedAssignment d/25-12-2025` |
+| **Delete Assignment** | `delete_assignment STUDENT_INDEX as/ASSIGNMENT_NAME`
e.g., `delete_assignment 1 as/Assignment 1` |
+| **Mark Assignment** | `mark_assignment STUDENT_INDEX as/ASSIGNMENT_NAME`
e.g., `mark_assignment 1 as/Assignment 1` |
+| **Unmark Assignment** | `unmark_assignment STUDENT_INDEX as/ASSIGNMENT_NAME`
e.g., `unmark_assignment 1 as/Assignment 1` |
+
+
+### 🧹 General Utility
+
+| Action | Format, Examples |
+|----------------|------------------|
+| **Clear** | `clear` |
+| **Help** | `help` |
+| **Exit** | `exit` | |
+
+
diff --git a/docs/_config.yml b/docs/_config.yml
index 6bd245d8f4e..ec7ad27d992 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -1,4 +1,4 @@
-title: "AB-3"
+title: "TutorTrack"
theme: minima
header_pages:
diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss
index 0d3f6e80ced..7b9dcd6816d 100644
--- a/docs/_sass/minima/_base.scss
+++ b/docs/_sass/minima/_base.scss
@@ -13,6 +13,16 @@ dl, dd, ol, ul, figure {
}
+h1, h2, h3 {
+ margin-bottom: 10px;
+ font-weight: bold;
+}
+
+p {
+ line-height: 1.6;
+ margin-bottom: 15px;
+}
+
/**
@@ -288,7 +298,7 @@ table {
text-align: center;
}
.site-header:before {
- content: "AB-3";
+ content: "TutorTrack";
font-size: 32px;
}
}
diff --git a/docs/diagrams/ArchitectureDiagram.puml b/docs/diagrams/ArchitectureDiagram.puml
index 4c5cf58212e..8e22ba34652 100644
--- a/docs/diagrams/ArchitectureDiagram.puml
+++ b/docs/diagrams/ArchitectureDiagram.puml
@@ -4,30 +4,57 @@
!include
!include style.puml
-Package " "<>{
- Class UI UI_COLOR
- Class Logic LOGIC_COLOR
- Class Storage STORAGE_COLOR
- Class Model MODEL_COLOR
- Class Main #grey
- Class Commons LOGIC_COLOR_T2
+skinparam {
+ Shadowing false
+ ArrowFontSize 12
}
-Class "<$user>" as User MODEL_COLOR_T2
-Class "<$documents>" as File UI_COLOR_T1
+title "TutorTrack Architecture Diagram"
+package "Main Components" <> {
+ class "UI" as UI UI_COLOR
+ class "Logic" as Logic LOGIC_COLOR
+ class "Storage" as Storage STORAGE_COLOR
+ class "Model" as Model MODEL_COLOR
+ class "Main" as Main #grey
+}
+
+class "<$user>\nUser" as User MODEL_COLOR_T2
+class "<$documents>\nData File" as File UI_COLOR_T1
+
+' Component Relationships
+Main --> UI : launches
+Main --> Logic : launches
+Main --> Storage : launches
+Main --> Model : accesses
+
+UI -> Logic : executes\ncommands
+UI -> Model : observes
+Logic -> Storage : saves/loads
+Logic -> Model : modifies
+
+Storage ..> Model : contains\npersisted data
+Storage ..> File : reads/writes\nJSON
+
+User ..> UI : interacts with
+
+note right of Storage
+ Data Flow:
+ • JSON format
+ • Auto-saves on changes
+end note
-UI -[#green]> Logic
-UI -right[#green]-> Model
-Logic -[#blue]-> Storage
-Logic -down[#blue]-> Model
-Main -[#grey]-> UI
-Main -[#grey]-> Logic
-Main -[#grey]-> Storage
-Main -up[#grey]-> Model
-Main -down[hidden]-> Commons
+note top of Logic
+ Command Processing:
+ • Parses input
+ • Executes logic
+ • Returns results
+end note
-Storage -up[STORAGE_COLOR].> Model
-Storage .right[STORAGE_COLOR].>File
-User ..> UI
+legend right
+ Color Legend:
+ <$globe_internet> = External Interaction
+ <$documents> = Data Storage
+ <$user> = Human Actor
+end legend
@enduml
diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml
index 48b6cc4333c..1d92bde10d0 100644
--- a/docs/diagrams/ArchitectureSequenceDiagram.puml
+++ b/docs/diagrams/ArchitectureSequenceDiagram.puml
@@ -1,39 +1,53 @@
@startuml
!include style.puml
-skinparam ArrowFontStyle plain
-Actor User as user USER_COLOR
-Participant ":UI" as ui UI_COLOR
-Participant ":Logic" as logic LOGIC_COLOR
-Participant ":Model" as model MODEL_COLOR
-Participant ":Storage" as storage STORAGE_COLOR
+skinparam {
+ ArrowFontStyle bold
+ ArrowFontSize 13
+ ParticipantFontStyle bold
+ ActorFontStyle bold
+ SequenceMessageAlignment center
+}
-user -[USER_COLOR]> ui : "delete 1"
+title "Sequence Diagram: Delete Student Command"
+
+actor "User" as user USER_COLOR
+participant ":UI" as ui UI_COLOR
+participant ":Logic" as logic LOGIC_COLOR
+participant ":Model" as model MODEL_COLOR
+participant ":Storage" as storage STORAGE_COLOR
+
+== Command Execution ==
+user -> ui : "delete_student 1"
activate ui UI_COLOR
-ui -[UI_COLOR]> logic : execute("delete 1")
+ui -> logic : execute("delete_student 1")
activate logic LOGIC_COLOR
-logic -[LOGIC_COLOR]> model : deletePerson(p)
+logic -> model : deleteStudent(1)
activate model MODEL_COLOR
-model -[MODEL_COLOR]-> logic
+model --> logic : Student removed
deactivate model
-logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook)
+== Data Persistence ==
+logic -> storage : saveAddressBook(addressBook)
activate storage STORAGE_COLOR
-storage -[STORAGE_COLOR]> storage : Save to file
+storage -> storage : JSON Serialization
activate storage STORAGE_COLOR_T1
-storage --[STORAGE_COLOR]> storage
+note right: Saves to tutortrack.json
+storage --> storage
deactivate storage
-storage --[STORAGE_COLOR]> logic
+storage --> logic : Save confirmed
deactivate storage
-logic --[LOGIC_COLOR]> ui
+== UI Update ==
+logic --> ui : CommandResult
deactivate logic
-ui--[UI_COLOR]> user
+ui --> user : "Deleted Student: Alice\n(1 student remaining)"
deactivate ui
+
@enduml
diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml
deleted file mode 100644
index 598474a5c82..00000000000
--- a/docs/diagrams/BetterModelClassDiagram.puml
+++ /dev/null
@@ -1,21 +0,0 @@
-@startuml
-!include style.puml
-skinparam arrowThickness 1.1
-skinparam arrowColor MODEL_COLOR
-skinparam classBackgroundColor MODEL_COLOR
-
-AddressBook *-right-> "1" UniquePersonList
-AddressBook *-right-> "1" UniqueTagList
-UniqueTagList -[hidden]down- UniquePersonList
-UniqueTagList -[hidden]down- UniquePersonList
-
-UniqueTagList -right-> "*" Tag
-UniquePersonList -right-> Person
-
-Person -up-> "*" Tag
-
-Person *--> Name
-Person *--> Phone
-Person *--> Email
-Person *--> Address
-@enduml
diff --git a/docs/diagrams/BetterModelClassDiagram_StudentManagementStructure.puml b/docs/diagrams/BetterModelClassDiagram_StudentManagementStructure.puml
new file mode 100644
index 00000000000..edeb6014311
--- /dev/null
+++ b/docs/diagrams/BetterModelClassDiagram_StudentManagementStructure.puml
@@ -0,0 +1,114 @@
+@startuml
+!include style.puml
+
+skinparam {
+ classFontSize 13
+ classFontStyle bold
+ arrowThickness 1.3
+ arrowColor #444444
+ classBackgroundColor #000000
+ classBorderColor MODEL_COLOR
+ classArrowColor MODEL_COLOR
+}
+
+title "Class Diagram of Models in TutorTrack"
+
+class AddressBook {
+ - students: UniqueStudentList
+ - subjects: UniqueLessonList
+}
+
+class UniqueStudentList {
+ - internalList: List
+ + add(Student)
+ + remove(Student)
+}
+
+class UniqueLessonList {
+ - internalList: List
+ + add(Subject)
+ + remove(Subject)
+}
+
+class UniqueAssignmentList {
+ - internalList: List
+ + add(Assignment)
+ + remove(Assignment)
+}
+
+class Student {
+ - name: Name
+ - phone: Phone
+ - email: Email
+ - address: Address
+ - subjects: Set
+ + addSubject(Subject)
+ + removeSubject(Subject)
+}
+
+class Lesson {
+ - studentName: Name
+ - subjects: Set
+ - date: Date
+ - time: Time
+}
+
+class Name {
+ - fullName: String
+}
+class Phone {
+ - value: String
+}
+class Email {
+ - value: String
+}
+class Address {
+ - value: String
+}
+class Subject {
+ - value: String
+}
+class Date {
+ - value: LocalDate
+}
+class Time {
+ - value: LocalTime
+}
+class Assignment {
+ - assignmentName: Name
+ - dueDate: Date
+ - isComplete: boolean
+}
+
+' Composition Relationships
+AddressBook "1" *-- "1" UniqueStudentList : contains
+AddressBook "1" *-- "1" UniqueLessonList : contains
+
+UniqueStudentList "1" *-- "*" Student : maintains
+UniqueLessonList "1" *-- "*" Lesson : maintains
+
+' Student Components
+Student *-- "1" Name : has
+Student *-- "1" Phone : has
+Student *-- "1" Email : has
+Student *-- "1" Address : has
+Student *-- "*" Subject : has
+Student *-- "1" UniqueAssignmentList : has
+
+' Subject Association
+Student "1" --> "*" Lesson : subjected with
+
+' Lesson Components
+Lesson *-- "1" Name : for student
+Lesson *-- "*" Subject : teaches
+Lesson *-- "1" Date : on
+Lesson *-- "1" Time : at
+
+' Assignment Components
+UniqueAssignmentList *-- "*" Assignment : maintains
+
+' Layout improvements
+UniqueLessonList -[hidden]right- UniqueStudentList
+Name -[hidden]down- Phone
+Email -[hidden]down- Address
+@enduml
diff --git a/docs/diagrams/CommitActivityDiagram.puml b/docs/diagrams/CommitActivityDiagram.puml
index 8c0892d6a70..6b5b903d875 100644
--- a/docs/diagrams/CommitActivityDiagram.puml
+++ b/docs/diagrams/CommitActivityDiagram.puml
@@ -8,10 +8,10 @@ start
'Since the beta syntax does not support placing the condition outside the
'diamond we place it as the true branch instead.
-if () then ([command commits AddressBook])
+if () then ([command commits TutorTrack])
:Purge redundant states;
- :Save AddressBook to
- addressBookStateList;
+ :Save TutorTrack to
+ tutorTrackStateList;
else ([else])
endif
stop
diff --git a/docs/diagrams/ComponentManagers.puml b/docs/diagrams/ComponentManagers.puml
index 564dd1ae32f..793bdaddc7d 100644
--- a/docs/diagrams/ComponentManagers.puml
+++ b/docs/diagrams/ComponentManagers.puml
@@ -4,6 +4,19 @@ skinparam arrowThickness 1.1
skinparam arrowColor LOGIC_COLOR_T4
skinparam classBackgroundColor LOGIC_COLOR
+
+skinparam {
+ ArrowThickness 1.3
+ ArrowColor LOGIC_COLOR_T4
+ ClassFontStyle bold
+ ClassFontSize 13
+ PackageBorderColor #444444
+ PackageFontSize 15
+ PackageFontStyle bold
+}
+
+title "Component Dependency Diagram"
+
package Logic as LogicPackage {
Class "<>\nLogic" as Logic
Class LogicManager
@@ -26,6 +39,7 @@ LogicManager .up.|> Logic
ModelManager .up.|> Model
StorageManager .up.|> Storage
-LogicManager --> Model
-LogicManager --> Storage
+LogicManager -> Model : uses
+LogicManager -> Storage : saves to
+
@enduml
diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml
index 5241e79d7da..5675507faf6 100644
--- a/docs/diagrams/DeleteSequenceDiagram.puml
+++ b/docs/diagrams/DeleteSequenceDiagram.puml
@@ -5,8 +5,8 @@ skinparam ArrowFontStyle plain
box Logic LOGIC_COLOR_T1
participant ":LogicManager" as LogicManager LOGIC_COLOR
participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
-participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR
-participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR
+participant ":DeleteStudentCommandParser" as DeleteStudentCommandParser LOGIC_COLOR
+participant "d:DeleteStudentCommand" as DeleteStudentCommand LOGIC_COLOR
participant "r:CommandResult" as CommandResult LOGIC_COLOR
end box
@@ -14,56 +14,56 @@ box Model MODEL_COLOR_T1
participant "m:Model" as Model MODEL_COLOR
end box
-[-> LogicManager : execute("delete 1")
+[-> LogicManager : execute("delete_student 1")
activate LogicManager
-LogicManager -> AddressBookParser : parseCommand("delete 1")
+LogicManager -> AddressBookParser : parseCommand("delete_student 1")
activate AddressBookParser
-create DeleteCommandParser
-AddressBookParser -> DeleteCommandParser
-activate DeleteCommandParser
+create DeleteStudentCommandParser
+AddressBookParser -> DeleteStudentCommandParser
+activate DeleteStudentCommandParser
-DeleteCommandParser --> AddressBookParser
-deactivate DeleteCommandParser
+DeleteStudentCommandParser --> AddressBookParser
+deactivate DeleteStudentCommandParser
-AddressBookParser -> DeleteCommandParser : parse("1")
-activate DeleteCommandParser
+AddressBookParser -> DeleteStudentCommandParser : parse("1")
+activate DeleteStudentCommandParser
-create DeleteCommand
-DeleteCommandParser -> DeleteCommand
-activate DeleteCommand
+create DeleteStudentCommand
+DeleteStudentCommandParser -> DeleteStudentCommand
+activate DeleteStudentCommand
-DeleteCommand --> DeleteCommandParser :
-deactivate DeleteCommand
+DeleteStudentCommand --> DeleteStudentCommandParser :
+deactivate DeleteStudentCommand
-DeleteCommandParser --> AddressBookParser : d
-deactivate DeleteCommandParser
+DeleteStudentCommandParser --> AddressBookParser : d
+deactivate DeleteStudentCommandParser
'Hidden arrow to position the destroy marker below the end of the activation bar.
-DeleteCommandParser -[hidden]-> AddressBookParser
-destroy DeleteCommandParser
+DeleteStudentCommandParser -[hidden]-> AddressBookParser
+destroy DeleteStudentCommandParser
AddressBookParser --> LogicManager : d
deactivate AddressBookParser
-LogicManager -> DeleteCommand : execute(m)
-activate DeleteCommand
+LogicManager -> DeleteStudentCommand : execute(m)
+activate DeleteStudentCommand
-DeleteCommand -> Model : deletePerson(1)
+DeleteStudentCommand -> Model : deleteStudent(1)
activate Model
-Model --> DeleteCommand
+Model --> DeleteStudentCommand
deactivate Model
create CommandResult
-DeleteCommand -> CommandResult
+DeleteStudentCommand -> CommandResult
activate CommandResult
-CommandResult --> DeleteCommand
+CommandResult --> DeleteStudentCommand
deactivate CommandResult
-DeleteCommand --> LogicManager : r
-deactivate DeleteCommand
+DeleteStudentCommand --> LogicManager : r
+deactivate DeleteStudentCommand
[<--LogicManager
deactivate LogicManager
diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml
index 58b4f602ce6..784dd707706 100644
--- a/docs/diagrams/LogicClassDiagram.puml
+++ b/docs/diagrams/LogicClassDiagram.puml
@@ -29,19 +29,19 @@ HiddenOutside ..> Logic
LogicManager .right.|> Logic
LogicManager -right->"1" ParserClasses
-ParserClasses ..> XYZCommand : <>
+ParserClasses ..> XYZCommand : create
XYZCommand -up-|> Command
-LogicManager .left.> Command : <>
+LogicManager .left.> Command : call
LogicManager --> Model
LogicManager --> Storage
Storage --[hidden] Model
Command .[hidden]up.> Storage
Command .right.> Model
-note right of XYZCommand: XYZCommand = AddCommand, \nFindCommand, etc
+note right of XYZCommand: XYZCommand = AddStudentCommand, \nFindStudentCommand, etc
Logic ..> CommandResult
LogicManager .down.> CommandResult
-Command .up.> CommandResult : <>
+Command .up.> CommandResult : create
@enduml
diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml
index 0de5673070d..7c3e431dfab 100644
--- a/docs/diagrams/ModelClassDiagram.puml
+++ b/docs/diagrams/ModelClassDiagram.puml
@@ -12,13 +12,15 @@ 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 Subject
+Class UniqueAssignmentList
+Class Assignment
Class I #FFFFFF
}
@@ -35,20 +37,22 @@ 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 *--> Name
+Student *--> Phone
+Student *--> Email
+Student *--> Address
+Student *--> "*" Subject
+Student *--> "1" UniqueAssignmentList
+UniqueAssignmentList --> "*" Assignment
-Person -[hidden]up--> I
-UniquePersonList -[hidden]right-> I
+Student -[hidden]up--> I
+UniqueStudentList -[hidden]right-> I
Name -[hidden]right-> Phone
Phone -[hidden]right-> Address
Address -[hidden]right-> Email
-ModelManager --> "~* filtered" Person
+ModelManager --> "~* filtered" Student
@enduml
diff --git a/docs/diagrams/ParserClasses.puml b/docs/diagrams/ParserClasses.puml
index ce4c5ce8c8d..fcd6dedebc3 100644
--- a/docs/diagrams/ParserClasses.puml
+++ b/docs/diagrams/ParserClasses.puml
@@ -3,6 +3,11 @@
skinparam arrowThickness 1.1
skinparam arrowColor LOGIC_COLOR_T4
skinparam classBackgroundColor LOGIC_COLOR
+skinparam class {
+ FontSize 13
+ FontStyle plain
+ BorderColor #333333
+}
Class "{abstract}\nCommand" as Command
Class XYZCommand
@@ -21,10 +26,11 @@ Class Prefix
Class HiddenOutside #FFFFFF
HiddenOutside ..> AddressBookParser
-AddressBookParser .down.> XYZCommandParser: <>
+AddressBookParser .down.> XYZCommandParser: create
+
-XYZCommandParser ..> XYZCommand : <>
-AddressBookParser ..> Command : <>
+XYZCommandParser ..> XYZCommand : create
+AddressBookParser ..> Command : use
XYZCommandParser .up.|> Parser
XYZCommandParser ..> ArgumentMultimap
XYZCommandParser ..> ArgumentTokenizer
diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml
index a821e06458c..907d0a0f1e5 100644
--- a/docs/diagrams/StorageClassDiagram.puml
+++ b/docs/diagrams/StorageClassDiagram.puml
@@ -18,8 +18,10 @@ package "AddressBook Storage" #F4F6F6{
Class "<>\nAddressBookStorage" as AddressBookStorage
Class JsonAddressBookStorage
Class JsonSerializableAddressBook
-Class JsonAdaptedPerson
-Class JsonAdaptedTag
+Class JsonAdaptedStudent
+Class JsonAdaptedLesson
+Class JsonAdaptedSubject
+Class JsonAdaptedAssignment
}
}
@@ -37,7 +39,9 @@ Storage -right-|> AddressBookStorage
JsonUserPrefsStorage .up.|> UserPrefsStorage
JsonAddressBookStorage .up.|> AddressBookStorage
JsonAddressBookStorage ..> JsonSerializableAddressBook
-JsonSerializableAddressBook --> "*" JsonAdaptedPerson
-JsonAdaptedPerson --> "*" JsonAdaptedTag
+JsonSerializableAddressBook --> "*" JsonAdaptedStudent
+JsonSerializableAddressBook --> "*" JsonAdaptedLesson
+JsonAdaptedStudent --> "*" JsonAdaptedSubject
+JsonAdaptedStudent --> "*" JsonAdaptedAssignment
@enduml
diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml
index 95473d5aa19..ae742bb1050 100644
--- a/docs/diagrams/UiClassDiagram.puml
+++ b/docs/diagrams/UiClassDiagram.puml
@@ -11,8 +11,9 @@ Class UiManager
Class MainWindow
Class HelpWindow
Class ResultDisplay
-Class PersonListPanel
-Class PersonCard
+Class ListPanel
+Class ListCard
+Class AssignmentCard
Class StatusBarFooter
Class CommandBox
}
@@ -32,26 +33,28 @@ UiManager .left.|> Ui
UiManager -down-> "1" MainWindow
MainWindow *-down-> "1" CommandBox
MainWindow *-down-> "1" ResultDisplay
-MainWindow *-down-> "1" PersonListPanel
+MainWindow *-down-> "1" ListPanel
MainWindow *-down-> "1" StatusBarFooter
MainWindow --> "0..1" HelpWindow
-PersonListPanel -down-> "*" PersonCard
+ListPanel -down-> "*" ListCard
+ListCard -down-> “*” AssignmentCard
MainWindow -left-|> UiPart
ResultDisplay --|> UiPart
CommandBox --|> UiPart
-PersonListPanel --|> UiPart
-PersonCard --|> UiPart
+ListPanel --|> UiPart
+ListCard --|> UiPart
+AssignmentCard --|> UiPart
StatusBarFooter --|> UiPart
HelpWindow --|> UiPart
-PersonCard ..> Model
+ListCard ..> Model
UiManager -right-> Logic
MainWindow -left-> Logic
-PersonListPanel -[hidden]left- HelpWindow
+ListPanel -[hidden]left- HelpWindow
HelpWindow -[hidden]left- CommandBox
CommandBox -[hidden]left- ResultDisplay
ResultDisplay -[hidden]left- StatusBarFooter
diff --git a/docs/diagrams/UndoRedoState0.puml b/docs/diagrams/UndoRedoState0.puml
index 43a45903ac9..2263fed2d12 100644
--- a/docs/diagrams/UndoRedoState0.puml
+++ b/docs/diagrams/UndoRedoState0.puml
@@ -7,9 +7,9 @@ skinparam ClassBackgroundColor #FFFFAA
title Initial state
package States {
- class State1 as "ab0:AddressBook "
- class State2 as "ab1:AddressBook "
- class State3 as "ab2:AddressBook "
+ class State1 as "tt0:TutorTrack "
+ class State2 as "tt1:TutorTrack "
+ class State3 as "tt2:TutorTrack "
}
State1 -[hidden]right-> State2
State2 -[hidden]right-> State3
diff --git a/docs/diagrams/UndoRedoState1.puml b/docs/diagrams/UndoRedoState1.puml
index 5a41e9e1651..785ce49ffdc 100644
--- a/docs/diagrams/UndoRedoState1.puml
+++ b/docs/diagrams/UndoRedoState1.puml
@@ -4,12 +4,12 @@ skinparam ClassFontColor #000000
skinparam ClassBorderColor #000000
skinparam ClassBackgroundColor #FFFFAA
-title After command "delete 5"
+title After command "delete_student 5"
package States <> {
- class State1 as "ab0:AddressBook "
- class State2 as "ab1:AddressBook "
- class State3 as "ab2:AddressBook "
+ class State1 as "tt0:TutorTrack "
+ class State2 as "tt1:TutorTrack "
+ class State3 as "tt2:TutorTrack "
}
State1 -[hidden]right-> State2
diff --git a/docs/diagrams/UndoRedoState2.puml b/docs/diagrams/UndoRedoState2.puml
index ad32fce1b0b..702acb202fe 100644
--- a/docs/diagrams/UndoRedoState2.puml
+++ b/docs/diagrams/UndoRedoState2.puml
@@ -4,12 +4,12 @@ skinparam ClassFontColor #000000
skinparam ClassBorderColor #000000
skinparam ClassBackgroundColor #FFFFAA
-title After command "add n/David"
+title After command "add_student n/David"
package States <> {
- class State1 as "ab0:AddressBook "
- class State2 as "ab1:AddressBook "
- class State3 as "ab2:AddressBook "
+ class State1 as "tt0:TutorTrack "
+ class State2 as "tt1:TutorTrack "
+ class State3 as "tt2:TutorTrack "
}
State1 -[hidden]right-> State2
diff --git a/docs/diagrams/UndoRedoState3.puml b/docs/diagrams/UndoRedoState3.puml
index 9187a690036..ae473537caa 100644
--- a/docs/diagrams/UndoRedoState3.puml
+++ b/docs/diagrams/UndoRedoState3.puml
@@ -7,9 +7,9 @@ skinparam ClassBackgroundColor #FFFFAA
title After command "undo"
package States <> {
- class State1 as "ab0:AddressBook "
- class State2 as "ab1:AddressBook "
- class State3 as "ab2:AddressBook "
+ class State1 as "tt0:TutorTrack "
+ class State2 as "tt1:TutorTrack "
+ class State3 as "tt2:TutorTrack "
}
State1 -[hidden]right-> State2
diff --git a/docs/diagrams/UndoRedoState4.puml b/docs/diagrams/UndoRedoState4.puml
index 2bc631ffcd0..ae37f744922 100644
--- a/docs/diagrams/UndoRedoState4.puml
+++ b/docs/diagrams/UndoRedoState4.puml
@@ -4,12 +4,12 @@ skinparam ClassFontColor #000000
skinparam ClassBorderColor #000000
skinparam ClassBackgroundColor #FFFFAA
-title After command "list"
+title After command "list_students"
package States <> {
- class State1 as "ab0:AddressBook "
- class State2 as "ab1:AddressBook "
- class State3 as "ab2:AddressBook "
+ class State1 as "tt0:TutorTrack "
+ class State2 as "tt1:TutorTrack "
+ class State3 as "tt2:TutorTrack "
}
State1 -[hidden]right-> State2
diff --git a/docs/diagrams/UndoRedoState5.puml b/docs/diagrams/UndoRedoState5.puml
index e77b04104aa..7adc8346c08 100644
--- a/docs/diagrams/UndoRedoState5.puml
+++ b/docs/diagrams/UndoRedoState5.puml
@@ -7,9 +7,9 @@ skinparam ClassBackgroundColor #FFFFAA
title After command "clear"
package States <> {
- class State1 as "ab0:AddressBook "
- class State2 as "ab1:AddressBook "
- class State3 as "ab3:AddressBook "
+ class State1 as "tt0:TutorTrack "
+ class State2 as "tt1:TutorTrack "
+ class State3 as "tt2:TutorTrack "
}
State1 -[hidden]right-> State2
@@ -18,5 +18,5 @@ State2 -[hidden]right-> State3
class Pointer as "Current State" #FFFFFF
Pointer -up-> State3
-note right on link: State ab2 deleted.
+note right on link: State tt2 deleted.
@end
diff --git a/docs/diagrams/UndoSequenceDiagram-Logic.puml b/docs/diagrams/UndoSequenceDiagram-Logic.puml
index e57368c5159..6b80e94dc96 100644
--- a/docs/diagrams/UndoSequenceDiagram-Logic.puml
+++ b/docs/diagrams/UndoSequenceDiagram-Logic.puml
@@ -2,35 +2,36 @@
!include style.puml
skinparam ArrowFontStyle plain
-box Logic LOGIC_COLOR_T1
-participant ":LogicManager" as LogicManager LOGIC_COLOR
-participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
-participant "u:UndoCommand" as UndoCommand LOGIC_COLOR
+box "Logic" LOGIC_COLOR_T1
+ participant ":LogicManager" as LogicManager LOGIC_COLOR
+ participant ":TutorTrackParser" as TutorTrackParser LOGIC_COLOR
+ participant "u:UndoCommand" as UndoCommand LOGIC_COLOR
end box
-box Model MODEL_COLOR_T1
-participant ":Model" as Model MODEL_COLOR
+box "Model" MODEL_COLOR_T1
+ participant ":Model" as Model MODEL_COLOR
end box
-[-> LogicManager : execute(undo)
+
+[-> LogicManager : execute("undo")
activate LogicManager
-LogicManager -> AddressBookParser : parseCommand(undo)
-activate AddressBookParser
+LogicManager -> TutorTrackParser : parseCommand("undo")
+activate TutorTrackParser
create UndoCommand
-AddressBookParser -> UndoCommand
+TutorTrackParser -> UndoCommand **: create
activate UndoCommand
-UndoCommand --> AddressBookParser
+UndoCommand --> TutorTrackParser
deactivate UndoCommand
-AddressBookParser --> LogicManager : u
-deactivate AddressBookParser
+TutorTrackParser --> LogicManager : u
+deactivate TutorTrackParser
LogicManager -> UndoCommand : execute()
activate UndoCommand
-UndoCommand -> Model : undoAddressBook()
+UndoCommand -> Model : undoTutorTrack()
activate Model
Model --> UndoCommand
@@ -38,9 +39,10 @@ deactivate Model
UndoCommand --> LogicManager : result
deactivate UndoCommand
-UndoCommand -[hidden]-> LogicManager : result
+
+UndoCommand -[hidden]-> LogicManager
destroy UndoCommand
-[<--LogicManager
+[<-- LogicManager
deactivate LogicManager
@enduml
diff --git a/docs/diagrams/UndoSequenceDiagram-Model.puml b/docs/diagrams/UndoSequenceDiagram-Model.puml
index 54d83208cb8..ec890135333 100644
--- a/docs/diagrams/UndoSequenceDiagram-Model.puml
+++ b/docs/diagrams/UndoSequenceDiagram-Model.puml
@@ -2,20 +2,20 @@
!include style.puml
skinparam ArrowFontStyle plain
-box Model MODEL_COLOR_T1
-participant ":Model" as Model MODEL_COLOR
-participant ":VersionedAddressBook" as VersionedAddressBook MODEL_COLOR
+box "Model" MODEL_COLOR_T1
+ participant ":Model" as Model MODEL_COLOR
+ participant ":VersionedTutorTrack" as VersionedTutorTrack MODEL_COLOR
end box
-[-> Model : undoAddressBook()
+[-> Model : undoTutorTrack()
activate Model
-Model -> VersionedAddressBook : undo()
-activate VersionedAddressBook
+Model -> VersionedTutorTrack : undo()
+activate VersionedTutorTrack
-VersionedAddressBook -> VersionedAddressBook :resetData(ReadOnlyAddressBook)
-VersionedAddressBook --> Model :
-deactivate VersionedAddressBook
+VersionedTutorTrack -> VersionedTutorTrack : resetData(ReadOnlyTutorTrack)
+VersionedTutorTrack --> Model
+deactivate VersionedTutorTrack
[<-- Model
deactivate Model
diff --git a/docs/diagrams/tracing/LogicSequenceDiagram.puml b/docs/diagrams/tracing/LogicSequenceDiagram.puml
index 42bf46d3ce8..eadb42975e9 100644
--- a/docs/diagrams/tracing/LogicSequenceDiagram.puml
+++ b/docs/diagrams/tracing/LogicSequenceDiagram.puml
@@ -4,8 +4,8 @@ skinparam ArrowFontStyle plain
Participant ":LogicManager" as logic LOGIC_COLOR
Participant ":AddressBookParser" as abp LOGIC_COLOR
-Participant ":EditCommandParser" as ecp LOGIC_COLOR
-Participant "command:EditCommand" as ec LOGIC_COLOR
+Participant ":EditStudentCommandParser" as ecp LOGIC_COLOR
+Participant "command:EditStudentCommand" as ec LOGIC_COLOR
[-> logic : execute
activate logic
@@ -14,7 +14,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 37ad06a2803..3297e3267ff 100644
Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ
diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png
index 02a42e35e76..f4126df9e0a 100644
Binary files a/docs/images/BetterModelClassDiagram.png and b/docs/images/BetterModelClassDiagram.png differ
diff --git a/docs/images/CommitActivityDiagram.png b/docs/images/CommitActivityDiagram.png
index 5b464126b35..1739ebf9a54 100644
Binary files a/docs/images/CommitActivityDiagram.png and b/docs/images/CommitActivityDiagram.png differ
diff --git a/docs/images/ComponentManagers-Component Dependency Diagram.png b/docs/images/ComponentManagers-Component Dependency Diagram.png
new file mode 100755
index 00000000000..a527f0aa6b4
Binary files /dev/null and b/docs/images/ComponentManagers-Component Dependency Diagram.png differ
diff --git a/docs/images/ComponentManagers-Component_Dependency_Diagram.png b/docs/images/ComponentManagers-Component_Dependency_Diagram.png
new file mode 100755
index 00000000000..a527f0aa6b4
Binary files /dev/null and b/docs/images/ComponentManagers-Component_Dependency_Diagram.png differ
diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png
index ac2ae217c51..54e1d22cab4 100644
Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ
diff --git a/docs/images/DeleteStudentSequenceDiagram.png b/docs/images/DeleteStudentSequenceDiagram.png
new file mode 100644
index 00000000000..54e1d22cab4
Binary files /dev/null and b/docs/images/DeleteStudentSequenceDiagram.png differ
diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png
old mode 100644
new mode 100755
index fe91c69efe7..5fb12c7845f
Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ
diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png
old mode 100644
new mode 100755
index a19fb1b4ac8..a5a81274194
Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ
diff --git a/docs/images/ParserClasses-0.png b/docs/images/ParserClasses-0.png
new file mode 100755
index 00000000000..bbf6018c689
Binary files /dev/null and b/docs/images/ParserClasses-0.png differ
diff --git a/docs/images/ParserClasses.png b/docs/images/ParserClasses.png
index 2caeeb1a067..bbfa9b45a20 100644
Binary files a/docs/images/ParserClasses.png and b/docs/images/ParserClasses.png differ
diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png
index 18fa4d0d51f..55b015e7ab7 100644
Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ
diff --git a/docs/images/Ui.png b/docs/images/Ui.png
index 5bd77847aa2..a1a00d64d97 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 11f06d68671..113edb7b3b5 100644
Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ
diff --git a/docs/images/UndoRedoState0.png b/docs/images/UndoRedoState0.png
index c5f91b58533..59c0092dce4 100644
Binary files a/docs/images/UndoRedoState0.png and b/docs/images/UndoRedoState0.png differ
diff --git a/docs/images/UndoRedoState1.png b/docs/images/UndoRedoState1.png
index 2d3ad09c047..16698b48f6e 100644
Binary files a/docs/images/UndoRedoState1.png and b/docs/images/UndoRedoState1.png differ
diff --git a/docs/images/UndoRedoState2.png b/docs/images/UndoRedoState2.png
index 20853694e03..2bc367cd2ef 100644
Binary files a/docs/images/UndoRedoState2.png and b/docs/images/UndoRedoState2.png differ
diff --git a/docs/images/UndoRedoState3.png b/docs/images/UndoRedoState3.png
index 1a9551b31be..5874ae34018 100644
Binary files a/docs/images/UndoRedoState3.png and b/docs/images/UndoRedoState3.png differ
diff --git a/docs/images/UndoRedoState4.png b/docs/images/UndoRedoState4.png
index 46dfae78c94..c197f8a64d4 100644
Binary files a/docs/images/UndoRedoState4.png and b/docs/images/UndoRedoState4.png differ
diff --git a/docs/images/UndoRedoState5.png b/docs/images/UndoRedoState5.png
index f45889b5fdf..82a7f60cae6 100644
Binary files a/docs/images/UndoRedoState5.png and b/docs/images/UndoRedoState5.png differ
diff --git a/docs/images/UndoSequenceDiagram-Logic-0.png b/docs/images/UndoSequenceDiagram-Logic-0.png
new file mode 100755
index 00000000000..97cdfaa0423
Binary files /dev/null and b/docs/images/UndoSequenceDiagram-Logic-0.png differ
diff --git a/docs/images/UndoSequenceDiagram-Logic.png b/docs/images/UndoSequenceDiagram-Logic.png
index 78e95214294..94512f8c54d 100644
Binary files a/docs/images/UndoSequenceDiagram-Logic.png and b/docs/images/UndoSequenceDiagram-Logic.png differ
diff --git a/docs/images/UndoSequenceDiagram-Model-0.png b/docs/images/UndoSequenceDiagram-Model-0.png
new file mode 100755
index 00000000000..203a1cd01a8
Binary files /dev/null and b/docs/images/UndoSequenceDiagram-Model-0.png differ
diff --git a/docs/images/UndoSequenceDiagram-Model.png b/docs/images/UndoSequenceDiagram-Model.png
index f0f3da6ae50..5e712bd3464 100644
Binary files a/docs/images/UndoSequenceDiagram-Model.png and b/docs/images/UndoSequenceDiagram-Model.png differ
diff --git a/docs/images/andong0909.png b/docs/images/andong0909.png
new file mode 100644
index 00000000000..59fdf4cd45c
Binary files /dev/null and b/docs/images/andong0909.png differ
diff --git a/docs/images/charlesl12.png b/docs/images/charlesl12.png
new file mode 100644
index 00000000000..83e680b965f
Binary files /dev/null and b/docs/images/charlesl12.png differ
diff --git a/docs/images/gztan23.png b/docs/images/gztan23.png
new file mode 100644
index 00000000000..f056736bffa
Binary files /dev/null and b/docs/images/gztan23.png differ
diff --git a/docs/images/helpMessage.png b/docs/images/helpMessage.png
index b1f70470137..2817f9b12a2 100644
Binary files a/docs/images/helpMessage.png and b/docs/images/helpMessage.png differ
diff --git a/docs/images/img.png b/docs/images/img.png
new file mode 100755
index 00000000000..30224642dc8
Binary files /dev/null and b/docs/images/img.png differ
diff --git a/docs/images/jiangsuwangjing.png b/docs/images/jiangsuwangjing.png
new file mode 100644
index 00000000000..1af2b2af885
Binary files /dev/null and b/docs/images/jiangsuwangjing.png differ
diff --git a/docs/images/lessonList.png b/docs/images/lessonList.png
new file mode 100644
index 00000000000..8c9d92948c4
Binary files /dev/null and b/docs/images/lessonList.png differ
diff --git a/docs/images/nicholasohjj.png b/docs/images/nicholasohjj.png
new file mode 100755
index 00000000000..0ece495e6d2
Binary files /dev/null and b/docs/images/nicholasohjj.png differ
diff --git a/docs/index.md b/docs/index.md
index 7601dbaad0d..45a317c0393 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,17 +1,17 @@
---
layout: page
-title: AddressBook Level-3
+title: TutorTrack
---
-[](https://github.com/se-edu/addressbook-level3/actions)
+[](https://github.com/AY2425S2-CS2103T-T13-4/tp/actions)
[](https://codecov.io/gh/se-edu/addressbook-level3)

-**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).
+**TutorTrack 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.
+* If you are interested in using TutorTrack, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start).
+* If you are interested about developing TutorTrack, the [**Developer Guide**](DeveloperGuide.html) is a good place to start.
**Acknowledgements**
diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/address/commons/core/LogsCenter.java
index 8cf8e15a0f0..3a9bca738fb 100644
--- a/src/main/java/seedu/address/commons/core/LogsCenter.java
+++ b/src/main/java/seedu/address/commons/core/LogsCenter.java
@@ -20,7 +20,7 @@
public class LogsCenter {
private static final int MAX_FILE_COUNT = 5;
private static final int MAX_FILE_SIZE_IN_BYTES = (int) (Math.pow(2, 20) * 5); // 5MB
- private static final String LOG_FILE = "addressbook.log";
+ private static final String LOG_FILE = "TutorTrack.log";
private static final Logger logger; // logger for this class
private static Logger baseLogger; // to be used as the parent of all other loggers created by this class.
private static Level currentLogLevel = Level.INFO;
@@ -75,11 +75,11 @@ private static void removeHandlers(Logger logger) {
}
/**
- * Creates a logger named 'ab3', containing a {@code ConsoleHandler} and a {@code FileHandler}.
+ * Creates a logger named 'tutortrack', containing a {@code ConsoleHandler} and a {@code FileHandler}.
* Sets it as the {@code baseLogger}, to be used as the parent logger of all other loggers.
*/
private static void setBaseLogger() {
- baseLogger = Logger.getLogger("ab3");
+ baseLogger = Logger.getLogger("tutortrack");
baseLogger.setUseParentHandlers(false);
removeHandlers(baseLogger);
diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java
index 92cd8fa605a..66a00a2773d 100644
--- a/src/main/java/seedu/address/logic/Logic.java
+++ b/src/main/java/seedu/address/logic/Logic.java
@@ -8,7 +8,8 @@
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.lesson.Lesson;
+import seedu.address.model.student.Student;
/**
* API of the Logic component
@@ -30,8 +31,14 @@ public interface Logic {
*/
ReadOnlyAddressBook getAddressBook();
- /** Returns an unmodifiable view of the filtered list of persons */
- ObservableList getFilteredPersonList();
+ /** Returns an unmodifiable view of the filtered list of students */
+ ObservableList getFilteredStudentList();
+
+ /** Returns an unmodifiable view of the filtered list of lessons */
+ ObservableList getFilteredLessonList();
+
+ /** Returns the current list on display */
+ ObservableList> getFilteredCurrList();
/**
* 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 5aa3b91c7d0..82578af58b2 100644
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ b/src/main/java/seedu/address/logic/LogicManager.java
@@ -15,7 +15,8 @@
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.lesson.Lesson;
+import seedu.address.model.student.Student;
import seedu.address.storage.Storage;
/**
@@ -67,8 +68,18 @@ public ReadOnlyAddressBook getAddressBook() {
}
@Override
- public ObservableList getFilteredPersonList() {
- return model.getFilteredPersonList();
+ public ObservableList getFilteredStudentList() {
+ return model.getFilteredStudentList();
+ }
+
+ @Override
+ public ObservableList getFilteredLessonList() {
+ return model.getFilteredLessonList();
+ }
+
+ @Override
+ public ObservableList> getFilteredCurrList() {
+ return model.getFilteredCurrList();
}
@Override
diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java
index ecd32c31b53..ef5a7f6e090 100644
--- a/src/main/java/seedu/address/logic/Messages.java
+++ b/src/main/java/seedu/address/logic/Messages.java
@@ -5,19 +5,31 @@
import java.util.stream.Stream;
import seedu.address.logic.parser.Prefix;
-import seedu.address.model.person.Person;
+import seedu.address.model.assignment.Assignment;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.student.Student;
/**
* 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_UNKNOWN_COMMAND = "Error: Unknown command";
+ public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Error: Invalid command format! \n%1$s";
+ public static final String MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX = "Error: The student index"
+ + " provided is out of bounds!";
+ public static final String MESSAGE_INDEX_OUT_OF_BOUNDS = "Error: The student index provided is out of bounds!";
+ public static final String MESSAGE_INVALID_LESSON_DISPLAYED_INDEX = "Error: The lesson index provided is invalid!";
+ public static final String MESSAGE_STUDENTS_LISTED_OVERVIEW = "%1$d students listed!";
+ public static final String MESSAGE_LESSONS_LISTED_OVERVIEW = "%1$d lessons listed!";
public static final String MESSAGE_DUPLICATE_FIELDS =
- "Multiple values specified for the following single-valued field(s): ";
+ "Error: Multiple values specified for the following single-valued field(s): ";
+ public static final String MESSAGE_ADD_ASSIGNMENT_SUCCESS = "New assignment added to %1$s: %2$s";
+ public static final String MESSAGE_EMPTY_STUDENT_LIST = "Error: Student list is empty! Nothing to delete";
+ public static final String MESSAGE_EMPTY_LESSON_LIST = "Error: Lesson list is empty! Nothing to delete";
+ public static final String MESSAGE_ASSIGNMENT_NOT_FOUND = "Error: Assignment with name \"%1$s\" not found!";
+ public static final String MESSAGE_STUDENT_VIEW_REQUIRED = "Error: You can only delete student on student list!";
+ public static final String MESSAGE_LESSON_VIEW_REQUIRED = "Error: You can only delete lesson on lesson list!";
/**
* Returns an error message indicating the duplicate prefixes.
@@ -32,20 +44,48 @@ public static String getErrorMessageForDuplicatePrefixes(Prefix... duplicatePref
}
/**
- * Formats the {@code person} for display to the user.
+ * Formats the {@code student} for display to the user.
*/
- public static String format(Person person) {
+ public static String format(Student student) {
final StringBuilder builder = new StringBuilder();
- builder.append(person.getName())
+ builder.append(student.getName())
.append("; Phone: ")
- .append(person.getPhone())
+ .append(student.getPhone())
.append("; Email: ")
- .append(person.getEmail())
+ .append(student.getEmail())
.append("; Address: ")
- .append(person.getAddress())
- .append("; Tags: ");
- person.getTags().forEach(builder::append);
+ .append(student.getAddress())
+ .append("; Subjects: ")
+ .append(student.getSubjects().toString().replace("[", "").replace("]", ""));
return builder.toString();
}
+ /**
+ * Formats the {@code Lesson} for display to the user.
+ */
+ public static String format(Lesson lesson) {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(lesson.getSubject())
+ .append(" lesson; Student Name: ")
+ .append(lesson.getStudentName())
+ .append("; Date: ")
+ .append(lesson.getDate())
+ .append("; Time: ")
+ .append(lesson.getTime());
+
+ return builder.toString();
+ }
+
+
+ /**
+ * Formats the {@code Assignment} for display to the user.
+ */
+ public static String format(Student student, Assignment assignment) {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("\"")
+ .append(assignment.getAssignmentName())
+ .append("\" for ")
+ .append(student.getName());
+ return builder.toString();
+ }
}
diff --git a/src/main/java/seedu/address/logic/commands/AddAssignmentCommand.java b/src/main/java/seedu/address/logic/commands/AddAssignmentCommand.java
new file mode 100644
index 00000000000..37e66346322
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/AddAssignmentCommand.java
@@ -0,0 +1,133 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ASSIGNMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.util.List;
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.assignment.Assignment;
+import seedu.address.model.datetimeutil.Date;
+import seedu.address.model.student.Student;
+
+/**
+ * Adds an assignment to a student.
+ */
+public class AddAssignmentCommand extends Command {
+
+ public static final String COMMAND_WORD = "add_assignment";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Assigns an assignment to a student.\n"
+ + "Parameters: STUDENT INDEX (must be a positive integer) "
+ + PREFIX_ASSIGNMENT + "ASSIGNMENT_NAME "
+ + PREFIX_DATE + "DUE_DATE\n"
+ + "Example: " + COMMAND_WORD + " 1 "
+ + PREFIX_ASSIGNMENT + "Math Exercise 1 "
+ + PREFIX_DATE + "31-12-2025";
+
+ public static final String MESSAGE_DUPLICATE_ASSIGNMENT = "Error: This assignment already exists in the student.\n"
+ + "Assignments of the same students cannot have the same name.\n"
+ + "Please use the edit_assignment command to edit the assignment.";
+ public static final String MESSAGE_EMPTY_STUDENT_LIST = "Error: Student list is empty";
+
+ private static final Logger logger = LogsCenter.getLogger(AddAssignmentCommand.class);
+
+ private final Index index;
+ private final Assignment assignment;
+ private final Date dueDate;
+
+ /**
+ * Creates an AddAssignmentCommand to add the specified {@code Assignment} to the student at the specified index.
+ */
+ public AddAssignmentCommand(Index index, Assignment assignment) {
+ requireAllNonNull(index, assignment, assignment.getDueDate());
+ this.index = index;
+ this.assignment = assignment;
+ this.dueDate = assignment.getDueDate();
+ logger.info("AddAssignmentCommand created for student index: " + index.getOneBased()
+ + " with assignment: " + assignment);
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ logger.info("Executing AddAssignmentCommand for student index: " + index.getOneBased());
+
+ List lastShownList = model.getFilteredStudentList();
+
+ if (lastShownList.isEmpty()) {
+ logger.warning("Attempted to add assignment to empty student list");
+ throw new CommandException(MESSAGE_EMPTY_STUDENT_LIST);
+ }
+
+ // Check if the index is valid
+ if (index.getZeroBased() >= lastShownList.size()) {
+ logger.warning("Invalid student index: " + index.getOneBased()
+ + " (list size: " + lastShownList.size() + ")");
+ throw new CommandException(Messages.MESSAGE_INDEX_OUT_OF_BOUNDS);
+ }
+
+ Student studentToEdit = lastShownList.get(index.getZeroBased());
+ assert studentToEdit != null : "Student should not be null";
+
+ // Check if the assignment already exists
+ if (studentToEdit.getAssignments().contains(assignment)) {
+ logger.warning("Duplicate assignment detected: " + assignment + " for student: "
+ + studentToEdit.getName());
+ throw new CommandException(MESSAGE_DUPLICATE_ASSIGNMENT);
+ }
+
+ // Add the new assignment to the student
+ if (studentToEdit.hasAssignment(assignment)) {
+ throw new CommandException(MESSAGE_DUPLICATE_ASSIGNMENT);
+ }
+
+ Student editedStudent = studentToEdit.addAssignment(assignment);
+ logger.info("Added assignment: " + assignment + " to student: " + studentToEdit.getName());
+
+ // Update the model with the edited student
+ model.setStudent(studentToEdit, editedStudent);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+
+ String successMessage = generateSuccessMessage(editedStudent);
+ logger.info("Success: " + successMessage);
+ return new CommandResult(successMessage, true);
+ }
+
+ private String generateSuccessMessage(Student editedStudent) {
+ return String.format(Messages.MESSAGE_ADD_ASSIGNMENT_SUCCESS, editedStudent.getName(), assignment);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof AddAssignmentCommand)) {
+ return false;
+ }
+
+ AddAssignmentCommand e = (AddAssignmentCommand) other;
+ return index.equals(e.index)
+ && assignment.equals(e.assignment)
+ && dueDate.equals(e.dueDate);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getCanonicalName() + "{index=" + index
+ + ", assignment=" + assignment
+ + ", dueDate=" + assignment.getDueDate() + "}";
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java
deleted file mode 100644
index 5d7185a9680..00000000000
--- a/src/main/java/seedu/address/logic/commands/AddCommand.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package seedu.address.logic.commands;
-
-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_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.model.Model;
-import seedu.address.model.person.Person;
-
-/**
- * Adds a person to the address book.
- */
-public class AddCommand extends Command {
-
- public static final String COMMAND_WORD = "add";
-
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. "
- + "Parameters: "
- + PREFIX_NAME + "NAME "
- + PREFIX_PHONE + "PHONE "
- + PREFIX_EMAIL + "EMAIL "
- + PREFIX_ADDRESS + "ADDRESS "
- + "[" + 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";
-
- 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";
-
- private final Person toAdd;
-
- /**
- * Creates an AddCommand to add the specified {@code Person}
- */
- public AddCommand(Person person) {
- requireNonNull(person);
- toAdd = person;
- }
-
- @Override
- public CommandResult execute(Model model) throws CommandException {
- requireNonNull(model);
-
- if (model.hasPerson(toAdd)) {
- throw new CommandException(MESSAGE_DUPLICATE_PERSON);
- }
-
- model.addPerson(toAdd);
- return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(toAdd)));
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof AddCommand)) {
- return false;
- }
-
- AddCommand otherAddCommand = (AddCommand) other;
- return toAdd.equals(otherAddCommand.toAdd);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("toAdd", toAdd)
- .toString();
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/AddLessonCommand.java b/src/main/java/seedu/address/logic/commands/AddLessonCommand.java
new file mode 100644
index 00000000000..83884154fd7
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/AddLessonCommand.java
@@ -0,0 +1,122 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SUBJECT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TIME;
+
+import java.util.HashSet;
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.assignment.UniqueAssignmentList;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.student.Address;
+import seedu.address.model.student.Email;
+import seedu.address.model.student.Phone;
+import seedu.address.model.student.Student;
+
+/**
+ * Adds lesson to the addressbook
+ */
+public class AddLessonCommand extends Command {
+ public static final String COMMAND_WORD = "add_lesson";
+ public static final Address VALID_ADDRESS = new Address("123 Main Street");
+ public static final Phone VALID_PHONE = new Phone("12345678");
+ public static final Email VALID_EMAIL = new Email("john@example.com");
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a lesson to the address book. "
+ + "Parameters: "
+ + PREFIX_NAME + "NAME "
+ + PREFIX_DATE + "DATE "
+ + PREFIX_TIME + "TIME "
+ + PREFIX_SUBJECT + "SUBJECT\n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_NAME + "Alex Yeoh "
+ + PREFIX_DATE + "17-09-2027 "
+ + PREFIX_TIME + "14:00 "
+ + PREFIX_SUBJECT + "Science";
+
+ public static final String MESSAGE_SUCCESS = "New lesson added: %1$s.";
+ public static final String MESSAGE_DUPLICATE_LESSON = "Error: This lesson already exists in the address book.";
+ public static final String MESSAGE_STUDENT_NOT_FOUND = "Error: "
+ + "The specified student does not exist in the address book.";
+ public static final String MESSAGE_LESSON_CONFLICT = "Error: The lesson clashes with an existing lesson "
+ + "in the address book.";
+ public static final String MESSAGE_SUBJECT_MISMATCH = "Error: The specified "
+ + "subject does not exist for the student.";
+
+ private static final Logger logger = LogsCenter.getLogger(AddLessonCommand.class);
+
+ private final Lesson toAdd;
+
+ /**
+ * Creates an AddLessonCommand to add the specified {@code Lesson}
+ */
+ public AddLessonCommand(Lesson lesson) {
+ requireNonNull(lesson);
+ this.toAdd = lesson;
+ logger.info("AddLessonCommand created for lesson: " + lesson);
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ logger.info("Executing AddLessonCommand for lesson: " + toAdd);
+
+ if (model.hasLesson(toAdd)) {
+ logger.warning("Duplicate lesson detected: " + toAdd);
+ throw new CommandException(MESSAGE_DUPLICATE_LESSON);
+ }
+
+ if (model.hasLessonConflict(toAdd)) {
+ logger.warning("Lesson conflict detected: " + toAdd);
+ throw new CommandException(MESSAGE_LESSON_CONFLICT);
+ }
+
+ Student dummyStudent = new Student(toAdd.getStudentName(), VALID_PHONE, VALID_EMAIL, VALID_ADDRESS,
+ new HashSet<>(), new UniqueAssignmentList());
+
+ if (!model.hasStudent(dummyStudent)) {
+ logger.warning("Student not found: " + toAdd.getStudentName());
+ throw new CommandException(MESSAGE_STUDENT_NOT_FOUND);
+ }
+
+ if (!model.hasStudentSubject(dummyStudent, toAdd.getSubject())) {
+ logger.warning("Subject mismatch for student: " + toAdd.getStudentName()
+ + " with subject: " + toAdd.getSubject());
+ throw new CommandException(MESSAGE_SUBJECT_MISMATCH);
+ }
+
+ model.addLesson(toAdd);
+ logger.info("Lesson successfully added: " + toAdd);
+
+ return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(toAdd)), true);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof AddLessonCommand otherAddLessonCommand)) {
+ return false;
+ }
+
+ return toAdd.equals(otherAddLessonCommand.toAdd);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("toAdd", toAdd)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/AddStudentCommand.java b/src/main/java/seedu/address/logic/commands/AddStudentCommand.java
new file mode 100644
index 00000000000..e0ce67d74d7
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/AddStudentCommand.java
@@ -0,0 +1,94 @@
+package seedu.address.logic.commands;
+
+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_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SUBJECT;
+
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.student.Student;
+
+/**
+ * Adds a student to the address book.
+ */
+public class AddStudentCommand extends Command {
+ public static final String COMMAND_WORD = "add_student";
+
+ 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_SUBJECT + "SUBJECT]\n"
+ + "Example: " + COMMAND_WORD + " "
+ + "n/ John Doe "
+ + "p/ 98765432 "
+ + "e/ johndoe@gmail.com "
+ + "a/ 311, Clementi Ave 2, #02-25 "
+ + "s/ Math";
+
+ public static final String MESSAGE_SUCCESS = "New student added: %1$s";
+ public static final String MESSAGE_DUPLICATE_STUDENT = "A student with the same name, email, or phone number "
+ + "already exists in the address book";
+
+ private static final Logger logger = LogsCenter.getLogger(AddStudentCommand.class);
+
+ private final Student toAdd;
+
+ /**
+ * Creates an AddStudentCommand to add the specified {@code Student}
+ */
+ public AddStudentCommand(Student student) {
+ requireNonNull(student);
+ this.toAdd = student;
+ logger.info("AddStudentCommand created for student: " + student.getName());
+
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ logger.info("Executing AddStudentCommand for student: " + toAdd.getName());
+
+
+ if (model.hasStudent(toAdd)) {
+ logger.warning("Duplicate student detected: " + toAdd.getName());
+ throw new CommandException(MESSAGE_DUPLICATE_STUDENT);
+ }
+
+ model.addStudent(toAdd);
+ logger.info("Student successfully added: " + toAdd.getName());
+ return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(toAdd)), true);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof AddStudentCommand)) {
+ return false;
+ }
+
+ AddStudentCommand otherAddStudentCommand = (AddStudentCommand) other;
+ return toAdd.equals(otherAddStudentCommand.toAdd);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("toAdd", toAdd)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java
index 9c86b1fa6e4..770dd6dc6c4 100644
--- a/src/main/java/seedu/address/logic/commands/ClearCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java
@@ -2,9 +2,14 @@
import static java.util.Objects.requireNonNull;
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
import seedu.address.model.AddressBook;
import seedu.address.model.Model;
+
+
/**
* Clears the address book.
*/
@@ -13,11 +18,26 @@ public class ClearCommand extends Command {
public static final String COMMAND_WORD = "clear";
public static final String MESSAGE_SUCCESS = "Address book has been cleared!";
+ private static final Logger logger = LogsCenter.getLogger(ClearCommand.class);
+
+ /**
+ * Creates a ClearCommand.
+ *
+ */
+ public ClearCommand() {
+ logger.info("ClearCommand created ");
+ }
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
+ logger.info("Executing ClearCommand");
+
+ int previousSize = model.getAddressBook().getStudentList().size();
model.setAddressBook(new AddressBook());
+ logger.info("Address book cleared. Previous size: " + previousSize);
+
return new CommandResult(MESSAGE_SUCCESS);
}
+
}
diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java
index 249b6072d0d..cd3db8cb497 100644
--- a/src/main/java/seedu/address/logic/commands/CommandResult.java
+++ b/src/main/java/seedu/address/logic/commands/CommandResult.java
@@ -18,6 +18,8 @@ public class CommandResult {
/** The application should exit. */
private final boolean exit;
+ /** Flag to update the list */
+ private final boolean updateList;
/**
* Constructs a {@code CommandResult} with the specified fields.
@@ -26,11 +28,23 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) {
this.feedbackToUser = requireNonNull(feedbackToUser);
this.showHelp = showHelp;
this.exit = exit;
+ this.updateList = false;
+ }
+
+ /**
+ * Constructs a {@code CommandResult} with the specified fields.
+ * Specifically to flag whether the list needs to be updated after command execution
+ */
+ public CommandResult(String feedbackToUser, boolean updateList) {
+ this.feedbackToUser = requireNonNull(feedbackToUser);
+ this.showHelp = false;
+ this.exit = false;
+ this.updateList = updateList;
}
/**
* Constructs a {@code CommandResult} with the specified {@code feedbackToUser},
- * and other fields set to their default value.
+ * and other fields set to their default assignmentName.
*/
public CommandResult(String feedbackToUser) {
this(feedbackToUser, false, false);
@@ -48,6 +62,9 @@ public boolean isExit() {
return exit;
}
+ public boolean isUpdateList() {
+ return updateList;
+ }
@Override
public boolean equals(Object other) {
if (other == this) {
@@ -62,12 +79,13 @@ public boolean equals(Object other) {
CommandResult otherCommandResult = (CommandResult) other;
return feedbackToUser.equals(otherCommandResult.feedbackToUser)
&& showHelp == otherCommandResult.showHelp
- && exit == otherCommandResult.exit;
+ && exit == otherCommandResult.exit
+ && updateList == otherCommandResult.updateList;
}
@Override
public int hashCode() {
- return Objects.hash(feedbackToUser, showHelp, exit);
+ return Objects.hash(feedbackToUser, showHelp, exit, updateList);
}
@Override
@@ -76,6 +94,7 @@ public String toString() {
.add("feedbackToUser", feedbackToUser)
.add("showHelp", showHelp)
.add("exit", exit)
+ .add("updateList", updateList)
.toString();
}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteAssignmentCommand.java b/src/main/java/seedu/address/logic/commands/DeleteAssignmentCommand.java
new file mode 100644
index 00000000000..f32426e3dba
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/DeleteAssignmentCommand.java
@@ -0,0 +1,120 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.assignment.Assignment;
+import seedu.address.model.datetimeutil.Date;
+import seedu.address.model.student.Student;
+
+/**
+ * Deletes a assignment identified using it's displayed index from the address book.
+ */
+public class DeleteAssignmentCommand extends Command {
+
+ public static final String COMMAND_WORD = "delete_assignment";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Deletes an assignment of student identified by the index number used in the displayed student list.\n"
+ + "Parameters: STUDENT INDEX (must be a positive integer)\n"
+ + "Parameters: ASSIGNMENT NAME (must be a string)\n"
+ + "Example: " + COMMAND_WORD + " 1 as/Math Exercise 1";
+
+ public static final String MESSAGE_DELETE_ASSIGNMENT_SUCCESS = "Assignment %1$s deleted successfully.";
+ public static final String MESSAGE_INVALID_ASSIGNMENT_DISPLAYED = "The assignment name provided cannot be found";
+ public static final String MESSAGE_EMPTY_STUDENT_LIST = "Error: Student list is empty";
+
+ private static final Logger logger = LogsCenter.getLogger(DeleteAssignmentCommand.class);
+
+ private final Index targetIndex;
+ private final String assignmentName;
+
+ /**
+ * Creates a DeleteAssignmentCommand to delete the specified {@code Assignment}.
+ *
+ * @param targetIndex Index of the student in the filtered student list
+ * @param assignmentName Name of the assignment to delete
+ */
+ public DeleteAssignmentCommand(Index targetIndex, String assignmentName) {
+ requireAllNonNull(targetIndex, assignmentName);
+ this.targetIndex = targetIndex;
+ this.assignmentName = Arrays.stream(assignmentName.split("\\s+"))
+ .map(word -> word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase())
+ .collect(Collectors.joining(" ")).trim();
+ logger.info("DeleteAssignmentCommand created for student index: " + targetIndex.getOneBased()
+ + " and assignment: " + assignmentName);
+ }
+
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ logger.info("Executing DeleteAssignmentCommand for student index: " + targetIndex.getOneBased()
+ + " and assignment: " + assignmentName);
+
+ List lastShownList = model.getFilteredStudentList();
+
+ if (lastShownList.isEmpty()) {
+ logger.warning("Attempted to delete assignment from empty student list");
+ throw new CommandException(MESSAGE_EMPTY_STUDENT_LIST);
+ }
+
+ if (targetIndex.getZeroBased() >= lastShownList.size()) {
+ logger.warning("Invalid student index: " + targetIndex.getOneBased()
+ + " (list size: " + lastShownList.size() + ")");
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ }
+
+ Student studentToDeleteAssignment = lastShownList.get(targetIndex.getZeroBased());
+ assert studentToDeleteAssignment != null : "Student should not be null";
+
+ Assignment assignmentToDelete = new Assignment(assignmentName, new Date("31-12-9999"));
+ if (!studentToDeleteAssignment.getAssignments().contains(assignmentToDelete)) {
+ logger.warning("Assignment not found: " + assignmentName + " for student: "
+ + studentToDeleteAssignment.getName());
+ throw new CommandException(MESSAGE_INVALID_ASSIGNMENT_DISPLAYED);
+ }
+
+ model.deleteAssignment(studentToDeleteAssignment, assignmentName);
+ logger.info("Successfully deleted assignment: " + assignmentName + " for student: "
+ + studentToDeleteAssignment.getName());
+
+ return new CommandResult(String.format(MESSAGE_DELETE_ASSIGNMENT_SUCCESS,
+ Messages.format(studentToDeleteAssignment, assignmentToDelete)), true);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof DeleteAssignmentCommand)) {
+ return false;
+ }
+
+ DeleteAssignmentCommand otherDeleteAssignmentCommand = (DeleteAssignmentCommand) other;
+ return targetIndex.equals(otherDeleteAssignmentCommand.targetIndex)
+ && assignmentName.equals(otherDeleteAssignmentCommand.assignmentName);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetIndex", targetIndex)
+ .add("assignmentName", assignmentName)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
deleted file mode 100644
index 1135ac19b74..00000000000
--- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-
-import java.util.List;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.model.Model;
-import seedu.address.model.person.Person;
-
-/**
- * Deletes a person identified using it's displayed index from the address book.
- */
-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";
-
- public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s";
-
- private final Index targetIndex;
-
- public DeleteCommand(Index targetIndex) {
- this.targetIndex = targetIndex;
- }
-
- @Override
- public CommandResult execute(Model model) throws CommandException {
- requireNonNull(model);
- List lastShownList = model.getFilteredPersonList();
-
- if (targetIndex.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
- }
-
- Person personToDelete = lastShownList.get(targetIndex.getZeroBased());
- model.deletePerson(personToDelete);
- return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, Messages.format(personToDelete)));
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof DeleteCommand)) {
- return false;
- }
-
- DeleteCommand otherDeleteCommand = (DeleteCommand) other;
- return targetIndex.equals(otherDeleteCommand.targetIndex);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("targetIndex", targetIndex)
- .toString();
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteLessonCommand.java b/src/main/java/seedu/address/logic/commands/DeleteLessonCommand.java
new file mode 100644
index 00000000000..0e0f6cc5d6d
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/DeleteLessonCommand.java
@@ -0,0 +1,96 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.lesson.Lesson;
+
+/**
+ * Deletes a lesson identified using it's displayed index from the address book.
+ */
+public class DeleteLessonCommand extends Command {
+
+ public static final String COMMAND_WORD = "delete_lesson";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Deletes the lesson identified by the index number used in the displayed lesson list.\n"
+ + "Parameters: LESSON INDEX (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_DELETE_LESSON_SUCCESS = "Deleted Lesson: %1$s";
+
+ private static final Logger logger = LogsCenter.getLogger(DeleteLessonCommand.class);
+
+ private final Index targetIndex;
+
+ /**
+ * Creates a DeleteLessonCommand to delete the specified {@code Lesson}.
+ *
+ * @param targetIndex of the lesson in the filtered lesson list to delete
+ */
+ public DeleteLessonCommand(Index targetIndex) {
+ requireNonNull(targetIndex);
+ this.targetIndex = targetIndex;
+ logger.info("DeleteLessonCommand created for index: " + targetIndex.getOneBased());
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ logger.info("Executing DeleteLessonCommand for index: " + targetIndex.getOneBased());
+
+ List lastShownList = model.getFilteredLessonList();
+ if (model.isStudentView()) {
+ throw new CommandException(Messages.MESSAGE_LESSON_VIEW_REQUIRED);
+ }
+
+ if (lastShownList.isEmpty()) {
+ logger.warning("Attempted to delete from empty lesson list");
+ throw new CommandException(Messages.MESSAGE_EMPTY_LESSON_LIST);
+ }
+
+ if (targetIndex.getZeroBased() >= lastShownList.size()) {
+ logger.warning("Invalid lesson index: " + targetIndex.getOneBased()
+ + " (list size: " + lastShownList.size() + ")");
+ throw new CommandException(Messages.MESSAGE_INVALID_LESSON_DISPLAYED_INDEX);
+ }
+
+ Lesson lessonToDelete = lastShownList.get(targetIndex.getZeroBased());
+ assert lessonToDelete != null : "Lesson to delete should not be null";
+
+ model.deleteLesson(lessonToDelete);
+ logger.info("Successfully deleted lesson: " + lessonToDelete);
+
+ return new CommandResult(String.format(MESSAGE_DELETE_LESSON_SUCCESS, Messages.format(lessonToDelete)), true);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof DeleteLessonCommand)) {
+ return false;
+ }
+
+ DeleteLessonCommand otherDeleteLessonCommand = (DeleteLessonCommand) other;
+ return targetIndex.equals(otherDeleteLessonCommand.targetIndex);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetIndex", targetIndex)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteStudentCommand.java b/src/main/java/seedu/address/logic/commands/DeleteStudentCommand.java
new file mode 100644
index 00000000000..319cd2c2f32
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/DeleteStudentCommand.java
@@ -0,0 +1,110 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.student.Student;
+
+/**
+ * Deletes a student identified using it's displayed index from the address book.
+ */
+public class DeleteStudentCommand extends Command {
+
+ public static final String COMMAND_WORD = "delete_student";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Deletes the student identified by the index number used in the displayed student list.\n"
+ + "Parameters: STUDENT INDEX (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_DELETE_STUDENT_SUCCESS =
+ "Student %1$s\ndeleted successfully, along with all associated lessons and assignments.";
+
+ private static final Logger logger = LogsCenter.getLogger(DeleteStudentCommand.class);
+
+ private final Index targetIndex;
+
+ /**
+ * Creates a DeleteStudentCommand to delete the specified {@code Student}.
+ *
+ * @param targetIndex of the student in the filtered student list to delete
+ */
+ public DeleteStudentCommand(Index targetIndex) {
+ requireNonNull(targetIndex);
+ this.targetIndex = targetIndex;
+ logger.info("DeleteStudentCommand created for index: " + targetIndex.getOneBased());
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ logger.info("Executing DeleteStudentCommand for index: " + targetIndex.getOneBased());
+
+ List lastShownList = model.getFilteredStudentList();
+ if (!model.isStudentView()) {
+ throw new CommandException(Messages.MESSAGE_STUDENT_VIEW_REQUIRED);
+ }
+
+ if (lastShownList.isEmpty()) {
+ logger.warning("Attempted to delete from empty student list");
+ throw new CommandException(Messages.MESSAGE_EMPTY_STUDENT_LIST);
+ }
+
+ if (targetIndex.getZeroBased() >= lastShownList.size()) {
+ logger.warning("Invalid student index: " + targetIndex.getOneBased());
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ }
+
+ Student studentToDelete = lastShownList.get(targetIndex.getZeroBased());
+ assert studentToDelete != null : "Student to delete should not be null";
+
+ // Delete all lessons associated with the student
+ List lessonsToDelete = model.getFilteredLessonList()
+ .stream()
+ .filter(lesson -> lesson.getStudentName().equals(studentToDelete.getName()))
+ .toList();
+
+ logger.info("Deleting " + lessonsToDelete.size() + " associated lessons");
+ lessonsToDelete.forEach(lesson -> {
+ model.deleteLesson(lesson);
+ logger.fine("Deleted lesson: " + lesson);
+ });
+
+ // Finally, delete the student
+ model.deleteStudent(studentToDelete);
+ logger.info("Successfully deleted student: " + studentToDelete.getName());
+
+ return new CommandResult(String.format(MESSAGE_DELETE_STUDENT_SUCCESS, Messages.format(studentToDelete)), true);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof DeleteStudentCommand)) {
+ return false;
+ }
+
+ DeleteStudentCommand otherDeleteStudentCommand = (DeleteStudentCommand) other;
+ return targetIndex.equals(otherDeleteStudentCommand.targetIndex);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetIndex", targetIndex)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/EditAssignmentCommand.java b/src/main/java/seedu/address/logic/commands/EditAssignmentCommand.java
new file mode 100644
index 00000000000..0f96e3b7a45
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/EditAssignmentCommand.java
@@ -0,0 +1,239 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ASSIGNMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NEW_ASSIGNMENT;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.assignment.Assignment;
+import seedu.address.model.assignment.UniqueAssignmentList;
+import seedu.address.model.datetimeutil.Date;
+import seedu.address.model.student.Student;
+
+/**
+ * Edits the details of an existing assignment in the address book.
+ */
+public class EditAssignmentCommand extends Command {
+
+ public static final String COMMAND_WORD = "edit_assignment";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the assignment identified "
+ + "by the index number of the student and the assignment name. "
+ + "Existing values will be overwritten by the input values.\n"
+ + "Parameters: STUDENT INDEX (must be a positive integer) "
+ + PREFIX_ASSIGNMENT + "ASSIGNMENT_NAME "
+ + "[" + PREFIX_NEW_ASSIGNMENT + "NEW_ASSIGNMENT_NAME] "
+ + "[" + PREFIX_DATE + "DATE] \n"
+ + "Example: " + COMMAND_WORD + " 1 "
+ + PREFIX_ASSIGNMENT + "Assignment 1 "
+ + PREFIX_NEW_ASSIGNMENT + "Assignment 1a "
+ + PREFIX_DATE + "30-09-2025";
+
+ public static final String MESSAGE_EDIT_ASSIGNMENT_SUCCESS = "Edited Assignment: \"%1$s\"";
+ public static final String MESSAGE_NOT_EDITED = "Error: At least one field to edit must be provided.";
+ public static final String MESSAGE_DUPLICATE_ASSIGNMENT = "Error: This assignment "
+ + "already exists in the address book.";
+ private static final Logger logger = LogsCenter.getLogger(EditAssignmentCommand.class);
+
+ private final Index index;
+ private final String assignmentName;
+ private final EditAssignmentDescriptor editAssignmentDescriptor;
+
+ /**
+ * @param index of the student in the filtered student list to edit
+ * @param editAssignmentDescriptor details to edit the assignment with
+ */
+ public EditAssignmentCommand(Index index, String assignmentName,
+ EditAssignmentDescriptor editAssignmentDescriptor) {
+ requireNonNull(index);
+ requireNonNull(assignmentName);
+ requireNonNull(editAssignmentDescriptor);
+
+ this.index = index;
+ this.assignmentName = Arrays.stream(assignmentName.split("\\s+"))
+ .map(word -> word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase())
+ .collect(Collectors.joining(" ")).trim();
+ this.editAssignmentDescriptor = new EditAssignmentDescriptor(editAssignmentDescriptor);
+
+ logger.info("EditAssignmentCommand created for student index: " + index.getOneBased()
+ + " with assignment: " + assignmentName);
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ logger.info("Executing EditAssignmentCommand for student index: " + index.getOneBased()
+ + " and assignment: " + assignmentName);
+ List lastShownList = model.getFilteredStudentList();
+
+ if (index.getZeroBased() >= lastShownList.size()) {
+ logger.warning("Invalid student index: " + index.getOneBased());
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ }
+
+ Student studentToEdit = lastShownList.get(index.getZeroBased());
+ UniqueAssignmentList assignmentsToEdit = studentToEdit.getAssignments();
+
+ assert assignmentsToEdit != null : "Assignments list should not be null";
+
+ Assignment assignmentToEdit = assignmentsToEdit.getAssignment(assignmentName);
+
+ if (assignmentToEdit == null) {
+ logger.warning("Assignment not found: " + assignmentName);
+ throw new CommandException(String.format(Messages.MESSAGE_ASSIGNMENT_NOT_FOUND, assignmentName));
+ }
+
+ if (!editAssignmentDescriptor.isAnyFieldEdited()) {
+ throw new CommandException(MESSAGE_NOT_EDITED);
+ }
+
+ Assignment editedAssignment = createEditedAssignment(assignmentToEdit, editAssignmentDescriptor);
+ assert editedAssignment != null : "Edited assignment should not be null";
+
+ if (!assignmentToEdit.isSameAssignment(editedAssignment)) {
+ if (assignmentsToEdit.contains(editedAssignment)) {
+ logger.warning("Duplicate assignment detected: " + editedAssignment.getAssignmentName());
+ throw new CommandException(MESSAGE_DUPLICATE_ASSIGNMENT);
+ }
+ }
+
+ UniqueAssignmentList updatedAssignments = assignmentsToEdit;
+ updatedAssignments.setAssignment(assignmentToEdit, editedAssignment);
+
+ Student editedStudent = new Student(studentToEdit.getName(), studentToEdit.getPhone(), studentToEdit.getEmail(),
+ studentToEdit.getAddress(), studentToEdit.getSubjects(), updatedAssignments);
+
+ model.setStudent(studentToEdit, editedStudent);
+ model.updateFilteredStudentList(Model.PREDICATE_SHOW_ALL_STUDENTS);
+
+ logger.info("Assignment successfully edited: " + editedAssignment);
+ return new CommandResult(String.format(MESSAGE_EDIT_ASSIGNMENT_SUCCESS,
+ editedAssignment), true);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditAssignmentCommand)) {
+ return false;
+ }
+
+ // state check
+ EditAssignmentCommand e = (EditAssignmentCommand) other;
+ return index.equals(e.index)
+ && assignmentName.equals(e.assignmentName)
+ && editAssignmentDescriptor.equals(e.editAssignmentDescriptor);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("index", index)
+ .add("assignmentName", assignmentName)
+ .add("editAssignmentDescriptor", editAssignmentDescriptor)
+ .toString();
+ }
+
+ /**
+ * Creates and returns a {@code Assignment} with the details of {@code assignmentToEdit}
+ * edited with {@code editAssignmentDescriptor}.
+ */
+ public Assignment createEditedAssignment(Assignment assignmentToEdit,
+ EditAssignmentDescriptor editAssignmentDescriptor) {
+ assert assignmentToEdit != null : "Assignment to edit cannot be null";
+ assert editAssignmentDescriptor != null : "Edit descriptor cannot be null";
+ assert editAssignmentDescriptor.isAnyFieldEdited() : "At least one field should be edited";
+
+
+ String updatedAssignmentName = editAssignmentDescriptor.getNewAssignmentName()
+ .orElse(assignmentToEdit.getAssignmentName());
+ Date updatedDate = editAssignmentDescriptor.getDate().orElse(assignmentToEdit.getDueDate());
+
+ return new Assignment(updatedAssignmentName, updatedDate, assignmentToEdit.isDone());
+ }
+
+ /**
+ * Stores the details to edit the assignment with. Each non-empty field value will replace the
+ * corresponding field value of the assignment.
+ */
+ public static class EditAssignmentDescriptor {
+ private String newAssignmentName;
+ private Date date;
+
+ public EditAssignmentDescriptor() {}
+
+ /**
+ * Copy constructor.
+ * A defensive copy of {@code tags} is used internally.
+ */
+ public EditAssignmentDescriptor(EditAssignmentDescriptor toCopy) {
+ setNewAssignmentName(toCopy.newAssignmentName);
+ setDate(toCopy.date);
+ }
+
+ /**
+ * Returns true if at least one field is edited.
+ */
+ public boolean isAnyFieldEdited() {
+ return newAssignmentName != null || date != null;
+ }
+
+ public void setNewAssignmentName(String assignmentName) {
+ this.newAssignmentName = assignmentName;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public Optional getNewAssignmentName() {
+ return Optional.ofNullable(newAssignmentName);
+ }
+
+ public Optional getDate() {
+ return Optional.ofNullable(date);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditAssignmentDescriptor)) {
+ return false;
+ }
+
+ EditAssignmentDescriptor otherEditAssignmentDescriptor = (EditAssignmentDescriptor) other;
+ return Objects.equals(newAssignmentName, otherEditAssignmentDescriptor.newAssignmentName)
+ && Objects.equals(date, otherEditAssignmentDescriptor.date);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("assignmentName", newAssignmentName)
+ .add("date", date)
+ .toString();
+ }
+
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java
deleted file mode 100644
index 4b581c7331e..00000000000
--- a/src/main/java/seedu/address/logic/commands/EditCommand.java
+++ /dev/null
@@ -1,242 +0,0 @@
-package seedu.address.logic.commands;
-
-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_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 java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.commons.util.CollectionUtil;
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-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.tag.Tag;
-
-/**
- * Edits the details of an existing person in the address book.
- */
-public class EditCommand extends Command {
-
- public static final String COMMAND_WORD = "edit";
-
- 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) "
- + "[" + PREFIX_NAME + "NAME] "
- + "[" + PREFIX_PHONE + "PHONE] "
- + "[" + PREFIX_EMAIL + "EMAIL] "
- + "[" + PREFIX_ADDRESS + "ADDRESS] "
- + "[" + PREFIX_TAG + "TAG]...\n"
- + "Example: " + COMMAND_WORD + " 1 "
- + PREFIX_PHONE + "91234567 "
- + PREFIX_EMAIL + "johndoe@example.com";
-
- public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s";
- public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided.";
- public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book.";
-
- private final Index index;
- private final EditPersonDescriptor editPersonDescriptor;
-
- /**
- * @param index of the person in the filtered person list to edit
- * @param editPersonDescriptor details to edit the person with
- */
- public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) {
- requireNonNull(index);
- requireNonNull(editPersonDescriptor);
-
- this.index = index;
- this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor);
- }
-
- @Override
- public CommandResult execute(Model model) throws CommandException {
- requireNonNull(model);
- List lastShownList = model.getFilteredPersonList();
-
- if (index.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
- }
-
- Person personToEdit = lastShownList.get(index.getZeroBased());
- Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor);
-
- if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) {
- throw new CommandException(MESSAGE_DUPLICATE_PERSON);
- }
-
- model.setPerson(personToEdit, editedPerson);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson)));
- }
-
- /**
- * Creates and returns a {@code Person} with the details of {@code personToEdit}
- * edited with {@code editPersonDescriptor}.
- */
- private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) {
- 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());
-
- return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags);
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof EditCommand)) {
- return false;
- }
-
- EditCommand otherEditCommand = (EditCommand) other;
- return index.equals(otherEditCommand.index)
- && editPersonDescriptor.equals(otherEditCommand.editPersonDescriptor);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("index", index)
- .add("editPersonDescriptor", editPersonDescriptor)
- .toString();
- }
-
- /**
- * 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 {
- private Name name;
- private Phone phone;
- private Email email;
- private Address address;
- private Set tags;
-
- public EditPersonDescriptor() {}
-
- /**
- * Copy constructor.
- * A defensive copy of {@code tags} is used internally.
- */
- public EditPersonDescriptor(EditPersonDescriptor toCopy) {
- setName(toCopy.name);
- setPhone(toCopy.phone);
- setEmail(toCopy.email);
- setAddress(toCopy.address);
- setTags(toCopy.tags);
- }
-
- /**
- * Returns true if at least one field is edited.
- */
- public boolean isAnyFieldEdited() {
- return CollectionUtil.isAnyNonNull(name, phone, email, address, tags);
- }
-
- public void setName(Name name) {
- this.name = name;
- }
-
- public Optional getName() {
- return Optional.ofNullable(name);
- }
-
- public void setPhone(Phone phone) {
- this.phone = phone;
- }
-
- public Optional getPhone() {
- return Optional.ofNullable(phone);
- }
-
- public void setEmail(Email email) {
- this.email = email;
- }
-
- public Optional getEmail() {
- return Optional.ofNullable(email);
- }
-
- public void setAddress(Address address) {
- this.address = address;
- }
-
- public Optional getAddress() {
- return Optional.ofNullable(address);
- }
-
- /**
- * Sets {@code tags} to this object's {@code tags}.
- * A defensive copy of {@code tags} is used internally.
- */
- public void setTags(Set tags) {
- this.tags = (tags != null) ? new HashSet<>(tags) : null;
- }
-
- /**
- * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException}
- * if modification is attempted.
- * Returns {@code Optional#empty()} if {@code tags} is null.
- */
- public Optional> getTags() {
- return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty();
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof EditPersonDescriptor)) {
- return false;
- }
-
- EditPersonDescriptor otherEditPersonDescriptor = (EditPersonDescriptor) other;
- return Objects.equals(name, otherEditPersonDescriptor.name)
- && Objects.equals(phone, otherEditPersonDescriptor.phone)
- && Objects.equals(email, otherEditPersonDescriptor.email)
- && Objects.equals(address, otherEditPersonDescriptor.address)
- && Objects.equals(tags, otherEditPersonDescriptor.tags);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("name", name)
- .add("phone", phone)
- .add("email", email)
- .add("address", address)
- .add("tags", tags)
- .toString();
- }
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/EditLessonCommand.java b/src/main/java/seedu/address/logic/commands/EditLessonCommand.java
new file mode 100644
index 00000000000..aaa72cbccca
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/EditLessonCommand.java
@@ -0,0 +1,261 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.commands.AddLessonCommand.MESSAGE_LESSON_CONFLICT;
+import static seedu.address.logic.commands.AddLessonCommand.MESSAGE_STUDENT_NOT_FOUND;
+import static seedu.address.logic.commands.AddLessonCommand.MESSAGE_SUBJECT_MISMATCH;
+import static seedu.address.logic.commands.AddLessonCommand.VALID_ADDRESS;
+import static seedu.address.logic.commands.AddLessonCommand.VALID_EMAIL;
+import static seedu.address.logic.commands.AddLessonCommand.VALID_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SUBJECT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TIME;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_LESSONS;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.CollectionUtil;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.assignment.UniqueAssignmentList;
+import seedu.address.model.datetimeutil.Date;
+import seedu.address.model.datetimeutil.Time;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.student.Name;
+import seedu.address.model.student.Student;
+import seedu.address.model.subject.Subject;
+
+/**
+ * Edits the details of an existing lesson in the address book.
+ */
+public class EditLessonCommand extends Command {
+
+ public static final String COMMAND_WORD = "edit_lesson";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the lesson identified "
+ + "by the index number used in the displayed lesson list. "
+ + "Existing values will be overwritten by the input values.\n"
+ + "Parameters: LESSON INDEX (must be a positive integer) "
+ + "[" + PREFIX_SUBJECT + "SUBJECT] "
+ + "[" + PREFIX_NAME + "NAME] "
+ + "[" + PREFIX_DATE + "DATE] "
+ + "[" + PREFIX_TIME + "TIME]\n"
+ + "Example: " + COMMAND_WORD + " 1 "
+ + PREFIX_DATE + "20-7-2026 "
+ + PREFIX_TIME + "19:00";
+
+ public static final String MESSAGE_EDIT_LESSON_SUCCESS = "Edited Lesson: %1$s";
+ public static final String MESSAGE_NOT_EDITED = "Error: At least one field to edit must be provided.";
+ public static final String MESSAGE_DUPLICATE_LESSON = "Error: This lesson already exists in the address book.";
+ private static final Logger logger = LogsCenter.getLogger(EditLessonCommand.class);
+
+ private final Index index;
+ private final EditLessonDescriptor editLessonDescriptor;
+
+ /**
+ * @param index of the student in the filtered lesson list to edit
+ * @param editLessonDescriptor details to edit the lesson with
+ */
+ public EditLessonCommand(Index index, EditLessonDescriptor editLessonDescriptor) {
+ requireNonNull(index);
+ requireNonNull(editLessonDescriptor);
+
+ this.index = index;
+ this.editLessonDescriptor = new EditLessonDescriptor(editLessonDescriptor);
+ logger.info("EditLessonCommand created for lesson index: " + index.getOneBased());
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ logger.info("Executing EditLessonCommand for lesson index: " + index.getOneBased());
+ List lastShownList = model.getFilteredLessonList();
+
+ if (index.getZeroBased() >= lastShownList.size()) {
+ logger.warning("Invalid lesson index: " + index.getOneBased());
+ throw new CommandException(Messages.MESSAGE_INVALID_LESSON_DISPLAYED_INDEX);
+ }
+
+
+ Lesson lessonToEdit = lastShownList.get(index.getZeroBased());
+ Lesson editedLesson = createEditedLesson(lessonToEdit, editLessonDescriptor);
+ assert editedLesson != null : "Edited lesson should not be null";
+
+ //check duplicate
+ if (!lessonToEdit.equals(editedLesson) && model.hasLesson(editedLesson)) {
+ logger.warning("Duplicate lesson detected: " + editedLesson);
+ throw new CommandException(MESSAGE_DUPLICATE_LESSON);
+ }
+
+ //check whether after editing, lesson clashes with any lesson in the addressbook
+ if (model.hasLessonConflict(editedLesson)) {
+ if (!lessonToEdit.isConflict(editedLesson)) {
+ logger.warning("Lesson conflict detected for: " + editedLesson);
+ throw new CommandException(MESSAGE_LESSON_CONFLICT);
+ }
+ }
+ //check whether student exists in the addressbook
+ Student dummyStudent = new Student(editedLesson.getStudentName(), VALID_PHONE, VALID_EMAIL, VALID_ADDRESS,
+ new HashSet<>(), new UniqueAssignmentList());
+ if (!model.hasStudent(dummyStudent)) {
+ logger.warning("Student not found: " + editedLesson.getStudentName());
+ throw new CommandException(MESSAGE_STUDENT_NOT_FOUND);
+ }
+ //check whether student has the subject
+ if (!model.hasStudentSubject(dummyStudent, editedLesson.getSubject())) {
+ throw new CommandException(MESSAGE_SUBJECT_MISMATCH);
+ }
+ model.setLesson(lessonToEdit, editedLesson);
+ model.updateFilteredLessonList(PREDICATE_SHOW_ALL_LESSONS);
+
+ logger.info("Lesson successfully edited: " + editedLesson);
+ return new CommandResult(String.format(MESSAGE_EDIT_LESSON_SUCCESS, Messages.format(editedLesson)), true);
+ }
+
+ /**
+ * Creates and returns a {@code Student} with the details of {@code studentToEdit}
+ * edited with {@code editStudentDescriptor}.
+ */
+ public static Lesson createEditedLesson(Lesson lessonToEdit, EditLessonDescriptor editLessonDescriptor) {
+ assert lessonToEdit != null : "Lesson to edit cannot be null";
+ assert editLessonDescriptor != null : "Edit descriptor cannot be null";
+ assert editLessonDescriptor.isAnyFieldEdited() : "At least one field should be edited";
+
+ Name updatedName = editLessonDescriptor.getName().orElse(lessonToEdit.getStudentName());
+ Date updatedDate = editLessonDescriptor.getDate().orElse(lessonToEdit.getDate());
+ Time updatedTime = editLessonDescriptor.getTime().orElse(lessonToEdit.getTime());
+ Subject updatedSubject = editLessonDescriptor.getSubject().orElse(lessonToEdit.getSubject());
+
+ return new Lesson(updatedSubject, updatedName, updatedDate, updatedTime);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditLessonCommand)) {
+ return false;
+ }
+
+ EditLessonCommand otherEditCommand = (EditLessonCommand) other;
+ return index.equals(otherEditCommand.index)
+ && editLessonDescriptor.equals(otherEditCommand.editLessonDescriptor);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("index", index)
+ .add("editLessonDescriptor", editLessonDescriptor)
+ .toString();
+ }
+
+ /**
+ * Stores the details to edit the lesson with.
+ */
+ public static class EditLessonDescriptor {
+ private Name name;
+ private Date date;
+ private Time time;
+ private Subject subject;
+
+ public EditLessonDescriptor() {}
+
+ /**
+ * Copy constructor.
+ * A defensive copy of {@code subjects} is used internally.
+ */
+ public EditLessonDescriptor(EditLessonDescriptor toCopy) {
+ setName(toCopy.name);
+ setDate(toCopy.date);
+ setTime(toCopy.time);
+ setSubject(toCopy.subject);
+ }
+
+
+
+ /**
+ * Returns true if at least one field is edited.
+ */
+ public boolean isAnyFieldEdited() {
+ return CollectionUtil.isAnyNonNull(name, date, time, subject);
+ }
+
+ public void setName(Name name) {
+ this.name = name;
+ }
+
+ public Optional getName() {
+ return Optional.ofNullable(name);
+ }
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public Optional getDate() {
+ return Optional.ofNullable(date);
+ }
+
+ public void setTime(Time time) {
+ this.time = time;
+ }
+
+ public Optional getTime() {
+ return Optional.ofNullable(time);
+ }
+
+ public void setSubject(Subject subject) {
+ this.subject = subject;
+ }
+
+ /**
+ * Returns an unmodifiable subject set, which throws {@code UnsupportedOperationException}
+ * if modification is attempted.
+ * Returns {@code Optional#empty()} if {@code subjects} is null.
+ */
+ public Optional getSubject() {
+ return (subject != null) ? Optional.of(subject) : Optional.empty();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditLessonDescriptor)) {
+ return false;
+ }
+
+ EditLessonDescriptor otherEditLessonDescriptor = (EditLessonDescriptor) other;
+ return Objects.equals(name, otherEditLessonDescriptor.name)
+ && Objects.equals(date, otherEditLessonDescriptor.date)
+ && Objects.equals(time, otherEditLessonDescriptor.time)
+ && Objects.equals(subject, otherEditLessonDescriptor.subject);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("subjects", subject)
+ .add("name", name)
+ .add("date", date)
+ .add("time", time)
+ .toString();
+ }
+
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/EditStudentCommand.java b/src/main/java/seedu/address/logic/commands/EditStudentCommand.java
new file mode 100644
index 00000000000..856178fc234
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/EditStudentCommand.java
@@ -0,0 +1,302 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.commands.EditLessonCommand.EditLessonDescriptor;
+import static seedu.address.logic.commands.EditLessonCommand.createEditedLesson;
+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_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.CollectionUtil;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.assignment.UniqueAssignmentList;
+import seedu.address.model.lesson.Lesson;
+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.subject.Subject;
+
+/**
+ * Edits the details of an existing student in the address book.
+ */
+public class EditStudentCommand extends Command {
+
+ public static final String COMMAND_WORD = "edit_student";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the student identified "
+ + "by the index number used in the displayed student list. "
+ + "Existing values will be overwritten by the input values.\n"
+ + "Parameters: STUDENT INDEX (must be a positive integer) "
+ + "[" + PREFIX_NAME + "NAME] "
+ + "[" + PREFIX_PHONE + "PHONE] "
+ + "[" + PREFIX_EMAIL + "EMAIL] "
+ + "[" + PREFIX_ADDRESS + "ADDRESS]\n"
+ + "Example: " + COMMAND_WORD + " 1 "
+ + PREFIX_PHONE + "91234567 "
+ + PREFIX_EMAIL + "johndoe@example.com";
+
+ public static final String MESSAGE_EDIT_STUDENT_SUCCESS = "Edited Student: %1$s";
+ public static final String MESSAGE_EDIT_SUBJECT_DISALLOWED = "Error: Editing subjects directly is not allowed.";
+ public static final String MESSAGE_NOT_EDITED = "Error: At least one field to edit must be provided.";
+ public static final String MESSAGE_DUPLICATE_STUDENT = "Error: This student already exists in the address book.";
+ private static final Logger logger = LogsCenter.getLogger(EditStudentCommand.class);
+
+ private final Index index;
+ private final EditStudentDescriptor editStudentDescriptor;
+
+ /**
+ * @param index of the student in the filtered student list to edit
+ * @param editStudentDescriptor details to edit the student with
+ */
+ public EditStudentCommand(Index index, EditStudentDescriptor editStudentDescriptor) {
+ requireNonNull(index);
+ requireNonNull(editStudentDescriptor);
+
+ this.index = index;
+ this.editStudentDescriptor = new EditStudentDescriptor(editStudentDescriptor);
+ logger.info("EditStudentCommand created for student index: " + index.getOneBased());
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ logger.info("Executing EditStudentCommand for student index: " + index.getOneBased());
+
+ List lastShownList = model.getFilteredStudentList();
+
+ if (index.getZeroBased() >= lastShownList.size()) {
+ logger.warning("Invalid student index: " + index.getOneBased());
+ throw new CommandException(Messages.MESSAGE_INDEX_OUT_OF_BOUNDS);
+ }
+
+ Student studentToEdit = lastShownList.get(index.getZeroBased());
+ Student editedStudent = createEditedStudent(studentToEdit, editStudentDescriptor);
+ assert editedStudent != null : "Edited student should not be null";
+
+ boolean isDuplicate = model.getFilteredStudentList().stream()
+ .filter(student -> !student.equals(studentToEdit)) // exclude the student being edited
+ .anyMatch(student -> student.isSameStudent(editedStudent));
+
+ if (isDuplicate) {
+ logger.warning("Duplicate student detected: " + editedStudent.getName());
+ throw new CommandException(MESSAGE_DUPLICATE_STUDENT);
+ }
+
+ // Edit all lessons associated with the student
+ List lessonsToEdit = model.getFilteredLessonList()
+ .stream()
+ .filter(lesson -> lesson.getStudentName().equals(studentToEdit.getName()))
+ .toList();
+ if (editStudentDescriptor.getName().isPresent()) {
+ EditLessonDescriptor editLessonDescriptor = new EditLessonDescriptor();
+ editLessonDescriptor.setName(editStudentDescriptor.getName().get());
+
+ logger.info("Editing " + lessonsToEdit.size() + " associated lessons");
+ lessonsToEdit.forEach(lesson -> {
+ Lesson editedLesson = createEditedLesson(lesson, editLessonDescriptor);
+ model.setLesson(lesson, editedLesson);
+ logger.fine("Edited lesson: " + lesson);
+ });
+ }
+
+ model.setStudent(studentToEdit, editedStudent);
+ model.updateFilteredStudentList(Model.PREDICATE_SHOW_ALL_STUDENTS);
+
+ logger.info("Student successfully edited: " + editedStudent.getName());
+ return new CommandResult(String.format(MESSAGE_EDIT_STUDENT_SUCCESS,
+ Messages.format(editedStudent)), true);
+ }
+
+ /**
+ * Creates and returns a {@code Student} with the details of {@code studentToEdit}
+ * edited with {@code editStudentDescriptor}.
+ */
+ public static Student createEditedStudent(Student studentToEdit,
+ EditStudentDescriptor editStudentDescriptor) throws CommandException {
+ requireNonNull(studentToEdit);
+ requireNonNull(editStudentDescriptor);
+
+ if (!editStudentDescriptor.isAnyFieldEdited()) {
+ throw new CommandException(MESSAGE_NOT_EDITED);
+ }
+
+ Name updatedName = editStudentDescriptor.getName().orElse(studentToEdit.getName());
+ Phone updatedPhone = editStudentDescriptor.getPhone().orElse(studentToEdit.getPhone());
+ Email updatedEmail = editStudentDescriptor.getEmail().orElse(studentToEdit.getEmail());
+ Address updatedAddress = editStudentDescriptor.getAddress().orElse(studentToEdit.getAddress());
+ Set updatedSubjects = editStudentDescriptor.getSubjects().orElse(studentToEdit.getSubjects());
+ UniqueAssignmentList updatedAssignments = editStudentDescriptor.getAssignments()
+ .orElse(studentToEdit.getAssignments());
+
+ return new Student(updatedName, updatedPhone, updatedEmail, updatedAddress,
+ updatedSubjects, updatedAssignments);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditStudentCommand)) {
+ return false;
+ }
+
+ EditStudentCommand otherEditStudentCommand = (EditStudentCommand) other;
+ return index.equals(otherEditStudentCommand.index)
+ && editStudentDescriptor.equals(otherEditStudentCommand.editStudentDescriptor);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("index", index)
+ .add("editStudentDescriptor", editStudentDescriptor)
+ .toString();
+ }
+
+ /**
+ * Stores the details to edit the student with. Each non-empty field assignmentName will replace the
+ * corresponding field assignmentName of the student.
+ */
+ public static class EditStudentDescriptor {
+ private Name name;
+ private Phone phone;
+ private Email email;
+ private Address address;
+ private Set subjects;
+ private UniqueAssignmentList assignments;
+
+ public EditStudentDescriptor() {}
+
+ /**
+ * Copy constructor.
+ * A defensive copy of {@code subjects} is used internally.
+ */
+ public EditStudentDescriptor(EditStudentDescriptor toCopy) {
+ setName(toCopy.name);
+ setPhone(toCopy.phone);
+ setEmail(toCopy.email);
+ setAddress(toCopy.address);
+ setSubjects(toCopy.subjects);
+ setAssignments(toCopy.assignments);
+ }
+
+
+
+ /**
+ * Returns true if at least one field is edited.
+ */
+ public boolean isAnyFieldEdited() {
+ return CollectionUtil.isAnyNonNull(name, phone, email, address,
+ subjects);
+ }
+
+ public void setName(Name name) {
+ this.name = name;
+ }
+
+ public Optional getName() {
+ return Optional.ofNullable(name);
+ }
+
+ public void setPhone(Phone phone) {
+ this.phone = phone;
+ }
+
+ public Optional getPhone() {
+ return Optional.ofNullable(phone);
+ }
+
+ public void setEmail(Email email) {
+ this.email = email;
+ }
+
+ public Optional getEmail() {
+ return Optional.ofNullable(email);
+ }
+
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+
+ public Optional getAddress() {
+ return Optional.ofNullable(address);
+ }
+
+ /**
+ * Sets {@code subjects} to this object's {@code subjects}.
+ * A defensive copy of {@code subjects} is used internally.
+ */
+ public void setSubjects(Set subjects) {
+ this.subjects = (subjects != null) ? new HashSet<>(subjects) : null;
+ }
+
+ /**
+ * Returns an unmodifiable subject set, which throws {@code UnsupportedOperationException}
+ * if modification is attempted.
+ * Returns {@code Optional#empty()} if {@code subjects} is null.
+ */
+ public Optional> getSubjects() {
+ return (subjects != null) ? Optional.of(Collections.unmodifiableSet(subjects)) : Optional.empty();
+ }
+
+ private void setAssignments(UniqueAssignmentList assignments) {
+ this.assignments = assignments;
+ }
+
+ public Optional getAssignments() {
+ return Optional.ofNullable(assignments);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditStudentDescriptor)) {
+ return false;
+ }
+
+ EditStudentDescriptor otherEditStudentDescriptor = (EditStudentDescriptor) other;
+ return Objects.equals(name, otherEditStudentDescriptor.name)
+ && Objects.equals(phone, otherEditStudentDescriptor.phone)
+ && Objects.equals(email, otherEditStudentDescriptor.email)
+ && Objects.equals(address, otherEditStudentDescriptor.address)
+ && Objects.equals(subjects, otherEditStudentDescriptor.subjects)
+ && Objects.equals(assignments, otherEditStudentDescriptor.assignments);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("name", name)
+ .add("phone", phone)
+ .add("email", email)
+ .add("address", address)
+ .add("subjects", subjects)
+ .add("assignments", assignments)
+ .toString();
+ }
+
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java
index 3dd85a8ba90..ad35be2243a 100644
--- a/src/main/java/seedu/address/logic/commands/ExitCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java
@@ -1,5 +1,8 @@
package seedu.address.logic.commands;
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
import seedu.address.model.Model;
/**
@@ -8,11 +11,25 @@
public class ExitCommand extends Command {
public static final String COMMAND_WORD = "exit";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Exits the application.\n"
+ + "Example: " + COMMAND_WORD;
public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ...";
+ private static final Logger logger = LogsCenter.getLogger(ExitCommand.class);
+
+ /**
+ * Creates an ExitCommand.
+ */
+ public ExitCommand() {
+ logger.info("ExitCommand created");
+ }
+
@Override
public CommandResult execute(Model model) {
+ logger.info("Executing ExitCommand");
+
+ logger.info("Application exit confirmed");
return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true);
}
diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java
deleted file mode 100644
index 72b9eddd3a7..00000000000
--- a/src/main/java/seedu/address/logic/commands/FindCommand.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-import seedu.address.model.Model;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
-
-/**
- * Finds and lists all persons in address book whose name contains any of the argument keywords.
- * Keyword matching is case insensitive.
- */
-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 "
- + "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";
-
- private final NameContainsKeywordsPredicate predicate;
-
- public FindCommand(NameContainsKeywordsPredicate predicate) {
- this.predicate = predicate;
- }
-
- @Override
- public CommandResult execute(Model model) {
- requireNonNull(model);
- model.updateFilteredPersonList(predicate);
- return new CommandResult(
- String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size()));
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof FindCommand)) {
- return false;
- }
-
- FindCommand otherFindCommand = (FindCommand) other;
- return predicate.equals(otherFindCommand.predicate);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("predicate", predicate)
- .toString();
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/FindStudentCommand.java b/src/main/java/seedu/address/logic/commands/FindStudentCommand.java
new file mode 100644
index 00000000000..42763c6cef0
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/FindStudentCommand.java
@@ -0,0 +1,72 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.student.NameContainsKeywordsPredicate;
+
+/**
+ * Finds and lists all students in address book whose name contains any of the argument keywords.
+ * Keyword matching is case-insensitive.
+ */
+public class FindStudentCommand extends Command {
+
+ public static final String COMMAND_WORD = "find_student";
+ private static final Logger logger = Logger.getLogger(FindStudentCommand.class.getName());
+
+
+ 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";
+
+ private final NameContainsKeywordsPredicate predicate;
+
+ /**
+ * Constructs new FindStudentCommand with given predicate
+ */
+ public FindStudentCommand(NameContainsKeywordsPredicate predicate) {
+ this.predicate = predicate;
+ logger.log(Level.FINE, "FindStudentCommand created with predicate: {0}", predicate);
+ }
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ logger.log(Level.INFO, "Executing FindStudentCommand with predicate: {0}", predicate);
+
+ model.updateFilteredStudentList(predicate);
+ int filteredListSize = model.getFilteredStudentList().size();
+
+ logger.log(Level.INFO, "FindStudentCommand found {0} students matching the criteria", filteredListSize);
+ return new CommandResult(
+ String.format(Messages.MESSAGE_STUDENTS_LISTED_OVERVIEW, model.getFilteredStudentList().size()), true);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof FindStudentCommand)) {
+ return false;
+ }
+
+ FindStudentCommand otherFindStudentCommand = (FindStudentCommand) other;
+ return predicate.equals(otherFindStudentCommand.predicate);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("predicate", predicate)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java
index bf824f91bd0..fb8232106df 100644
--- a/src/main/java/seedu/address/logic/commands/HelpCommand.java
+++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java
@@ -1,5 +1,8 @@
package seedu.address.logic.commands;
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
import seedu.address.model.Model;
/**
@@ -12,10 +15,31 @@ public class HelpCommand extends Command {
public static final String MESSAGE_USAGE = COMMAND_WORD + ": Shows program usage instructions.\n"
+ "Example: " + COMMAND_WORD;
- public static final String SHOWING_HELP_MESSAGE = "Opened help window.";
+ public static final String GENERAL_HELP_MESSAGE =
+ "Available commands (non-exhaustive):\n"
+ + "1. add_student - Add a new student\n"
+ + "2. list_students - List all students\n"
+ + "3. delete_student - Delete a student\n"
+ + "4. edit_student - Edit a student\n"
+ + "5. add_assignment - Add an assignment\n"
+ + "6. mark_assignment - Mark assignment as complete\n"
+ + "7. clear - Clear all data\n"
+ + "8. exit - Exit the program\n"
+ + "9. help - Show this help\n";
+ private static final Logger logger = LogsCenter.getLogger(HelpCommand.class);
+
+ /**
+ * Creates a HelpCommand for general help information.
+ */
+ public HelpCommand() {
+ logger.info("HelpCommand created for general help");
+ }
@Override
public CommandResult execute(Model model) {
- return new CommandResult(SHOWING_HELP_MESSAGE, true, false);
+ logger.info("Executing HelpCommand");
+
+ logger.info("Showing general help");
+ return new CommandResult(GENERAL_HELP_MESSAGE, true, false);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java
deleted file mode 100644
index 84be6ad2596..00000000000
--- a/src/main/java/seedu/address/logic/commands/ListCommand.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
-
-import seedu.address.model.Model;
-
-/**
- * Lists all persons in the address book to the user.
- */
-public class ListCommand extends Command {
-
- public static final String COMMAND_WORD = "list";
-
- public static final String MESSAGE_SUCCESS = "Listed all persons";
-
-
- @Override
- public CommandResult execute(Model model) {
- requireNonNull(model);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(MESSAGE_SUCCESS);
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/ListLessonsCommand.java b/src/main/java/seedu/address/logic/commands/ListLessonsCommand.java
new file mode 100644
index 00000000000..6ab798939cb
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ListLessonsCommand.java
@@ -0,0 +1,122 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.commands.AddLessonCommand.VALID_ADDRESS;
+import static seedu.address.logic.commands.AddLessonCommand.VALID_EMAIL;
+import static seedu.address.logic.commands.AddLessonCommand.VALID_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.function.Predicate;
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.assignment.UniqueAssignmentList;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.student.Name;
+import seedu.address.model.student.Student;
+
+/**
+ * Finds and lists all lessons in address book whose student name contains any of the argument keywords.
+ * Keyword matching is case insensitive.
+ */
+public class ListLessonsCommand extends Command {
+
+ public static final String COMMAND_WORD = "list_lessons";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lists all lessons. "
+ + "Can optionally filter by student name.\n"
+ + "Format: " + COMMAND_WORD + " [" + PREFIX_NAME + "STUDENT_NAME]\n"
+ + "Examples:\n"
+ + "1. " + COMMAND_WORD + " " + PREFIX_NAME + "alice - Shows lessons for students named 'alice'\n"
+ + "2. " + COMMAND_WORD + " - Shows all lessons";
+
+ public static final String MESSAGE_STUDENT_NOT_FOUND = "Error: The specified "
+ + "student does not exist in the address book";
+ public static final String MESSAGE_NO_LESSONS_FOUND = "Error: No lessons found matching the criteria";
+
+ private static final Logger logger = LogsCenter.getLogger(ListLessonsCommand.class);
+
+ private final Predicate predicate;
+ private final Optional studentName;
+
+ /**
+ * Constructor for the ListLessonsCommand
+ * @param predicate Predicate to filter the UniqueLessonList with
+ */
+ public ListLessonsCommand(Predicate predicate) {
+ this.predicate = predicate;
+ this.studentName = Optional.empty();
+ logger.info("ListLessonsCommand created with predicate filter");
+ }
+
+ /**
+ * Constructor for the ListLessonsCommand with student name
+ * @param predicate
+ * @param studentName
+ */
+ public ListLessonsCommand(Predicate predicate, Name studentName) {
+ this.predicate = predicate;
+ this.studentName = Optional.of(studentName);
+ logger.info("ListLessonsCommand created for student: " + studentName);
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ logger.info("Executing ListLessonsCommand");
+
+ // Validate student exists if name was specified
+ if (studentName.isPresent()) {
+ Student dummyStudent = new Student(studentName.get(), VALID_PHONE, VALID_EMAIL,
+ VALID_ADDRESS, new HashSet<>(), new UniqueAssignmentList());
+
+ if (!model.hasStudent(dummyStudent)) {
+ logger.warning("Student not found: " + studentName.get());
+ throw new CommandException(MESSAGE_STUDENT_NOT_FOUND);
+ }
+ }
+
+ model.updateFilteredLessonList(predicate);
+ int lessonCount = model.getFilteredLessonList().size();
+
+ if (lessonCount == 0) {
+ logger.info("No lessons found matching the criteria");
+ return new CommandResult(MESSAGE_NO_LESSONS_FOUND, true);
+ }
+
+ logger.info("Found " + lessonCount + " lessons matching the criteria");
+ return new CommandResult(
+ String.format(Messages.MESSAGE_LESSONS_LISTED_OVERVIEW, lessonCount),
+ true);
+ }
+
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof ListLessonsCommand)) {
+ return false;
+ }
+
+ ListLessonsCommand otherFindStudentCommand = (ListLessonsCommand) other;
+ return predicate.equals(otherFindStudentCommand.predicate)
+ && studentName.equals(otherFindStudentCommand.studentName);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("predicate", predicate)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ListStudentsCommand.java b/src/main/java/seedu/address/logic/commands/ListStudentsCommand.java
new file mode 100644
index 00000000000..77c25ebdd90
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ListStudentsCommand.java
@@ -0,0 +1,43 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
+
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.model.Model;
+
+/**
+ * Lists all students in the address book to the user.
+ */
+public class ListStudentsCommand extends Command {
+
+ public static final String COMMAND_WORD = "list_students";
+
+ public static final String MESSAGE_SUCCESS = "Listed all students";
+ public static final String MESSAGE_EMPTY_LIST = "No students found";
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Lists all students in the address book.\n"
+ + "Example: " + COMMAND_WORD;
+
+ private static final Logger logger = LogsCenter.getLogger(ListStudentsCommand.class);
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ logger.info("Executing ListStudentsCommand");
+
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+ int studentCount = model.getFilteredStudentList().size();
+
+ if (studentCount == 0) {
+ logger.info("Address book is empty - no students found");
+ return new CommandResult(MESSAGE_EMPTY_LIST, true);
+ }
+
+ logger.info("Listed " + studentCount + " students");
+ return new CommandResult(
+ String.format(MESSAGE_SUCCESS, studentCount),
+ true);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/MarkAssignmentCommand.java b/src/main/java/seedu/address/logic/commands/MarkAssignmentCommand.java
new file mode 100644
index 00000000000..061d161d078
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/MarkAssignmentCommand.java
@@ -0,0 +1,121 @@
+package seedu.address.logic.commands;
+
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ASSIGNMENT;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.assignment.Assignment;
+import seedu.address.model.datetimeutil.Date;
+import seedu.address.model.student.Student;
+
+/**
+ * Marks an assignment of a student identified using it's displayed index from the address book.
+ */
+public class MarkAssignmentCommand extends Command {
+
+ public static final String COMMAND_WORD = "mark_assignment";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Marks the assignment identified by the index number used in the displayed assignment list.\n"
+ + "Parameters: STUDENT INDEX (must be a positive integer) "
+ + PREFIX_ASSIGNMENT + "Assignment Name\n"
+ + "Example: " + COMMAND_WORD + " 1 "
+ + PREFIX_ASSIGNMENT + "Assignment 1";
+
+ public static final String MESSAGE_MARK_ASSIGNMENT_SUCCESS = "Assignment \"%1$s\" marked as completed for %2$s";
+ public static final String MESSAGE_EMPTY_STUDENT_LIST = "There are no students in the address book";
+
+ private static final Logger logger = LogsCenter.getLogger(MarkAssignmentCommand.class);
+ private final Index studentIndex;
+ private final String assignmentName;
+
+ /**
+ * Creates a MarkAssignmentCommand to mark the specified assignment as completed.
+ *
+ * @param studentIndex Index of the student in the filtered student list
+ * @param assignmentName Name of the assignment to mark
+ */
+ public MarkAssignmentCommand(Index studentIndex, String assignmentName) {
+ requireAllNonNull(studentIndex, assignmentName);
+ this.studentIndex = studentIndex;
+ this.assignmentName = Arrays.stream(assignmentName.split("\\s+"))
+ .map(word -> word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase())
+ .collect(Collectors.joining(" ")).trim();
+ logger.info(String.format("Created MarkAssignmentCommand for student index %d, assignment: %s",
+ studentIndex.getOneBased(), assignmentName));
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ List lastShownList = model.getFilteredStudentList();
+ logger.info("Executing MarkAssignmentCommand");
+
+ List studentList = model.getFilteredStudentList();
+
+ if (studentList.isEmpty()) {
+ logger.warning("Attempted to mark assignment in empty student list");
+ throw new CommandException(MESSAGE_EMPTY_STUDENT_LIST);
+ }
+
+ if (studentIndex.getZeroBased() >= studentList.size()) {
+ logger.warning(String.format("Invalid student index: %d (list size: %d)",
+ studentIndex.getOneBased(), studentList.size()));
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ }
+
+ Student student = studentList.get(studentIndex.getZeroBased());
+ assert student != null : "Student should not be null";
+
+ Assignment targetAssignment = new Assignment(assignmentName, new Date("31-12-9999"));
+ if (!student.getAssignments().contains(targetAssignment)) {
+ logger.warning(String.format("Assignment not found: %s for student %s",
+ assignmentName, student.getName()));
+ throw new CommandException(String.format(Messages.MESSAGE_ASSIGNMENT_NOT_FOUND, assignmentName));
+ }
+
+ // Mark the assignment
+ Student updatedStudent = student.markAssignment(assignmentName);
+ model.setStudent(student, updatedStudent);
+ model.updateFilteredStudentList(Model.PREDICATE_SHOW_ALL_STUDENTS);
+
+ logger.info(String.format("Successfully marked assignment %s for student %s",
+ assignmentName, student.getName()));
+ return new CommandResult(
+ String.format(MESSAGE_MARK_ASSIGNMENT_SUCCESS, assignmentName, student.getName()),
+ true);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof MarkAssignmentCommand)) {
+ return false;
+ }
+
+ MarkAssignmentCommand otherCommand = (MarkAssignmentCommand) other;
+ return studentIndex.equals(otherCommand.studentIndex)
+ && assignmentName.equals(otherCommand.assignmentName);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("studentIndex", studentIndex)
+ .add("assignmentName", assignmentName)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/UnmarkAssignmentCommand.java b/src/main/java/seedu/address/logic/commands/UnmarkAssignmentCommand.java
new file mode 100644
index 00000000000..d02b3490112
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/UnmarkAssignmentCommand.java
@@ -0,0 +1,127 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ASSIGNMENT;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.assignment.Assignment;
+import seedu.address.model.datetimeutil.Date;
+import seedu.address.model.student.Student;
+
+/**
+ * Unmarks an assignment of a student identified using it's displayed index from the address book.
+ */
+public class UnmarkAssignmentCommand extends Command {
+
+ public static final String COMMAND_WORD = "unmark_assignment";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Unmarks the assignment identified by the index number used in the displayed assignment list.\n"
+ + "Parameters: STUDENT INDEX (must be a positive integer) "
+ + PREFIX_ASSIGNMENT + "Assignment Name\n"
+ + "Example: " + COMMAND_WORD + " 1 "
+ + PREFIX_ASSIGNMENT + "Assignment 1";
+
+ public static final String MESSAGE_SUCCESS = "Assignment \"%1$s\" unmarked as incomplete for %2$s";
+ public static final String MESSAGE_EMPTY_STUDENT_LIST = "There are no students in the address book";
+
+ private static final Logger logger = LogsCenter.getLogger(UnmarkAssignmentCommand.class);
+
+ private final Index studentIndex;
+ private final String assignmentName;
+
+ /**
+ * Creates an UnmarkAssignmentCommand to unmark the specified assignment.
+ *
+ * @param studentIndex Index of the student in the filtered student list
+ * @param assignmentName Name of the assignment to unmark
+ */
+ public UnmarkAssignmentCommand(Index studentIndex, String assignmentName) {
+ requireAllNonNull(studentIndex, assignmentName);
+ this.studentIndex = studentIndex;
+ this.assignmentName = Arrays.stream(assignmentName.split("\\s+"))
+ .map(word -> word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase())
+ .collect(Collectors.joining(" ")).trim();
+ logger.info(String.format("Created UnmarkAssignmentCommand for student index %d, assignment: %s",
+ studentIndex.getOneBased(), assignmentName));
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ logger.info("Executing UnmarkAssignmentCommand");
+
+ List studentList = model.getFilteredStudentList();
+
+ // Validate student list
+ if (studentList.isEmpty()) {
+ logger.warning("Attempted to unmark assignment in empty student list");
+ throw new CommandException(MESSAGE_EMPTY_STUDENT_LIST);
+ }
+
+ // Validate student index
+ if (studentIndex.getZeroBased() >= studentList.size()) {
+ logger.warning(String.format("Invalid student index: %d (list size: %d)",
+ studentIndex.getOneBased(), studentList.size()));
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ }
+
+ Student student = studentList.get(studentIndex.getZeroBased());
+ assert student != null : "Student should not be null";
+
+ // Check if the assignment exists
+ Assignment targetAssignment = new Assignment(assignmentName, new Date("31-12-9999"));
+ if (!student.getAssignments().contains(targetAssignment)) {
+ logger.warning(String.format("Assignment not found: %s for student %s",
+ assignmentName, student.getName()));
+ throw new CommandException(String.format(Messages.MESSAGE_ASSIGNMENT_NOT_FOUND, assignmentName));
+ }
+
+ // Unmark the assignment
+ Student updatedStudent = student.unmarkAssignment(assignmentName);
+ model.setStudent(student, updatedStudent);
+ model.updateFilteredStudentList(Model.PREDICATE_SHOW_ALL_STUDENTS);
+
+ logger.info(String.format("Successfully unmarked assignment %s for student %s",
+ assignmentName, student.getName()));
+
+ return new CommandResult(
+ String.format(MESSAGE_SUCCESS, assignmentName, student.getName()),
+ true);
+ }
+
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof UnmarkAssignmentCommand)) {
+ return false;
+ }
+
+ UnmarkAssignmentCommand otherCommand = (UnmarkAssignmentCommand) other;
+ return studentIndex.equals(otherCommand.studentIndex)
+ && assignmentName.equals(otherCommand.assignmentName);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("studentIndex", studentIndex)
+ .add("assignmentName", assignmentName)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddAssignmentCommandParser.java b/src/main/java/seedu/address/logic/parser/AddAssignmentCommandParser.java
new file mode 100644
index 00000000000..d38e2748b9e
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/AddAssignmentCommandParser.java
@@ -0,0 +1,57 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ASSIGNMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.AddAssignmentCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.assignment.Assignment;
+import seedu.address.model.datetimeutil.Date;
+
+/**
+ * Parses input arguments and creates a new AddAssignmentCommand object
+ */
+public class AddAssignmentCommandParser implements Parser {
+ private static final Logger logger = LogsCenter.getLogger(AddAssignmentCommandParser.class);
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the AddAssignmentCommand
+ * and returns an AddAssignmentCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public AddAssignmentCommand parse(String args) throws ParseException {
+ assert args != null : "Input arguments string cannot be null";
+ logger.info("Parsing AddAssignmentCommand with arguments: " + args);
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_ASSIGNMENT, PREFIX_DATE);
+
+ if (!argMultimap.arePrefixesPresent(PREFIX_ASSIGNMENT, PREFIX_DATE)
+ || argMultimap.getPreamble().isEmpty()) {
+ logger.warning("Invalid command format for AddAssignmentCommand: " + args);
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddAssignmentCommand.MESSAGE_USAGE));
+ }
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_ASSIGNMENT, PREFIX_DATE);
+
+ try {
+ Index index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ String assignmentName = ParserUtil.parseAssignmentName(argMultimap.getValue(PREFIX_ASSIGNMENT).orElse(""));
+ Date dueDate = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).orElse(""));
+
+ assert assignmentName != null && !assignmentName.isEmpty() : "Assignment name cannot be null or empty";
+ assert dueDate != null : "Due date cannot be null";
+
+ Assignment assignment = new Assignment(assignmentName, dueDate);
+ logger.info("Successfully parsed AddAssignmentCommand with index: " + index.getOneBased()
+ + ", assignment: " + assignment);
+ return new AddAssignmentCommand(index, assignment);
+ } catch (ParseException pe) {
+ logger.warning("Error parsing AddAssignmentCommand: " + pe.getMessage());
+ throw pe;
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
deleted file mode 100644
index 4ff1a97ed77..00000000000
--- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.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_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-
-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.tag.Tag;
-
-/**
- * Parses input arguments and creates a new AddCommand object
- */
-public class AddCommandParser implements Parser {
-
- /**
- * Parses the given {@code String} of arguments in the context of the AddCommand
- * and returns an AddCommand object for execution.
- * @throws ParseException if the user input does not conform the expected format
- */
- public AddCommand parse(String args) throws ParseException {
- ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
-
- 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));
- }
-
- argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
- 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);
-
- return new AddCommand(person);
- }
-
- /**
- * Returns true if none of the prefixes contains empty {@code Optional} values 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/AddLessonCommandParser.java b/src/main/java/seedu/address/logic/parser/AddLessonCommandParser.java
new file mode 100644
index 00000000000..88ce0b1ad93
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/AddLessonCommandParser.java
@@ -0,0 +1,65 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SUBJECT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TIME;
+
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.logic.commands.AddLessonCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.datetimeutil.Date;
+import seedu.address.model.datetimeutil.Time;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.student.Name;
+import seedu.address.model.subject.Subject;
+
+/**
+ * Parses input arguments and creates a new AddLessonCommand object
+ */
+public class AddLessonCommandParser implements Parser {
+ private static final Logger logger = LogsCenter.getLogger(AddLessonCommandParser.class);
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the AddLessonCommand
+ * and returns an AddLessonCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public AddLessonCommand parse(String args) throws ParseException {
+ assert args != null : "Input arguments string cannot be null";
+ logger.info("Parsing AddLessonCommand with arguments: " + args);
+
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_DATE, PREFIX_TIME, PREFIX_SUBJECT);
+
+ if (!argMultimap.arePrefixesPresent(PREFIX_NAME, PREFIX_DATE, PREFIX_TIME, PREFIX_SUBJECT)
+ || !argMultimap.getPreamble().isEmpty()) {
+ logger.warning("Invalid command format for AddLessonCommand: " + args);
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddLessonCommand.MESSAGE_USAGE));
+ }
+
+ try {
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_DATE, PREFIX_TIME, PREFIX_SUBJECT);
+ Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get());
+ Date date = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get());
+ Time time = ParserUtil.parseTime(argMultimap.getValue(PREFIX_TIME).get());
+ Subject subject = ParserUtil.parseSubject(argMultimap.getValue(PREFIX_SUBJECT).get());
+
+ assert name != null : "Name cannot be null";
+ assert date != null : "Date cannot be null";
+ assert time != null : "Time cannot be null";
+ assert subject != null : "Subject cannot be null";
+
+ Lesson lesson = new Lesson(subject, name, date, time);
+ logger.info("Successfully parsed AddLessonCommand: " + lesson);
+ return new AddLessonCommand(lesson);
+ } catch (ParseException pe) {
+ logger.warning("Error parsing AddLessonCommand: " + pe.getMessage());
+ throw pe;
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddStudentCommandParser.java b/src/main/java/seedu/address/logic/parser/AddStudentCommandParser.java
new file mode 100644
index 00000000000..9bb0d5ac3b2
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/AddStudentCommandParser.java
@@ -0,0 +1,76 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.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_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SUBJECT;
+
+import java.util.Set;
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.logic.commands.AddStudentCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.assignment.UniqueAssignmentList;
+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.subject.Subject;
+
+/**
+ * Parses input arguments and creates a new AddStudentCommand object
+ */
+public class AddStudentCommandParser implements Parser {
+ private static final Logger logger = LogsCenter.getLogger(AddStudentCommandParser.class);
+ /**
+ * Parses the given {@code String} of arguments in the context of the AddStudentCommand
+ * and returns an AddStudentCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public AddStudentCommand parse(String args) throws ParseException {
+ assert args != null : "Input arguments string cannot be null";
+ logger.info("Parsing AddStudentCommand with arguments: " + args);
+
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL,
+ PREFIX_ADDRESS, PREFIX_SUBJECT);
+
+ if (!argMultimap.arePrefixesPresent(PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL)
+ || !argMultimap.getPreamble().isEmpty()) {
+ logger.warning("Invalid command format for AddStudentCommand: " + args);
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddStudentCommand.MESSAGE_USAGE));
+ }
+
+ try {
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
+ 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 subjectList = ParserUtil.parseSubjects(argMultimap.getAllValues(PREFIX_SUBJECT));
+
+ assert name != null : "Name cannot be null";
+ assert phone != null : "Phone cannot be null";
+ assert email != null : "Email cannot be null";
+ assert address != null : "Address cannot be null";
+ assert subjectList != null : "Subject list cannot be null";
+
+ Student student = new Student(name, phone, email, address,
+ subjectList,
+ new UniqueAssignmentList());
+ logger.info("Successfully parsed AddStudentCommand for student: " + name);
+ return new AddStudentCommand(student);
+ } catch (ParseException pe) {
+ logger.warning("Error parsing AddStudentCommand: " + pe.getMessage());
+ throw pe;
+ } catch (NullPointerException npe) {
+ logger.severe("Null value encountered while parsing AddStudentCommand");
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddStudentCommand.MESSAGE_USAGE));
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
index 3149ee07e0b..b46af733b90 100644
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
@@ -8,15 +8,24 @@
import java.util.regex.Pattern;
import seedu.address.commons.core.LogsCenter;
-import seedu.address.logic.commands.AddCommand;
+import seedu.address.logic.commands.AddAssignmentCommand;
+import seedu.address.logic.commands.AddLessonCommand;
+import seedu.address.logic.commands.AddStudentCommand;
import seedu.address.logic.commands.ClearCommand;
import seedu.address.logic.commands.Command;
-import seedu.address.logic.commands.DeleteCommand;
-import seedu.address.logic.commands.EditCommand;
+import seedu.address.logic.commands.DeleteAssignmentCommand;
+import seedu.address.logic.commands.DeleteLessonCommand;
+import seedu.address.logic.commands.DeleteStudentCommand;
+import seedu.address.logic.commands.EditAssignmentCommand;
+import seedu.address.logic.commands.EditLessonCommand;
+import seedu.address.logic.commands.EditStudentCommand;
import seedu.address.logic.commands.ExitCommand;
-import seedu.address.logic.commands.FindCommand;
+import seedu.address.logic.commands.FindStudentCommand;
import seedu.address.logic.commands.HelpCommand;
-import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.ListLessonsCommand;
+import seedu.address.logic.commands.ListStudentsCommand;
+import seedu.address.logic.commands.MarkAssignmentCommand;
+import seedu.address.logic.commands.UnmarkAssignmentCommand;
import seedu.address.logic.parser.exceptions.ParseException;
/**
@@ -38,38 +47,76 @@ public class AddressBookParser {
* @throws ParseException if the user input does not conform the expected format
*/
public Command parseCommand(String userInput) throws ParseException {
+ assert userInput != null : "User input cannot be null";
+ logger.info("Starting to parse command: " + userInput);
+
final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim());
if (!matcher.matches()) {
+ logger.warning("Failed to parse command - invalid format: " + userInput);
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE));
}
final String commandWord = matcher.group("commandWord");
final String arguments = matcher.group("arguments");
- // Note to developers: Change the log level in config.json to enable lower level (i.e., FINE, FINER and lower)
- // log messages such as the one below.
- // Lower level log messages are used sparingly to minimize noise in the code.
- logger.fine("Command word: " + commandWord + "; Arguments: " + arguments);
+ logger.fine("Parsed command word: " + commandWord + " with arguments: " + arguments);
+
+ try {
+ Command command = parseCommandByWord(commandWord, arguments);
+ logger.info("Successfully parsed command: " + commandWord);
+ return command;
+ } catch (ParseException pe) {
+ logger.warning("Failed to parse command '" + commandWord + "': " + pe.getMessage());
+ throw pe;
+ } catch (NullPointerException npe) {
+ logger.severe("Null pointer encountered while parsing command '" + commandWord + "'");
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE));
+ }
+ }
+
+ /**
+ * Parses the command based on the command word.
+ */
+ private Command parseCommandByWord(String commandWord, String arguments) throws ParseException {
+ assert commandWord != null : "Command word cannot be null";
+ assert arguments != null : "Arguments cannot be null";
switch (commandWord) {
+ case AddStudentCommand.COMMAND_WORD:
+ return new AddStudentCommandParser().parse(arguments);
+
+ case AddLessonCommand.COMMAND_WORD:
+ return new AddLessonCommandParser().parse(arguments);
- case AddCommand.COMMAND_WORD:
- return new AddCommandParser().parse(arguments);
+ case AddAssignmentCommand.COMMAND_WORD:
+ return new AddAssignmentCommandParser().parse(arguments);
- case EditCommand.COMMAND_WORD:
- return new EditCommandParser().parse(arguments);
+ case EditLessonCommand.COMMAND_WORD:
+ return new EditLessonCommandParser().parse(arguments);
- case DeleteCommand.COMMAND_WORD:
- return new DeleteCommandParser().parse(arguments);
+ case EditStudentCommand.COMMAND_WORD:
+ return new EditStudentCommandParser().parse(arguments);
+
+ case DeleteLessonCommand.COMMAND_WORD:
+ return new DeleteLessonCommandParser().parse(arguments);
+
+ case DeleteStudentCommand.COMMAND_WORD:
+ return new DeleteStudentCommandParser().parse(arguments);
+
+ case DeleteAssignmentCommand.COMMAND_WORD:
+ return new DeleteAssignmentCommandParser().parse(arguments);
case ClearCommand.COMMAND_WORD:
return new ClearCommand();
- case FindCommand.COMMAND_WORD:
- return new FindCommandParser().parse(arguments);
+ case FindStudentCommand.COMMAND_WORD:
+ return new FindStudentCommandParser().parse(arguments);
+
+ case ListStudentsCommand.COMMAND_WORD:
+ return new ListStudentsCommand();
- case ListCommand.COMMAND_WORD:
- return new ListCommand();
+ case ListLessonsCommand.COMMAND_WORD:
+ return new ListLessonsCommandParser().parse(arguments);
case ExitCommand.COMMAND_WORD:
return new ExitCommand();
@@ -77,10 +124,18 @@ public Command parseCommand(String userInput) throws ParseException {
case HelpCommand.COMMAND_WORD:
return new HelpCommand();
+ case MarkAssignmentCommand.COMMAND_WORD:
+ return new MarkAssignmentCommandParser().parse(arguments);
+
+ case UnmarkAssignmentCommand.COMMAND_WORD:
+ return new UnmarkAssignmentCommandParser().parse(arguments);
+
+ case EditAssignmentCommand.COMMAND_WORD:
+ return new EditAssignmentCommandParser().parse(arguments);
+
default:
- logger.finer("This user input caused a ParseException: " + userInput);
+ logger.warning("Unknown command encountered: " + commandWord);
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
}
}
-
}
diff --git a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java b/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java
index 21e26887a83..392123f1de6 100644
--- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java
+++ b/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java
@@ -14,8 +14,8 @@
* Stores mapping of prefixes to their respective arguments.
* Each key may be associated with multiple argument values.
* Values for a given key are stored in a list, and the insertion ordering is maintained.
- * Keys are unique, but the list of argument values may contain duplicate argument values, i.e. the same argument value
- * can be inserted multiple times for the same prefix.
+ * Keys are unique, but the list of argument values may contain duplicate argument values,
+ * i.e. the same argument value can be inserted multiple times for the same prefix.
*/
public class ArgumentMultimap {
@@ -75,4 +75,16 @@ public void verifyNoDuplicatePrefixesFor(Prefix... prefixes) throws ParseExcepti
throw new ParseException(Messages.getErrorMessageForDuplicatePrefixes(duplicatedPrefixes));
}
}
+
+ /**
+ * Returns true if the prefixes are present in the argument multimap.
+ */
+ public boolean arePrefixesPresent(Prefix... prefixes) {
+ for (Prefix prefix : prefixes) {
+ if (!argMultimap.containsKey(prefix) || argMultimap.get(prefix).isEmpty()) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
index 5c9aebfa488..e5d54891b43 100644
--- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
+++ b/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
@@ -121,7 +121,7 @@ private static String extractArgumentValue(String argsString,
int valueStartPos = currentPrefixPosition.getStartPosition() + prefix.getPrefix().length();
String value = argsString.substring(valueStartPos, nextPrefixPosition.getStartPosition());
- return value.trim();
+ return value.trim().replaceAll("\\s+", " ");
}
/**
diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java
index 75b1a9bf119..95315c87780 100644
--- a/src/main/java/seedu/address/logic/parser/CliSyntax.java
+++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java
@@ -10,6 +10,10 @@ public class CliSyntax {
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_SUBJECT = new Prefix("s/");
+ public static final Prefix PREFIX_ASSIGNMENT = new Prefix("as/");
+ public static final Prefix PREFIX_DATE = new Prefix("d/");
+ public static final Prefix PREFIX_TIME = new Prefix("t/");
+ public static final Prefix PREFIX_NEW_ASSIGNMENT = new Prefix("nas/");
}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteAssignmentCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteAssignmentCommandParser.java
new file mode 100644
index 00000000000..f4e4d9ea2d0
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/DeleteAssignmentCommandParser.java
@@ -0,0 +1,44 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ASSIGNMENT;
+
+import java.util.stream.Stream;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.DeleteAssignmentCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new DeleteStudentCommand object
+ */
+public class DeleteAssignmentCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the DeleteStudentCommand
+ * and returns a DeleteStudentCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public DeleteAssignmentCommand parse(String args) throws ParseException {
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_ASSIGNMENT);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_ASSIGNMENT)
+ || argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ DeleteAssignmentCommand.MESSAGE_USAGE));
+ }
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_ASSIGNMENT);
+
+ Index index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ String assignmentName = ParserUtil.parseAssignmentName(argMultimap.getValue(PREFIX_ASSIGNMENT).get());
+ return new DeleteAssignmentCommand(index, assignmentName);
+ }
+
+ /**
+ * Returns true if none of the prefixes contains empty {@code Optional} values 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/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
deleted file mode 100644
index 3527fe76a3e..00000000000
--- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.logic.commands.DeleteCommand;
-import seedu.address.logic.parser.exceptions.ParseException;
-
-/**
- * Parses input arguments and creates a new DeleteCommand object
- */
-public class DeleteCommandParser implements Parser {
-
- /**
- * Parses the given {@code String} of arguments in the context of the DeleteCommand
- * and returns a DeleteCommand object for execution.
- * @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);
- }
- }
-
-}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteLessonCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteLessonCommandParser.java
new file mode 100644
index 00000000000..8d2414b193f
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/DeleteLessonCommandParser.java
@@ -0,0 +1,55 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.DeleteLessonCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new DeleteLessonCommand object
+ */
+public class DeleteLessonCommandParser implements Parser {
+ private static final Logger logger = LogsCenter.getLogger(DeleteLessonCommandParser.class);
+
+ /**
+ * 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 {
+ logger.info("Attempting to parse DeleteLessonCommand with arguments: " + args);
+
+ if (args == null || args.trim().isEmpty()) {
+ logger.warning("Empty input received for DeleteLessonCommand");
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteLessonCommand.MESSAGE_USAGE));
+ }
+
+ try {
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args);
+
+ if (argMultimap.getPreamble().isEmpty()) {
+ logger.warning("Missing lesson index in DeleteLessonCommand");
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteLessonCommand.MESSAGE_USAGE));
+ }
+
+ Index index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ logger.info("Successfully parsed DeleteLessonCommand for lesson index: " + index.getOneBased());
+
+ return new DeleteLessonCommand(index);
+ } catch (ParseException pe) {
+ logger.warning("Failed to parse index in DeleteLessonCommand: " + pe.getMessage());
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteLessonCommand.MESSAGE_USAGE), pe);
+ } catch (Exception e) {
+ logger.severe("Unexpected error parsing DeleteLessonCommand: " + e.getMessage());
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteLessonCommand.MESSAGE_USAGE));
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteStudentCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteStudentCommandParser.java
new file mode 100644
index 00000000000..a3ca019a68b
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/DeleteStudentCommandParser.java
@@ -0,0 +1,46 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.DeleteStudentCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new DeleteStudentCommand object
+ */
+public class DeleteStudentCommandParser implements Parser {
+ private static final Logger logger = LogsCenter.getLogger(DeleteStudentCommandParser.class);
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the DeleteStudentCommand
+ * and returns a DeleteStudentCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public DeleteStudentCommand parse(String args) throws ParseException {
+ logger.info("Attempting to parse DeleteStudentCommand with arguments: " + args);
+
+ if (args == null || args.trim().isEmpty()) {
+ logger.warning("Empty input received for DeleteStudentCommand");
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteStudentCommand.MESSAGE_USAGE));
+ }
+
+ try {
+ Index index = ParserUtil.parseIndex(args.trim());
+ logger.info("Successfully parsed DeleteStudentCommand for student index: " + index.getOneBased());
+ return new DeleteStudentCommand(index);
+ } catch (ParseException pe) {
+ logger.warning("Failed to parse index in DeleteStudentCommand: " + pe.getMessage());
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteStudentCommand.MESSAGE_USAGE), pe);
+ } catch (Exception e) {
+ logger.severe("Unexpected error parsing DeleteStudentCommand: " + e.getMessage());
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteStudentCommand.MESSAGE_USAGE));
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/EditAssignmentCommandParser.java b/src/main/java/seedu/address/logic/parser/EditAssignmentCommandParser.java
new file mode 100644
index 00000000000..2c56b2a10af
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/EditAssignmentCommandParser.java
@@ -0,0 +1,92 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ASSIGNMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NEW_ASSIGNMENT;
+
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.EditAssignmentCommand;
+import seedu.address.logic.commands.EditAssignmentCommand.EditAssignmentDescriptor;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new EditAssignmentCommand object
+ */
+public class EditAssignmentCommandParser implements Parser {
+ private static final Logger logger = LogsCenter.getLogger(EditAssignmentCommandParser.class);
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the EditAssignmentCommand
+ * and returns an EditAssignmentCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public EditAssignmentCommand parse(String args) throws ParseException {
+ logger.info("Parsing EditAssignmentCommand with arguments: " + args);
+
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_ASSIGNMENT, PREFIX_NEW_ASSIGNMENT, PREFIX_DATE);
+
+ if (argMultimap.getPreamble().isEmpty()) {
+ logger.warning("Empty preamble in EditAssignmentCommand");
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ EditAssignmentCommand.MESSAGE_USAGE));
+ }
+
+ try {
+ Index index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ logger.fine("Parsed student index: " + index.getOneBased());
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_ASSIGNMENT, PREFIX_NEW_ASSIGNMENT, PREFIX_DATE);
+
+ String assignmentName = ParserUtil.parseAssignmentName(
+ argMultimap.getValue(PREFIX_ASSIGNMENT).orElseThrow(() -> {
+ logger.warning("Missing required assignment name");
+ return new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ EditAssignmentCommand.MESSAGE_USAGE));
+ }));
+ logger.fine("Parsed original assignment name: " + assignmentName);
+
+ EditAssignmentDescriptor editAssignmentDescriptor = new EditAssignmentDescriptor();
+ parseEditableFields(argMultimap, editAssignmentDescriptor);
+
+ if (!editAssignmentDescriptor.isAnyFieldEdited()) {
+ logger.warning("No editable fields provided in EditAssignmentCommand");
+ throw new ParseException(EditAssignmentCommand.MESSAGE_NOT_EDITED);
+ }
+
+ logger.info("Successfully parsed EditAssignmentCommand for student " + index.getOneBased()
+ + ", assignment: " + assignmentName);
+ return new EditAssignmentCommand(index, assignmentName, editAssignmentDescriptor);
+ } catch (ParseException pe) {
+ logger.warning("Parse error in EditAssignmentCommand: " + pe.getMessage());
+ throw pe;
+ } catch (Exception e) {
+ logger.severe("Unexpected error parsing EditAssignmentCommand: " + e.getMessage());
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ EditAssignmentCommand.MESSAGE_USAGE));
+ }
+ }
+
+ /**
+ * Parses the editable fields from the argument multimap into the edit descriptor.
+ */
+ private void parseEditableFields(ArgumentMultimap argMultimap,
+ EditAssignmentDescriptor descriptor) throws ParseException {
+ if (argMultimap.getValue(PREFIX_NEW_ASSIGNMENT).isPresent()) {
+ String newName = ParserUtil.parseAssignmentName(
+ argMultimap.getValue(PREFIX_NEW_ASSIGNMENT).get());
+ descriptor.setNewAssignmentName(newName);
+ logger.fine("Parsed new assignment name: " + newName);
+ }
+
+ if (argMultimap.getValue(PREFIX_DATE).isPresent()) {
+ String newDate = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get()).toString();
+ descriptor.setDate(ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get()));
+ logger.fine("Parsed new due date: " + newDate);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
deleted file mode 100644
index 46b3309a78b..00000000000
--- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package seedu.address.logic.parser;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.logic.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_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Optional;
-import java.util.Set;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.logic.commands.EditCommand;
-import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
-import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.tag.Tag;
-
-/**
- * Parses input arguments and creates a new EditCommand object
- */
-public class EditCommandParser implements Parser {
-
- /**
- * Parses the given {@code String} of arguments in the context of the EditCommand
- * and returns an EditCommand object for execution.
- * @throws ParseException if the user input does not conform the expected format
- */
- public EditCommand parse(String args) throws ParseException {
- requireNonNull(args);
- ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, 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);
- }
-
- argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
-
- EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
-
- if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
- editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()));
- }
- if (argMultimap.getValue(PREFIX_PHONE).isPresent()) {
- editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()));
- }
- if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
- editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()));
- }
- if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
- editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()));
- }
- parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags);
-
- if (!editPersonDescriptor.isAnyFieldEdited()) {
- throw new ParseException(EditCommand.MESSAGE_NOT_EDITED);
- }
-
- return new EditCommand(index, editPersonDescriptor);
- }
-
- /**
- * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty.
- * If {@code tags} contain only one element which is an empty string, it will be parsed into a
- * {@code Set} containing zero tags.
- */
- private Optional> parseTagsForEdit(Collection tags) throws ParseException {
- assert tags != null;
-
- if (tags.isEmpty()) {
- return Optional.empty();
- }
- Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags;
- return Optional.of(ParserUtil.parseTags(tagSet));
- }
-
-}
diff --git a/src/main/java/seedu/address/logic/parser/EditLessonCommandParser.java b/src/main/java/seedu/address/logic/parser/EditLessonCommandParser.java
new file mode 100644
index 00000000000..873f59e5508
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/EditLessonCommandParser.java
@@ -0,0 +1,89 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SUBJECT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TIME;
+
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.EditLessonCommand;
+import seedu.address.logic.commands.EditLessonCommand.EditLessonDescriptor;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new EditLessonCommand object
+ */
+public class EditLessonCommandParser implements Parser {
+ private static final Logger logger = LogsCenter.getLogger(EditLessonCommandParser.class);
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the EditLessonCommand
+ * and returns an EditLessonCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public EditLessonCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ logger.info("Parsing EditLessonCommand with arguments: " + args);
+
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_SUBJECT, PREFIX_NAME, PREFIX_DATE, PREFIX_TIME);
+
+ if (argMultimap.getPreamble().isEmpty()) {
+ logger.warning("Empty preamble in EditLessonCommand");
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ EditLessonCommand.MESSAGE_USAGE));
+ }
+
+ try {
+ Index index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ logger.fine("Parsed lesson index: " + index.getOneBased());
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_DATE, PREFIX_TIME, PREFIX_SUBJECT);
+
+ EditLessonDescriptor editLessonDescriptor = new EditLessonDescriptor();
+ parseFields(argMultimap, editLessonDescriptor);
+
+ if (!editLessonDescriptor.isAnyFieldEdited()) {
+ logger.warning("No fields edited in EditLessonCommand");
+ throw new ParseException(EditLessonCommand.MESSAGE_NOT_EDITED);
+ }
+
+ logger.info("Successfully parsed EditLessonCommand for lesson index: " + index.getOneBased());
+ return new EditLessonCommand(index, editLessonDescriptor);
+ } catch (ParseException pe) {
+ logger.warning("Error parsing EditLessonCommand: " + pe.getMessage());
+ throw pe;
+ } catch (Exception e) {
+ logger.severe("Unexpected error parsing EditLessonCommand: " + e.getMessage());
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ EditLessonCommand.MESSAGE_USAGE));
+ }
+ }
+
+ /**
+ * Parses individual fields from the argument multimap into the edit descriptor.
+ */
+ private void parseFields(ArgumentMultimap argMultimap, EditLessonDescriptor descriptor) throws ParseException {
+ if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
+ descriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()));
+ logger.fine("Parsed name field");
+ }
+ if (argMultimap.getValue(PREFIX_DATE).isPresent()) {
+ descriptor.setDate(ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get()));
+ logger.fine("Parsed date field");
+ }
+ if (argMultimap.getValue(PREFIX_TIME).isPresent()) {
+ descriptor.setTime(ParserUtil.parseTime(argMultimap.getValue(PREFIX_TIME).get()));
+ logger.fine("Parsed time field");
+ }
+ if (argMultimap.getValue(PREFIX_SUBJECT).isPresent()) {
+ descriptor.setSubject(ParserUtil.parseSubject(argMultimap.getValue(PREFIX_SUBJECT).get()));
+ logger.fine("Parsed subject field");
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/EditStudentCommandParser.java b/src/main/java/seedu/address/logic/parser/EditStudentCommandParser.java
new file mode 100644
index 00000000000..24bc81d9e51
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/EditStudentCommandParser.java
@@ -0,0 +1,95 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.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_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SUBJECT;
+
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.EditStudentCommand;
+import seedu.address.logic.commands.EditStudentCommand.EditStudentDescriptor;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new EditStudentCommand object
+ */
+public class EditStudentCommandParser implements Parser {
+ private static final Logger logger = LogsCenter.getLogger(EditStudentCommandParser.class);
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the EditStudentCommand
+ * and returns an EditStudentCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public EditStudentCommand parse(String args) throws ParseException {
+ assert args != null : "Input arguments string cannot be null";
+ logger.info("Parsing EditStudentCommand with arguments: " + args);
+
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL,
+ PREFIX_ADDRESS, PREFIX_SUBJECT);
+
+ if (argMultimap.getPreamble().isEmpty()) {
+ logger.warning("Empty preamble in EditStudentCommand");
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ EditStudentCommand.MESSAGE_USAGE));
+ }
+
+ try {
+ Index index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ logger.fine("Parsed student index: " + index.getOneBased());
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL,
+ PREFIX_ADDRESS, PREFIX_SUBJECT);
+
+ EditStudentDescriptor editStudentDescriptor = new EditStudentDescriptor();
+ parseFields(argMultimap, editStudentDescriptor);
+
+ if (!editStudentDescriptor.isAnyFieldEdited()) {
+ logger.warning("No fields edited in EditStudentCommand");
+ throw new ParseException(EditStudentCommand.MESSAGE_NOT_EDITED);
+ }
+
+ logger.info("Successfully parsed EditStudentCommand for student index: " + index.getOneBased());
+ return new EditStudentCommand(index, editStudentDescriptor);
+ } catch (ParseException pe) {
+ logger.warning("Error parsing EditStudentCommand: " + pe.getMessage());
+ throw pe;
+ } catch (Exception e) {
+ logger.severe("Unexpected error parsing EditStudentCommand: " + e.getMessage());
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ EditStudentCommand.MESSAGE_USAGE));
+ }
+ }
+
+ /**
+ * Parses individual fields from the argument multimap into the edit descriptor.
+ */
+ private void parseFields(ArgumentMultimap argMultimap, EditStudentDescriptor descriptor) throws ParseException {
+ if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
+ descriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()));
+ logger.fine("Parsed name field");
+ }
+ if (argMultimap.getValue(PREFIX_PHONE).isPresent()) {
+ descriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()));
+ logger.fine("Parsed phone field");
+ }
+ if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
+ descriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()));
+ logger.fine("Parsed email field");
+ }
+ if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
+ descriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()));
+ logger.fine("Parsed address field");
+ }
+ if (argMultimap.getValue(PREFIX_SUBJECT).isPresent()) {
+ logger.warning("Attempt to edit subject field detected");
+ throw new ParseException(EditStudentCommand.MESSAGE_EDIT_SUBJECT_DISALLOWED);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java
deleted file mode 100644
index 2867bde857b..00000000000
--- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-
-import java.util.Arrays;
-
-import seedu.address.logic.commands.FindCommand;
-import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
-
-/**
- * Parses input arguments and creates a new FindCommand object
- */
-public class FindCommandParser implements Parser {
-
- /**
- * Parses the given {@code String} of arguments in the context of the FindCommand
- * and returns a FindCommand object for execution.
- * @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));
- }
-
- String[] nameKeywords = trimmedArgs.split("\\s+");
-
- return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
- }
-
-}
diff --git a/src/main/java/seedu/address/logic/parser/FindStudentCommandParser.java b/src/main/java/seedu/address/logic/parser/FindStudentCommandParser.java
new file mode 100644
index 00000000000..8c16ad51187
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/FindStudentCommandParser.java
@@ -0,0 +1,53 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Arrays;
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.logic.commands.FindStudentCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.student.NameContainsKeywordsPredicate;
+
+/**
+ * Parses input arguments and creates a new FindStudentCommand object
+ */
+public class FindStudentCommandParser implements Parser {
+ private static final Logger logger = LogsCenter.getLogger(FindStudentCommandParser.class);
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the FindStudentCommand
+ * and returns a FindStudentCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public FindStudentCommand parse(String args) throws ParseException {
+ assert args != null : "Input arguments string cannot be null";
+ logger.info("Parsing FindStudentCommand with arguments: " + args);
+
+ String trimmedArgs = args.trim();
+ if (trimmedArgs.isEmpty()) {
+ logger.warning("Empty arguments provided to FindStudentCommand");
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindStudentCommand.MESSAGE_USAGE));
+ }
+
+ try {
+ String[] nameKeywords = trimmedArgs.split("\\s+");
+ assert nameKeywords != null && nameKeywords.length > 0 : "Keywords array cannot be null or empty";
+
+ logger.info("Successfully parsed FindStudentCommand with keywords: "
+ + Arrays.toString(nameKeywords));
+ return new FindStudentCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
+ } catch (NullPointerException npe) {
+ logger.severe("Null value encountered while parsing FindStudentCommand");
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ FindStudentCommand.MESSAGE_USAGE));
+ } catch (Exception e) {
+ logger.warning("Unexpected error parsing FindStudentCommand: " + e.getMessage());
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ FindStudentCommand.MESSAGE_USAGE));
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/ListLessonsCommandParser.java b/src/main/java/seedu/address/logic/parser/ListLessonsCommandParser.java
new file mode 100644
index 00000000000..63abb23618e
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/ListLessonsCommandParser.java
@@ -0,0 +1,58 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_LESSONS;
+
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.logic.commands.ListLessonsCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.lesson.StudentNameLessonPredicate;
+import seedu.address.model.student.Name;
+
+/**
+ * Parses input arguments and creates a new ListLessonsCommand object
+ */
+public class ListLessonsCommandParser implements Parser {
+ private static final Logger logger = LogsCenter.getLogger(ListLessonsCommandParser.class);
+ /**
+ * Parses the given {@code String} of arguments in the context of the ListLessonsCommand
+ * and returns a ListLessonsCommand object for execution.
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+ public ListLessonsCommand parse(String args) throws ParseException {
+ assert args != null : "Input arguments string cannot be null";
+ logger.info("Parsing ListLessonsCommand with arguments: " + args);
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME);
+
+ if (!argMultimap.getPreamble().isEmpty()) {
+ logger.warning("Invalid command format for ListLessonsCommand - preamble not empty: " + args);
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ListLessonsCommand.MESSAGE_USAGE));
+ }
+
+ if (!argMultimap.arePrefixesPresent(PREFIX_NAME)) {
+ logger.info("No name prefix found - returning all lessons");
+ return new ListLessonsCommand(PREDICATE_SHOW_ALL_LESSONS);
+ }
+
+ try {
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME);
+ Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get());
+ assert name != null : "Parsed name cannot be null";
+
+ logger.info("Successfully parsed ListLessonsCommand for student: " + name);
+ return new ListLessonsCommand(new StudentNameLessonPredicate(name), name);
+ } catch (ParseException pe) {
+ logger.warning("Error parsing name in ListLessonsCommand: " + pe.getMessage());
+ throw pe;
+ } catch (NullPointerException npe) {
+ logger.severe("Null value encountered while parsing ListLessonsCommand");
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ListLessonsCommand.MESSAGE_USAGE));
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/MarkAssignmentCommandParser.java b/src/main/java/seedu/address/logic/parser/MarkAssignmentCommandParser.java
new file mode 100644
index 00000000000..91edd664dc7
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/MarkAssignmentCommandParser.java
@@ -0,0 +1,55 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ASSIGNMENT;
+
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.MarkAssignmentCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new MarkAssignmentCommand object
+ */
+public class MarkAssignmentCommandParser implements Parser {
+ private static final Logger logger = LogsCenter.getLogger(MarkAssignmentCommandParser.class);
+ /**
+ * Parses the given {@code String} of arguments in the context of the MarkAssignmentCommand
+ * and returns a MarkAssignmentCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public MarkAssignmentCommand parse(String args) throws ParseException {
+ assert args != null : "Input arguments string cannot be null";
+ logger.info("Parsing MarkAssignmentCommand with arguments: " + args);
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_ASSIGNMENT);
+ if (!argMultimap.arePrefixesPresent(PREFIX_ASSIGNMENT)
+ || argMultimap.getPreamble().isEmpty()) {
+ logger.warning("Invalid command format for MarkAssignmentCommand: " + args);
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ MarkAssignmentCommand.MESSAGE_USAGE));
+ }
+
+ try {
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_ASSIGNMENT);
+ Index index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ String assignmentName = ParserUtil.parseAssignmentName(argMultimap.getValue(PREFIX_ASSIGNMENT).get());
+
+ assert assignmentName != null && !assignmentName.isEmpty() : "Assignment name cannot be null or empty";
+
+ logger.info("Successfully parsed MarkAssignmentCommand for index: "
+ + index.getOneBased() + " and assignment: " + assignmentName);
+ return new MarkAssignmentCommand(index, assignmentName);
+ } catch (ParseException pe) {
+ logger.warning("Error parsing MarkAssignmentCommand: " + pe.getMessage());
+ throw pe;
+ } catch (NullPointerException npe) {
+ logger.severe("Null value encountered while parsing MarkAssignmentCommand");
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ MarkAssignmentCommand.MESSAGE_USAGE));
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java
index b117acb9c55..7853cb3c5f0 100644
--- a/src/main/java/seedu/address/logic/parser/ParserUtil.java
+++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java
@@ -9,18 +9,21 @@
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.tag.Tag;
+import seedu.address.model.assignment.Assignment;
+import seedu.address.model.datetimeutil.Date;
+import seedu.address.model.datetimeutil.Time;
+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.subject.Subject;
/**
* 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.";
+ public static final String MESSAGE_INVALID_INDEX = "Error: Index must be a positive integer (1 or greater)";
/**
* Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be
@@ -96,29 +99,81 @@ public static Email parseEmail(String email) throws ParseException {
}
/**
- * Parses a {@code String tag} into a {@code Tag}.
+ * Parses a {@code String date} into a {@code Date}.
* Leading and trailing whitespaces will be trimmed.
*
- * @throws ParseException if the given {@code tag} is invalid.
+ * @throws ParseException if the given {@code date} is invalid.
*/
- public static Tag parseTag(String tag) throws ParseException {
- requireNonNull(tag);
- String trimmedTag = tag.trim();
- if (!Tag.isValidTagName(trimmedTag)) {
- throw new ParseException(Tag.MESSAGE_CONSTRAINTS);
+ public static Date parseDate(String date) throws ParseException {
+ requireNonNull(date);
+ String trimmedDate = date.trim();
+ if (!Date.isValidDate(trimmedDate)) {
+ throw new ParseException(Date.MESSAGE_CONSTRAINTS);
}
- return new Tag(trimmedTag);
+ return new Date(trimmedDate);
}
/**
- * Parses {@code Collection tags} into a {@code Set}.
+ * Parses a {@code String time} into a {@code Time}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code time} is invalid.
+ */
+ public static Time parseTime(String time) throws ParseException {
+ requireNonNull(time);
+ String trimmedTime = time.trim();
+ if (!Time.isValidTime(trimmedTime)) {
+ throw new ParseException(Time.MESSAGE_CONSTRAINTS);
+ }
+ return new Time(trimmedTime);
+
+ }
+
+ /**
+ * Parses a {@code String subject} into a {@code Subject}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code subject} is invalid.
+ */
+ public static Subject parseSubject(String subject) throws ParseException {
+ requireNonNull(subject);
+ String trimmedSubject = subject.trim();
+ if (!Subject.isValidSubjectName(trimmedSubject)) {
+ throw new ParseException(Subject.MESSAGE_CONSTRAINTS);
+ }
+ return new Subject(trimmedSubject);
+ }
+
+ /**
+ * Parses {@code Collection subjects} into a {@code Set}.
+ */
+ public static Set parseSubjects(Collection subjects) throws ParseException {
+ requireNonNull(subjects);
+ final Set subjectSet = new HashSet<>();
+ for (String subjectName : subjects) {
+ Subject subject = parseSubject(subjectName);
+ for (Subject existingSubject : subjectSet) {
+ if (existingSubject.equals(subject)) {
+ throw new ParseException(Subject.MESSAGE_DUPLICATE_SUBJECT);
+ }
+ }
+ subjectSet.add(subject);
+ }
+ return subjectSet;
+ }
+
+ /**
+ * Parses a {@code String assignmentName} into a {@code String}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code assignmentName} is invalid.
*/
- public static Set parseTags(Collection tags) throws ParseException {
- requireNonNull(tags);
- final Set tagSet = new HashSet<>();
- for (String tagName : tags) {
- tagSet.add(parseTag(tagName));
+ public static String parseAssignmentName(String assignmentName) throws ParseException {
+ requireNonNull(assignmentName);
+ String trimmedAssignmentName = assignmentName.trim();
+ if (!Assignment.isValidAssignmentName(trimmedAssignmentName)) {
+ throw new ParseException(Assignment.MESSAGE_CONSTRAINTS);
}
- return tagSet;
+ return trimmedAssignmentName;
}
}
diff --git a/src/main/java/seedu/address/logic/parser/UnmarkAssignmentCommandParser.java b/src/main/java/seedu/address/logic/parser/UnmarkAssignmentCommandParser.java
new file mode 100644
index 00000000000..21172b89634
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/UnmarkAssignmentCommandParser.java
@@ -0,0 +1,58 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ASSIGNMENT;
+
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.UnmarkAssignmentCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new UnmarkAssignmentCommand object
+ */
+public class UnmarkAssignmentCommandParser implements Parser {
+ private static final Logger logger = LogsCenter.getLogger(UnmarkAssignmentCommandParser.class);
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the UnmarkAssignmentCommand
+ * and returns a UnmarkAssignmentCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public UnmarkAssignmentCommand parse(String args) throws ParseException {
+ logger.info("Parsing UnmarkAssignmentCommand with arguments: " + args);
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_ASSIGNMENT);
+
+ if (!argMultimap.arePrefixesPresent(PREFIX_ASSIGNMENT) || argMultimap.getPreamble().isEmpty()) {
+ logger.warning("Invalid command format for UnmarkAssignmentCommand: " + args);
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ UnmarkAssignmentCommand.MESSAGE_USAGE));
+ }
+
+ try {
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_ASSIGNMENT);
+
+ Index index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ String assignmentName = ParserUtil.parseAssignmentName(
+ argMultimap.getValue(PREFIX_ASSIGNMENT).orElseThrow(() -> {
+ logger.warning("Missing assignment name prefix");
+ return new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ UnmarkAssignmentCommand.MESSAGE_USAGE));
+ }));
+
+ logger.info("Successfully parsed UnmarkAssignmentCommand for student " + index.getOneBased()
+ + ", assignment: " + assignmentName);
+ return new UnmarkAssignmentCommand(index, assignmentName);
+ } catch (ParseException pe) {
+ logger.warning("Parse error in UnmarkAssignmentCommand: " + pe.getMessage());
+ throw pe;
+ } catch (Exception e) {
+ logger.severe("Unexpected error parsing UnmarkAssignmentCommand: " + e.getMessage());
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ UnmarkAssignmentCommand.MESSAGE_USAGE));
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java
index 73397161e84..95c6879fdac 100644
--- a/src/main/java/seedu/address/model/AddressBook.java
+++ b/src/main/java/seedu/address/model/AddressBook.java
@@ -1,21 +1,27 @@
package seedu.address.model;
import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
import java.util.List;
+import java.util.Objects;
import javafx.collections.ObservableList;
import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.UniquePersonList;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.lesson.UniqueLessonList;
+import seedu.address.model.student.Student;
+import seedu.address.model.student.UniqueStudentList;
+import seedu.address.model.subject.Subject;
/**
* Wraps all data at the address-book level
- * Duplicates are not allowed (by .isSamePerson comparison)
+ * Duplicates are not allowed (by .isSameStudent and .isSameLesson comparison)
*/
public class AddressBook implements ReadOnlyAddressBook {
- private final UniquePersonList persons;
+ private final UniqueStudentList students;
+ private final UniqueLessonList lessons;
/*
* The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication
@@ -25,13 +31,14 @@ public class AddressBook implements ReadOnlyAddressBook {
* among constructors.
*/
{
- persons = new UniquePersonList();
+ lessons = new UniqueLessonList();
+ students = new UniqueStudentList();
}
public AddressBook() {}
/**
- * Creates an AddressBook using the Persons in the {@code toBeCopied}
+ * Creates an AddressBook using the Students in the {@code toBeCopied}
*/
public AddressBook(ReadOnlyAddressBook toBeCopied) {
this();
@@ -41,11 +48,19 @@ public AddressBook(ReadOnlyAddressBook toBeCopied) {
//// list overwrite operations
/**
- * Replaces the contents of the person list with {@code persons}.
- * {@code persons} must not contain duplicate persons.
+ * Replaces the contents of the student list with {@code students}.
+ * {@code students} must not contain duplicate students.
*/
- public void setPersons(List persons) {
- this.persons.setPersons(persons);
+ public void setStudents(List students) {
+ this.students.setStudents(students);
+ }
+
+ /**
+ * Replaces the contents of the lesson list with {@code students}.
+ * {@code lessons} must not contain duplicate students.
+ */
+ public void setLessons(List lessons) {
+ this.lessons.setLessons(lessons);
}
/**
@@ -54,44 +69,106 @@ public void setPersons(List persons) {
public void resetData(ReadOnlyAddressBook newData) {
requireNonNull(newData);
- setPersons(newData.getPersonList());
+ setLessons(newData.getLessonList());
+ setStudents(newData.getStudentList());
}
- //// person-level operations
+ //// student-level operations
+
+ /**
+ * Returns true if a student with the same identity as {@code student} exists in the address book.
+ */
+ public boolean hasStudent(Student student) {
+ requireNonNull(student);
+ return students.contains(student);
+ }
/**
- * Returns true if a person with the same identity as {@code person} exists in the address book.
+ * Returns true if a student has a subject.
*/
- public boolean hasPerson(Person person) {
- requireNonNull(person);
- return persons.contains(person);
+ public boolean hasStudentSubjects(Student student, Subject subjects) {
+ requireAllNonNull(student, subjects);
+ return students.hasStudentSubjects(student, subjects);
}
/**
- * Adds a person to the address book.
- * The person must not already exist in the address book.
+ * Adds a student to the address book.
+ * The student must not already exist in the address book.
*/
- public void addPerson(Person p) {
- persons.add(p);
+ public void addStudent(Student p) {
+ students.add(p);
}
/**
- * Replaces the given person {@code target} in the list with {@code editedPerson}.
+ * Replaces the given student {@code target} in the list with {@code editedStudent}.
* {@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.
+ * The student identity of {@code editedStudent} must not be the same as
+ * another existing student in the address book.
*/
- public void setPerson(Person target, Person editedPerson) {
- requireNonNull(editedPerson);
+ public void setStudent(Student target, Student editedStudent) {
+ requireNonNull(editedStudent);
- persons.setPerson(target, editedPerson);
+ students.setStudent(target, editedStudent);
}
/**
* 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 removeStudent(Student key) {
+ students.remove(key);
+ }
+
+ //// Assignment-level operations
+
+ public void deleteAssignment(Student student, String assignmentName) {
+ students.deleteAssignment(student, assignmentName);
+ }
+
+ //// lesson-level methods
+
+ /**
+ * Returns true if a lesson with the same identity as {@code lesson} exists in the address book.
+ */
+ public boolean hasLesson(Lesson lesson) {
+ requireNonNull(lesson);
+ return lessons.contains(lesson);
+ }
+
+ /**
+ * Returns true if a lesson with the same data and time as {@code lesson} exists in the address book.
+ */
+ public boolean hasLessonConflict(Lesson lesson) {
+ requireNonNull(lesson);
+ return lessons.clashes(lesson);
+ }
+
+ /**
+ * Adds a lesson to the address book.
+ * The lesson must not already exist in the address book.
+ */
+ public void addLesson(Lesson lesson) {
+ lessons.add(lesson);
+ }
+
+ /**
+ * Removes {@code lesson} from this {@code AddressBook}.
+ * {@code lesson} must exist in the address book.
+ */
+ public void removeLesson(Lesson lesson) {
+ lessons.remove(lesson);
+ }
+
+ /**
+ * Replaces the given lesson {@code target} in the list with {@code editedLesson}.
+ * {@code target} must exist in the address book.
+ * The lesson identity of {@code editedLesson} must not be the same as
+ * another existing lesson in the address book.
+ */
+ public void setLesson(Lesson target, Lesson editedLesson) {
+ requireNonNull(editedLesson);
+
+ lessons.setLesson(target, editedLesson);
}
//// util methods
@@ -99,13 +176,19 @@ public void removePerson(Person key) {
@Override
public String toString() {
return new ToStringBuilder(this)
- .add("persons", persons)
+ .add("students", students)
+ .add("lessons", lessons)
.toString();
}
@Override
- public ObservableList getPersonList() {
- return persons.asUnmodifiableObservableList();
+ public ObservableList getStudentList() {
+ return students.asUnmodifiableObservableList();
+ }
+
+ @Override
+ public ObservableList getLessonList() {
+ return lessons.asUnmodifiableObservableList();
}
@Override
@@ -120,11 +203,12 @@ public boolean equals(Object other) {
}
AddressBook otherAddressBook = (AddressBook) other;
- return persons.equals(otherAddressBook.persons);
+ return students.equals(otherAddressBook.students)
+ && lessons.equals(otherAddressBook.lessons);
}
@Override
public int hashCode() {
- return persons.hashCode();
+ return Objects.hash(students, lessons);
}
}
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
index d54df471c1f..91506036f25 100644
--- a/src/main/java/seedu/address/model/Model.java
+++ b/src/main/java/seedu/address/model/Model.java
@@ -5,14 +5,19 @@
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
-import seedu.address.model.person.Person;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.student.Student;
+import seedu.address.model.subject.Subject;
/**
* The API of the Model component.
*/
public interface Model {
/** {@code Predicate} that always evaluate to true */
- Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true;
+ Predicate PREDICATE_SHOW_ALL_STUDENTS = unused -> true;
+
+ /** {@code Predicate} that always evaluate to true */
+ Predicate PREDICATE_SHOW_ALL_LESSONS = unused -> true;
/**
* Replaces user prefs data with the data in {@code userPrefs}.
@@ -53,35 +58,96 @@ public interface Model {
ReadOnlyAddressBook getAddressBook();
/**
- * Returns true if a person with the same identity as {@code person} exists in the address book.
+ * Returns true if a student with the same identity as {@code student} exists in the address book.
+ */
+ boolean hasStudent(Student student);
+
+ /**
+ * Returns true if a student has a subject.
*/
- boolean hasPerson(Person person);
+ boolean hasStudentSubject(Student student, Subject subject);
/**
- * Deletes the given person.
- * The person must exist in the address book.
+ * Deletes the given student.
+ * The student must exist in the address book.
*/
- void deletePerson(Person target);
+ void deleteStudent(Student target);
/**
- * Adds the given person.
- * {@code person} must not already exist in the address book.
+ * Adds the given student.
+ * {@code student} must not already exist in the address book.
*/
- void addPerson(Person person);
+ void addStudent(Student student);
/**
- * Replaces the given person {@code target} with {@code editedPerson}.
+ * Replaces the given student {@code target} with {@code editedStudent}.
* {@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.
+ * The student identity of {@code editedStudent} must not be the same
+ * as another existing student in the address book.
+ */
+ void setStudent(Student target, Student editedStudent);
+
+ /**
+ * Deletes the given assignment to the student
+ * {@code assignment} must not already exist in the student
+ */
+ void deleteAssignment(Student student, String assignmentName);
+
+ /**
+ * Adds the given lesson
+ * {@code lesson} must not already exist in the address book
*/
- void setPerson(Person target, Person editedPerson);
+ void addLesson(Lesson lesson);
- /** Returns an unmodifiable view of the filtered person list */
- ObservableList getFilteredPersonList();
+ /**
+ * Deletes the given lesson.
+ * The lesson must exist in the address book.
+ */
+ void deleteLesson(Lesson lesson);
/**
- * Updates the filter of the filtered person list to filter by the given {@code predicate}.
+ * Returns true if a lesson with the same identity as {@code lesson} exists in the address book
+ */
+ boolean hasLesson(Lesson lesson);
+
+ /**
+ * Returns true if a lesson with the same date and time as {@code lesson} exists in the address book
+ */
+ boolean hasLessonConflict(Lesson lesson);
+
+ /**
+ * Replaces the given lesson {@code target} with {@code editedLesson}.
+ * {@code target} must exist in the address book.
+ * The lesson identity of {@code editedStudent} must not be the same
+ * as another existing lesson in the address book.
+ */
+ void setLesson(Lesson target, Lesson editedLesson);
+
+ /** Returns an unmodifiable view of the filtered student list */
+ ObservableList getFilteredStudentList();
+
+ /**
+ * Updates the filter of the filtered student list to filter by the given {@code predicate}.
* @throws NullPointerException if {@code predicate} is null.
*/
- void updateFilteredPersonList(Predicate predicate);
+ void updateFilteredStudentList(Predicate predicate);
+
+ /** Returns an unmodifiable view of the filtered lesson list */
+ ObservableList getFilteredLessonList();
+
+ /**
+ * Returns the current predicate of the filtered lesson list
+ */
+ boolean isStudentView();
+
+ /**
+ * Updates the filter of the filtered lesson list to filter by the given {@code predicate}.
+ * @throws NullPointerException if {@code predicate} is null.
+ */
+ void updateFilteredLessonList(Predicate predicate);
+
+ /**
+ * Returns the current list on display
+ */
+ ObservableList> getFilteredCurrList();
}
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
index 57bc563fde6..7bb7e011b52 100644
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ b/src/main/java/seedu/address/model/ModelManager.java
@@ -11,7 +11,9 @@
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.lesson.Lesson;
+import seedu.address.model.student.Student;
+import seedu.address.model.subject.Subject;
/**
* Represents the in-memory model of the address book data.
@@ -21,8 +23,9 @@ public class ModelManager implements Model {
private final AddressBook addressBook;
private final UserPrefs userPrefs;
- private final FilteredList filteredPersons;
-
+ private final FilteredList filteredStudents;
+ private final FilteredList filteredLessons;
+ private FilteredList> filteredCurr;
/**
* Initializes a ModelManager with the given addressBook and userPrefs.
*/
@@ -33,7 +36,9 @@ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs
this.addressBook = new AddressBook(addressBook);
this.userPrefs = new UserPrefs(userPrefs);
- filteredPersons = new FilteredList<>(this.addressBook.getPersonList());
+ filteredStudents = new FilteredList<>(this.addressBook.getStudentList());
+ filteredLessons = new FilteredList<>(this.addressBook.getLessonList());
+ filteredCurr = filteredStudents;
}
public ModelManager() {
@@ -88,44 +93,116 @@ public ReadOnlyAddressBook getAddressBook() {
}
@Override
- public boolean hasPerson(Person person) {
- requireNonNull(person);
- return addressBook.hasPerson(person);
+ public boolean hasStudent(Student student) {
+ requireNonNull(student);
+ return addressBook.hasStudent(student);
}
@Override
- public void deletePerson(Person target) {
- addressBook.removePerson(target);
+ public boolean hasStudentSubject(Student student, Subject subject) {
+ requireAllNonNull(student, subject);
+ return addressBook.hasStudentSubjects(student, subject);
}
@Override
- public void addPerson(Person person) {
- addressBook.addPerson(person);
- updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ public void deleteStudent(Student target) {
+ addressBook.removeStudent(target);
+ updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+ }
+
+
+ @Override
+ public void addStudent(Student student) {
+ addressBook.addStudent(student);
+ updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
}
@Override
- public void setPerson(Person target, Person editedPerson) {
- requireAllNonNull(target, editedPerson);
+ public void setStudent(Student target, Student editedStudent) {
+ requireAllNonNull(target, editedStudent);
+ addressBook.setStudent(target, editedStudent);
+ }
- addressBook.setPerson(target, editedPerson);
+ @Override
+ public void deleteAssignment(Student target, String assignmentName) {
+ addressBook.deleteAssignment(target, assignmentName);
+ updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
}
- //=========== Filtered Person List Accessors =============================================================
+ @Override
+ public boolean hasLesson(Lesson lesson) {
+ requireNonNull(lesson);
+ return addressBook.hasLesson(lesson);
+ }
+ @Override
+ public boolean hasLessonConflict(Lesson lesson) {
+ requireNonNull(lesson);
+ return addressBook.hasLessonConflict(lesson);
+ }
+
+ @Override
+ public void addLesson(Lesson lesson) {
+ requireNonNull(lesson);
+ addressBook.addLesson(lesson);
+ updateFilteredLessonList(PREDICATE_SHOW_ALL_LESSONS);
+ }
+
+ @Override
+ public void deleteLesson(Lesson lesson) {
+ requireNonNull(lesson);
+ addressBook.removeLesson(lesson);
+ updateFilteredLessonList(PREDICATE_SHOW_ALL_LESSONS);
+ }
+
+ @Override
+ public void setLesson(Lesson target, Lesson editedLesson) {
+ requireAllNonNull(target, editedLesson);
+ addressBook.setLesson(target, editedLesson);
+ }
+
+ //=========== Filtered Student List Accessors =============================================================
+
+ /**
+ * Returns an unmodifiable view of the list of {@code Student} backed by the internal list of
+ * {@code versionedAddressBook}
+ */
+ @Override
+ public ObservableList getFilteredStudentList() {
+ return filteredStudents;
+ }
+
+ @Override
+ public void updateFilteredStudentList(Predicate predicate) {
+ requireNonNull(predicate);
+ filteredStudents.setPredicate(predicate);
+ filteredCurr = filteredStudents;
+ }
+ //=========== Filtered Lesson List Acessors ===============================================================
/**
- * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of
+ * Returns an unmodifiable view of the list of {@code Lesson} backed by the internal list of
* {@code versionedAddressBook}
*/
@Override
- public ObservableList getFilteredPersonList() {
- return filteredPersons;
+ public ObservableList getFilteredLessonList() {
+ return filteredLessons;
}
@Override
- public void updateFilteredPersonList(Predicate predicate) {
+ public void updateFilteredLessonList(Predicate predicate) {
requireNonNull(predicate);
- filteredPersons.setPredicate(predicate);
+ filteredLessons.setPredicate(predicate);
+ filteredCurr = filteredLessons;
+ }
+
+ @Override
+ public boolean isStudentView() {
+ return filteredCurr.equals(filteredStudents);
+ }
+
+ @Override
+ public ObservableList> getFilteredCurrList() {
+ return filteredCurr;
}
@Override
@@ -142,7 +219,8 @@ public boolean equals(Object other) {
ModelManager otherModelManager = (ModelManager) other;
return addressBook.equals(otherModelManager.addressBook)
&& userPrefs.equals(otherModelManager.userPrefs)
- && filteredPersons.equals(otherModelManager.filteredPersons);
+ && filteredStudents.equals(otherModelManager.filteredStudents)
+ && filteredLessons.equals(otherModelManager.filteredLessons);
}
}
diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
index 6ddc2cd9a29..76326eed077 100644
--- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
+++ b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
@@ -1,7 +1,8 @@
package seedu.address.model;
import javafx.collections.ObservableList;
-import seedu.address.model.person.Person;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.student.Student;
/**
* Unmodifiable view of an address book
@@ -9,9 +10,15 @@
public interface ReadOnlyAddressBook {
/**
- * Returns an unmodifiable view of the persons list.
- * This list will not contain any duplicate persons.
+ * Returns an unmodifiable view of the students list.
+ * This list will not contain any duplicate students.
*/
- ObservableList getPersonList();
+ ObservableList getStudentList();
+
+ /**
+ * Returns an unmodifiable view of the lessons list.
+ * This list will not contain any duplicate lessons.
+ */
+ ObservableList getLessonList();
}
diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/address/model/UserPrefs.java
index 6be655fb4c7..03c00467316 100644
--- a/src/main/java/seedu/address/model/UserPrefs.java
+++ b/src/main/java/seedu/address/model/UserPrefs.java
@@ -14,7 +14,7 @@
public class UserPrefs implements ReadOnlyUserPrefs {
private GuiSettings guiSettings = new GuiSettings();
- private Path addressBookFilePath = Paths.get("data" , "addressbook.json");
+ private Path addressBookFilePath = Paths.get("data" , "TutorTrack.json");
/**
* Creates a {@code UserPrefs} with default values.
diff --git a/src/main/java/seedu/address/model/assignment/Assignment.java b/src/main/java/seedu/address/model/assignment/Assignment.java
new file mode 100644
index 00000000000..7041af855b7
--- /dev/null
+++ b/src/main/java/seedu/address/model/assignment/Assignment.java
@@ -0,0 +1,134 @@
+package seedu.address.model.assignment;
+
+import static seedu.address.commons.util.AppUtil.checkArgument;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import seedu.address.model.datetimeutil.Date;
+
+/**
+ * Represents an Assignment in the address book.
+ * Guarantees: immutable; is valid as declared in {@link #isValidAssignmentName(String)}
+ */
+public class Assignment implements Comparable {
+
+ public static final String MESSAGE_CONSTRAINTS = "Error: Assignment names should be alphanumeric "
+ + "and can only contain spaces. "
+ + "It should not exceed 50 characters.";
+
+ public static final String VALIDATION_REGEX = "^(?=.*[a-zA-Z0-9])[a-zA-Z0-9 ]{1,50}$";
+
+ // Identity fields
+ public final String assignmentName;
+
+ // Data fields
+ public final Date dueDate;
+ private boolean isDone;
+
+ /**
+ * Constructs a {@code Assignment} that is not done.
+ *
+ * @param assignment A valid assignment name.
+ * @param dueDate A valid due date.
+ */
+ public Assignment(String assignment, Date dueDate) {
+ requireAllNonNull(assignment, dueDate);
+ checkArgument(isValidAssignmentName(assignment), MESSAGE_CONSTRAINTS);
+ this.assignmentName = Arrays.stream(assignment.trim().split("\\s+"))
+ .map(word -> word.isEmpty() ? word
+ : Character.toUpperCase(word.charAt(0)) + word.substring(1).toLowerCase())
+ .collect(Collectors.joining(" "));
+ this.dueDate = dueDate;
+ this.isDone = false;
+ }
+
+ /**
+ * Constructs a {@code Assignment} given
+ * if it is done via {@code isDone}.
+ *
+ * @param assignment A valid assignment name.
+ * @param dueDate A valid due date.
+ */
+ public Assignment(String assignment, Date dueDate, boolean isDone) {
+ requireAllNonNull(assignment, dueDate);
+ checkArgument(isValidAssignmentName(assignment), MESSAGE_CONSTRAINTS);
+ this.assignmentName = Arrays.stream(assignment.trim().split("\\s+"))
+ .map(word -> word.isEmpty() ? word
+ : Character.toUpperCase(word.charAt(0)) + word.substring(1).toLowerCase())
+ .collect(Collectors.joining(" "));
+ this.dueDate = dueDate;
+ this.isDone = isDone;
+ }
+
+ @Override
+ public String toString() {
+ return assignmentName;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof Assignment)) {
+ return false;
+ }
+
+ Assignment otherAssignment = (Assignment) other;
+ return assignmentName.equals(otherAssignment.assignmentName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(assignmentName, dueDate);
+ }
+
+ public static boolean isValidAssignmentName(String value) {
+ return value.matches(VALIDATION_REGEX);
+ }
+
+ public static boolean isValidAssignmentDate(String date) {
+ return Date.isValidDate(date);
+ }
+
+ public boolean isSameAssignment(Assignment assignment) {
+ return this.assignmentName.equals(assignment.assignmentName);
+ }
+
+ public String getAssignmentName() {
+ return assignmentName;
+ }
+
+ public Date getDueDate() {
+ return dueDate;
+ }
+
+ public Assignment setDone() {
+ isDone = true;
+ return this;
+ }
+
+ public void setUndone() {
+ isDone = false;
+ }
+
+ public boolean isDone() {
+ return isDone;
+ }
+
+ /**
+ * Compares the assignment based on due date and assignment name.
+ */
+ @Override
+ public int compareTo(Assignment o2) {
+ if (this.dueDate.equals(o2.dueDate)) {
+ return this.assignmentName.compareTo(o2.assignmentName);
+ } else {
+ return this.dueDate.compareTo(o2.dueDate);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/model/assignment/UniqueAssignmentList.java b/src/main/java/seedu/address/model/assignment/UniqueAssignmentList.java
new file mode 100644
index 00000000000..4fd479094bd
--- /dev/null
+++ b/src/main/java/seedu/address/model/assignment/UniqueAssignmentList.java
@@ -0,0 +1,186 @@
+package seedu.address.model.assignment;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.assignment.expections.AssignmentNotFoundException;
+import seedu.address.model.assignment.expections.DuplicateAssignmentException;
+
+/**
+ * A list of assignments that enforces uniqueness between its elements and does not allow nulls.
+ * An assignment is considered unique by comparing using {@code Assignment#isSameAssignment(Assignment)}.
+ * As such, adding and updating of assignments uses Assignment
+ * #isSameAssignment(Assignment) for equality to ensure that the assignment being added or updated is
+ * unique in terms of identity in the UniqueAssignmentList.
+ *
+ **/
+public class UniqueAssignmentList implements Iterable, Comparator {
+
+ public static final String MESSAGE_ALREADY_MARKED = "Error: Assignment is already marked";
+ public static final String MESSAGE_ALREADY_UNMARKED = "Error: Assignment is already unmarked";
+
+
+ private final ObservableList internalList = FXCollections.observableArrayList();
+ private final ObservableList internalUnmodifiableList =
+ FXCollections.unmodifiableObservableList(internalList);
+ /**
+ * Returns true if the list contains an equivalent assignment as the given argument.
+ */
+ public boolean contains(Assignment toCheck) {
+ requireNonNull(toCheck);
+ return internalList.stream().anyMatch(toCheck::isSameAssignment);
+ }
+
+ /**
+ * Adds an assignment to the list.
+ * The assignment must not already exist in the list.
+ */
+ public void add(Assignment toAdd) {
+ requireNonNull(toAdd);
+ if (contains(toAdd)) {
+ throw new DuplicateAssignmentException();
+ }
+ internalList.add(toAdd);
+ }
+
+ /**
+ * Replaces the assignment {@code target} in the list with {@code editedAssignment}.
+ * {@code target} must exist in the list.
+ * The assignment identity of {@code editedAssignment} must not be the same as another
+ * existing assignment in the list.
+ */
+ public void setAssignment(Assignment target, Assignment editedAssignment) {
+ requireAllNonNull(target, editedAssignment);
+
+ int index = internalList.indexOf(target);
+ if (index == -1) {
+ throw new AssignmentNotFoundException();
+ }
+
+ if (!target.isSameAssignment(editedAssignment) && contains(editedAssignment)) {
+ throw new DuplicateAssignmentException();
+ }
+
+ internalList.set(index, editedAssignment);
+ }
+
+ public ObservableList asUnmodifiableObservableList() {
+ return internalUnmodifiableList;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return internalList.iterator();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof UniqueAssignmentList)) {
+ return false;
+ }
+
+ UniqueAssignmentList otherUniqueAssignmentList = (UniqueAssignmentList) other;
+ return internalList.equals(otherUniqueAssignmentList.internalList);
+ }
+
+ @Override
+ public int hashCode() {
+ return internalList.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return internalList.toString();
+ }
+
+ private boolean assignmentsAreUnique(List assignments) {
+ for (int i = 0; i < assignments.size(); i++) {
+ for (int j = i + 1; j < assignments.size(); j++) {
+ if (assignments.get(i).isSameAssignment(assignments.get(j))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int compare(Assignment o1, Assignment o2) {
+ return o1.compareTo(o2);
+ }
+
+ /**
+ * Marks an assignment as done.
+ */
+ public void markAssignment(String assignmentName) throws CommandException {
+ requireNonNull(assignmentName);
+ for (Assignment assignment : internalList) {
+ if (assignment.getAssignmentName().equals(assignmentName)) {
+ if (assignment.isDone()) {
+ throw new CommandException(MESSAGE_ALREADY_MARKED);
+ }
+ assignment.setDone();
+ }
+ }
+ }
+
+ /**
+ * Marks an assignment as undone.
+ */
+ public void unmarkAssignment(String assignmentName) throws CommandException {
+ requireNonNull(assignmentName);
+ for (Assignment assignment : internalList) {
+ if (assignment.getAssignmentName().equals(assignmentName)) {
+ if (!assignment.isDone()) {
+ throw new CommandException(MESSAGE_ALREADY_UNMARKED);
+ }
+ assignment.setUndone();
+ }
+ }
+ }
+
+ /**
+ * Deletes an assignment from the list.
+ */
+ public void deleteAssignment(String assignmentName) {
+ requireNonNull(assignmentName);
+ Iterator iterator = internalList.iterator();
+ while (iterator.hasNext()) {
+ Assignment assignment = iterator.next();
+ if (assignment.getAssignmentName().equals(assignmentName)) {
+ iterator.remove();
+ break;
+ }
+ }
+ }
+
+ /**
+ * Returns a specified assignment from the list.
+ * @param assignmentName
+ */
+ public Assignment getAssignment(String assignmentName) {
+ requireNonNull(assignmentName);
+ for (Assignment assignment : internalList) {
+ if (assignment.getAssignmentName().equals(assignmentName)) {
+ return assignment;
+ }
+ }
+ return null;
+ }
+
+ public void clear() {
+ internalList.clear();
+ }
+}
diff --git a/src/main/java/seedu/address/model/assignment/expections/AssignmentNotFoundException.java b/src/main/java/seedu/address/model/assignment/expections/AssignmentNotFoundException.java
new file mode 100644
index 00000000000..3825cb5debd
--- /dev/null
+++ b/src/main/java/seedu/address/model/assignment/expections/AssignmentNotFoundException.java
@@ -0,0 +1,7 @@
+package seedu.address.model.assignment.expections;
+
+/**
+ * Signals that the operation is unable to find the specified assignment.
+ */
+public class AssignmentNotFoundException extends RuntimeException {
+}
diff --git a/src/main/java/seedu/address/model/assignment/expections/DuplicateAssignmentException.java b/src/main/java/seedu/address/model/assignment/expections/DuplicateAssignmentException.java
new file mode 100644
index 00000000000..7d32cce1a1a
--- /dev/null
+++ b/src/main/java/seedu/address/model/assignment/expections/DuplicateAssignmentException.java
@@ -0,0 +1,12 @@
+package seedu.address.model.assignment.expections;
+
+/**
+ * Signals that the operation will result in duplicate Assignments
+ * (Assignments are considered duplicates if they have the same
+ * identity).
+ */
+public class DuplicateAssignmentException extends RuntimeException {
+ public DuplicateAssignmentException() {
+ super("This assignment already exists in the student");
+ }
+}
diff --git a/src/main/java/seedu/address/model/datetimeutil/Date.java b/src/main/java/seedu/address/model/datetimeutil/Date.java
new file mode 100644
index 00000000000..0f632a8ff6a
--- /dev/null
+++ b/src/main/java/seedu/address/model/datetimeutil/Date.java
@@ -0,0 +1,76 @@
+package seedu.address.model.datetimeutil;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.format.ResolverStyle;
+
+/**
+ * Represents a Lesson's date in the address book.
+ * Guarantees: immutable; is valid as declared in {@link #isValidDate(String)}
+ */
+public class Date implements Comparable {
+
+ public static final String MESSAGE_CONSTRAINTS = "Error: Date must be a valid date, "
+ + "in DD-MM-YYYY or D-M-YYYY format (e.g., 05-10-2023 or 5-10-2023),"
+ + "\nand must be a future date (after today)";
+ public static final DateTimeFormatter VALID_FORMAT = DateTimeFormatter.ofPattern("d-M-uuuu")
+ .withResolverStyle(ResolverStyle.STRICT);
+
+ public final String date;
+
+ /**
+ * Constructs a {@code Date}.
+ *
+ * @param date A valid date.
+ */
+ public Date(String date) {
+ requireNonNull(date);
+ checkArgument(isValidDate(date), MESSAGE_CONSTRAINTS);
+ this.date = date;
+ }
+
+ /**
+ * Returns true if a given string is a valid date.
+ */
+ public static boolean isValidDate(String test) {
+ LocalDate parsedDate;
+ try {
+ parsedDate = LocalDate.parse(test, VALID_FORMAT);
+ return parsedDate.isAfter(LocalDate.now());
+ } catch (DateTimeParseException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return DateTimeFormatter.ofPattern("dd MMM yyyy").format(LocalDate.parse(date, VALID_FORMAT));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Date otherDate)) {
+ return false;
+ }
+
+ return LocalDate.parse(this.date, VALID_FORMAT).equals(LocalDate.parse(otherDate.date, VALID_FORMAT));
+ }
+
+ @Override
+ public int hashCode() {
+ return date.hashCode();
+ }
+
+ public int compareTo(Date dueDate) {
+ return LocalDate.parse(this.date, VALID_FORMAT).compareTo(LocalDate.parse(dueDate.date, VALID_FORMAT));
+ }
+}
diff --git a/src/main/java/seedu/address/model/datetimeutil/Time.java b/src/main/java/seedu/address/model/datetimeutil/Time.java
new file mode 100644
index 00000000000..65eaecac91f
--- /dev/null
+++ b/src/main/java/seedu/address/model/datetimeutil/Time.java
@@ -0,0 +1,68 @@
+package seedu.address.model.datetimeutil;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+
+/**
+ * Represents a Lesson's time in the address book.
+ * Guarantees: immutable; is valid as declared in {@link #isValidTime(String)}
+ */
+public class Time {
+
+ public static final String MESSAGE_CONSTRAINTS =
+ "Error: Time should be of format: HH:MM, be in 24 hour format.";
+ public static final DateTimeFormatter VALID_FORMAT = DateTimeFormatter.ofPattern("H:m");
+ public final String time;
+
+ /**
+ * Constructs a {@code Time}.
+ *
+ * @param time A valid time.
+ */
+ public Time(String time) {
+ requireNonNull(time);
+ checkArgument(isValidTime(time), MESSAGE_CONSTRAINTS);
+ this.time = time;
+ }
+
+ /**
+ * Returns true if a given string is a valid time.
+ */
+ public static boolean isValidTime(String test) {
+ LocalTime parsedTime;
+ try {
+ parsedTime = LocalTime.parse(test, VALID_FORMAT);
+ } catch (DateTimeParseException e) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return time;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Time otherTime)) {
+ return false;
+ }
+
+ return LocalTime.parse(this.time, VALID_FORMAT).equals(LocalTime.parse(otherTime.time, VALID_FORMAT));
+ }
+
+ @Override
+ public int hashCode() {
+ return time.hashCode();
+ }
+}
diff --git a/src/main/java/seedu/address/model/lesson/Lesson.java b/src/main/java/seedu/address/model/lesson/Lesson.java
new file mode 100644
index 00000000000..7eea5bf226b
--- /dev/null
+++ b/src/main/java/seedu/address/model/lesson/Lesson.java
@@ -0,0 +1,99 @@
+package seedu.address.model.lesson;
+
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.Objects;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.model.datetimeutil.Date;
+import seedu.address.model.datetimeutil.Time;
+import seedu.address.model.student.Name;
+import seedu.address.model.subject.Subject;
+
+/**
+ * Represents a Lesson in the address book.
+ * Guarantees: details are present and not null, field values are validated, immutable.
+ */
+public class Lesson {
+
+ // Identity fields
+ private final Name studentName;
+ private final Subject subject;
+ private final Date date;
+ private final Time time;
+
+
+ /**
+ * Every field must be present and not null.
+ * This is the original constructor for lesson from AB3
+ */
+ public Lesson(Subject subject, Name studentName, Date date, Time time) {
+ requireAllNonNull(subject, studentName, date, time);
+ this.studentName = studentName;
+ this.date = date;
+ this.time = time;
+ this.subject = subject;
+
+ }
+
+ public Name getStudentName() {
+ return studentName;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public Time getTime() {
+ return time;
+ }
+
+ public Subject getSubject() {
+ return subject;
+ }
+
+ /**
+ * Returns true if both lessons have same date and time
+ */
+ public boolean isConflict(Lesson toCheck) {
+ return date.equals(toCheck.getDate())
+ && time.equals(toCheck.getTime());
+ }
+
+ /**
+ * Returns true if both lessons have the same identity fields.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Lesson otherLesson)) {
+ return false;
+ }
+
+ return studentName.equals(otherLesson.studentName)
+ && date.equals(otherLesson.date)
+ && time.equals(otherLesson.time)
+ && subject.equals(otherLesson.subject);
+ }
+
+ @Override
+ public int hashCode() {
+ // use this method for custom fields hashing instead of implementing your own
+ return Objects.hash(studentName, date, time, subject);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("subject", subject)
+ .add("name", studentName)
+ .add("date", date)
+ .add("time", time)
+ .toString();
+ }
+}
+
diff --git a/src/main/java/seedu/address/model/lesson/StudentNameLessonPredicate.java b/src/main/java/seedu/address/model/lesson/StudentNameLessonPredicate.java
new file mode 100644
index 00000000000..c9dc1bceafd
--- /dev/null
+++ b/src/main/java/seedu/address/model/lesson/StudentNameLessonPredicate.java
@@ -0,0 +1,50 @@
+package seedu.address.model.lesson;
+
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.model.student.Name;
+
+/**
+ * Tests that a {@code Lesson}'s {@code Name} matches the keyword given.
+ */
+public class StudentNameLessonPredicate implements Predicate {
+ private final Name studentName;
+
+ /**
+ * Constructor for StudentNameLessonPredicate
+ * @param studentName StudentName to match
+ */
+ public StudentNameLessonPredicate(Name studentName) {
+ this.studentName = studentName;
+ }
+
+ @Override
+ public boolean test(Lesson lesson) {
+ String lessonName = lesson.getStudentName().fullName;
+ String targetName = studentName.fullName;
+
+ return lessonName != null && targetName != null
+ && lessonName.toLowerCase().contains(targetName.toLowerCase());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof StudentNameLessonPredicate)) {
+ return false;
+ }
+
+ StudentNameLessonPredicate otherStudentNameLessonPredicate = (StudentNameLessonPredicate) other;
+ return studentName.equals(otherStudentNameLessonPredicate.studentName);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add("studentName", studentName).toString();
+ }
+}
diff --git a/src/main/java/seedu/address/model/lesson/UniqueLessonList.java b/src/main/java/seedu/address/model/lesson/UniqueLessonList.java
new file mode 100644
index 00000000000..088970b56d2
--- /dev/null
+++ b/src/main/java/seedu/address/model/lesson/UniqueLessonList.java
@@ -0,0 +1,159 @@
+package seedu.address.model.lesson;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.Iterator;
+import java.util.List;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import seedu.address.model.lesson.exceptions.DuplicateLessonException;
+import seedu.address.model.lesson.exceptions.LessonNotFoundException;
+
+/**
+ * A list of lessons that enforces uniqueness between its elements and does not allow nulls.
+ * A lesson is considered unique by comparing using {@code Lesson#isSameLesson(Lesson)}.
+ * As such, adding of
+ * lessons uses Lesson#isSameLesson(Lesson) for equality to ensure that the
+ * lesson being added is
+ * unique in terms of identity in the UniqueLessonList.
+ * However, the removal of a lesson uses Lesson#equals(Object)
+ * to ensure that the lesson with exactly the same fields will be removed.
+ * Supports a minimal set of list operations.
+ *
+ * @see Lesson#equals(Object)
+ */
+public class UniqueLessonList implements Iterable {
+
+ private final ObservableList internalList = FXCollections.observableArrayList();
+ private final ObservableList internalUnmodifiableList =
+ FXCollections.unmodifiableObservableList(internalList);
+
+ /**
+ * Returns true if the list contains an equivalent lesson as the given argument.
+ */
+ public boolean contains(Lesson toCheck) {
+ requireNonNull(toCheck);
+ return internalList.stream().anyMatch(toCheck::equals);
+ }
+
+ /**
+ * Returns true if the list contains a lesson that clashes with the lesson given
+ */
+ public boolean clashes(Lesson toCheck) {
+ requireNonNull(toCheck);
+ return internalList.stream().anyMatch(toCheck::isConflict);
+ }
+
+ /**
+ * Adds a lesson to the list.
+ * The lesson must not already exist in the list.
+ */
+ public void add(Lesson toAdd) {
+ requireNonNull(toAdd);
+ if (contains(toAdd)) {
+ throw new DuplicateLessonException();
+ }
+ internalList.add(toAdd);
+ }
+
+ /**
+ * Replaces the lesson {@code target} in the list with {@code editedLesson}.
+ * {@code target} must exist in the list.
+ * The lesson identity of {@code editedLesson} must not be the same as another existing lesson in the list.
+ */
+ public void setLesson(Lesson target, Lesson editedLesson) {
+ requireAllNonNull(target, editedLesson);
+
+ int index = internalList.indexOf(target);
+ if (index == -1) {
+ throw new LessonNotFoundException();
+ }
+
+ if (!target.equals(editedLesson) && contains(editedLesson)) {
+ throw new DuplicateLessonException();
+ }
+
+ internalList.set(index, editedLesson);
+ }
+
+ /**
+ * Removes the equivalent lesson from the list.
+ * The lesson must exist in the list.
+ */
+ public void remove(Lesson toRemove) {
+ requireNonNull(toRemove);
+ if (!internalList.remove(toRemove)) {
+ throw new LessonNotFoundException();
+ }
+ }
+
+ public void setLessons(UniqueLessonList replacement) {
+ requireNonNull(replacement);
+ internalList.setAll(replacement.internalList);
+ }
+
+ /**
+ * Replaces the contents of this list with {@code lessons}.
+ * {@code lessons} must not contain duplicate lessons.
+ */
+ public void setLessons(List lessons) {
+ requireAllNonNull(lessons);
+ if (!lessonsAreUnique(lessons)) {
+ throw new DuplicateLessonException();
+ }
+
+ internalList.setAll(lessons);
+ }
+
+ /**
+ * Returns the backing list as an unmodifiable {@code ObservableList}.
+ */
+ public ObservableList asUnmodifiableObservableList() {
+ return internalUnmodifiableList;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return internalList.iterator();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof UniqueLessonList otherUniqueLessonList)) {
+ return false;
+ }
+
+ return internalList.equals(otherUniqueLessonList.internalList);
+ }
+
+ @Override
+ public int hashCode() {
+ return internalList.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return internalList.toString();
+ }
+
+ /**
+ * Returns true if {@code lessons} contains only unique lessons.
+ */
+ private boolean lessonsAreUnique(List lessons) {
+ for (int i = 0; i < lessons.size() - 1; i++) {
+ for (int j = i + 1; j < lessons.size(); j++) {
+ if (lessons.get(i).equals(lessons.get(j))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/seedu/address/model/lesson/exceptions/DuplicateLessonException.java b/src/main/java/seedu/address/model/lesson/exceptions/DuplicateLessonException.java
new file mode 100644
index 00000000000..1552c91926f
--- /dev/null
+++ b/src/main/java/seedu/address/model/lesson/exceptions/DuplicateLessonException.java
@@ -0,0 +1,12 @@
+package seedu.address.model.lesson.exceptions;
+
+/**
+ * Signals that the operation will result in duplicate Lessons
+ * (Lessons are considered duplicates if they have the same
+ * identity).
+ */
+public class DuplicateLessonException extends RuntimeException {
+ public DuplicateLessonException() {
+ super("Operation would result in duplicate lessons");
+ }
+}
diff --git a/src/main/java/seedu/address/model/lesson/exceptions/LessonNotFoundException.java b/src/main/java/seedu/address/model/lesson/exceptions/LessonNotFoundException.java
new file mode 100644
index 00000000000..5272237f884
--- /dev/null
+++ b/src/main/java/seedu/address/model/lesson/exceptions/LessonNotFoundException.java
@@ -0,0 +1,6 @@
+package seedu.address.model.lesson.exceptions;
+
+/**
+ * Signals that the operation is unable to find the specified lesson.
+ */
+public class LessonNotFoundException extends RuntimeException {}
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 abe8c46b535..00000000000
--- a/src/main/java/seedu/address/model/person/Person.java
+++ /dev/null
@@ -1,117 +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.commons.util.ToStringBuilder;
-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 getTags() {
- return Collections.unmodifiableSet(tags);
- }
-
- /**
- * Returns true if both persons have the same name.
- * This defines a weaker notion of equality between two persons.
- */
- public boolean isSamePerson(Person otherPerson) {
- if (otherPerson == this) {
- return true;
- }
-
- return otherPerson != null
- && otherPerson.getName().equals(getName());
- }
-
- /**
- * Returns true if both persons have the same identity and data fields.
- * This defines a stronger notion of equality between two persons.
- */
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Person)) {
- return false;
- }
-
- Person otherPerson = (Person) other;
- return name.equals(otherPerson.name)
- && phone.equals(otherPerson.phone)
- && email.equals(otherPerson.email)
- && address.equals(otherPerson.address)
- && tags.equals(otherPerson.tags);
- }
-
- @Override
- public int hashCode() {
- // use this method for custom fields hashing instead of implementing your own
- return Objects.hash(name, phone, email, address, tags);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("name", name)
- .add("phone", phone)
- .add("email", email)
- .add("address", address)
- .add("tags", tags)
- .toString();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java
deleted file mode 100644
index cc0a68d79f9..00000000000
--- a/src/main/java/seedu/address/model/person/UniquePersonList.java
+++ /dev/null
@@ -1,150 +0,0 @@
-package seedu.address.model.person;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
-
-import java.util.Iterator;
-import java.util.List;
-
-import javafx.collections.FXCollections;
-import javafx.collections.ObservableList;
-import seedu.address.model.person.exceptions.DuplicatePersonException;
-import seedu.address.model.person.exceptions.PersonNotFoundException;
-
-/**
- * A list of persons that enforces uniqueness between its elements and does not allow nulls.
- * A person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, adding and updating of
- * persons uses Person#isSamePerson(Person) for equality so as to ensure that the person being added or updated is
- * unique in terms of identity in the UniquePersonList. However, the removal of a person uses Person#equals(Object) so
- * as to ensure that the person with exactly the same fields will be removed.
- *
- * Supports a minimal set of list operations.
- *
- * @see Person#isSamePerson(Person)
- */
-public class UniquePersonList implements Iterable {
-
- private final ObservableList internalList = FXCollections.observableArrayList();
- private final ObservableList internalUnmodifiableList =
- FXCollections.unmodifiableObservableList(internalList);
-
- /**
- * Returns true if the list contains an equivalent person as the given argument.
- */
- public boolean contains(Person toCheck) {
- requireNonNull(toCheck);
- return internalList.stream().anyMatch(toCheck::isSamePerson);
- }
-
- /**
- * Adds a person to the list.
- * The person must not already exist in the list.
- */
- public void add(Person toAdd) {
- requireNonNull(toAdd);
- if (contains(toAdd)) {
- throw new DuplicatePersonException();
- }
- internalList.add(toAdd);
- }
-
- /**
- * Replaces the person {@code target} in the list with {@code editedPerson}.
- * {@code target} must exist in the list.
- * The person identity of {@code editedPerson} must not be the same as another existing person in the list.
- */
- public void setPerson(Person target, Person editedPerson) {
- requireAllNonNull(target, editedPerson);
-
- int index = internalList.indexOf(target);
- if (index == -1) {
- throw new PersonNotFoundException();
- }
-
- if (!target.isSamePerson(editedPerson) && contains(editedPerson)) {
- throw new DuplicatePersonException();
- }
-
- internalList.set(index, editedPerson);
- }
-
- /**
- * Removes the equivalent person from the list.
- * The person must exist in the list.
- */
- public void remove(Person toRemove) {
- requireNonNull(toRemove);
- if (!internalList.remove(toRemove)) {
- throw new PersonNotFoundException();
- }
- }
-
- public void setPersons(UniquePersonList replacement) {
- requireNonNull(replacement);
- internalList.setAll(replacement.internalList);
- }
-
- /**
- * Replaces the contents of this list with {@code persons}.
- * {@code persons} must not contain duplicate persons.
- */
- public void setPersons(List persons) {
- requireAllNonNull(persons);
- if (!personsAreUnique(persons)) {
- throw new DuplicatePersonException();
- }
-
- internalList.setAll(persons);
- }
-
- /**
- * Returns the backing list as an unmodifiable {@code ObservableList}.
- */
- public ObservableList asUnmodifiableObservableList() {
- return internalUnmodifiableList;
- }
-
- @Override
- public Iterator iterator() {
- return internalList.iterator();
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof UniquePersonList)) {
- return false;
- }
-
- UniquePersonList otherUniquePersonList = (UniquePersonList) other;
- return internalList.equals(otherUniquePersonList.internalList);
- }
-
- @Override
- public int hashCode() {
- return internalList.hashCode();
- }
-
- @Override
- public String toString() {
- return internalList.toString();
- }
-
- /**
- * Returns true if {@code persons} contains only unique persons.
- */
- private boolean personsAreUnique(List persons) {
- for (int i = 0; i < persons.size() - 1; i++) {
- for (int j = i + 1; j < persons.size(); j++) {
- if (persons.get(i).isSamePerson(persons.get(j))) {
- return false;
- }
- }
- }
- return true;
- }
-}
diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java
deleted file mode 100644
index d7290f59442..00000000000
--- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package seedu.address.model.person.exceptions;
-
-/**
- * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same
- * identity).
- */
-public class DuplicatePersonException extends RuntimeException {
- public DuplicatePersonException() {
- super("Operation would result in duplicate persons");
- }
-}
diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java
deleted file mode 100644
index fa764426ca7..00000000000
--- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package seedu.address.model.person.exceptions;
-
-/**
- * Signals that the operation is unable to find the specified person.
- */
-public class PersonNotFoundException extends RuntimeException {}
diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/student/Address.java
similarity index 94%
rename from src/main/java/seedu/address/model/person/Address.java
rename to src/main/java/seedu/address/model/student/Address.java
index 469a2cc9a1e..3a873f95842 100644
--- a/src/main/java/seedu/address/model/person/Address.java
+++ b/src/main/java/seedu/address/model/student/Address.java
@@ -1,10 +1,10 @@
-package seedu.address.model.person;
+package seedu.address.model.student;
import static java.util.Objects.requireNonNull;
import static seedu.address.commons.util.AppUtil.checkArgument;
/**
- * Represents a Person's address in the address book.
+ * Represents a Student's address in the address book.
* Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)}
*/
public class Address {
diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/student/Email.java
similarity index 87%
rename from src/main/java/seedu/address/model/person/Email.java
rename to src/main/java/seedu/address/model/student/Email.java
index c62e512bc29..e999a32fc43 100644
--- a/src/main/java/seedu/address/model/person/Email.java
+++ b/src/main/java/seedu/address/model/student/Email.java
@@ -1,10 +1,10 @@
-package seedu.address.model.person;
+package seedu.address.model.student;
import static java.util.Objects.requireNonNull;
import static seedu.address.commons.util.AppUtil.checkArgument;
/**
- * Represents a Person's email in the address book.
+ * Represents a Student's email in the address book.
* Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)}
*/
public class Email {
@@ -12,11 +12,9 @@ public class Email {
private static final String SPECIAL_CHARACTERS = "+_.-";
public static final String MESSAGE_CONSTRAINTS = "Emails should be of the format local-part@domain "
+ "and adhere to the following constraints:\n"
- + "1. The local-part should only contain alphanumeric characters and these special characters, excluding "
- + "the parentheses, (" + SPECIAL_CHARACTERS + "). The local-part may not start or end with any special "
- + "characters.\n"
- + "2. This is followed by a '@' and then a domain name. The domain name is made up of domain labels "
- + "separated by periods.\n"
+ + "1. The local-part should only contain alphanumeric characters and these special characters: ("
+ + SPECIAL_CHARACTERS + "). The local-part may not start or end with any special characters.\n"
+ + "2. This is followed by a '@' and then a domain name.\n"
+ "The domain name must:\n"
+ " - end with a domain label at least 2 characters long\n"
+ " - have each domain label start and end with alphanumeric characters\n"
diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/student/Name.java
similarity index 67%
rename from src/main/java/seedu/address/model/person/Name.java
rename to src/main/java/seedu/address/model/student/Name.java
index 173f15b9b00..5d2f87f7dcb 100644
--- a/src/main/java/seedu/address/model/person/Name.java
+++ b/src/main/java/seedu/address/model/student/Name.java
@@ -1,10 +1,10 @@
-package seedu.address.model.person;
+package seedu.address.model.student;
import static java.util.Objects.requireNonNull;
import static seedu.address.commons.util.AppUtil.checkArgument;
/**
- * Represents a Person's name in the address book.
+ * Represents a Student's name in the address book.
* Guarantees: immutable; is valid as declared in {@link #isValidName(String)}
*/
public class Name {
@@ -28,7 +28,21 @@ public class Name {
public Name(String name) {
requireNonNull(name);
checkArgument(isValidName(name), MESSAGE_CONSTRAINTS);
- fullName = name;
+ this.fullName = standardizeCapitalization(name);
+ }
+
+ private String standardizeCapitalization(String name) {
+ name = name.toLowerCase();
+ String[] words = name.split("\\s+");
+ StringBuilder result = new StringBuilder();
+ for (String word : words) {
+ if (!word.isEmpty()) {
+ result.append(Character.toUpperCase(word.charAt(0)))
+ .append(word.substring(1))
+ .append(" ");
+ }
+ }
+ return result.toString().trim();
}
/**
@@ -56,7 +70,7 @@ public boolean equals(Object other) {
}
Name otherName = (Name) other;
- return fullName.equals(otherName.fullName);
+ return fullName.equalsIgnoreCase(otherName.fullName);
}
@Override
diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/student/NameContainsKeywordsPredicate.java
similarity index 83%
rename from src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java
rename to src/main/java/seedu/address/model/student/NameContainsKeywordsPredicate.java
index 62d19be2977..cd97267de2d 100644
--- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java
+++ b/src/main/java/seedu/address/model/student/NameContainsKeywordsPredicate.java
@@ -1,4 +1,4 @@
-package seedu.address.model.person;
+package seedu.address.model.student;
import java.util.List;
import java.util.function.Predicate;
@@ -7,9 +7,9 @@
import seedu.address.commons.util.ToStringBuilder;
/**
- * Tests that a {@code Person}'s {@code Name} matches any of the keywords given.
+ * Tests that a {@code Student}'s {@code Name} matches any of the keywords given.
*/
-public class NameContainsKeywordsPredicate implements Predicate {
+public class NameContainsKeywordsPredicate implements Predicate {
private final List keywords;
public NameContainsKeywordsPredicate(List keywords) {
@@ -17,9 +17,9 @@ public NameContainsKeywordsPredicate(List keywords) {
}
@Override
- public boolean test(Person person) {
+ public boolean test(Student student) {
return keywords.stream()
- .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword));
+ .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(student.getName().fullName, keyword));
}
@Override
diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/student/Phone.java
similarity index 87%
rename from src/main/java/seedu/address/model/person/Phone.java
rename to src/main/java/seedu/address/model/student/Phone.java
index d733f63d739..67dc98fd22a 100644
--- a/src/main/java/seedu/address/model/person/Phone.java
+++ b/src/main/java/seedu/address/model/student/Phone.java
@@ -1,18 +1,18 @@
-package seedu.address.model.person;
+package seedu.address.model.student;
import static java.util.Objects.requireNonNull;
import static seedu.address.commons.util.AppUtil.checkArgument;
/**
- * Represents a Person's phone number in the address book.
+ * Represents a Student's phone number in the address book.
* Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)}
*/
public class Phone {
public static final String MESSAGE_CONSTRAINTS =
- "Phone numbers should only contain numbers, and it should be at least 3 digits long";
- public static final String VALIDATION_REGEX = "\\d{3,}";
+ "Phone numbers should only contain numbers, and it should be 8 digits long";
+ public static final String VALIDATION_REGEX = "\\d{8}";
public final String value;
/**
diff --git a/src/main/java/seedu/address/model/student/Student.java b/src/main/java/seedu/address/model/student/Student.java
new file mode 100644
index 00000000000..dacf5f10f29
--- /dev/null
+++ b/src/main/java/seedu/address/model/student/Student.java
@@ -0,0 +1,221 @@
+package seedu.address.model.student;
+
+import static java.util.Objects.requireNonNull;
+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.commons.util.ToStringBuilder;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.assignment.Assignment;
+import seedu.address.model.assignment.UniqueAssignmentList;
+import seedu.address.model.subject.Subject;
+
+/**
+ * Represents a Student in the address book.
+ * Guarantees: details are present and not null, field values are validated, immutable.
+ */
+public class Student {
+
+ // Identity fields
+ private final Name name;
+ private final Phone phone;
+ private final Email email;
+ private final Address address;
+
+ // Data fields
+ private final Set subjects = new HashSet<>();
+ private final UniqueAssignmentList assignments;
+
+
+ /**
+ * Every field must be present and not null.
+ */
+ public Student(Name name, Phone phone, Email email, Address address) {
+ requireAllNonNull(name, phone, email, address);
+ this.name = name;
+ this.phone = phone;
+ this.email = email;
+ this.address = address;
+ this.assignments = new UniqueAssignmentList();
+ }
+
+ /**
+ * Every field must be present and not null.
+ * Additional constructor for student to include subject, tags and assignments.
+ */
+ public Student(Name name, Phone phone, Email email, Address address,
+ Set subjects) {
+ requireAllNonNull(name, phone, email, address, subjects);
+ this.name = name;
+ this.phone = phone;
+ this.email = email;
+ this.address = address;
+ this.subjects.addAll(subjects);
+ this.assignments = new UniqueAssignmentList();
+ }
+
+ /**
+ * Every field must be present and not null.
+ * Additional constructor for student to include assignments.
+ */
+ public Student(Name name, Phone phone, Email email, Address address,
+ Set subjects,
+ UniqueAssignmentList assignments) {
+ requireAllNonNull(name, phone, email, address, subjects, assignments);
+ this.name = name;
+ this.phone = phone;
+ this.email = email;
+ this.address = address;
+ this.subjects.addAll(subjects);
+ this.assignments = assignments;
+ }
+
+
+
+ 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 getSubjects() {
+ return Collections.unmodifiableSet(subjects);
+ }
+
+ /**
+ * Returns true if the student has the subject.
+ */
+ public boolean hasSubject(Subject targetSubject) {
+ return subjects.contains(targetSubject);
+ }
+
+ public UniqueAssignmentList getAssignments() {
+ return assignments;
+ }
+
+ /**
+ * Returns a new student with the assignment added.
+ */
+ public Student addAssignment(Assignment assignment) {
+ requireNonNull(assignment);
+ UniqueAssignmentList newAssignments = this.assignments;
+ newAssignments.add(assignment);
+ return new Student(name, phone, email, address, subjects, newAssignments);
+ }
+
+ /**
+ * Returns boolean on whether assignment already exists in student.
+ */
+ public boolean hasAssignment(Assignment assignment) {
+ requireNonNull(assignment);
+ return assignments.contains(assignment);
+ }
+
+ /**
+ * Returns true if both students have the same name or phone or email.
+ * This defines a weaker notion of equality between two students.
+ */
+ public boolean isSameStudent(Student otherStudent) {
+ if (otherStudent == this) {
+ return true;
+ }
+
+ return otherStudent != null && ((otherStudent.getName().equals(this.getName())
+ || otherStudent.getPhone().equals(this.getPhone()) || otherStudent.getEmail().equals(this.getEmail())));
+ }
+
+ /**
+ * Returns true if both students have the same identity and data fields.
+ * This defines a stronger notion of equality between two students.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Student)) {
+ return false;
+ }
+
+ Student otherStudent = (Student) other;
+ return name.equals(otherStudent.name)
+ && phone.equals(otherStudent.phone)
+ && email.equals(otherStudent.email)
+ && address.equals(otherStudent.address)
+ && subjects.equals(otherStudent.subjects);
+ }
+
+ @Override
+ public int hashCode() {
+ // use this method for custom fields hashing instead of implementing your own
+ return Objects.hash(name, phone, email, address, subjects, assignments);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("name", name)
+ .add("phone", phone)
+ .add("email", email)
+ .add("address", address)
+ .add("subjects", subjects)
+ .add("assignments", assignments)
+ .toString();
+ }
+
+ /**
+ * Marks an assignment identified by its name as completed for the student.
+ *
+ * @param assignmentName The name of the assignment to mark as completed.
+ * @return A new Student instance with the updated assignment status.
+ * @throws CommandException if no assignment with the specified name exists for this student.
+ */
+ public Student markAssignment(String assignmentName) throws CommandException {
+ assignments.markAssignment(assignmentName);
+ return new Student(name, phone, email, address, subjects, assignments);
+ }
+
+ /**
+ * Unmarks an assignment identified by its name as uncompleted for the student.
+ *
+ * @param assignmentName The name of the assignment to mark as uncompleted.
+ * @return A new Student instance with the updated assignment status.
+ * @throws CommandException if no assignment with the specified name exists for this student.
+ */
+ public Student unmarkAssignment(String assignmentName) throws CommandException {
+ assignments.unmarkAssignment(assignmentName);
+ return new Student(name, phone, email, address, subjects, assignments);
+ }
+
+ public void deleteAssignment(String assignmentName) {
+ assignments.deleteAssignment(assignmentName);
+ }
+
+ public Student copy() {
+ return new Student(name, phone, email, address, subjects, assignments);
+ }
+
+ public void clearAssignments() {
+ assignments.clear();
+ }
+}
diff --git a/src/main/java/seedu/address/model/student/UniqueStudentList.java b/src/main/java/seedu/address/model/student/UniqueStudentList.java
new file mode 100644
index 00000000000..4d0276cfa2f
--- /dev/null
+++ b/src/main/java/seedu/address/model/student/UniqueStudentList.java
@@ -0,0 +1,181 @@
+package seedu.address.model.student;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.Iterator;
+import java.util.List;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import seedu.address.model.student.exceptions.DuplicateStudentException;
+import seedu.address.model.student.exceptions.StudentNotFoundException;
+import seedu.address.model.subject.Subject;
+
+/**
+ * A list of students that enforces uniqueness between its elements and does not allow nulls.
+ * A student is considered unique by comparing using {@code Student#isSameStudent(Student)}.
+ * As such, adding and updating of
+ * students uses Student#isSameStudent(Student) for equality to ensure that the
+ * student being added or updated is
+ * unique in terms of identity in the UniqueStudentList.
+ * However, the removal of a student uses Student#equals(Object)
+ * to ensure that the student with exactly the same fields will be removed.
+ * Supports a minimal set of list operations.
+ *
+ * @see Student#isSameStudent(Student)
+ */
+public class UniqueStudentList implements Iterable {
+
+ private final ObservableList internalList = FXCollections.observableArrayList();
+ private final ObservableList internalUnmodifiableList =
+ FXCollections.unmodifiableObservableList(internalList);
+
+ /**
+ * Returns true if the list contains an equivalent student as the given argument.
+ */
+ public boolean contains(Student toCheck) {
+ requireNonNull(toCheck);
+ return internalList.stream().anyMatch(toCheck::isSameStudent);
+ }
+
+ /**
+ * Adds a student to the list.
+ * The student must not already exist in the list.
+ */
+ public void add(Student toAdd) {
+ requireNonNull(toAdd);
+ if (contains(toAdd)) {
+ throw new DuplicateStudentException();
+ }
+ internalList.add(toAdd);
+ }
+
+ /**
+ * Replaces the student {@code target} in the list with {@code editedStudent}.
+ * {@code target} must exist in the list.
+ * The student identity of {@code editedStudent} must not be the same as another existing student in the list.
+ */
+ public void setStudent(Student target, Student editedStudent) {
+ requireAllNonNull(target, editedStudent);
+
+ int index = internalList.indexOf(target);
+ if (index == -1) {
+ throw new StudentNotFoundException();
+ }
+
+ if (!target.isSameStudent(editedStudent) && contains(editedStudent)) {
+ throw new DuplicateStudentException();
+ }
+
+ internalList.set(index, editedStudent);
+ }
+
+ /**
+ * Removes the equivalent student from the list.
+ * The student must exist in the list.
+ */
+ public void remove(Student toRemove) {
+ requireNonNull(toRemove);
+ if (!internalList.remove(toRemove)) {
+ throw new StudentNotFoundException();
+ }
+ }
+
+ public void setStudents(UniqueStudentList replacement) {
+ requireNonNull(replacement);
+ internalList.setAll(replacement.internalList);
+ }
+
+ /**
+ * Replaces the contents of this list with {@code students}.
+ * {@code students} must not contain duplicate students.
+ */
+ public void setStudents(List students) {
+ requireAllNonNull(students);
+ if (!studentsAreUnique(students)) {
+ throw new DuplicateStudentException();
+ }
+
+ internalList.setAll(students);
+ }
+
+ /**
+ * Returns true if a student has a subject.
+ */
+ public boolean hasStudentSubjects(Student student, Subject subject) {
+ requireAllNonNull(student, subject);
+ Student studentFound = internalList.stream().filter(student::isSameStudent).findFirst().orElse(null);
+ if (studentFound == null) {
+ throw new StudentNotFoundException();
+ }
+
+ return studentFound.hasSubject(subject);
+ }
+
+ /**
+ * Deletes an assignment from a student.
+ * The assignment must belong to a student in the list.
+ */
+ public void deleteAssignment(Student target, String assignmentName) {
+ requireAllNonNull(target, assignmentName);
+ int index = internalList.indexOf(target);
+ if (index == -1) {
+ throw new StudentNotFoundException();
+ }
+ Student student = internalList.get(index);
+ student.deleteAssignment(assignmentName);
+ internalList.set(index, student);
+ }
+
+ /**
+ * Returns the backing list as an unmodifiable {@code ObservableList}.
+ */
+ public ObservableList asUnmodifiableObservableList() {
+ return internalUnmodifiableList;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return internalList.iterator();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof UniqueStudentList)) {
+ return false;
+ }
+
+ UniqueStudentList otherUniqueStudentList = (UniqueStudentList) other;
+ return internalList.equals(otherUniqueStudentList.internalList);
+ }
+
+ @Override
+ public int hashCode() {
+ return internalList.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return internalList.toString();
+ }
+
+ /**
+ * Returns true if {@code students} contains only unique students.
+ */
+ private boolean studentsAreUnique(List students) {
+ for (int i = 0; i < students.size() - 1; i++) {
+ for (int j = i + 1; j < students.size(); j++) {
+ if (students.get(i).isSameStudent(students.get(j))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/seedu/address/model/student/exceptions/DuplicateStudentException.java b/src/main/java/seedu/address/model/student/exceptions/DuplicateStudentException.java
new file mode 100644
index 00000000000..43a1cab1e3b
--- /dev/null
+++ b/src/main/java/seedu/address/model/student/exceptions/DuplicateStudentException.java
@@ -0,0 +1,12 @@
+package seedu.address.model.student.exceptions;
+
+/**
+ * Signals that the operation will result in duplicate Students
+ * (Students are considered duplicates if they have the same
+ * identity).
+ */
+public class DuplicateStudentException extends RuntimeException {
+ public DuplicateStudentException() {
+ super("Operation would result in duplicate students");
+ }
+}
diff --git a/src/main/java/seedu/address/model/student/exceptions/StudentNotFoundException.java b/src/main/java/seedu/address/model/student/exceptions/StudentNotFoundException.java
new file mode 100644
index 00000000000..2b41e9e0296
--- /dev/null
+++ b/src/main/java/seedu/address/model/student/exceptions/StudentNotFoundException.java
@@ -0,0 +1,6 @@
+package seedu.address.model.student.exceptions;
+
+/**
+ * Signals that the operation is unable to find the specified student.
+ */
+public class StudentNotFoundException extends RuntimeException {}
diff --git a/src/main/java/seedu/address/model/subject/Subject.java b/src/main/java/seedu/address/model/subject/Subject.java
new file mode 100644
index 00000000000..2a46500687a
--- /dev/null
+++ b/src/main/java/seedu/address/model/subject/Subject.java
@@ -0,0 +1,71 @@
+package seedu.address.model.subject;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+/**
+ * Represents a Subject in the address book.
+ * Guarantees: immutable; name is valid as declared in {@link #isValidSubjectName(String)}
+ */
+public class Subject {
+
+ public static final int MAX_LENGTH = 50;
+ public static final String MESSAGE_CONSTRAINTS = "Subject names should be alphanumeric and can contain spaces, "
+ + "apostrophes, ampersands, and hyphens. It should not exceed " + MAX_LENGTH + " characters.";
+ public static final String MESSAGE_DUPLICATE_SUBJECT = "Error: This subject is a duplicate of another subject.";
+ public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} '&-]*";
+
+ public final String subjectName;
+
+ /**
+ * Constructs a {@code Subject}.
+ *
+ * @param subjectName A valid subject name.
+ */
+ public Subject(String subjectName) {
+ requireNonNull(subjectName);
+ checkArgument(isValidSubjectName(subjectName), MESSAGE_CONSTRAINTS);
+ this.subjectName = Arrays.stream(subjectName.trim().split("\\s+"))
+ .map(word -> word.isEmpty() ? word
+ : Character.toUpperCase(word.charAt(0)) + word.substring(1).toLowerCase())
+ .collect(Collectors.joining(" "));
+ }
+
+ /**
+ * Returns true if a given string is a valid subject name.
+ */
+ public static boolean isValidSubjectName(String test) {
+ return test.matches(VALIDATION_REGEX) && test.length() <= MAX_LENGTH;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Subject)) {
+ return false;
+ }
+
+ Subject otherSubject = (Subject) other;
+ return subjectName.toLowerCase().equals(otherSubject.subjectName.toLowerCase());
+ }
+
+ @Override
+ public int hashCode() {
+ return subjectName.hashCode();
+ }
+
+ /**
+ * Format state as text for viewing.
+ */
+ public String toString() {
+ return subjectName;
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java
deleted file mode 100644
index f1a0d4e233b..00000000000
--- a/src/main/java/seedu/address/model/tag/Tag.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package seedu.address.model.tag;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
-
-/**
- * Represents a Tag in the address book.
- * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)}
- */
-public class Tag {
-
- public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric";
- public static final String VALIDATION_REGEX = "\\p{Alnum}+";
-
- public final String tagName;
-
- /**
- * Constructs a {@code Tag}.
- *
- * @param tagName A valid tag name.
- */
- public Tag(String tagName) {
- requireNonNull(tagName);
- checkArgument(isValidTagName(tagName), MESSAGE_CONSTRAINTS);
- this.tagName = tagName;
- }
-
- /**
- * Returns true if a given string is a valid tag name.
- */
- public static boolean isValidTagName(String test) {
- return test.matches(VALIDATION_REGEX);
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Tag)) {
- return false;
- }
-
- Tag otherTag = (Tag) other;
- return tagName.equals(otherTag.tagName);
- }
-
- @Override
- public int hashCode() {
- return tagName.hashCode();
- }
-
- /**
- * Format state as text for viewing.
- */
- public String toString() {
- return '[' + tagName + ']';
- }
-
-}
diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java
index 1806da4facf..1f3a9d48539 100644
--- a/src/main/java/seedu/address/model/util/SampleDataUtil.java
+++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java
@@ -6,55 +6,135 @@
import seedu.address.model.AddressBook;
import seedu.address.model.ReadOnlyAddressBook;
-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.tag.Tag;
+import seedu.address.model.assignment.Assignment;
+import seedu.address.model.assignment.UniqueAssignmentList;
+import seedu.address.model.datetimeutil.Date;
+import seedu.address.model.datetimeutil.Time;
+import seedu.address.model.lesson.Lesson;
+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.subject.Subject;
+
/**
* Contains utility methods for populating {@code AddressBook} with sample data.
*/
public class SampleDataUtil {
- public static Person[] getSamplePersons() {
- return new Person[] {
- new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"),
+ public static Student[] getSampleStudents() {
+ return new Student[] {
+ new Student(new Name("Alex Yeoh"),
+ new Phone("87438807"),
+ new Email("alexyeoh@example.com"),
new Address("Blk 30 Geylang Street 29, #06-40"),
- getTagSet("friends")),
- new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"),
- new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"),
- getTagSet("colleagues", "friends")),
- new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"),
- new Address("Blk 11 Ang Mo Kio Street 74, #11-04"),
- getTagSet("neighbours")),
- new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"),
- new Address("Blk 436 Serangoon Gardens Street 26, #16-43"),
- getTagSet("family")),
- new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"),
- new Address("Blk 47 Tampines Street 20, #17-35"),
- getTagSet("classmates")),
- new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"),
- new Address("Blk 45 Aljunied Street 85, #11-31"),
- getTagSet("colleagues"))
+ getSubjectSet("Math", "Physics"),
+ getAssignmentSet(getAssignment("Assignment 1", "30-05-2025"),
+ getAssignment("Assignment 2", "31-05-2025", true))),
+
+ new Student(new Name("Bernice Yu"),
+ new Phone("99272758"),
+ new Email("berniceyu@example.com"),
+ new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"),
+ getSubjectSet("Science", "English"),
+ getAssignmentSet(getAssignment("English Essay", "15-07-2025"))),
+
+ new Student(new Name("Charlotte Oliveiro"),
+ new Phone("93210283"),
+ new Email("charlotte@example.com"),
+ new Address("Blk 11 Ang Mo Kio Street 74, #11-04"),
+ getSubjectSet("Literature"),
+ getAssignmentSet()),
+
+ new Student(new Name("David Li"),
+ new Phone("91031282"),
+ new Email("lidavid@example.com"),
+ new Address("Blk 436 Serangoon Gardens Street 26, #16-43"),
+ getSubjectSet("Chemistry"),
+ getAssignmentSet()),
+
+ new Student(new Name("Irfan Ibrahim"),
+ new Phone("92492021"),
+ new Email("irfan@example.com"),
+ new Address("Blk 47 Tampines Street 20, #17-35"),
+ getSubjectSet("Biology", "Math"),
+ getAssignmentSet()),
+
+ new Student(new Name("Roy Balakrishnan"),
+ new Phone("92624417"),
+ new Email("royb@example.com"),
+ new Address("Blk 45 Aljunied Street 85, #11-31"),
+ getSubjectSet("Economics"),
+ getAssignmentSet()),
+
+ new Student(new Name("Zoy White"),
+ new Phone("94351253"),
+ new Email("zoyw@gnail.com"),
+ new Address("Blk 45 Aljunied Street 85, #11-31"),
+ getSubjectSet("History"),
+ getAssignmentSet())
+ };
+ }
+ public static Lesson[] getSampleLessons() {
+ return new Lesson[] {
+ new Lesson(new Subject("Math"), new Name("Alex Yeoh"),
+ new Date("07-07-2025"), new Time("09:00")),
+
+ new Lesson(new Subject("English"), new Name("Bernice Yu"),
+ new Date("08-07-2025"), new Time("10:30")),
+
+ new Lesson(new Subject("Literature"), new Name("Charlotte Oliveiro"),
+ new Date("09-07-2025"), new Time("11:00")),
+
+ new Lesson(new Subject("Chemistry"), new Name("David Li"),
+ new Date("10-07-2025"), new Time("13:00")),
+
+ new Lesson(new Subject("Biology"), new Name("Irfan Ibrahim"),
+ new Date("11-07-2025"), new Time("14:30")),
+
+ new Lesson(new Subject("Economics"), new Name("Roy Balakrishnan"),
+ new Date("12-07-2025"), new Time("16:00")),
+
+ new Lesson(new Subject("History"), new Name("Zoy White"),
+ new Date("13-07-2025"), new Time("17:30")),
};
}
public static ReadOnlyAddressBook getSampleAddressBook() {
AddressBook sampleAb = new AddressBook();
- for (Person samplePerson : getSamplePersons()) {
- sampleAb.addPerson(samplePerson);
+ for (Student sampleStudent : getSampleStudents()) {
+ sampleAb.addStudent(sampleStudent);
+ }
+ for (Lesson sampleLesson : getSampleLessons()) {
+ sampleAb.addLesson(sampleLesson);
}
return sampleAb;
}
/**
- * Returns a tag set containing the list of strings given.
+ * Returns a subject set containing the list of strings given.
*/
- public static Set getTagSet(String... strings) {
+ public static Set getSubjectSet(String... strings) {
return Arrays.stream(strings)
- .map(Tag::new)
+ .map(Subject::new)
.collect(Collectors.toSet());
}
+ public static UniqueAssignmentList getAssignmentSet(Assignment... assignments) {
+ UniqueAssignmentList assignmentSet = new UniqueAssignmentList();
+ for (Assignment assignment : assignments) {
+ assignmentSet.add(assignment);
+ }
+ return assignmentSet;
+ }
+
+ public static Assignment getAssignment(String assignment, String dueDate) {
+ return new Assignment(assignment, new Date(dueDate));
+ }
+
+ public static Assignment getAssignment(String assignment, String dueDate, boolean isDone) {
+ return new Assignment(assignment, new Date(dueDate), isDone);
+ }
+
}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedAssignment.java b/src/main/java/seedu/address/storage/JsonAdaptedAssignment.java
new file mode 100644
index 00000000000..333b2169b52
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedAssignment.java
@@ -0,0 +1,67 @@
+package seedu.address.storage;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.assignment.Assignment;
+import seedu.address.model.datetimeutil.Date;
+
+/**
+ * Jackson-friendly version of {@link Assignment}.
+ */
+class JsonAdaptedAssignment {
+
+ public static final String MISSING_FIELD_MESSAGE_FORMAT = "Assignment's %s field is missing!";
+
+ private final String assignmentName;
+ private final String dueDate;
+ private final int isCompleted;
+
+ /**
+ * Constructs a {@code JsonAdaptedAssignment} with the given {@code assignmentName}.
+ */
+ @JsonCreator
+ public JsonAdaptedAssignment(@JsonProperty("assignment name") String assignmentName,
+ @JsonProperty("due date") String dueDate,
+ @JsonProperty("is done") boolean isDone) {
+ this.assignmentName = assignmentName;
+ this.dueDate = dueDate;
+ this.isCompleted = isDone ? 1 : 0;
+ }
+
+ /**
+ * Converts a given {@code assignment} into this class for Jackson use.
+ */
+ public JsonAdaptedAssignment(Assignment source) {
+ assignmentName = source.assignmentName;
+ dueDate = source.dueDate.date;
+ isCompleted = source.isDone() ? 1 : 0;
+ }
+
+ /**
+ * Converts this Json-friendly adapted assignment object into the model's {@code Assignment} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted assignment.
+ */
+ public Assignment toModelType() throws IllegalValueException {
+ if (assignmentName == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ Assignment.class.getSimpleName()));
+ }
+ if (!Assignment.isValidAssignmentName(assignmentName)) {
+ throw new IllegalValueException(Assignment.MESSAGE_CONSTRAINTS);
+ }
+ if (dueDate == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ Date.class.getSimpleName()));
+ }
+ if (!Assignment.isValidAssignmentDate(dueDate)) {
+ throw new IllegalValueException(Assignment.MESSAGE_CONSTRAINTS);
+ }
+ final Date modelDate = new Date(dueDate);
+
+ return new Assignment(assignmentName, modelDate, isCompleted == 1);
+ }
+
+}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedLesson.java b/src/main/java/seedu/address/storage/JsonAdaptedLesson.java
new file mode 100644
index 00000000000..2d48388ecfa
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedLesson.java
@@ -0,0 +1,88 @@
+package seedu.address.storage;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.datetimeutil.Date;
+import seedu.address.model.datetimeutil.Time;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.student.Name;
+import seedu.address.model.subject.Subject;
+
+/**
+ * Jackson-friendly version of {@link Lesson}.
+ */
+class JsonAdaptedLesson {
+
+ public static final String MISSING_FIELD_MESSAGE_FORMAT = "Lesson's %s field is missing!";
+
+ private final String name;
+ private final String date;
+ private final String time;
+ private final String subject;
+
+ /**
+ * Constructs a {@code JsonAdaptedLesson} with the given lesson details.
+ */
+ @JsonCreator
+ public JsonAdaptedLesson(@JsonProperty("subject") String subject,
+ @JsonProperty("name") String name,
+ @JsonProperty("date") String date, @JsonProperty("time") String time) {
+ this.name = name;
+ this.date = date;
+ this.time = time;
+ this.subject = subject;
+ }
+
+ /**
+ * Converts a given {@code Lesson} into this class for Jackson use.
+ */
+ public JsonAdaptedLesson(Lesson source) {
+ name = source.getStudentName().fullName;
+ date = source.getDate().date;
+ time = source.getTime().time;
+ subject = source.getSubject().subjectName;
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted lesson object into the model's {@code Lesson} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted lesson.
+ */
+ public Lesson toModelType() throws IllegalValueException {
+ if (name == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()));
+ }
+ if (!Name.isValidName(name)) {
+ throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS);
+ }
+ final Name modelName = new Name(name);
+
+ if (date == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Date.class.getSimpleName()));
+ }
+ if (!Date.isValidDate(date)) {
+ throw new IllegalValueException(Date.MESSAGE_CONSTRAINTS);
+ }
+ final Date modelDate = new Date(date);
+
+ if (time == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Time.class.getSimpleName()));
+ }
+ if (!Time.isValidTime(time)) {
+ throw new IllegalValueException(Time.MESSAGE_CONSTRAINTS);
+ }
+ final Time modelTime = new Time(time);
+
+ if (subject == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Subject.class.getSimpleName()));
+ }
+ if (!Subject.isValidSubjectName(subject)) {
+ throw new IllegalValueException(Subject.MESSAGE_CONSTRAINTS);
+ }
+ final Subject modelSubject = new Subject(subject);
+
+ return new Lesson(modelSubject, modelName, modelDate, modelTime);
+ }
+}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
deleted file mode 100644
index bd1ca0f56c8..00000000000
--- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package seedu.address.storage;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import seedu.address.commons.exceptions.IllegalValueException;
-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.tag.Tag;
-
-/**
- * Jackson-friendly version of {@link Person}.
- */
-class JsonAdaptedPerson {
-
- public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!";
-
- private final String name;
- private final String phone;
- private final String email;
- private final String address;
- private final List tags = new ArrayList<>();
-
- /**
- * Constructs a {@code JsonAdaptedPerson} with the given person details.
- */
- @JsonCreator
- public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone,
- @JsonProperty("email") String email, @JsonProperty("address") String address,
- @JsonProperty("tags") List tags) {
- this.name = name;
- this.phone = phone;
- this.email = email;
- this.address = address;
- if (tags != null) {
- this.tags.addAll(tags);
- }
- }
-
- /**
- * Converts a given {@code Person} into this class for Jackson use.
- */
- public JsonAdaptedPerson(Person source) {
- name = source.getName().fullName;
- phone = source.getPhone().value;
- email = source.getEmail().value;
- address = source.getAddress().value;
- tags.addAll(source.getTags().stream()
- .map(JsonAdaptedTag::new)
- .collect(Collectors.toList()));
- }
-
- /**
- * Converts this Jackson-friendly adapted person object into the model's {@code Person} object.
- *
- * @throws IllegalValueException if there were any data constraints violated in the adapted person.
- */
- public Person toModelType() throws IllegalValueException {
- final List personTags = new ArrayList<>();
- for (JsonAdaptedTag tag : tags) {
- personTags.add(tag.toModelType());
- }
-
- if (name == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()));
- }
- if (!Name.isValidName(name)) {
- throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS);
- }
- final Name modelName = new Name(name);
-
- if (phone == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()));
- }
- if (!Phone.isValidPhone(phone)) {
- throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS);
- }
- final Phone modelPhone = new Phone(phone);
-
- if (email == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName()));
- }
- if (!Email.isValidEmail(email)) {
- throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS);
- }
- final Email modelEmail = new Email(email);
-
- if (address == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()));
- }
- if (!Address.isValidAddress(address)) {
- throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS);
- }
- final Address modelAddress = new Address(address);
-
- final Set modelTags = new HashSet<>(personTags);
- return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags);
- }
-
-}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedStudent.java b/src/main/java/seedu/address/storage/JsonAdaptedStudent.java
new file mode 100644
index 00000000000..bf931d51d46
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedStudent.java
@@ -0,0 +1,140 @@
+package seedu.address.storage;
+
+import static seedu.address.logic.commands.AddAssignmentCommand.MESSAGE_DUPLICATE_ASSIGNMENT;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.assignment.Assignment;
+import seedu.address.model.assignment.UniqueAssignmentList;
+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.subject.Subject;
+
+
+/**
+ * Jackson-friendly version of {@link Student}.
+ */
+class JsonAdaptedStudent {
+
+ public static final String MISSING_FIELD_MESSAGE_FORMAT = "Student's %s field is missing!";
+
+ private final String name;
+ private final String phone;
+ private final String address;
+ private final String email;
+ private final List subjects = new ArrayList<>();
+ private final List assignments;
+
+ /**
+ * Constructs a {@code JsonAdaptedStudent} with the given student details.
+ */
+ @JsonCreator
+ public JsonAdaptedStudent(@JsonProperty("name") String name, @JsonProperty("phone") String phone,
+ @JsonProperty("address") String address, @JsonProperty("email") String email,
+ @JsonProperty("subjects") List subjects,
+ @JsonProperty("assignments") List assignments) {
+ this.name = name;
+ this.phone = phone;
+ this.address = address;
+ this.email = email;
+ if (subjects != null) {
+ this.subjects.addAll(subjects);
+ }
+ this.assignments = assignments;
+ }
+
+ /**
+ * Converts a given {@code Student} into this class for Jackson use.
+ */
+ public JsonAdaptedStudent(Student source) {
+ name = source.getName().fullName;
+ phone = source.getPhone().value;
+ email = source.getEmail().value;
+ address = source.getAddress().value;
+ subjects.addAll(source.getSubjects().stream()
+ .map(JsonAdaptedSubject::new)
+ .toList());
+ assignments = source.getAssignments().asUnmodifiableObservableList().stream()
+ .map(JsonAdaptedAssignment::new)
+ .collect(java.util.stream.Collectors.toList());
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted student object into the model's {@code Student} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted student.
+ */
+ public Student toModelType() throws IllegalValueException {
+ // model type for subjects
+ final Set studentSubjects = new HashSet<>();
+
+ for (JsonAdaptedSubject subject : subjects) {
+ studentSubjects.add(subject.toModelType());
+ }
+
+ // model type for assignments
+ final UniqueAssignmentList studentAssignments = new UniqueAssignmentList();
+ if (assignments != null) {
+ for (JsonAdaptedAssignment assignment : assignments) {
+ Assignment assignmentToAdd = assignment.toModelType();
+ if (studentAssignments.contains(assignmentToAdd)) {
+ throw new IllegalValueException(MESSAGE_DUPLICATE_ASSIGNMENT);
+ }
+ studentAssignments.add(assignment.toModelType());
+ }
+ }
+
+ // model type for name
+ if (name == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()));
+ }
+ if (!Name.isValidName(name)) {
+ throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS);
+ }
+ final Name modelName = new Name(name);
+
+ // model type for phone
+ if (phone == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()));
+ }
+ if (!Phone.isValidPhone(phone)) {
+ throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS);
+ }
+ final Phone modelPhone = new Phone(phone);
+
+ // model type for address
+ if (address == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()));
+ }
+ if (!Address.isValidAddress(address)) {
+ throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS);
+ }
+ final Address modelAddress = new Address(address);
+
+ // model type for email
+ if (email == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName()));
+ }
+ if (!Email.isValidEmail(email)) {
+ throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS);
+ }
+ final Email modelEmail = new Email(email);
+
+
+ final Set modelSubjects = new HashSet<>(studentSubjects);
+
+ return new Student(modelName, modelPhone, modelEmail, modelAddress, modelSubjects,
+ studentAssignments);
+ }
+
+}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedSubject.java b/src/main/java/seedu/address/storage/JsonAdaptedSubject.java
new file mode 100644
index 00000000000..f5ff7f254c0
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedSubject.java
@@ -0,0 +1,48 @@
+package seedu.address.storage;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.subject.Subject;
+
+/**
+ * Jackson-friendly version of {@link Subject}.
+ */
+class JsonAdaptedSubject {
+
+ private final String subjectName;
+
+ /**
+ * Constructs a {@code JsonAdaptedSubject} with the given {@code subjectName}.
+ */
+ @JsonCreator
+ public JsonAdaptedSubject(String subjectName) {
+ this.subjectName = subjectName;
+ }
+
+ /**
+ * Converts a given {@code Subject} into this class for Jackson use.
+ */
+ public JsonAdaptedSubject(Subject source) {
+ subjectName = source.subjectName;
+ }
+
+ @JsonValue
+ public String getSubjectName() {
+ return subjectName;
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted subject object into the model's {@code Subject} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted subject.
+ */
+ public Subject toModelType() throws IllegalValueException {
+ if (!Subject.isValidSubjectName(subjectName)) {
+ throw new IllegalValueException(Subject.MESSAGE_CONSTRAINTS);
+ }
+ return new Subject(subjectName);
+ }
+
+}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/seedu/address/storage/JsonAdaptedTag.java
deleted file mode 100644
index 0df22bdb754..00000000000
--- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package seedu.address.storage;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonValue;
-
-import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.tag.Tag;
-
-/**
- * Jackson-friendly version of {@link Tag}.
- */
-class JsonAdaptedTag {
-
- private final String tagName;
-
- /**
- * Constructs a {@code JsonAdaptedTag} with the given {@code tagName}.
- */
- @JsonCreator
- public JsonAdaptedTag(String tagName) {
- this.tagName = tagName;
- }
-
- /**
- * Converts a given {@code Tag} into this class for Jackson use.
- */
- public JsonAdaptedTag(Tag source) {
- tagName = source.tagName;
- }
-
- @JsonValue
- public String getTagName() {
- return tagName;
- }
-
- /**
- * Converts this Jackson-friendly adapted tag object into the model's {@code Tag} object.
- *
- * @throws IllegalValueException if there were any data constraints violated in the adapted tag.
- */
- public Tag toModelType() throws IllegalValueException {
- if (!Tag.isValidTagName(tagName)) {
- throw new IllegalValueException(Tag.MESSAGE_CONSTRAINTS);
- }
- return new Tag(tagName);
- }
-
-}
diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
index 5efd834091d..142b05631fb 100644
--- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
+++ b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
@@ -11,24 +11,29 @@
import seedu.address.commons.exceptions.IllegalValueException;
import seedu.address.model.AddressBook;
import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Person;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.student.Student;
/**
* An Immutable AddressBook that is serializable to JSON format.
*/
-@JsonRootName(value = "addressbook")
+@JsonRootName(value = "TutorTrack")
class JsonSerializableAddressBook {
- public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s).";
+ public static final String MESSAGE_DUPLICATE_STUDENT = "Students list contains duplicate student(s).";
+ public static final String MESSAGE_DUPLICATE_LESSON = "Lessons list contains duplicate lesson(s).";
- private final List persons = new ArrayList<>();
+ private final List students = new ArrayList<>();
+ private final List lessons = new ArrayList<>();
/**
- * Constructs a {@code JsonSerializableAddressBook} with the given persons.
+ * Constructs a {@code JsonSerializableAddressBook} with the given students and lessons.
*/
@JsonCreator
- public JsonSerializableAddressBook(@JsonProperty("persons") List persons) {
- this.persons.addAll(persons);
+ public JsonSerializableAddressBook(@JsonProperty("students") List students,
+ @JsonProperty("lessons") List lessons) {
+ this.students.addAll(students);
+ this.lessons.addAll(lessons);
}
/**
@@ -37,7 +42,8 @@ public JsonSerializableAddressBook(@JsonProperty("persons") List {
+
+ private static final String FXML = "AssignmentCard.fxml";
+
+ @FXML
+ private Label assignmentName;
+
+ @FXML
+ private Label dueDate;
+
+ @FXML
+ private HBox cardPane;
+
+ /**
+ * Creates a {@code AssignmentCard} with the given {@code Assignment}.
+ */
+ public AssignmentCard(Assignment assignment) {
+ super(FXML);
+ assignmentName.setText(assignment.getAssignmentName());
+ dueDate.setText("Due: " + assignment.getDueDate().toString());
+
+ if (assignment.isDone()) {
+ cardPane.setStyle("-fx-background-color: #4CAF50;");
+ } else {
+ cardPane.setStyle("-fx-background-color: #f44336;");
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/address/ui/HelpWindow.java
index 3f16b2fcf26..055e3bfdf7f 100644
--- a/src/main/java/seedu/address/ui/HelpWindow.java
+++ b/src/main/java/seedu/address/ui/HelpWindow.java
@@ -15,7 +15,7 @@
*/
public class HelpWindow extends UiPart {
- public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html";
+ public static final String USERGUIDE_URL = "https://ay2425s2-cs2103t-t13-4.github.io/tp/UserGuide.html";
public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL;
private static final Logger logger = LogsCenter.getLogger(HelpWindow.class);
diff --git a/src/main/java/seedu/address/ui/ListCard.java b/src/main/java/seedu/address/ui/ListCard.java
new file mode 100644
index 00000000000..34bc79da857
--- /dev/null
+++ b/src/main/java/seedu/address/ui/ListCard.java
@@ -0,0 +1,86 @@
+package seedu.address.ui;
+
+import java.util.Comparator;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Label;
+import javafx.scene.layout.FlowPane;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Region;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.student.Student;
+
+/**
+ * A UI component that displays information of a {@code Lesson} or {@code Student}.
+ */
+public class ListCard extends UiPart {
+
+ /**
+ * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX.
+ * As a consequence, UI elements' variable names cannot be set to such keywords
+ * or an exception will be thrown by JavaFX during runtime.
+ *
+ * @see The issue on AddressBook level 4
+ */
+
+ public final Lesson lesson;
+ public final Student student;
+
+ @FXML
+ private HBox cardPane;
+ @FXML
+ private Label name;
+ @FXML
+ private Label id;
+ @FXML
+ private Label phone;
+ @FXML
+ private Label address;
+ @FXML
+ private Label email;
+ @FXML
+ private Label date;
+ @FXML
+ private Label time;
+ @FXML
+ private Label subject;
+ @FXML
+ private FlowPane subjects;
+ @FXML
+ private FlowPane assignments;
+
+ /**
+ * Creates a {@code LessonCard} with the given {@code Lesson} and index to display.
+ */
+ public ListCard(Lesson lesson, int displayedIndex) {
+ super("LessonListCard.fxml");
+ this.lesson = lesson;
+ this.student = null;
+ id.setText(displayedIndex + ". ");
+ name.setText(lesson.getStudentName().fullName);
+ date.setText(lesson.getDate().toString());
+ time.setText(lesson.getTime().time);
+ subject.setText(lesson.getSubject().subjectName);
+ }
+
+ /**
+ * Creates a {@code StudentCard} with the given {@code Student} and index to display.
+ */
+ public ListCard(Student student, int displayedIndex) {
+ super("StudentListCard.fxml");
+ this.student = student;
+ this.lesson = null;
+ id.setText(displayedIndex + ". ");
+ name.setText(student.getName().fullName);
+ phone.setText(student.getPhone().value);
+ address.setText(student.getAddress().value);
+ email.setText(student.getEmail().value);
+ student.getSubjects().stream()
+ .sorted(Comparator.comparing(subject -> subject.subjectName))
+ .forEach(subject -> subjects.getChildren().add(new Label(subject.subjectName)));
+ student.getAssignments().asUnmodifiableObservableList().stream()
+ .sorted(Comparator.comparing(assignment -> assignment.dueDate))
+ .forEach(assignment -> assignments.getChildren()
+ .add(new AssignmentCard(assignment).getRoot()));
+ }
+}
diff --git a/src/main/java/seedu/address/ui/ListPanel.java b/src/main/java/seedu/address/ui/ListPanel.java
new file mode 100644
index 00000000000..561ffeccd29
--- /dev/null
+++ b/src/main/java/seedu/address/ui/ListPanel.java
@@ -0,0 +1,66 @@
+package seedu.address.ui;
+
+import java.util.logging.Logger;
+
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.ListCell;
+import javafx.scene.control.ListView;
+import javafx.scene.layout.Region;
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.student.Student;
+
+/**
+ * Panel containing the list of lessons/students.
+ */
+public class ListPanel extends UiPart {
+ private static final String FXML = "ListPanel.fxml";
+ private final Logger logger = LogsCenter.getLogger(ListPanel.class);
+
+ @FXML
+ private ListView listView;
+
+ /**
+ * Creates a {@code LessonListPanel} with the given {@code ObservableList}.
+ */
+ @SuppressWarnings("unchecked")
+ // This is to allow for the method to accept both ObservableList and ObservableList
+ // and since both are generated from UniqueListStudent and UniqueListLesson respectively, there is no
+ // risk of data pollution
+ public ListPanel(ObservableList> list) {
+ super(FXML);
+ listView.setItems((ObservableList) list);
+ listView.setCellFactory(listView -> new ListViewCell());
+ }
+
+ /**
+ * Custom {@code ListCell} that displays the graphics of a {@code Lesson} or
+ * {@code Student} using a {@code ListCard}.
+ */
+ class ListViewCell extends ListCell {
+ @Override
+ protected void updateItem(Object obj, boolean empty) {
+ if (obj instanceof Lesson curr) {
+ super.updateItem(curr, empty);
+ if (empty || curr == null) {
+ setGraphic(null);
+ setText(null);
+ } else {
+ setGraphic(new ListCard(curr, getIndex() + 1).getRoot());
+ }
+ } else if (obj instanceof Student curr) {
+ super.updateItem(curr, empty);
+ if (empty || curr == null) {
+ setGraphic(null);
+ setText(null);
+ } else {
+ setGraphic(new ListCard(curr, getIndex() + 1).getRoot());
+ }
+ }
+
+
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java
index 79e74ef37c0..6d4e88688c4 100644
--- a/src/main/java/seedu/address/ui/MainWindow.java
+++ b/src/main/java/seedu/address/ui/MainWindow.java
@@ -31,9 +31,9 @@ public class MainWindow extends UiPart {
private Logic logic;
// Independent Ui parts residing in this Ui container
- private PersonListPanel personListPanel;
private ResultDisplay resultDisplay;
private HelpWindow helpWindow;
+ private ListPanel listPanel;
@FXML
private StackPane commandBoxPlaceholder;
@@ -42,7 +42,7 @@ public class MainWindow extends UiPart {
private MenuItem helpMenuItem;
@FXML
- private StackPane personListPanelPlaceholder;
+ private StackPane listPanelPlaceholder;
@FXML
private StackPane resultDisplayPlaceholder;
@@ -78,7 +78,7 @@ private void setAccelerators() {
/**
* Sets the accelerator of a MenuItem.
- * @param keyCombination the KeyCombination value of the accelerator
+ * @param keyCombination the KeyCombination assignmentName of the accelerator
*/
private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) {
menuItem.setAccelerator(keyCombination);
@@ -110,8 +110,9 @@ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) {
* Fills up all the placeholders of this window.
*/
void fillInnerParts() {
- personListPanel = new PersonListPanel(logic.getFilteredPersonList());
- personListPanelPlaceholder.getChildren().add(personListPanel.getRoot());
+
+ listPanel = new ListPanel(logic.getFilteredCurrList());
+ listPanelPlaceholder.getChildren().add(listPanel.getRoot());
resultDisplay = new ResultDisplay();
resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot());
@@ -163,8 +164,10 @@ private void handleExit() {
primaryStage.hide();
}
- public PersonListPanel getPersonListPanel() {
- return personListPanel;
+ private void handleUpdateList() {
+ listPanelPlaceholder.getChildren().clear();
+ listPanel = new ListPanel(logic.getFilteredCurrList());
+ listPanelPlaceholder.getChildren().add(listPanel.getRoot());
}
/**
@@ -186,6 +189,10 @@ private CommandResult executeCommand(String commandText) throws CommandException
handleExit();
}
+ if (commandResult.isUpdateList()) {
+ handleUpdateList();
+ }
+
return commandResult;
} catch (CommandException | ParseException e) {
logger.info("An error occurred while executing command: " + commandText);
diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java
deleted file mode 100644
index 094c42cda82..00000000000
--- a/src/main/java/seedu/address/ui/PersonCard.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package seedu.address.ui;
-
-import java.util.Comparator;
-
-import javafx.fxml.FXML;
-import javafx.scene.control.Label;
-import javafx.scene.layout.FlowPane;
-import javafx.scene.layout.HBox;
-import javafx.scene.layout.Region;
-import seedu.address.model.person.Person;
-
-/**
- * An UI component that displays information of a {@code Person}.
- */
-public class PersonCard extends UiPart {
-
- private static final String FXML = "PersonListCard.fxml";
-
- /**
- * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX.
- * As a consequence, UI elements' variable names cannot be set to such keywords
- * or an exception will be thrown by JavaFX during runtime.
- *
- * @see The issue on AddressBook level 4
- */
-
- public final Person person;
-
- @FXML
- private HBox cardPane;
- @FXML
- private Label name;
- @FXML
- private Label id;
- @FXML
- private Label phone;
- @FXML
- private Label address;
- @FXML
- private Label email;
- @FXML
- private FlowPane tags;
-
- /**
- * Creates a {@code PersonCode} with the given {@code Person} and index to display.
- */
- public PersonCard(Person person, int displayedIndex) {
- super(FXML);
- this.person = person;
- id.setText(displayedIndex + ". ");
- name.setText(person.getName().fullName);
- phone.setText(person.getPhone().value);
- address.setText(person.getAddress().value);
- email.setText(person.getEmail().value);
- person.getTags().stream()
- .sorted(Comparator.comparing(tag -> tag.tagName))
- .forEach(tag -> tags.getChildren().add(new Label(tag.tagName)));
- }
-}
diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java
deleted file mode 100644
index f4c501a897b..00000000000
--- a/src/main/java/seedu/address/ui/PersonListPanel.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package seedu.address.ui;
-
-import java.util.logging.Logger;
-
-import javafx.collections.ObservableList;
-import javafx.fxml.FXML;
-import javafx.scene.control.ListCell;
-import javafx.scene.control.ListView;
-import javafx.scene.layout.Region;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.model.person.Person;
-
-/**
- * Panel containing the list of persons.
- */
-public class PersonListPanel extends UiPart {
- private static final String FXML = "PersonListPanel.fxml";
- private final Logger logger = LogsCenter.getLogger(PersonListPanel.class);
-
- @FXML
- private ListView personListView;
-
- /**
- * Creates a {@code PersonListPanel} with the given {@code ObservableList}.
- */
- public PersonListPanel(ObservableList personList) {
- super(FXML);
- personListView.setItems(personList);
- personListView.setCellFactory(listView -> new PersonListViewCell());
- }
-
- /**
- * Custom {@code ListCell} that displays the graphics of a {@code Person} using a {@code PersonCard}.
- */
- class PersonListViewCell extends ListCell {
- @Override
- protected void updateItem(Person person, boolean empty) {
- super.updateItem(person, empty);
-
- if (empty || person == null) {
- setGraphic(null);
- setText(null);
- } else {
- setGraphic(new PersonCard(person, getIndex() + 1).getRoot());
- }
- }
- }
-
-}
diff --git a/src/main/resources/view/AssignmentCard.fxml b/src/main/resources/view/AssignmentCard.fxml
new file mode 100644
index 00000000000..c18c0a98664
--- /dev/null
+++ b/src/main/resources/view/AssignmentCard.fxml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css
index 36e6b001cd8..26e42e8e8de 100644
--- a/src/main/resources/view/DarkTheme.css
+++ b/src/main/resources/view/DarkTheme.css
@@ -1,6 +1,5 @@
.background {
- -fx-background-color: derive(#1d1d1d, 20%);
- background-color: #383838; /* Used in the default.html file */
+ -fx-background-color: #383838; /* Dark background */
}
.label {
@@ -27,6 +26,14 @@
.text-field {
-fx-font-size: 12pt;
-fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: white;
+ -fx-prompt-text-fill: #a0a0a0;
+ -fx-border-radius: 3;
+ -fx-border-color: #4d4d4d;
+}
+
+.text-field:focused {
+ -fx-border-color: #3e7b91;
}
.tab-pane {
@@ -40,16 +47,16 @@
}
.table-view {
- -fx-base: #1d1d1d;
- -fx-control-inner-background: #1d1d1d;
- -fx-background-color: #1d1d1d;
+ -fx-base: #2d2d2d;
+ -fx-control-inner-background: #2d2d2d;
+ -fx-background-color: #2d2d2d;
-fx-table-cell-border-color: transparent;
-fx-table-header-border-color: transparent;
-fx-padding: 5;
}
.table-view .column-header-background {
- -fx-background-color: transparent;
+ -fx-background-color: #383838;
}
.table-view .column-header, .table-view .filler {
@@ -65,11 +72,10 @@
}
.table-view .column-header .label {
- -fx-font-size: 20pt;
- -fx-font-family: "Segoe UI Light";
+ -fx-font-size: 12pt;
+ -fx-font-family: "Segoe UI Semibold";
-fx-text-fill: white;
-fx-alignment: center-left;
- -fx-opacity: 1;
}
.table-view:focused .table-row-cell:filled:focused:selected {
@@ -88,15 +94,13 @@
}
.list-view {
- -fx-background-insets: 0;
- -fx-padding: 0;
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: #2d2d2d;
+ -fx-border-color: #4d4d4d;
}
-
.list-cell {
- -fx-label-padding: 0 0 0 0;
- -fx-graphic-text-gap : 0;
- -fx-padding: 0 0 0 0;
+ -fx-background-color: #2d2d2d;
+ -fx-text-fill: white;
+ -fx-padding: 5;
}
.list-cell:filled:even {
@@ -108,7 +112,12 @@
}
.list-cell:filled:selected {
- -fx-background-color: #424d5f;
+ -fx-background-color: #3e7b91;
+ -fx-text-fill: white;
+}
+
+.list-cell:filled:hover {
+ -fx-background-color: #4d4d4d;
}
.list-cell:filled:selected #cardPane {
@@ -123,13 +132,13 @@
.cell_big_label {
-fx-font-family: "Segoe UI Semibold";
-fx-font-size: 16px;
- -fx-text-fill: #010504;
+ -fx-text-fill: white;
}
.cell_small_label {
-fx-font-family: "Segoe UI";
-fx-font-size: 13px;
- -fx-text-fill: #010504;
+ -fx-text-fill: #a0a0a0;
}
.stack-pane {
@@ -214,13 +223,13 @@
*/
.button {
-fx-padding: 5 22 5 22;
- -fx-border-color: #e2e2e2;
+ -fx-border-color: #4d4d4d;
-fx-border-width: 2;
- -fx-background-radius: 0;
- -fx-background-color: #1d1d1d;
+ -fx-background-radius: 3;
+ -fx-background-color: #2d2d2d;
-fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
-fx-font-size: 11pt;
- -fx-text-fill: #d8d8d8;
+ -fx-text-fill: #e0e0e0;
-fx-background-insets: 0 0 0 0, 0, 1, 2;
}
@@ -229,8 +238,8 @@
}
.button:pressed, .button:default:hover:pressed {
- -fx-background-color: white;
- -fx-text-fill: #1d1d1d;
+ -fx-background-color: #4d4d4d;
+ -fx-text-fill: white;
}
.button:focused {
@@ -242,9 +251,9 @@
}
.button:disabled, .button:default:disabled {
- -fx-opacity: 0.4;
- -fx-background-color: #1d1d1d;
- -fx-text-fill: white;
+ -fx-opacity: 0.6;
+ -fx-background-color: #2d2d2d;
+ -fx-text-fill: #a0a0a0;
}
.button:default {
@@ -282,19 +291,19 @@
}
.scroll-bar {
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: #2d2d2d;
}
.scroll-bar .thumb {
- -fx-background-color: derive(#1d1d1d, 50%);
+ -fx-background-color: #4d4d4d;
-fx-background-insets: 3;
}
.scroll-bar .increment-button, .scroll-bar .decrement-button {
-fx-background-color: transparent;
- -fx-padding: 0 0 0 0;
}
+
.scroll-bar .increment-arrow, .scroll-bar .decrement-arrow {
-fx-shape: " ";
}
@@ -308,8 +317,10 @@
}
#cardPane {
- -fx-background-color: transparent;
- -fx-border-width: 0;
+ -fx-background-color: #2d2d2d;
+ -fx-border-color: #4d4d4d;
+ -fx-border-radius: 5;
+ -fx-padding: 10;
}
#commandTypeLabel {
@@ -328,7 +339,7 @@
-fx-text-fill: white;
}
-#filterField, #personListPanel, #personWebpage {
+#filterField, #studentListPanel, #studentWebpage {
-fx-effect: innershadow(gaussian, black, 10, 0, 0, 0);
}
@@ -337,12 +348,12 @@
-fx-background-radius: 0;
}
-#tags {
+#subjects {
-fx-hgap: 7;
-fx-vgap: 3;
}
-#tags .label {
+#subjects .label {
-fx-text-fill: white;
-fx-background-color: #3e7b91;
-fx-padding: 1 3 1 3;
@@ -350,3 +361,26 @@
-fx-background-radius: 2;
-fx-font-size: 11;
}
+
+#assignments {
+ -fx-hgap: 7;
+ -fx-vgap: 3;
+}
+
+.assignment-card {
+ -fx-text-fill: white;
+ -fx-padding: 1 3 1 3;
+ -fx-border-radius: 5;
+ -fx-background-radius: 6;
+ -fx-spacing: 5px;
+ -fx-alignment: center-left;
+}
+
+.assignment-name {
+ -fx-font-size: 11;
+ -fx-font-weight: bold;
+}
+
+.due-date {
+ -fx-font-size: 8;
+}
diff --git a/src/main/resources/view/Extensions.css b/src/main/resources/view/Extensions.css
index bfe82a85964..2c3f2fa0a5e 100644
--- a/src/main/resources/view/Extensions.css
+++ b/src/main/resources/view/Extensions.css
@@ -1,6 +1,6 @@
.error {
- -fx-text-fill: #d06651 !important; /* The error class should always override the default text-fill style */
+ -fx-text-fill: #ff4444 !important;
}
.list-cell:empty {
@@ -17,4 +17,8 @@
.tooltip-text {
-fx-text-fill: white;
+ -fx-background-color: #2d2d2d;
+ -fx-border-color: #4d4d4d;
+ -fx-border-radius: 3;
+ -fx-padding: 5;
}
diff --git a/src/main/resources/view/LessonListCard.fxml b/src/main/resources/view/LessonListCard.fxml
new file mode 100644
index 00000000000..3b5bee4bd7e
--- /dev/null
+++ b/src/main/resources/view/LessonListCard.fxml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/PersonListPanel.fxml b/src/main/resources/view/ListPanel.fxml
similarity index 77%
rename from src/main/resources/view/PersonListPanel.fxml
rename to src/main/resources/view/ListPanel.fxml
index a1bb6bbace8..b1b5ebe1f5c 100644
--- a/src/main/resources/view/PersonListPanel.fxml
+++ b/src/main/resources/view/ListPanel.fxml
@@ -4,5 +4,5 @@
-
+
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
index 7778f666a0a..9a0d69ee150 100644
--- a/src/main/resources/view/MainWindow.fxml
+++ b/src/main/resources/view/MainWindow.fxml
@@ -12,7 +12,7 @@
+ title="TutorTrack" minWidth="450" minHeight="600" onCloseRequest="#handleExit">
@@ -46,11 +46,11 @@
-
+
-
+
diff --git a/src/main/resources/view/ResultDisplay.fxml b/src/main/resources/view/ResultDisplay.fxml
index 01b691792a9..a3b58dc1ac0 100644
--- a/src/main/resources/view/ResultDisplay.fxml
+++ b/src/main/resources/view/ResultDisplay.fxml
@@ -1,9 +1,8 @@
-
-
+
+
-
-
+
+
diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/StudentListCard.fxml
similarity index 94%
rename from src/main/resources/view/PersonListCard.fxml
rename to src/main/resources/view/StudentListCard.fxml
index 84e09833a87..5c5756b8e33 100644
--- a/src/main/resources/view/PersonListCard.fxml
+++ b/src/main/resources/view/StudentListCard.fxml
@@ -27,10 +27,11 @@
-
+
-
+
+
diff --git a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json
deleted file mode 100644
index 6a4d2b7181c..00000000000
--- a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "persons": [ {
- "name": "Valid Person",
- "phone": "9482424",
- "email": "hans@example.com",
- "address": "4th street"
- }, {
- "name": "Person With Invalid Phone Field",
- "phone": "948asdf2424",
- "email": "hans@example.com",
- "address": "4th street"
- } ]
-}
diff --git a/src/test/data/JsonAddressBookStorageTest/invalidAndValidStudentAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidAndValidStudentAddressBook.json
new file mode 100644
index 00000000000..94cbd824704
--- /dev/null
+++ b/src/test/data/JsonAddressBookStorageTest/invalidAndValidStudentAddressBook.json
@@ -0,0 +1,16 @@
+{
+ "students": [ {
+ "name": "Valid Student",
+ "phone": "94828424",
+ "email": "hans@example.com",
+ "address": "4th street",
+ "subject": "CS2103T"
+ }, {
+ "name": "Student With Invalid Phone Field",
+ "phone": "948asdf2424",
+ "email": "hans@example.com",
+ "address": "4th street",
+ "subject": "CS2103T"
+ } ],
+ "lessons": []
+}
diff --git a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json
deleted file mode 100644
index ccd21f7d1a9..00000000000
--- a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "persons": [ {
- "name": "Person with invalid name field: Ha!ns Mu@ster",
- "phone": "9482424",
- "email": "hans@example.com",
- "address": "4th street"
- } ]
-}
diff --git a/src/test/data/JsonAddressBookStorageTest/invalidStudentAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidStudentAddressBook.json
new file mode 100644
index 00000000000..c0cb26e27e3
--- /dev/null
+++ b/src/test/data/JsonAddressBookStorageTest/invalidStudentAddressBook.json
@@ -0,0 +1,10 @@
+{
+ "students": [ {
+ "name": "Student with invalid name field: Ha!ns Mu@ster",
+ "phone" : "94351253",
+ "email" : "alice@example.com",
+ "address" : "123, Jurong West Ave 6, #08-111",
+ "subject" : "CS2109S"
+ } ],
+ "lessons": []
+}
diff --git a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/duplicateStudentAddressBook.json
similarity index 62%
rename from src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
rename to src/test/data/JsonSerializableAddressBookTest/duplicateStudentAddressBook.json
index a7427fe7aa2..97cde25082d 100644
--- a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
+++ b/src/test/data/JsonSerializableAddressBookTest/duplicateStudentAddressBook.json
@@ -1,14 +1,16 @@
{
- "persons": [ {
+ "students": [ {
"name": "Alice Pauline",
"phone": "94351253",
- "email": "alice@example.com",
"address": "123, Jurong West Ave 6, #08-111",
- "tags": [ "friends" ]
+ "email": "alice@example.com",
+ "subject": "CS2109S"
}, {
"name": "Alice Pauline",
"phone": "94351253",
+ "address": "123, Jurong West Ave 6, #08-111",
"email": "pauline@example.com",
- "address": "4th street"
- } ]
+ "subject": "CS2109S"
+ } ],
+ "lessons": []
}
diff --git a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/invalidStudentAddressBook.json
similarity index 73%
rename from src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
rename to src/test/data/JsonSerializableAddressBookTest/invalidStudentAddressBook.json
index ad3f135ae42..8275e9982ae 100644
--- a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
+++ b/src/test/data/JsonSerializableAddressBookTest/invalidStudentAddressBook.json
@@ -1,8 +1,9 @@
{
- "persons": [ {
+ "students": [ {
"name": "Hans Muster",
"phone": "9482424",
"email": "invalid@email!3e",
"address": "4th street"
- } ]
+ } ],
+ "lessons": []
}
diff --git a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
deleted file mode 100644
index 72262099d35..00000000000
--- a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
+++ /dev/null
@@ -1,46 +0,0 @@
-{
- "_comment": "AddressBook save file which contains the same Person values as in TypicalPersons#getTypicalAddressBook()",
- "persons" : [ {
- "name" : "Alice Pauline",
- "phone" : "94351253",
- "email" : "alice@example.com",
- "address" : "123, Jurong West Ave 6, #08-111",
- "tags" : [ "friends" ]
- }, {
- "name" : "Benson Meier",
- "phone" : "98765432",
- "email" : "johnd@example.com",
- "address" : "311, Clementi Ave 2, #02-25",
- "tags" : [ "owesMoney", "friends" ]
- }, {
- "name" : "Carl Kurz",
- "phone" : "95352563",
- "email" : "heinz@example.com",
- "address" : "wall street",
- "tags" : [ ]
- }, {
- "name" : "Daniel Meier",
- "phone" : "87652533",
- "email" : "cornelia@example.com",
- "address" : "10th street",
- "tags" : [ "friends" ]
- }, {
- "name" : "Elle Meyer",
- "phone" : "9482224",
- "email" : "werner@example.com",
- "address" : "michegan ave",
- "tags" : [ ]
- }, {
- "name" : "Fiona Kunz",
- "phone" : "9482427",
- "email" : "lydia@example.com",
- "address" : "little tokyo",
- "tags" : [ ]
- }, {
- "name" : "George Best",
- "phone" : "9482442",
- "email" : "anna@example.com",
- "address" : "4th street",
- "tags" : [ ]
- } ]
-}
diff --git a/src/test/data/JsonSerializableAddressBookTest/typicalStudentsAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/typicalStudentsAddressBook.json
new file mode 100644
index 00000000000..43fb1c69255
--- /dev/null
+++ b/src/test/data/JsonSerializableAddressBookTest/typicalStudentsAddressBook.json
@@ -0,0 +1,62 @@
+{
+ "_comment": "AddressBook save file which contains the same Student values as in TypicalStudents#getTypicalAddressBook()",
+ "students": [
+ {
+ "name": "Alice Pauline",
+ "phone": "94351253",
+ "address": "123, Jurong West Ave 6, #08-111",
+ "email": "alice@example.com",
+ "subjects": ["CS2109S"]
+ },
+ {
+ "name": "Benson Meier",
+ "phone": "98765432",
+ "address": "456, Orchard Road, #10-20",
+ "email": "johnd@example.com",
+ "subjects": ["MA1522"]
+ },
+ {
+ "name": "Carl Kurz",
+ "phone": "95352563",
+ "address": "789, Bukit Timah Road, #05-10",
+ "email": "heinz@example.com",
+ "subjects": ["CS"]
+ },
+ {
+ "name": "Daniel Meier",
+ "phone": "87652533",
+ "address": "321, Clementi Ave 3, #02-15",
+ "email": "cornelia@example.com",
+ "subjects": ["CS"]
+ },
+ {
+ "name": "Elle Meyer",
+ "phone": "94823224",
+ "address": "654, Tampines Street 62, #07-12",
+ "email": "werner@example.com",
+ "subjects": ["BZA"]
+ },
+ {
+ "name": "Fiona Kunz",
+ "phone": "94832427",
+ "address": "987, Pasir Ris Drive, #03-21",
+ "email": "lydia@example.com",
+ "subjects": ["CS2100"]
+ },
+ {
+ "name": "George Best",
+ "phone": "94820442",
+ "address": "555, Bedok North Ave 1, #11-22",
+ "email": "anna@example.com",
+ "subjects": ["CS2105"]
+ },
+ {
+ "name": "Harry Potter",
+ "phone": "94824420",
+ "address": "777, Changi Road, #09-33",
+ "email": "harry@gmail.com",
+ "subjects": ["CS2103T"]
+ }
+ ],
+ "lessons": []
+}
diff --git a/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json b/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json
index 1037548a9cd..5e9bb92e70e 100644
--- a/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json
+++ b/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json
@@ -9,5 +9,5 @@
"z" : 99
}
},
- "addressBookFilePath" : "addressbook.json"
+ "addressBookFilePath" : "TutorTrack.json"
}
diff --git a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json
index b819bed900a..c243c9df9fc 100644
--- a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json
+++ b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json
@@ -7,5 +7,5 @@
"y" : 100
}
},
- "addressBookFilePath" : "addressbook.json"
+ "addressBookFilePath" : "TutorTrack.json"
}
diff --git a/src/test/java/seedu/address/commons/core/index/IndexTest.java b/src/test/java/seedu/address/commons/core/index/IndexTest.java
index fc395ab964b..5e660d409b8 100644
--- a/src/test/java/seedu/address/commons/core/index/IndexTest.java
+++ b/src/test/java/seedu/address/commons/core/index/IndexTest.java
@@ -39,23 +39,23 @@ public void createZeroBasedIndex() {
@Test
public void equals() {
- final Index fifthPersonIndex = Index.fromOneBased(5);
+ final Index fifthStudentIndex = Index.fromOneBased(5);
// same values -> returns true
- assertTrue(fifthPersonIndex.equals(Index.fromOneBased(5)));
- assertTrue(fifthPersonIndex.equals(Index.fromZeroBased(4)));
+ assertTrue(fifthStudentIndex.equals(Index.fromOneBased(5)));
+ assertTrue(fifthStudentIndex.equals(Index.fromZeroBased(4)));
// same object -> returns true
- assertTrue(fifthPersonIndex.equals(fifthPersonIndex));
+ assertTrue(fifthStudentIndex.equals(fifthStudentIndex));
// null -> returns false
- assertFalse(fifthPersonIndex.equals(null));
+ assertFalse(fifthStudentIndex.equals(null));
// different types -> returns false
- assertFalse(fifthPersonIndex.equals(5.0f));
+ assertFalse(fifthStudentIndex.equals(5.0f));
// different index -> returns false
- assertFalse(fifthPersonIndex.equals(Index.fromOneBased(1)));
+ assertFalse(fifthStudentIndex.equals(Index.fromOneBased(1)));
}
@Test
diff --git a/src/test/java/seedu/address/commons/util/StringUtilTest.java b/src/test/java/seedu/address/commons/util/StringUtilTest.java
index c56d407bf3f..9d3c120b401 100644
--- a/src/test/java/seedu/address/commons/util/StringUtilTest.java
+++ b/src/test/java/seedu/address/commons/util/StringUtilTest.java
@@ -16,7 +16,7 @@ public class StringUtilTest {
public void isNonZeroUnsignedInteger() {
// EP: empty strings
- assertFalse(StringUtil.isNonZeroUnsignedInteger("")); // Boundary value
+ assertFalse(StringUtil.isNonZeroUnsignedInteger("")); // Boundary assignmentName
assertFalse(StringUtil.isNonZeroUnsignedInteger(" "));
// EP: not a number
@@ -41,7 +41,7 @@ public void isNonZeroUnsignedInteger() {
assertFalse(StringUtil.isNonZeroUnsignedInteger(Long.toString(Integer.MAX_VALUE + 1)));
// EP: valid numbers, should return true
- assertTrue(StringUtil.isNonZeroUnsignedInteger("1")); // Boundary value
+ assertTrue(StringUtil.isNonZeroUnsignedInteger("1")); // Boundary assignmentName
assertTrue(StringUtil.isNonZeroUnsignedInteger("10"));
}
diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/address/logic/LogicManagerTest.java
index baf8ce336a2..5494173122f 100644
--- a/src/test/java/seedu/address/logic/LogicManagerTest.java
+++ b/src/test/java/seedu/address/logic/LogicManagerTest.java
@@ -1,14 +1,16 @@
package seedu.address.logic;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX;
+import static seedu.address.logic.Messages.MESSAGE_EMPTY_STUDENT_LIST;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX;
import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.SUBJECT_DESC_AMY;
import static seedu.address.testutil.Assert.assertThrows;
-import static seedu.address.testutil.TypicalPersons.AMY;
+import static seedu.address.testutil.TypicalStudents.AMY;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
@@ -18,20 +20,20 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
-import seedu.address.logic.commands.AddCommand;
+import seedu.address.logic.commands.AddStudentCommand;
import seedu.address.logic.commands.CommandResult;
-import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.ListStudentsCommand;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.Model;
import seedu.address.model.ModelManager;
import seedu.address.model.ReadOnlyAddressBook;
import seedu.address.model.UserPrefs;
-import seedu.address.model.person.Person;
+import seedu.address.model.student.Student;
import seedu.address.storage.JsonAddressBookStorage;
import seedu.address.storage.JsonUserPrefsStorage;
import seedu.address.storage.StorageManager;
-import seedu.address.testutil.PersonBuilder;
+import seedu.address.testutil.StudentBuilder;
public class LogicManagerTest {
private static final IOException DUMMY_IO_EXCEPTION = new IOException("dummy IO exception");
@@ -46,7 +48,7 @@ public class LogicManagerTest {
@BeforeEach
public void setUp() {
JsonAddressBookStorage addressBookStorage =
- new JsonAddressBookStorage(temporaryFolder.resolve("addressBook.json"));
+ new JsonAddressBookStorage(temporaryFolder.resolve("tutorTrack.json"));
JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("userPrefs.json"));
StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage);
logic = new LogicManager(model, storage);
@@ -59,15 +61,24 @@ public void execute_invalidCommandFormat_throwsParseException() {
}
@Test
- public void execute_commandExecutionError_throwsCommandException() {
- String deleteCommand = "delete 9";
- assertCommandException(deleteCommand, MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ public void execute_commandExecutionErrorOnEmptyList_throwsEmptyListException() {
+ String deleteStudentCommand = "delete_student 1";
+ assertCommandException(deleteStudentCommand, MESSAGE_EMPTY_STUDENT_LIST);
+ }
+
+ @Test
+ public void execute_commandExecutionErrorForInvalidIndex_throwsInvalidIndexException() {
+ Student sampleStudent = new StudentBuilder().build();
+ model.addStudent(sampleStudent);
+
+ String deleteStudentCommand = "delete_student 9";
+ assertCommandException(deleteStudentCommand, MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
}
@Test
public void execute_validCommand_success() throws Exception {
- String listCommand = ListCommand.COMMAND_WORD;
- assertCommandSuccess(listCommand, ListCommand.MESSAGE_SUCCESS, model);
+ String listStudentsCommand = ListStudentsCommand.COMMAND_WORD;
+ assertCommandSuccess(listStudentsCommand, ListStudentsCommand.MESSAGE_EMPTY_LIST, model);
}
@Test
@@ -83,8 +94,8 @@ public void execute_storageThrowsAdException_throwsCommandException() {
}
@Test
- public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() {
- assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredPersonList().remove(0));
+ public void getFilteredStudentList_modifyList_throwsUnsupportedOperationException() {
+ assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredStudentList().remove(0));
}
/**
@@ -165,11 +176,11 @@ public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath)
logic = new LogicManager(model, storage);
// Triggers the saveAddressBook method by executing an add command
- String addCommand = AddCommand.COMMAND_WORD + NAME_DESC_AMY + PHONE_DESC_AMY
- + EMAIL_DESC_AMY + ADDRESS_DESC_AMY;
- Person expectedPerson = new PersonBuilder(AMY).withTags().build();
+ String addStudentCommand = AddStudentCommand.COMMAND_WORD + NAME_DESC_AMY + PHONE_DESC_AMY
+ + EMAIL_DESC_AMY + SUBJECT_DESC_AMY + ADDRESS_DESC_AMY;
+ Student expectedStudent = new StudentBuilder(AMY).build();
ModelManager expectedModel = new ModelManager();
- expectedModel.addPerson(expectedPerson);
- assertCommandFailure(addCommand, CommandException.class, expectedMessage, expectedModel);
+ expectedModel.addStudent(expectedStudent);
+ assertCommandFailure(addStudentCommand, CommandException.class, expectedMessage, expectedModel);
}
}
diff --git a/src/test/java/seedu/address/logic/commands/AddAssignmentCommandTest.java b/src/test/java/seedu/address/logic/commands/AddAssignmentCommandTest.java
new file mode 100644
index 00000000000..4b0b721de6f
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/AddAssignmentCommandTest.java
@@ -0,0 +1,117 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND;
+import static seedu.address.testutil.TypicalStudents.getTypicalAddressBook;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.assignment.Assignment;
+import seedu.address.model.datetimeutil.Date;
+import seedu.address.model.student.Student;
+
+public class AddAssignmentCommandTest {
+
+ private Model model;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ }
+
+ @Test
+ public void execute_duplicateAssignment_throwsCommandException() {
+ Student student = model.getFilteredStudentList().get(INDEX_FIRST.getZeroBased());
+ Assignment assignment = new Assignment("Assignment 1", new Date("10-04-2025"));
+ student.addAssignment(assignment);
+ AddAssignmentCommand addAssignmentCommand = new AddAssignmentCommand(INDEX_FIRST, assignment);
+
+ assertThrows(CommandException.class,
+ AddAssignmentCommand.MESSAGE_DUPLICATE_ASSIGNMENT, () ->
+ addAssignmentCommand.execute(model));
+ }
+
+ @Test
+ public void execute_emptyStudentList_throwsCommandException() {
+ Model emptyModel = new ModelManager();
+ Assignment assignment = new Assignment("Assignment 1", new Date("10-04-2025"));
+ AddAssignmentCommand addAssignmentCommand = new AddAssignmentCommand(INDEX_FIRST, assignment);
+
+ assertThrows(CommandException.class,
+ AddAssignmentCommand.MESSAGE_EMPTY_STUDENT_LIST, () ->
+ addAssignmentCommand.execute(emptyModel));
+ }
+
+ @Test
+ public void execute_invalidIndex_throwsCommandException() {
+ Index outOfBoundIndex = Index.fromOneBased(model.getFilteredStudentList().size() + 1);
+ Assignment assignment = new Assignment("Assignment 1", new Date("10-04-2025"));
+ AddAssignmentCommand addAssignmentCommand = new AddAssignmentCommand(outOfBoundIndex, assignment);
+
+ assertThrows(CommandException.class,
+ Messages.MESSAGE_INDEX_OUT_OF_BOUNDS, () ->
+ addAssignmentCommand.execute(model));
+ }
+
+ @Test
+ public void execute_validAssignment_success() throws Exception {
+ Student studentToEdit = model.getFilteredStudentList().get(INDEX_FIRST.getZeroBased());
+ Assignment assignment = new Assignment("New Assignment", new Date("10-04-2025"));
+ AddAssignmentCommand addAssignmentCommand = new AddAssignmentCommand(INDEX_FIRST, assignment);
+
+ CommandResult commandResult = addAssignmentCommand.execute(model);
+
+ assertTrue(studentToEdit.getAssignments().contains(assignment));
+ assertEquals(String.format(Messages.MESSAGE_ADD_ASSIGNMENT_SUCCESS,
+ studentToEdit.getName(), assignment),
+ commandResult.getFeedbackToUser());
+ }
+
+ @Test
+ public void equals() {
+ Assignment assignment1 = new Assignment("Assignment 1", new Date("10-04-2025"));
+ Assignment assignment2 = new Assignment("Assignment 2", new Date("11-04-2025"));
+ AddAssignmentCommand addFirstCommand = new AddAssignmentCommand(INDEX_FIRST, assignment1);
+ AddAssignmentCommand addSecondCommand = new AddAssignmentCommand(INDEX_SECOND, assignment1);
+ AddAssignmentCommand addThirdCommand = new AddAssignmentCommand(INDEX_FIRST, assignment2);
+
+ // same object -> returns true
+ assertTrue(addFirstCommand.equals(addFirstCommand));
+
+ // same values -> returns true
+ AddAssignmentCommand addFirstCommandCopy = new AddAssignmentCommand(INDEX_FIRST, assignment1);
+ assertTrue(addFirstCommand.equals(addFirstCommandCopy));
+
+ // different types -> returns false
+ assertFalse(addFirstCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(addFirstCommand.equals(null));
+
+ // different index -> returns false
+ assertFalse(addFirstCommand.equals(addSecondCommand));
+
+ // different assignment -> returns false
+ assertFalse(addFirstCommand.equals(addThirdCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Assignment assignment = new Assignment("Assignment 1", new Date("10-04-2025"));
+ AddAssignmentCommand addAssignmentCommand = new AddAssignmentCommand(INDEX_FIRST, assignment);
+ String expected = AddAssignmentCommand.class.getCanonicalName() + "{index=" + INDEX_FIRST
+ + ", assignment=" + assignment + ", dueDate=" + assignment.getDueDate() + "}";
+ assertEquals(expected, addAssignmentCommand.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java b/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java
deleted file mode 100644
index 162a0c86031..00000000000
--- a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package seedu.address.logic.commands;
-
-import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
-import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
-import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import seedu.address.logic.Messages;
-import seedu.address.model.Model;
-import seedu.address.model.ModelManager;
-import seedu.address.model.UserPrefs;
-import seedu.address.model.person.Person;
-import seedu.address.testutil.PersonBuilder;
-
-/**
- * Contains integration tests (interaction with the Model) for {@code AddCommand}.
- */
-public class AddCommandIntegrationTest {
-
- private Model model;
-
- @BeforeEach
- public void setUp() {
- model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
- }
-
- @Test
- public void execute_newPerson_success() {
- Person validPerson = new PersonBuilder().build();
-
- Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
- expectedModel.addPerson(validPerson);
-
- assertCommandSuccess(new AddCommand(validPerson), model,
- String.format(AddCommand.MESSAGE_SUCCESS, Messages.format(validPerson)),
- expectedModel);
- }
-
- @Test
- public void execute_duplicatePerson_throwsCommandException() {
- Person personInList = model.getAddressBook().getPersonList().get(0);
- assertCommandFailure(new AddCommand(personInList), model,
- AddCommand.MESSAGE_DUPLICATE_PERSON);
- }
-
-}
diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java
deleted file mode 100644
index 90e8253f48e..00000000000
--- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java
+++ /dev/null
@@ -1,204 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.testutil.Assert.assertThrows;
-import static seedu.address.testutil.TypicalPersons.ALICE;
-
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.function.Predicate;
-
-import org.junit.jupiter.api.Test;
-
-import javafx.collections.ObservableList;
-import seedu.address.commons.core.GuiSettings;
-import seedu.address.logic.Messages;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.model.AddressBook;
-import seedu.address.model.Model;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.ReadOnlyUserPrefs;
-import seedu.address.model.person.Person;
-import seedu.address.testutil.PersonBuilder;
-
-public class AddCommandTest {
-
- @Test
- public void constructor_nullPerson_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> new AddCommand(null));
- }
-
- @Test
- public void execute_personAcceptedByModel_addSuccessful() throws Exception {
- ModelStubAcceptingPersonAdded modelStub = new ModelStubAcceptingPersonAdded();
- Person validPerson = new PersonBuilder().build();
-
- CommandResult commandResult = new AddCommand(validPerson).execute(modelStub);
-
- assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, Messages.format(validPerson)),
- commandResult.getFeedbackToUser());
- assertEquals(Arrays.asList(validPerson), modelStub.personsAdded);
- }
-
- @Test
- public void execute_duplicatePerson_throwsCommandException() {
- Person validPerson = new PersonBuilder().build();
- AddCommand addCommand = new AddCommand(validPerson);
- ModelStub modelStub = new ModelStubWithPerson(validPerson);
-
- assertThrows(CommandException.class, AddCommand.MESSAGE_DUPLICATE_PERSON, () -> addCommand.execute(modelStub));
- }
-
- @Test
- public void equals() {
- Person alice = new PersonBuilder().withName("Alice").build();
- Person bob = new PersonBuilder().withName("Bob").build();
- AddCommand addAliceCommand = new AddCommand(alice);
- AddCommand addBobCommand = new AddCommand(bob);
-
- // same object -> returns true
- assertTrue(addAliceCommand.equals(addAliceCommand));
-
- // same values -> returns true
- AddCommand addAliceCommandCopy = new AddCommand(alice);
- assertTrue(addAliceCommand.equals(addAliceCommandCopy));
-
- // different types -> returns false
- assertFalse(addAliceCommand.equals(1));
-
- // null -> returns false
- assertFalse(addAliceCommand.equals(null));
-
- // different person -> returns false
- assertFalse(addAliceCommand.equals(addBobCommand));
- }
-
- @Test
- public void toStringMethod() {
- AddCommand addCommand = new AddCommand(ALICE);
- String expected = AddCommand.class.getCanonicalName() + "{toAdd=" + ALICE + "}";
- assertEquals(expected, addCommand.toString());
- }
-
- /**
- * A default model stub that have all of the methods failing.
- */
- private class ModelStub implements Model {
- @Override
- public void setUserPrefs(ReadOnlyUserPrefs userPrefs) {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public ReadOnlyUserPrefs getUserPrefs() {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public GuiSettings getGuiSettings() {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public void setGuiSettings(GuiSettings guiSettings) {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public Path getAddressBookFilePath() {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public void setAddressBookFilePath(Path addressBookFilePath) {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public void addPerson(Person person) {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public void setAddressBook(ReadOnlyAddressBook newData) {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public ReadOnlyAddressBook getAddressBook() {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public boolean hasPerson(Person person) {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public void deletePerson(Person target) {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public void setPerson(Person target, Person editedPerson) {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public ObservableList getFilteredPersonList() {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public void updateFilteredPersonList(Predicate predicate) {
- throw new AssertionError("This method should not be called.");
- }
- }
-
- /**
- * A Model stub that contains a single person.
- */
- private class ModelStubWithPerson extends ModelStub {
- private final Person person;
-
- ModelStubWithPerson(Person person) {
- requireNonNull(person);
- this.person = person;
- }
-
- @Override
- public boolean hasPerson(Person person) {
- requireNonNull(person);
- return this.person.isSamePerson(person);
- }
- }
-
- /**
- * A Model stub that always accept the person being added.
- */
- private class ModelStubAcceptingPersonAdded extends ModelStub {
- final ArrayList personsAdded = new ArrayList<>();
-
- @Override
- public boolean hasPerson(Person person) {
- requireNonNull(person);
- return personsAdded.stream().anyMatch(person::isSamePerson);
- }
-
- @Override
- public void addPerson(Person person) {
- requireNonNull(person);
- personsAdded.add(person);
- }
-
- @Override
- public ReadOnlyAddressBook getAddressBook() {
- return new AddressBook();
- }
- }
-
-}
diff --git a/src/test/java/seedu/address/logic/commands/AddLessonCommandTest.java b/src/test/java/seedu/address/logic/commands/AddLessonCommandTest.java
new file mode 100644
index 00000000000..ddceeb20207
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/AddLessonCommandTest.java
@@ -0,0 +1,147 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalLessons.MATH_LEARNING;
+import static seedu.address.testutil.TypicalLessons.SCIENCE_TEACHING;
+import static seedu.address.testutil.TypicalStudents.ALICE;
+import static seedu.address.testutil.TypicalStudents.BOB;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.student.Student;
+import seedu.address.testutil.LessonBuilder;
+import seedu.address.testutil.StudentBuilder;
+
+public class AddLessonCommandTest {
+
+ private Model model;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager();
+ model.addStudent(ALICE);
+ }
+
+ @Test
+ public void constructor_nullLesson_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new AddLessonCommand(null));
+ }
+
+ @Test
+ public void execute_duplicateLesson_throwsCommandException() {
+ Lesson lesson = new LessonBuilder().withName(ALICE.getName().toString()).build();
+ model.addLesson(lesson);
+ AddLessonCommand addLessonCommand = new AddLessonCommand(lesson);
+
+ assertThrows(CommandException.class,
+ AddLessonCommand.MESSAGE_DUPLICATE_LESSON, () ->
+ addLessonCommand.execute(model));
+ }
+
+ @Test
+ public void execute_lessonConflict_throwsCommandException() {
+ // Add a lesson that conflicts with the new lesson
+ Lesson existingLesson = new LessonBuilder()
+ .withName(ALICE.getName().toString())
+ .withDate("17-09-2027")
+ .withTime("14:00")
+ .build();
+ model.addLesson(existingLesson);
+
+ Lesson conflictingLesson = new LessonBuilder()
+ .withName(BOB.getName().toString())
+ .withDate("17-09-2027")
+ .withTime("14:00")
+ .build();
+ AddLessonCommand addLessonCommand = new AddLessonCommand(conflictingLesson);
+
+ assertThrows(CommandException.class,
+ AddLessonCommand.MESSAGE_LESSON_CONFLICT, () ->
+ addLessonCommand.execute(model));
+ }
+
+ @Test
+ public void execute_studentNotFound_throwsCommandException() {
+ Lesson lesson = new LessonBuilder().withName("Nonexistent Student").build();
+ AddLessonCommand addLessonCommand = new AddLessonCommand(lesson);
+
+ assertThrows(CommandException.class,
+ AddLessonCommand.MESSAGE_STUDENT_NOT_FOUND, () ->
+ addLessonCommand.execute(model));
+ }
+
+ @Test
+ public void execute_subjectMismatch_throwsCommandException() {
+ // Create a student without the lesson's subject
+ Student studentWithoutSubject = new StudentBuilder(ALICE)
+ .withSubjects("Math")
+ .build();
+ model.setStudent(ALICE, studentWithoutSubject);
+
+ Lesson lesson = new LessonBuilder()
+ .withName(ALICE.getName().toString())
+ .withSubject("Science")
+ .build();
+ AddLessonCommand addLessonCommand = new AddLessonCommand(lesson);
+
+ assertThrows(CommandException.class,
+ AddLessonCommand.MESSAGE_SUBJECT_MISMATCH, () ->
+ addLessonCommand.execute(model));
+ }
+
+ @Test
+ public void execute_validLesson_success() throws Exception {
+ Lesson validLesson = new LessonBuilder()
+ .withName(ALICE.getName().toString())
+ .withSubject("CS2109S")
+ .build();
+ AddLessonCommand addLessonCommand = new AddLessonCommand(validLesson);
+
+ CommandResult commandResult = addLessonCommand.execute(model);
+
+ assertTrue(model.hasLesson(validLesson));
+ assertEquals(String.format(AddLessonCommand.MESSAGE_SUCCESS, Messages.format(validLesson)),
+ commandResult.getFeedbackToUser());
+ }
+
+ @Test
+ public void equals() {
+ AddLessonCommand addMathCommand = new AddLessonCommand(MATH_LEARNING);
+ AddLessonCommand addScienceCommand = new AddLessonCommand(SCIENCE_TEACHING);
+
+ // same object -> returns true
+ assertTrue(addMathCommand.equals(addMathCommand));
+
+ // same values -> returns true
+ AddLessonCommand addMathCommandCopy = new AddLessonCommand(MATH_LEARNING);
+ assertTrue(addMathCommand.equals(addMathCommandCopy));
+
+ // different types -> returns false
+ assertFalse(addMathCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(addMathCommand.equals(null));
+
+ // different lesson -> returns false
+ assertFalse(addMathCommand.equals(addScienceCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ AddLessonCommand addLessonCommand = new AddLessonCommand(MATH_LEARNING);
+ String expected = new ToStringBuilder(addLessonCommand)
+ .add("toAdd", MATH_LEARNING)
+ .toString();
+ assertEquals(expected, addLessonCommand.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/AddStudentCommandIntegrationTest.java b/src/test/java/seedu/address/logic/commands/AddStudentCommandIntegrationTest.java
new file mode 100644
index 00000000000..a0593c00875
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/AddStudentCommandIntegrationTest.java
@@ -0,0 +1,48 @@
+package seedu.address.logic.commands;
+
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.testutil.TypicalStudents.getTypicalAddressBook;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.student.Student;
+import seedu.address.testutil.StudentBuilder;
+
+/**
+ * Contains integration tests (interaction with the Model) for {@code AddStudentCommand}.
+ */
+public class AddStudentCommandIntegrationTest {
+
+ private Model model;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ }
+
+ @Test
+ public void execute_newStudent_success() {
+ Student validStudent = new StudentBuilder().build();
+
+ Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ expectedModel.addStudent(validStudent);
+ String expectedMessage = String.format(AddStudentCommand.MESSAGE_SUCCESS, Messages.format(validStudent));
+ CommandResult expectedResult = new CommandResult(expectedMessage, true);
+
+ assertCommandSuccess(new AddStudentCommand(validStudent), model, expectedResult, expectedModel);
+ }
+
+ @Test
+ public void execute_duplicateStudent_throwsCommandException() {
+ Student studentInList = model.getAddressBook().getStudentList().get(0);
+ assertCommandFailure(new AddStudentCommand(studentInList), model,
+ AddStudentCommand.MESSAGE_DUPLICATE_STUDENT);
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/commands/AddStudentCommandTest.java b/src/test/java/seedu/address/logic/commands/AddStudentCommandTest.java
new file mode 100644
index 00000000000..d63fd0244db
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/AddStudentCommandTest.java
@@ -0,0 +1,260 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalStudents.ALICE;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.function.Predicate;
+
+import org.junit.jupiter.api.Test;
+
+import javafx.collections.ObservableList;
+import seedu.address.commons.core.GuiSettings;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.AddressBook;
+import seedu.address.model.Model;
+import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyUserPrefs;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.student.Student;
+import seedu.address.model.subject.Subject;
+import seedu.address.testutil.StudentBuilder;
+
+public class AddStudentCommandTest {
+ @Test
+ public void constructor_nullStudent_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new AddStudentCommand(null));
+ }
+
+ @Test
+ public void execute_studentAcceptedByModel_addSuccessful() throws Exception {
+ AddStudentCommandTest.ModelStubAcceptingStudentAdded modelStub =
+ new AddStudentCommandTest.ModelStubAcceptingStudentAdded();
+ Student validStudent = new StudentBuilder().build();
+
+ CommandResult commandResult = new AddStudentCommand(validStudent).execute(modelStub);
+
+ assertEquals(String.format(AddStudentCommand.MESSAGE_SUCCESS, Messages.format(validStudent)),
+ commandResult.getFeedbackToUser());
+ assertEquals(Arrays.asList(validStudent), modelStub.studentsAdded);
+ }
+
+ @Test
+ public void execute_duplicateStudent_throwsCommandException() {
+ Student validStudent = new StudentBuilder().build();
+ AddStudentCommand addStudentCommand = new AddStudentCommand(validStudent);
+ AddStudentCommandTest.ModelStub modelStub = new AddStudentCommandTest.ModelStubWithStudent(validStudent);
+
+ assertThrows(CommandException.class, AddStudentCommand
+ .MESSAGE_DUPLICATE_STUDENT, () -> addStudentCommand.execute(modelStub));
+ }
+
+ @Test
+ public void equals() {
+ Student alice = new StudentBuilder().withName("Alice").build();
+ Student bob = new StudentBuilder().withName("Bob").build();
+ AddStudentCommand addAliceCommand = new AddStudentCommand(alice);
+ AddStudentCommand addBobCommand = new AddStudentCommand(bob);
+
+ // same object -> returns true
+ assertTrue(addAliceCommand.equals(addAliceCommand));
+
+ // same values -> returns true
+ AddStudentCommand addAliceCommandCopy = new AddStudentCommand(alice);
+ assertTrue(addAliceCommand.equals(addAliceCommandCopy));
+
+ // different types -> returns false
+ assertFalse(addAliceCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(addAliceCommand.equals(null));
+
+ // different student -> returns false
+ assertFalse(addAliceCommand.equals(addBobCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ AddStudentCommand addStudentCommand = new AddStudentCommand(ALICE);
+ String expected = AddStudentCommand.class.getCanonicalName() + "{toAdd=" + ALICE + "}";
+ assertEquals(expected, addStudentCommand.toString());
+ }
+
+ /**
+ * A default model stub that have all of the methods failing.
+ */
+ private class ModelStub implements Model {
+ @Override
+ public void setUserPrefs(ReadOnlyUserPrefs userPrefs) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ReadOnlyUserPrefs getUserPrefs() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public GuiSettings getGuiSettings() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void deleteAssignment(Student student, String assignmentName) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setGuiSettings(GuiSettings guiSettings) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public Path getAddressBookFilePath() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setAddressBookFilePath(Path addressBookFilePath) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void addStudent(Student student) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setAddressBook(ReadOnlyAddressBook newData) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ReadOnlyAddressBook getAddressBook() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public boolean hasStudent(Student student) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void deleteStudent(Student target) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setStudent(Student target, Student editedStudent) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public boolean hasStudentSubject(Student student, Subject subject) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public boolean hasLesson(Lesson lesson) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public boolean hasLessonConflict(Lesson lesson) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void addLesson(Lesson lesson) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void deleteLesson(Lesson lesson) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setLesson(Lesson target, Lesson editedLesson) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ObservableList getFilteredStudentList() {
+ throw new AssertionError("This method should not be called.");
+ }
+ @Override
+ public ObservableList getFilteredLessonList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ObservableList getFilteredCurrList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public boolean isStudentView() {
+ return true;
+ }
+
+ @Override
+ public void updateFilteredStudentList(Predicate predicate) {
+ throw new AssertionError("This method should not be called.");
+ }
+ @Override
+ public void updateFilteredLessonList(Predicate predicate) {
+ throw new AssertionError("This method should not be called.");
+ }
+ }
+
+ /**
+ * A Model stub that contains a single student.
+ */
+ private class ModelStubWithStudent extends AddStudentCommandTest.ModelStub {
+ private final Student student;
+
+ ModelStubWithStudent(Student student) {
+ requireNonNull(student);
+ this.student = student;
+ }
+
+ @Override
+ public boolean hasStudent(Student student) {
+ requireNonNull(student);
+ return this.student.isSameStudent(student);
+ }
+ }
+
+ /**
+ * A Model stub that always accept the student being added.
+ */
+ private class ModelStubAcceptingStudentAdded extends AddStudentCommandTest.ModelStub {
+ final ArrayList studentsAdded = new ArrayList<>();
+
+ @Override
+ public boolean hasStudent(Student student) {
+ requireNonNull(student);
+ return studentsAdded.stream().anyMatch(student::isSameStudent);
+ }
+
+ @Override
+ public void addStudent(Student student) {
+ requireNonNull(student);
+ studentsAdded.add(student);
+ }
+
+ @Override
+ public ReadOnlyAddressBook getAddressBook() {
+ return new AddressBook();
+ }
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java b/src/test/java/seedu/address/logic/commands/ClearCommandTest.java
index 80d9110c03a..2020fcec74b 100644
--- a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/ClearCommandTest.java
@@ -1,7 +1,7 @@
package seedu.address.logic.commands;
import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
-import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import static seedu.address.testutil.TypicalStudents.getTypicalAddressBook;
import org.junit.jupiter.api.Test;
diff --git a/src/test/java/seedu/address/logic/commands/CommandResultTest.java b/src/test/java/seedu/address/logic/commands/CommandResultTest.java
index 7b8c7cd4546..e480c21aa1a 100644
--- a/src/test/java/seedu/address/logic/commands/CommandResultTest.java
+++ b/src/test/java/seedu/address/logic/commands/CommandResultTest.java
@@ -8,56 +8,138 @@
import org.junit.jupiter.api.Test;
public class CommandResultTest {
+
@Test
- public void equals() {
+ public void constructor_defaultValues() {
CommandResult commandResult = new CommandResult("feedback");
- // same values -> returns true
- assertTrue(commandResult.equals(new CommandResult("feedback")));
- assertTrue(commandResult.equals(new CommandResult("feedback", false, false)));
+ assertEquals("feedback", commandResult.getFeedbackToUser());
+ assertFalse(commandResult.isShowHelp());
+ assertFalse(commandResult.isExit());
+ assertFalse(commandResult.isUpdateList());
+ }
- // same object -> returns true
- assertTrue(commandResult.equals(commandResult));
+ @Test
+ public void constructor_allFields() {
+ CommandResult commandResult = new CommandResult("feedback", true, true);
- // null -> returns false
- assertFalse(commandResult.equals(null));
+ assertEquals("feedback", commandResult.getFeedbackToUser());
+ assertTrue(commandResult.isShowHelp());
+ assertTrue(commandResult.isExit());
+ assertFalse(commandResult.isUpdateList());
+ }
- // different types -> returns false
- assertFalse(commandResult.equals(0.5f));
+ @Test
+ public void constructor_updateList() {
+ CommandResult commandResult = new CommandResult("feedback", true);
- // different feedbackToUser value -> returns false
- assertFalse(commandResult.equals(new CommandResult("different")));
+ assertEquals("feedback", commandResult.getFeedbackToUser());
+ assertFalse(commandResult.isShowHelp());
+ assertFalse(commandResult.isExit());
+ assertTrue(commandResult.isUpdateList());
+ }
- // different showHelp value -> returns false
- assertFalse(commandResult.equals(new CommandResult("feedback", true, false)));
+ @Test
+ public void equals_sameValues() {
+ CommandResult commandResult1 = new CommandResult("feedback", true, false);
+ CommandResult commandResult2 = new CommandResult("feedback", true, false);
- // different exit value -> returns false
- assertFalse(commandResult.equals(new CommandResult("feedback", false, true)));
+ assertTrue(commandResult1.equals(commandResult2));
}
@Test
- public void hashcode() {
- CommandResult commandResult = new CommandResult("feedback");
+ public void equals_differentFeedback() {
+ CommandResult commandResult1 = new CommandResult("feedback1", true, false);
+ CommandResult commandResult2 = new CommandResult("feedback2", true, false);
- // same values -> returns same hashcode
- assertEquals(commandResult.hashCode(), new CommandResult("feedback").hashCode());
+ assertFalse(commandResult1.equals(commandResult2));
+ }
- // different feedbackToUser value -> returns different hashcode
- assertNotEquals(commandResult.hashCode(), new CommandResult("different").hashCode());
+ @Test
+ public void equals_differentShowHelp() {
+ CommandResult commandResult1 = new CommandResult("feedback", true, false);
+ CommandResult commandResult2 = new CommandResult("feedback", false, false);
- // different showHelp value -> returns different hashcode
- assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false).hashCode());
+ assertFalse(commandResult1.equals(commandResult2));
+ }
+
+ @Test
+ public void equals_differentExit() {
+ CommandResult commandResult1 = new CommandResult("feedback", false, true);
+ CommandResult commandResult2 = new CommandResult("feedback", false, false);
+
+ assertFalse(commandResult1.equals(commandResult2));
+ }
+
+ @Test
+ public void equals_differentUpdateList() {
+ CommandResult commandResult1 = new CommandResult("feedback", true);
+ CommandResult commandResult2 = new CommandResult("feedback", false);
+
+ assertFalse(commandResult1.equals(commandResult2));
+ }
+
+ @Test
+ public void equals_null() {
+ CommandResult commandResult = new CommandResult("feedback");
- // different exit value -> returns different hashcode
- assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true).hashCode());
+ assertFalse(commandResult.equals(null));
}
@Test
- public void toStringMethod() {
+ public void equals_differentType() {
CommandResult commandResult = new CommandResult("feedback");
- String expected = CommandResult.class.getCanonicalName() + "{feedbackToUser="
- + commandResult.getFeedbackToUser() + ", showHelp=" + commandResult.isShowHelp()
- + ", exit=" + commandResult.isExit() + "}";
+
+ assertFalse(commandResult.equals("feedback"));
+ }
+
+ @Test
+ public void hashCode_sameValues() {
+ CommandResult commandResult1 = new CommandResult("feedback", true, false);
+ CommandResult commandResult2 = new CommandResult("feedback", true, false);
+
+ assertEquals(commandResult1.hashCode(), commandResult2.hashCode());
+ }
+
+ @Test
+ public void hashCode_differentValues() {
+ CommandResult commandResult1 = new CommandResult("feedback1", true, false);
+ CommandResult commandResult2 = new CommandResult("feedback2", true, false);
+
+ assertNotEquals(commandResult1.hashCode(), commandResult2.hashCode());
+ }
+
+ @Test
+ public void toString_allFields() {
+ CommandResult commandResult = new CommandResult("feedback", true, true);
+ String expected = "seedu.address.logic.commands.CommandResult{"
+ + "feedbackToUser=feedback, "
+ + "showHelp=true, "
+ + "exit=true, "
+ + "updateList=false}";
+
assertEquals(expected, commandResult.toString());
}
+
+ @Test
+ public void toString_updateList() {
+ CommandResult commandResult = new CommandResult("feedback", true);
+ String expected = "seedu.address.logic.commands.CommandResult{"
+ + "feedbackToUser=feedback, "
+ + "showHelp=false, "
+ + "exit=false, "
+ + "updateList=true}";
+
+ assertEquals(expected, commandResult.toString());
+ }
+
+ @Test
+ public void getters() {
+ CommandResult commandResult = new CommandResult("test feedback", true, false);
+
+ assertEquals("test feedback", commandResult.getFeedbackToUser());
+ assertTrue(commandResult.isShowHelp());
+ assertFalse(commandResult.isExit());
+ assertFalse(commandResult.isUpdateList());
+ }
}
diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
index 643a1d08069..74c6a554bdf 100644
--- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
+++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
@@ -3,10 +3,13 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ASSIGNMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
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.CliSyntax.PREFIX_SUBJECT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TIME;
import static seedu.address.testutil.Assert.assertThrows;
import java.util.ArrayList;
@@ -17,13 +20,14 @@
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.AddressBook;
import seedu.address.model.Model;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
-import seedu.address.model.person.Person;
-import seedu.address.testutil.EditPersonDescriptorBuilder;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.student.NameContainsKeywordsPredicate;
+import seedu.address.model.student.Student;
+import seedu.address.testutil.EditLessonDescriptorBuilder;
+import seedu.address.testutil.EditStudentDescriptorBuilder;
/**
- * Contains helper methods for testing commands.
- */
+ * Contains helper methods for testing commands. */
public class CommandTestUtil {
public static final String VALID_NAME_AMY = "Amy Bee";
@@ -34,8 +38,17 @@ public class CommandTestUtil {
public static final String VALID_EMAIL_BOB = "bob@example.com";
public static final String VALID_ADDRESS_AMY = "Block 312, Amy Street 1";
public static final String VALID_ADDRESS_BOB = "Block 123, Bobby Street 3";
- public static final String VALID_TAG_HUSBAND = "husband";
- public static final String VALID_TAG_FRIEND = "friend";
+ public static final String VALID_SUBJECT_HUSBAND = "husband";
+ public static final String VALID_SUBJECT_FRIEND = "friend";
+ public static final String VALID_SUBJECT_AMY = "CS2103T";
+ public static final String VALID_SUBJECT_BOB = "CS2101";
+ public static final String VALID_DATE_AMY = "28-02-2026";
+ public static final String VALID_DATE_BOB = "26-09-2026";
+ public static final String VALID_TIME_AMY = "12:00";
+ public static final String VALID_TIME_BOB = "15:00";
+ public static final String VALID_ASSIGNMENT_NAME_AMY = " Some Assignment ";
+ public static final String VALID_ASSIGNMENT_NAME_BOB = " some other assignment ";
+ public static final String VALID_PREAMBLE = " 1 ";
public static final String NAME_DESC_AMY = " " + PREFIX_NAME + VALID_NAME_AMY;
public static final String NAME_DESC_BOB = " " + PREFIX_NAME + VALID_NAME_BOB;
@@ -45,28 +58,46 @@ public class CommandTestUtil {
public static final String EMAIL_DESC_BOB = " " + PREFIX_EMAIL + VALID_EMAIL_BOB;
public static final String ADDRESS_DESC_AMY = " " + PREFIX_ADDRESS + VALID_ADDRESS_AMY;
public static final String ADDRESS_DESC_BOB = " " + PREFIX_ADDRESS + VALID_ADDRESS_BOB;
- public static final String TAG_DESC_FRIEND = " " + PREFIX_TAG + VALID_TAG_FRIEND;
- public static final String TAG_DESC_HUSBAND = " " + PREFIX_TAG + VALID_TAG_HUSBAND;
+ public static final String SUBJECT_DESC_FRIEND = " " + PREFIX_SUBJECT + VALID_SUBJECT_FRIEND;
+ public static final String SUBJECT_DESC_HUSBAND = " " + PREFIX_SUBJECT + VALID_SUBJECT_HUSBAND;
+ public static final String SUBJECT_DESC_AMY = " " + PREFIX_SUBJECT + VALID_SUBJECT_AMY;
+ public static final String SUBJECT_DESC_BOB = " " + PREFIX_SUBJECT + VALID_SUBJECT_BOB;
+ public static final String DATE_DESC_AMY = " " + PREFIX_DATE + VALID_DATE_AMY;
+ public static final String DATE_DESC_BOB = " " + PREFIX_DATE + VALID_DATE_BOB;
+ public static final String TIME_DESC_BOB = " " + PREFIX_TIME + VALID_TIME_BOB;
+ public static final String ASSIGNMENT_NAME_DESC_AMY = " " + PREFIX_ASSIGNMENT + VALID_ASSIGNMENT_NAME_AMY;
+ public static final String ASSIGNMENT_NAME_DESC_BOB = " " + PREFIX_ASSIGNMENT + VALID_ASSIGNMENT_NAME_BOB;
public static final String INVALID_NAME_DESC = " " + PREFIX_NAME + "James&"; // '&' not allowed in names
public static final String INVALID_PHONE_DESC = " " + PREFIX_PHONE + "911a"; // 'a' not allowed in phones
public static final String INVALID_EMAIL_DESC = " " + PREFIX_EMAIL + "bob!yahoo"; // missing '@' symbol
public static final String INVALID_ADDRESS_DESC = " " + PREFIX_ADDRESS; // empty string not allowed for addresses
- public static final String INVALID_TAG_DESC = " " + PREFIX_TAG + "hubby*"; // '*' not allowed in tags
+ public static final String INVALID_SUBJECT_DESC = " " + PREFIX_SUBJECT + "@@@";
+ public static final String INVALID_DATE_DESC = " " + PREFIX_DATE + "17-19-2024"; // month must be within 0-12
+ public static final String INVALID_TIME_DESC = " " + PREFIX_TIME + "27:00"; // time must be within 0-24
+ public static final String INVALID_ASSIGNMENT_NAME_DESC = " "
+ + PREFIX_ASSIGNMENT + "Math Exercise #1"; // '#' not allowed in assignment names
public static final String PREAMBLE_WHITESPACE = "\t \r \n";
public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble";
- public static final EditCommand.EditPersonDescriptor DESC_AMY;
- public static final EditCommand.EditPersonDescriptor DESC_BOB;
+ public static final EditStudentCommand.EditStudentDescriptor STUDENT_DESC_AMY;
+ public static final EditStudentCommand.EditStudentDescriptor STUDENT_DESC_BOB;
+ public static final EditLessonCommand.EditLessonDescriptor LESSON_DESC_AMY;
+ public static final EditLessonCommand.EditLessonDescriptor LESSON_DESC_BOB;
static {
- DESC_AMY = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY)
+ STUDENT_DESC_AMY = new EditStudentDescriptorBuilder().withName(VALID_NAME_AMY)
.withPhone(VALID_PHONE_AMY).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY)
- .withTags(VALID_TAG_FRIEND).build();
- DESC_BOB = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB)
+ .withSubjects(VALID_SUBJECT_FRIEND).build();
+ STUDENT_DESC_BOB = new EditStudentDescriptorBuilder().withName(VALID_NAME_BOB)
.withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB)
- .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build();
+ .withSubjects(VALID_SUBJECT_HUSBAND, VALID_SUBJECT_FRIEND).build();
+
+ LESSON_DESC_AMY = new EditLessonDescriptorBuilder().withName(VALID_NAME_AMY)
+ .withDate(VALID_DATE_AMY).withTime(VALID_TIME_AMY).withSubject(VALID_SUBJECT_FRIEND).build();
+ LESSON_DESC_BOB = new EditLessonDescriptorBuilder().withName(VALID_NAME_BOB)
+ .withDate(VALID_DATE_BOB).withTime(VALID_TIME_BOB).withSubject(VALID_SUBJECT_FRIEND).build();
}
/**
@@ -95,34 +126,57 @@ public static void assertCommandSuccess(Command command, Model actualModel, Stri
assertCommandSuccess(command, actualModel, expectedCommandResult, expectedModel);
}
+ /**
+ * Convenience wrapper to {@link #assertCommandSuccess(Command, Model, CommandResult, Model)}
+ * that takes a string {@code expectedMessage}.
+ */
+ public static void assertCommandSuccessWithUpdate(Command command, Model actualModel, String expectedMessage,
+ Model expectedModel) {
+ CommandResult expectedCommandResult = new CommandResult(expectedMessage, true);
+ assertCommandSuccess(command, actualModel, expectedCommandResult, expectedModel);
+ }
+
/**
* Executes the given {@code command}, confirms that
* - a {@code CommandException} is thrown
* - the CommandException message matches {@code expectedMessage}
- * - the address book, filtered person list and selected person in {@code actualModel} remain unchanged
+ * - the address book, filtered student list and selected student in {@code actualModel} remain unchanged
*/
public static void assertCommandFailure(Command command, Model actualModel, String expectedMessage) {
// we are unable to defensively copy the model for comparison later, so we can
// only do so by copying its components.
AddressBook expectedAddressBook = new AddressBook(actualModel.getAddressBook());
- List expectedFilteredList = new ArrayList<>(actualModel.getFilteredPersonList());
+ List expectedFilteredList = new ArrayList<>(actualModel.getFilteredStudentList());
assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel));
assertEquals(expectedAddressBook, actualModel.getAddressBook());
- assertEquals(expectedFilteredList, actualModel.getFilteredPersonList());
+ assertEquals(expectedFilteredList, actualModel.getFilteredStudentList());
}
+
/**
- * Updates {@code model}'s filtered list to show only the person at the given {@code targetIndex} in the
+ * Updates {@code model}'s filtered list to show only the student at the given {@code targetIndex} in the
* {@code model}'s address book.
*/
- public static void showPersonAtIndex(Model model, Index targetIndex) {
- assertTrue(targetIndex.getZeroBased() < model.getFilteredPersonList().size());
+ public static void showStudentAtIndex(Model model, Index targetIndex) {
+ assertTrue(targetIndex.getZeroBased() < model.getFilteredStudentList().size());
- Person person = model.getFilteredPersonList().get(targetIndex.getZeroBased());
- final String[] splitName = person.getName().fullName.split("\\s+");
- model.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(splitName[0])));
+ Student student = model.getFilteredStudentList().get(targetIndex.getZeroBased());
+ final String[] splitName = student.getName().fullName.split("\\s+");
+ model.updateFilteredStudentList(new NameContainsKeywordsPredicate(Arrays.asList(splitName[0])));
- assertEquals(1, model.getFilteredPersonList().size());
+ assertEquals(1, model.getFilteredStudentList().size());
}
+ /**
+ * Updates {@code model}'s filtered list to show only the lesson at the given {@code targetIndex} in the
+ * {@code model}'s address book.
+ */
+ public static void showLessonAtIndex(Model model, Index targetIndex) {
+ assertTrue(targetIndex.getZeroBased() < model.getFilteredLessonList().size());
+
+ Lesson lesson = model.getFilteredLessonList().get(targetIndex.getZeroBased());
+ model.updateFilteredLessonList(l -> l.equals(lesson));
+
+ assertEquals(1, model.getFilteredLessonList().size());
+ }
}
diff --git a/src/test/java/seedu/address/logic/commands/DeleteAssignmentCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteAssignmentCommandTest.java
new file mode 100644
index 00000000000..3e8c7d9f602
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/DeleteAssignmentCommandTest.java
@@ -0,0 +1,121 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalAssignments.ASSIGNMENT1;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND;
+import static seedu.address.testutil.TypicalStudents.getTypicalAddressBook;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.assignment.Assignment;
+import seedu.address.model.student.Student;
+
+public class DeleteAssignmentCommandTest {
+
+ private Model model;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ }
+
+ @Test
+ public void constructor_nullArguments_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new DeleteAssignmentCommand(null, "Assignment 1"));
+ assertThrows(NullPointerException.class, () -> new DeleteAssignmentCommand(INDEX_FIRST, null));
+ }
+
+ @Test
+ public void execute_emptyStudentList_throwsCommandException() {
+ Model emptyModel = new ModelManager();
+ DeleteAssignmentCommand command = new DeleteAssignmentCommand(INDEX_FIRST, "Assignment 1");
+
+ assertThrows(CommandException.class,
+ DeleteAssignmentCommand.MESSAGE_EMPTY_STUDENT_LIST, () ->
+ command.execute(emptyModel));
+ }
+
+ @Test
+ public void execute_invalidStudentIndex_throwsCommandException() {
+ Index outOfBoundIndex = Index.fromOneBased(model.getFilteredStudentList().size() + 1);
+ DeleteAssignmentCommand command = new DeleteAssignmentCommand(outOfBoundIndex, "Assignment 1");
+
+ assertThrows(CommandException.class,
+ Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX, () ->
+ command.execute(model));
+ }
+
+ @Test
+ public void execute_assignmentNotFound_throwsCommandException() {
+ Student student = model.getFilteredStudentList().get(INDEX_FIRST.getZeroBased());
+ String nonExistentAssignment = "Nonexistent Assignment";
+ DeleteAssignmentCommand command = new DeleteAssignmentCommand(INDEX_FIRST, nonExistentAssignment);
+
+ assertThrows(CommandException.class,
+ DeleteAssignmentCommand.MESSAGE_INVALID_ASSIGNMENT_DISPLAYED, () ->
+ command.execute(model));
+ }
+
+ @Test
+ public void execute_validAssignment_success() throws Exception {
+ Student student = model.getFilteredStudentList().get(INDEX_FIRST.getZeroBased());
+ Assignment assignmentToDelete = ASSIGNMENT1;
+ student.clearAssignments();
+ student.addAssignment(assignmentToDelete);
+
+ DeleteAssignmentCommand command = new DeleteAssignmentCommand(INDEX_FIRST,
+ assignmentToDelete.getAssignmentName());
+ CommandResult result = command.execute(model);
+
+ assertFalse(student.getAssignments().contains(assignmentToDelete));
+ assertEquals(String.format(DeleteAssignmentCommand.MESSAGE_DELETE_ASSIGNMENT_SUCCESS,
+ Messages.format(student, assignmentToDelete)),
+ result.getFeedbackToUser());
+ }
+
+ @Test
+ public void equals() {
+ DeleteAssignmentCommand deleteFirstCommand = new DeleteAssignmentCommand(INDEX_FIRST, "Assignment 1");
+ DeleteAssignmentCommand deleteSecondCommand = new DeleteAssignmentCommand(INDEX_SECOND, "Assignment 1");
+ DeleteAssignmentCommand deleteDifferentAssignmentCommand =
+ new DeleteAssignmentCommand(INDEX_FIRST, "Assignment 2");
+
+ // same object -> returns true
+ assertTrue(deleteFirstCommand.equals(deleteFirstCommand));
+
+ // same values -> returns true
+ DeleteAssignmentCommand deleteFirstCommandCopy = new DeleteAssignmentCommand(INDEX_FIRST, "Assignment 1");
+ assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy));
+
+ // different types -> returns false
+ assertFalse(deleteFirstCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(deleteFirstCommand.equals(null));
+
+ // different index -> returns false
+ assertFalse(deleteFirstCommand.equals(deleteSecondCommand));
+
+ // different assignment name -> returns false
+ assertFalse(deleteFirstCommand.equals(deleteDifferentAssignmentCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ DeleteAssignmentCommand command = new DeleteAssignmentCommand(INDEX_FIRST, "Assignment 1");
+ String expected = DeleteAssignmentCommand.class.getCanonicalName() + "{targetIndex=" + INDEX_FIRST
+ + ", assignmentName=Assignment 1}";
+ assertEquals(expected, command.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
deleted file mode 100644
index b6f332eabca..00000000000
--- a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package seedu.address.logic.commands;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
-import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
-import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex;
-import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
-import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON;
-import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
-
-import org.junit.jupiter.api.Test;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.logic.Messages;
-import seedu.address.model.Model;
-import seedu.address.model.ModelManager;
-import seedu.address.model.UserPrefs;
-import seedu.address.model.person.Person;
-
-/**
- * Contains integration tests (interaction with the Model) and unit tests for
- * {@code DeleteCommand}.
- */
-public class DeleteCommandTest {
-
- private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
-
- @Test
- public void execute_validIndexUnfilteredList_success() {
- Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
- DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON);
-
- String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS,
- Messages.format(personToDelete));
-
- ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
- expectedModel.deletePerson(personToDelete);
-
- assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel);
- }
-
- @Test
- public void execute_invalidIndexUnfilteredList_throwsCommandException() {
- Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
- DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex);
-
- assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
- }
-
- @Test
- public void execute_validIndexFilteredList_success() {
- showPersonAtIndex(model, INDEX_FIRST_PERSON);
-
- Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
- DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON);
-
- String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS,
- Messages.format(personToDelete));
-
- Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
- expectedModel.deletePerson(personToDelete);
- showNoPerson(expectedModel);
-
- assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel);
- }
-
- @Test
- public void execute_invalidIndexFilteredList_throwsCommandException() {
- showPersonAtIndex(model, INDEX_FIRST_PERSON);
-
- Index outOfBoundIndex = INDEX_SECOND_PERSON;
- // ensures that outOfBoundIndex is still in bounds of address book list
- assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size());
-
- DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex);
-
- assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
- }
-
- @Test
- public void equals() {
- DeleteCommand deleteFirstCommand = new DeleteCommand(INDEX_FIRST_PERSON);
- DeleteCommand deleteSecondCommand = new DeleteCommand(INDEX_SECOND_PERSON);
-
- // same object -> returns true
- assertTrue(deleteFirstCommand.equals(deleteFirstCommand));
-
- // same values -> returns true
- DeleteCommand deleteFirstCommandCopy = new DeleteCommand(INDEX_FIRST_PERSON);
- assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy));
-
- // different types -> returns false
- assertFalse(deleteFirstCommand.equals(1));
-
- // null -> returns false
- assertFalse(deleteFirstCommand.equals(null));
-
- // different person -> returns false
- assertFalse(deleteFirstCommand.equals(deleteSecondCommand));
- }
-
- @Test
- public void toStringMethod() {
- Index targetIndex = Index.fromOneBased(1);
- DeleteCommand deleteCommand = new DeleteCommand(targetIndex);
- String expected = DeleteCommand.class.getCanonicalName() + "{targetIndex=" + targetIndex + "}";
- assertEquals(expected, deleteCommand.toString());
- }
-
- /**
- * Updates {@code model}'s filtered list to show no one.
- */
- private void showNoPerson(Model model) {
- model.updateFilteredPersonList(p -> false);
-
- assertTrue(model.getFilteredPersonList().isEmpty());
- }
-}
diff --git a/src/test/java/seedu/address/logic/commands/DeleteLessonCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteLessonCommandTest.java
new file mode 100644
index 00000000000..54426e41ab1
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/DeleteLessonCommandTest.java
@@ -0,0 +1,131 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.logic.commands.CommandTestUtil.showLessonAtIndex;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND;
+import static seedu.address.testutil.TypicalLessons.getTypicalAddressBook;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.lesson.Lesson;
+
+/**
+ * Contains integration tests (interaction with the Model) and unit tests for
+ * {@code DeleteLessonCommand}.
+ */
+public class DeleteLessonCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_validIndexUnfilteredList_success() {
+ Lesson lessonToDelete = model.getFilteredLessonList().get(INDEX_FIRST.getZeroBased());
+ DeleteLessonCommand deleteLessonCommand = new DeleteLessonCommand(INDEX_FIRST);
+
+ model.updateFilteredLessonList(Model.PREDICATE_SHOW_ALL_LESSONS);
+ String expectedMessage = String.format(DeleteLessonCommand.MESSAGE_DELETE_LESSON_SUCCESS,
+ Messages.format(lessonToDelete));
+ CommandResult expectedResult = new CommandResult(expectedMessage, true);
+ ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ expectedModel.deleteLesson(lessonToDelete);
+
+ assertCommandSuccess(deleteLessonCommand, model, expectedResult, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidIndexUnfilteredList_throwsCommandException() {
+ Index outOfBoundIndex = Index.fromOneBased(model.getFilteredLessonList().size() + 1);
+ DeleteLessonCommand deleteLessonCommand = new DeleteLessonCommand(outOfBoundIndex);
+ model.updateFilteredLessonList(Model.PREDICATE_SHOW_ALL_LESSONS);
+
+ assertCommandFailure(deleteLessonCommand, model, Messages.MESSAGE_INVALID_LESSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void execute_validIndexFilteredList_success() {
+ showLessonAtIndex(model, INDEX_FIRST);
+
+ Lesson lessonToDelete = model.getFilteredLessonList().get(INDEX_FIRST.getZeroBased());
+ DeleteLessonCommand deleteLessonCommand = new DeleteLessonCommand(INDEX_FIRST);
+
+ String expectedMessage = String.format(DeleteLessonCommand.MESSAGE_DELETE_LESSON_SUCCESS,
+ Messages.format(lessonToDelete));
+
+ CommandResult expectedResult = new CommandResult(expectedMessage, true);
+
+ Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ expectedModel.deleteLesson(lessonToDelete);
+
+ assertCommandSuccess(deleteLessonCommand, model, expectedResult, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidIndexFilteredList_throwsCommandException() {
+ showLessonAtIndex(model, INDEX_FIRST);
+
+ Index outOfBoundIndex = INDEX_SECOND;
+ // ensures that outOfBoundIndex is still in bounds of address book list
+ assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getLessonList().size());
+
+ DeleteLessonCommand deleteLessonCommand = new DeleteLessonCommand(outOfBoundIndex);
+
+ assertCommandFailure(deleteLessonCommand, model, Messages.MESSAGE_INVALID_LESSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void equals() {
+ DeleteLessonCommand deleteFirstLessonCommand = new DeleteLessonCommand(INDEX_FIRST);
+ DeleteLessonCommand deleteSecondLessonCommand = new DeleteLessonCommand(INDEX_SECOND);
+
+ // same object -> returns true
+ assertTrue(deleteFirstLessonCommand.equals(deleteFirstLessonCommand));
+
+ // same values -> returns true
+ DeleteLessonCommand deleteFirstLessonCommandCopy = new DeleteLessonCommand(INDEX_FIRST);
+ assertTrue(deleteFirstLessonCommand.equals(deleteFirstLessonCommandCopy));
+
+ // different types -> returns false
+ assertFalse(deleteFirstLessonCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(deleteFirstLessonCommand.equals(null));
+
+ // different lesson -> returns false
+ assertFalse(deleteFirstLessonCommand.equals(deleteSecondLessonCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index targetIndex = Index.fromOneBased(1);
+ DeleteLessonCommand deleteLessonCommand = new DeleteLessonCommand(targetIndex);
+ String expected = DeleteLessonCommand.class.getCanonicalName() + "{targetIndex=" + targetIndex + "}";
+ assertEquals(expected, deleteLessonCommand.toString());
+ }
+
+ @Test
+ public void execute_deleteFromEmptyList_throwsCommandException() {
+ model.updateFilteredLessonList(p -> false); // Empty the list
+ DeleteLessonCommand deleteLessonCommand = new DeleteLessonCommand(INDEX_FIRST);
+
+ assertCommandFailure(deleteLessonCommand, model, Messages.MESSAGE_EMPTY_LESSON_LIST);
+ }
+
+ /**
+ * Updates {@code model}'s filtered list to show no one.
+ */
+ private void showNoStudent(Model model) {
+ model.updateFilteredStudentList(p -> false);
+
+ assertTrue(model.getFilteredStudentList().isEmpty());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/DeleteStudentCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteStudentCommandTest.java
new file mode 100644
index 00000000000..379fc0d749a
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/DeleteStudentCommandTest.java
@@ -0,0 +1,205 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.logic.commands.CommandTestUtil.showStudentAtIndex;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND;
+import static seedu.address.testutil.TypicalStudents.getTypicalAddressBook;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.datetimeutil.Date;
+import seedu.address.model.datetimeutil.Time;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.student.Student;
+import seedu.address.model.subject.Subject;
+
+public class DeleteStudentCommandTest {
+
+ private Model model;
+ private TestLogHandler testLogHandler;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ testLogHandler = new TestLogHandler();
+ Logger logger = LogsCenter.getLogger(DeleteStudentCommand.class);
+ logger.setUseParentHandlers(false); // Prevent default handlers from interfering
+ logger.addHandler(testLogHandler);
+ logger.setLevel(java.util.logging.Level.ALL); // Capture all log levels
+ }
+
+
+ @Test
+ public void execute_validIndexUnfilteredList_success() {
+ Student studentToDelete = model.getFilteredStudentList().get(INDEX_FIRST.getZeroBased());
+ DeleteStudentCommand deleteStudentCommand = new DeleteStudentCommand(INDEX_FIRST);
+
+ String expectedMessage = String.format(DeleteStudentCommand.MESSAGE_DELETE_STUDENT_SUCCESS,
+ Messages.format(studentToDelete));
+ CommandResult expectedResult = new CommandResult(expectedMessage, true);
+ ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ expectedModel.deleteStudent(studentToDelete);
+
+ assertCommandSuccess(deleteStudentCommand, model, expectedResult, expectedModel);
+
+ // Verify logging
+ assertTrue(testLogHandler.containsMessage("Executing DeleteStudentCommand for index: 1"));
+ assertTrue(testLogHandler.containsMessage("Successfully deleted student: " + studentToDelete.getName()));
+ }
+
+ @Test
+ public void execute_validIndexFilteredList_success() {
+ showStudentAtIndex(model, INDEX_FIRST);
+
+ Student studentToDelete = model.getFilteredStudentList().get(INDEX_FIRST.getZeroBased());
+ DeleteStudentCommand deleteStudentCommand = new DeleteStudentCommand(INDEX_FIRST);
+
+ String expectedMessage = String.format(DeleteStudentCommand.MESSAGE_DELETE_STUDENT_SUCCESS,
+ Messages.format(studentToDelete));
+
+ CommandResult expectedResult = new CommandResult(expectedMessage, true);
+
+ Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ showStudentAtIndex(expectedModel, INDEX_FIRST);
+ expectedModel.deleteStudent(studentToDelete);
+
+ assertCommandSuccess(deleteStudentCommand, model, expectedResult, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidIndexFilteredList_throwsCommandException() {
+ showStudentAtIndex(model, INDEX_FIRST);
+
+ Index outOfBoundIndex = INDEX_SECOND;
+ // ensures that outOfBoundIndex is still in bounds of address book list
+ assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getStudentList().size());
+
+ DeleteStudentCommand deleteStudentCommand = new DeleteStudentCommand(outOfBoundIndex);
+
+ assertCommandFailure(deleteStudentCommand, model, Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void execute_deleteStudentWithLessons_lessonsAlsoDeleted() throws Exception {
+ // Add a lesson to the first student
+ Student studentWithLessons = model.getFilteredStudentList()
+ .get(INDEX_FIRST.getZeroBased());
+ Lesson lesson = new Lesson(new Subject("Math"),
+ studentWithLessons.getName(), new Date("01-01-2026"), new Time("12:00"));
+ model.addLesson(lesson);
+
+ int initialLessonCount = model.getFilteredLessonList().size();
+ model.updateFilteredStudentList(Model.PREDICATE_SHOW_ALL_STUDENTS);
+
+ DeleteStudentCommand deleteStudentCommand = new DeleteStudentCommand(INDEX_FIRST);
+ deleteStudentCommand.execute(model);
+
+ assertEquals(initialLessonCount - 1, model.getFilteredLessonList().size());
+
+ // Verify logging for lesson deletion
+ assertTrue(testLogHandler.containsMessage("Deleting 1 associated lessons"));
+ }
+
+ @Test
+ public void execute_deleteFromEmptyList_throwsCommandException() {
+ model.updateFilteredStudentList(p -> false); // Empty the list
+ DeleteStudentCommand deleteStudentCommand = new DeleteStudentCommand(INDEX_FIRST);
+
+ assertCommandFailure(deleteStudentCommand, model, Messages.MESSAGE_EMPTY_STUDENT_LIST);
+
+ // Verify logging
+ assertTrue(testLogHandler.containsMessage("Attempted to delete from empty student list"));
+ }
+
+ @Test
+ public void equals() {
+ DeleteStudentCommand deleteFirstCommand = new DeleteStudentCommand(INDEX_FIRST);
+ DeleteStudentCommand deleteSecondCommand = new DeleteStudentCommand(INDEX_SECOND);
+
+ // same object -> returns true
+ assertTrue(deleteFirstCommand.equals(deleteFirstCommand));
+
+ // same values -> returns true
+ DeleteStudentCommand deleteFirstCommandCopy = new DeleteStudentCommand(INDEX_FIRST);
+ assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy));
+
+ // different types -> returns false
+ assertFalse(deleteFirstCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(deleteFirstCommand.equals(null));
+
+ // different student -> returns false
+ assertFalse(deleteFirstCommand.equals(deleteSecondCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index targetIndex = Index.fromOneBased(1);
+ DeleteStudentCommand deleteStudentCommand = new DeleteStudentCommand(targetIndex);
+ String expected = DeleteStudentCommand.class.getCanonicalName() + "{targetIndex=" + targetIndex + "}";
+ assertEquals(expected, deleteStudentCommand.toString());
+ }
+
+ @Test
+ public void constructor_nullIndex_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new DeleteStudentCommand(null));
+ }
+
+ @Test
+ public void execute_commandResultHasCorrectFormat() throws Exception {
+ Student studentToDelete = model.getFilteredStudentList().get(INDEX_FIRST.getZeroBased());
+ DeleteStudentCommand deleteStudentCommand = new DeleteStudentCommand(INDEX_FIRST);
+ CommandResult result = deleteStudentCommand.execute(model);
+
+ String expectedMessage = String.format(DeleteStudentCommand.MESSAGE_DELETE_STUDENT_SUCCESS,
+ Messages.format(studentToDelete));
+
+ assertEquals(expectedMessage, result.getFeedbackToUser());
+ assertTrue(result.isUpdateList());
+ }
+
+ // Helper class to test logging
+ private static class TestLogHandler extends Handler {
+ private final List messages = new ArrayList<>();
+
+ @Override
+ public void publish(LogRecord record) {
+ messages.add(record.getLevel() + ": " + record.getMessage());
+ }
+
+ @Override
+ public void flush() {}
+
+ public boolean containsMessage(String expectedMessagePart) {
+ return messages.stream().anyMatch(message -> message.contains(expectedMessagePart));
+ }
+
+ @Override
+ public void close() throws SecurityException {}
+
+ public void printMessages() {
+ messages.forEach(System.out::println); // debugging purpose
+ }
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/EditAssignmentCommandTest.java b/src/test/java/seedu/address/logic/commands/EditAssignmentCommandTest.java
new file mode 100644
index 00000000000..9687287b8ee
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/EditAssignmentCommandTest.java
@@ -0,0 +1,185 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.testutil.TypicalAssignments.ASSIGNMENT1;
+import static seedu.address.testutil.TypicalAssignments.ASSIGNMENT2;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND;
+import static seedu.address.testutil.TypicalStudents.getTypicalAddressBook;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.assignment.Assignment;
+import seedu.address.model.datetimeutil.Date;
+import seedu.address.model.student.Student;
+import seedu.address.testutil.AssignmentBuilder;
+import seedu.address.testutil.EditAssignmentDescriptorBuilder;
+
+public class EditAssignmentCommandTest {
+
+ private Model model;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ }
+
+ @Test
+ public void execute_noFieldSpecified_throwsCommandException() {
+ Student studentToEdit = model.getFilteredStudentList().get(INDEX_FIRST.getZeroBased());
+ Assignment assignmentToEdit = ASSIGNMENT1;
+ studentToEdit.clearAssignments();
+ studentToEdit.addAssignment(assignmentToEdit);
+
+
+ EditAssignmentCommand.EditAssignmentDescriptor descriptor =
+ new EditAssignmentCommand.EditAssignmentDescriptor();
+ EditAssignmentCommand editAssignmentCommand =
+ new EditAssignmentCommand(INDEX_FIRST, assignmentToEdit.getAssignmentName(), descriptor);
+
+ assertCommandFailure(editAssignmentCommand, model, EditAssignmentCommand.MESSAGE_NOT_EDITED);
+ }
+
+ @Test
+ public void execute_duplicateAssignment_throwsCommandException() {
+ Student studentToEdit = model.getFilteredStudentList().get(INDEX_FIRST.getZeroBased());
+ Assignment assignment1 = ASSIGNMENT1;
+ Assignment assignment2 = ASSIGNMENT2;
+ studentToEdit.clearAssignments();
+ studentToEdit.addAssignment(assignment1);
+ studentToEdit.addAssignment(assignment2);
+
+
+ EditAssignmentCommand.EditAssignmentDescriptor descriptor =
+ new EditAssignmentDescriptorBuilder(assignment2).build();
+ EditAssignmentCommand editAssignmentCommand =
+ new EditAssignmentCommand(INDEX_FIRST, assignment1.getAssignmentName(), descriptor);
+
+ assertCommandFailure(editAssignmentCommand, model, EditAssignmentCommand.MESSAGE_DUPLICATE_ASSIGNMENT);
+ }
+
+ @Test
+ public void execute_invalidStudentIndex_throwsCommandException() {
+ Index outOfBoundIndex = Index.fromOneBased(model.getFilteredStudentList().size() + 1);
+ EditAssignmentCommand.EditAssignmentDescriptor descriptor =
+ new EditAssignmentDescriptorBuilder().withNewAssignmentName("New Name").build();
+ EditAssignmentCommand editAssignmentCommand =
+ new EditAssignmentCommand(outOfBoundIndex, "Assignment", descriptor);
+
+ assertCommandFailure(editAssignmentCommand, model, Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void execute_assignmentNotFound_throwsCommandException() {
+ Student studentToEdit = model.getFilteredStudentList().get(INDEX_FIRST.getZeroBased());
+ Assignment assignmentToEdit = ASSIGNMENT1;
+ studentToEdit.clearAssignments();
+ studentToEdit.addAssignment(assignmentToEdit);
+
+ EditAssignmentCommand.EditAssignmentDescriptor descriptor =
+ new EditAssignmentDescriptorBuilder().withNewAssignmentName("New Name").build();
+ EditAssignmentCommand editAssignmentCommand =
+ new EditAssignmentCommand(INDEX_FIRST, "Non-existent Assignment", descriptor);
+
+ assertCommandFailure(editAssignmentCommand, model,
+ String.format(Messages.MESSAGE_ASSIGNMENT_NOT_FOUND, "Non-existent Assignment"));
+ }
+
+ @Test
+ public void equals() {
+ final EditAssignmentCommand standardCommand =
+ new EditAssignmentCommand(INDEX_FIRST, "Assignment",
+ new EditAssignmentDescriptorBuilder().withNewAssignmentName("New Name").build());
+
+ // same values -> returns true
+ EditAssignmentCommand.EditAssignmentDescriptor copyDescriptor =
+ new EditAssignmentDescriptorBuilder().withNewAssignmentName("New Name").build();
+ EditAssignmentCommand commandWithSameValues =
+ new EditAssignmentCommand(INDEX_FIRST, "Assignment", copyDescriptor);
+ assertTrue(standardCommand.equals(commandWithSameValues));
+
+ // same object -> returns true
+ assertTrue(standardCommand.equals(standardCommand));
+
+ // null -> returns false
+ assertFalse(standardCommand.equals(null));
+
+ // different types -> returns false
+ assertFalse(standardCommand.equals(new ClearCommand()));
+
+ // different index -> returns false
+ assertFalse(standardCommand.equals(
+ new EditAssignmentCommand(INDEX_SECOND, "Assignment", copyDescriptor)));
+
+ // different assignment name -> returns false
+ assertFalse(standardCommand.equals(
+ new EditAssignmentCommand(INDEX_FIRST, "Different Assignment", copyDescriptor)));
+
+ // different descriptor -> returns false
+ assertFalse(standardCommand.equals(
+ new EditAssignmentCommand(INDEX_FIRST, "Assignment",
+ new EditAssignmentDescriptorBuilder().withNewAssignmentName("Different Name").build())));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index index = Index.fromOneBased(1);
+ EditAssignmentCommand.EditAssignmentDescriptor editAssignmentDescriptor =
+ new EditAssignmentDescriptorBuilder().withNewAssignmentName("New Name").build();
+ EditAssignmentCommand editAssignmentCommand =
+ new EditAssignmentCommand(index, "Assignment", editAssignmentDescriptor);
+ String expected = EditAssignmentCommand.class.getCanonicalName() + "{index=" + index
+ + ", assignmentName=Assignment"
+ + ", editAssignmentDescriptor=" + editAssignmentDescriptor + "}";
+ assertEquals(expected, editAssignmentCommand.toString());
+ }
+
+ @Test
+ public void createEditedAssignment_allFieldsEdited() {
+ Assignment original = new AssignmentBuilder().withAssignmentName("Original")
+ .withDueDate(new Date("25-01-2026")).build();
+ EditAssignmentCommand.EditAssignmentDescriptor descriptor =
+ new EditAssignmentDescriptorBuilder()
+ .withNewAssignmentName("Edited")
+ .withDate("20-12-2026")
+ .build();
+ Assignment edited = new EditAssignmentCommand(INDEX_FIRST, "name", descriptor)
+ .createEditedAssignment(original, descriptor);
+
+ assertEquals("Edited", edited.getAssignmentName());
+ assertEquals(new Date("20-12-2026"), edited.getDueDate());
+ }
+
+ @Test
+ public void editAssignmentDescriptor_isAnyFieldEdited() {
+ // No fields edited
+ assertFalse(new EditAssignmentCommand.EditAssignmentDescriptor().isAnyFieldEdited());
+
+ // Name edited
+ EditAssignmentCommand.EditAssignmentDescriptor nameDescriptor =
+ new EditAssignmentDescriptorBuilder().withNewAssignmentName("New Name").build();
+ assertTrue(nameDescriptor.isAnyFieldEdited());
+
+ // Date edited
+ EditAssignmentCommand.EditAssignmentDescriptor dateDescriptor =
+ new EditAssignmentDescriptorBuilder().withDate("31-12-2026").build();
+ assertTrue(dateDescriptor.isAnyFieldEdited());
+
+ // Both fields edited
+ EditAssignmentCommand.EditAssignmentDescriptor bothDescriptor =
+ new EditAssignmentDescriptorBuilder()
+ .withNewAssignmentName("New Name")
+ .withDate("31-12-2026")
+ .build();
+ assertTrue(bothDescriptor.isAnyFieldEdited());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/EditAssignmentDescriptorTest.java b/src/test/java/seedu/address/logic/commands/EditAssignmentDescriptorTest.java
new file mode 100644
index 00000000000..2e6794e038c
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/EditAssignmentDescriptorTest.java
@@ -0,0 +1,58 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_DATE_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.EditAssignmentCommand.EditAssignmentDescriptor;
+import seedu.address.testutil.EditAssignmentDescriptorBuilder;
+
+public class EditAssignmentDescriptorTest {
+
+ @Test
+ public void equals() {
+ // same values -> returns true
+ EditAssignmentDescriptor descriptor = new EditAssignmentDescriptorBuilder()
+ .withNewAssignmentName(VALID_NAME_BOB)
+ .withDate(VALID_DATE_BOB).build();
+ EditAssignmentDescriptor descriptorWithSameValues = new EditAssignmentDescriptor(descriptor);
+ assertTrue(descriptor.equals(descriptorWithSameValues));
+
+ // same object -> returns true
+ assertTrue(descriptor.equals(descriptor));
+
+ // null -> returns false
+ assertFalse(descriptor.equals(null));
+
+ // different types -> returns false
+ assertFalse(descriptor.equals(5));
+
+ // different values -> returns false
+ EditAssignmentDescriptor differentDescriptor = new EditAssignmentDescriptorBuilder()
+ .withNewAssignmentName("DifferentName")
+ .withDate(VALID_DATE_BOB).build();
+ assertFalse(descriptor.equals(differentDescriptor));
+
+ // different new assignment name -> returns false
+ EditAssignmentDescriptor editedDescriptor = new EditAssignmentDescriptorBuilder(descriptor)
+ .withNewAssignmentName("NewName").build();
+ assertFalse(descriptor.equals(editedDescriptor));
+
+ // different date -> returns false
+ editedDescriptor = new EditAssignmentDescriptorBuilder(descriptor).withDate("09-09-2025").build();
+ assertFalse(descriptor.equals(editedDescriptor));
+ }
+
+ @Test
+ public void toStringMethod() {
+ EditAssignmentDescriptor editAssignmentDescriptor = new EditAssignmentDescriptor();
+ String expected = EditAssignmentDescriptor.class.getCanonicalName() + "{assignmentName="
+ + editAssignmentDescriptor.getNewAssignmentName().orElse(null) + ", date="
+ + editAssignmentDescriptor.getDate().orElse(null) + "}";
+ assertEquals(expected, editAssignmentDescriptor.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/EditCommandTest.java b/src/test/java/seedu/address/logic/commands/EditCommandTest.java
deleted file mode 100644
index 469dd97daa7..00000000000
--- a/src/test/java/seedu/address/logic/commands/EditCommandTest.java
+++ /dev/null
@@ -1,184 +0,0 @@
-package seedu.address.logic.commands;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
-import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
-import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
-import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex;
-import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
-import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON;
-import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
-
-import org.junit.jupiter.api.Test;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.logic.Messages;
-import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
-import seedu.address.model.AddressBook;
-import seedu.address.model.Model;
-import seedu.address.model.ModelManager;
-import seedu.address.model.UserPrefs;
-import seedu.address.model.person.Person;
-import seedu.address.testutil.EditPersonDescriptorBuilder;
-import seedu.address.testutil.PersonBuilder;
-
-/**
- * Contains integration tests (interaction with the Model) and unit tests for EditCommand.
- */
-public class EditCommandTest {
-
- private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
-
- @Test
- public void execute_allFieldsSpecifiedUnfilteredList_success() {
- Person editedPerson = new PersonBuilder().build();
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(editedPerson).build();
- EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, descriptor);
-
- String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson));
-
- Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
- expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson);
-
- assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
- }
-
- @Test
- public void execute_someFieldsSpecifiedUnfilteredList_success() {
- Index indexLastPerson = Index.fromOneBased(model.getFilteredPersonList().size());
- Person lastPerson = model.getFilteredPersonList().get(indexLastPerson.getZeroBased());
-
- PersonBuilder personInList = new PersonBuilder(lastPerson);
- Person editedPerson = personInList.withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB)
- .withTags(VALID_TAG_HUSBAND).build();
-
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB)
- .withPhone(VALID_PHONE_BOB).withTags(VALID_TAG_HUSBAND).build();
- EditCommand editCommand = new EditCommand(indexLastPerson, descriptor);
-
- String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson));
-
- Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
- expectedModel.setPerson(lastPerson, editedPerson);
-
- assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
- }
-
- @Test
- public void execute_noFieldSpecifiedUnfilteredList_success() {
- EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, new EditPersonDescriptor());
- Person editedPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
-
- String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson));
-
- Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
-
- assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
- }
-
- @Test
- public void execute_filteredList_success() {
- showPersonAtIndex(model, INDEX_FIRST_PERSON);
-
- Person personInFilteredList = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
- Person editedPerson = new PersonBuilder(personInFilteredList).withName(VALID_NAME_BOB).build();
- EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON,
- new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build());
-
- String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson));
-
- Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
- expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson);
-
- assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
- }
-
- @Test
- public void execute_duplicatePersonUnfilteredList_failure() {
- Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(firstPerson).build();
- EditCommand editCommand = new EditCommand(INDEX_SECOND_PERSON, descriptor);
-
- assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_PERSON);
- }
-
- @Test
- public void execute_duplicatePersonFilteredList_failure() {
- showPersonAtIndex(model, INDEX_FIRST_PERSON);
-
- // edit person in filtered list into a duplicate in address book
- Person personInList = model.getAddressBook().getPersonList().get(INDEX_SECOND_PERSON.getZeroBased());
- EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON,
- new EditPersonDescriptorBuilder(personInList).build());
-
- assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_PERSON);
- }
-
- @Test
- public void execute_invalidPersonIndexUnfilteredList_failure() {
- Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build();
- EditCommand editCommand = new EditCommand(outOfBoundIndex, descriptor);
-
- assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
- }
-
- /**
- * Edit filtered list where index is larger than size of filtered list,
- * but smaller than size of address book
- */
- @Test
- public void execute_invalidPersonIndexFilteredList_failure() {
- showPersonAtIndex(model, INDEX_FIRST_PERSON);
- Index outOfBoundIndex = INDEX_SECOND_PERSON;
- // ensures that outOfBoundIndex is still in bounds of address book list
- assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size());
-
- EditCommand editCommand = new EditCommand(outOfBoundIndex,
- new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build());
-
- assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
- }
-
- @Test
- public void equals() {
- final EditCommand standardCommand = new EditCommand(INDEX_FIRST_PERSON, DESC_AMY);
-
- // same values -> returns true
- EditPersonDescriptor copyDescriptor = new EditPersonDescriptor(DESC_AMY);
- EditCommand commandWithSameValues = new EditCommand(INDEX_FIRST_PERSON, copyDescriptor);
- assertTrue(standardCommand.equals(commandWithSameValues));
-
- // same object -> returns true
- assertTrue(standardCommand.equals(standardCommand));
-
- // null -> returns false
- assertFalse(standardCommand.equals(null));
-
- // different types -> returns false
- assertFalse(standardCommand.equals(new ClearCommand()));
-
- // different index -> returns false
- assertFalse(standardCommand.equals(new EditCommand(INDEX_SECOND_PERSON, DESC_AMY)));
-
- // different descriptor -> returns false
- assertFalse(standardCommand.equals(new EditCommand(INDEX_FIRST_PERSON, DESC_BOB)));
- }
-
- @Test
- public void toStringMethod() {
- Index index = Index.fromOneBased(1);
- EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
- EditCommand editCommand = new EditCommand(index, editPersonDescriptor);
- String expected = EditCommand.class.getCanonicalName() + "{index=" + index + ", editPersonDescriptor="
- + editPersonDescriptor + "}";
- assertEquals(expected, editCommand.toString());
- }
-
-}
diff --git a/src/test/java/seedu/address/logic/commands/EditLessonCommandTest.java b/src/test/java/seedu/address/logic/commands/EditLessonCommandTest.java
new file mode 100644
index 00000000000..390d6c5905e
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/EditLessonCommandTest.java
@@ -0,0 +1,213 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.LESSON_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.LESSON_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_DATE_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_SUBJECT_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.logic.commands.CommandTestUtil.showLessonAtIndex;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND;
+import static seedu.address.testutil.TypicalLessons.getTypicalAddressBook;
+import static seedu.address.testutil.TypicalStudents.AMY;
+import static seedu.address.testutil.TypicalStudents.BOB;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.EditLessonCommand.EditLessonDescriptor;
+import seedu.address.model.AddressBook;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.testutil.EditLessonDescriptorBuilder;
+import seedu.address.testutil.LessonBuilder;
+
+/**
+ * Contains integration tests (interaction with the Model) and unit tests for EditLessonCommand.
+ */
+public class EditLessonCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @BeforeEach
+ public void setUp() {
+ model.addStudent(AMY);
+ model.addStudent(BOB);
+ }
+
+ @Test
+ public void execute_allFieldsSpecifiedUnfilteredList_success() {
+ Lesson editedLesson = new LessonBuilder().build();
+ EditLessonDescriptor descriptor = new EditLessonDescriptorBuilder(editedLesson).build();
+ EditLessonCommand editLessonCommand = new EditLessonCommand(INDEX_FIRST, descriptor);
+
+ String expectedMessage = String.format(EditLessonCommand.MESSAGE_EDIT_LESSON_SUCCESS,
+ Messages.format(editedLesson));
+
+ CommandResult expectedResult = new CommandResult(expectedMessage, true);
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setLesson(model.getFilteredLessonList().get(0), editedLesson);
+
+ assertCommandSuccess(editLessonCommand, model, expectedResult, expectedModel);
+ }
+
+ @Test
+ public void execute_someFieldsSpecifiedUnfilteredList_success() {
+ Index indexLastLesson = Index.fromOneBased(model.getFilteredLessonList().size());
+ Lesson lastLesson = model.getFilteredLessonList().get(indexLastLesson.getZeroBased());
+
+ LessonBuilder lessonInList = new LessonBuilder(lastLesson);
+ Lesson editedLesson = lessonInList.withName(VALID_NAME_BOB).withDate(VALID_DATE_BOB)
+ .withSubject(VALID_SUBJECT_BOB).build();
+
+ EditLessonDescriptor descriptor = new EditLessonDescriptorBuilder().withName(VALID_NAME_BOB)
+ .withDate(VALID_DATE_BOB).withSubject(VALID_SUBJECT_BOB).build();
+ EditLessonCommand editLessonCommand = new EditLessonCommand(indexLastLesson, descriptor);
+
+ String expectedMessage = String.format(EditLessonCommand.MESSAGE_EDIT_LESSON_SUCCESS,
+ Messages.format(editedLesson));
+ CommandResult expectedResult = new CommandResult(expectedMessage, true);
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setLesson(lastLesson, editedLesson);
+
+ assertCommandSuccess(editLessonCommand, model, expectedResult, expectedModel);
+ }
+
+ @Test
+ public void execute_noFieldSpecified_throwsCommandException() {
+ EditLessonCommand editLessonCommand = new EditLessonCommand(INDEX_FIRST, new EditLessonDescriptor());
+ assertThrows(AssertionError.class, () -> editLessonCommand.execute(model),
+ EditLessonCommand.MESSAGE_NOT_EDITED);
+ }
+
+ @Test
+ public void execute_editStudentNameWithoutEditingSubject_failure() {
+ Index indexLastLesson = Index.fromOneBased(model.getFilteredLessonList().size());
+ Lesson lastLesson = model.getFilteredLessonList().get(indexLastLesson.getZeroBased());
+
+ LessonBuilder lessonInList = new LessonBuilder(lastLesson);
+ Lesson editedLesson = lessonInList.withName(VALID_NAME_BOB).build();
+
+ EditLessonDescriptor descriptor = new EditLessonDescriptorBuilder().withName(VALID_NAME_BOB).build();
+ EditLessonCommand editLessonCommand = new EditLessonCommand(indexLastLesson, descriptor);
+
+ String expectedMessage = String.format(AddLessonCommand.MESSAGE_SUBJECT_MISMATCH,
+ Messages.format(editedLesson));
+
+ assertCommandFailure(editLessonCommand, model, expectedMessage);
+ }
+
+
+ @Test
+ public void execute_filteredList_success() {
+ showLessonAtIndex(model, INDEX_FIRST);
+
+ Lesson lessonInFilteredList = model.getFilteredLessonList().get(INDEX_FIRST.getZeroBased());
+ Lesson editedLesson = new LessonBuilder(lessonInFilteredList).withName(VALID_NAME_BOB)
+ .withSubject(VALID_SUBJECT_BOB).build();
+ EditLessonCommand editLessonCommand = new EditLessonCommand(INDEX_FIRST,
+ new EditLessonDescriptorBuilder().withName(VALID_NAME_BOB).withSubject(VALID_SUBJECT_BOB).build());
+
+ String expectedMessage = String.format(EditLessonCommand.MESSAGE_EDIT_LESSON_SUCCESS,
+ Messages.format(editedLesson));
+ CommandResult expectedResult = new CommandResult(expectedMessage, true);
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setLesson(model.getFilteredLessonList().get(INDEX_FIRST.getZeroBased()), editedLesson);
+
+ assertCommandSuccess(editLessonCommand, model, expectedResult, expectedModel);
+ }
+
+ @Test
+ public void execute_duplicateLessonUnfilteredList_failure() {
+ Lesson firstLesson = model.getFilteredLessonList().get(INDEX_FIRST.getZeroBased());
+ EditLessonDescriptor descriptor = new EditLessonDescriptorBuilder(firstLesson).build();
+ EditLessonCommand editLessonCommand = new EditLessonCommand(INDEX_SECOND, descriptor);
+
+ assertCommandFailure(editLessonCommand, model, EditLessonCommand.MESSAGE_DUPLICATE_LESSON);
+ }
+
+ @Test
+ public void execute_duplicateLessonFilteredList_failure() {
+ showLessonAtIndex(model, INDEX_FIRST);
+
+ // edit lesson in filtered list into a duplicate in address book
+ Lesson lessonInList = model.getAddressBook().getLessonList().get(INDEX_SECOND.getZeroBased());
+ EditLessonCommand editLessonCommand = new EditLessonCommand(INDEX_FIRST,
+ new EditLessonDescriptorBuilder(lessonInList).build());
+
+ assertCommandFailure(editLessonCommand, model, EditLessonCommand.MESSAGE_DUPLICATE_LESSON);
+ }
+
+ @Test
+ public void execute_invalidLessonIndexUnfilteredList_failure() {
+ Index outOfBoundIndex = Index.fromOneBased(model.getFilteredLessonList().size() + 1);
+ EditLessonDescriptor descriptor = new EditLessonDescriptorBuilder().withName(VALID_NAME_BOB).build();
+ EditLessonCommand editLessonCommand = new EditLessonCommand(outOfBoundIndex, descriptor);
+
+ assertCommandFailure(editLessonCommand, model, Messages.MESSAGE_INVALID_LESSON_DISPLAYED_INDEX);
+ }
+
+ /**
+ * Edit filtered list where index is larger than size of filtered list,
+ * but smaller than size of address book
+ */
+ @Test
+ public void execute_invalidLessonIndexFilteredList_failure() {
+ showLessonAtIndex(model, INDEX_FIRST);
+ Index outOfBoundIndex = INDEX_SECOND;
+ // ensures that outOfBoundIndex is still in bounds of address book list
+ assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getLessonList().size());
+
+ EditLessonCommand editCommand = new EditLessonCommand(outOfBoundIndex,
+ new EditLessonDescriptorBuilder().withName(VALID_NAME_BOB).build());
+
+ assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_LESSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void equals() {
+ final EditLessonCommand standardCommand = new EditLessonCommand(INDEX_FIRST, LESSON_DESC_AMY);
+
+ // same values -> returns true
+ EditLessonDescriptor copyDescriptor = new EditLessonDescriptor(LESSON_DESC_AMY);
+ EditLessonCommand commandWithSameValues = new EditLessonCommand(INDEX_FIRST, copyDescriptor);
+ assertTrue(standardCommand.equals(commandWithSameValues));
+
+ // same object -> returns true
+ assertTrue(standardCommand.equals(standardCommand));
+
+ // null -> returns false
+ assertFalse(standardCommand.equals(null));
+
+ // different types -> returns false
+ assertFalse(standardCommand.equals(new ClearCommand()));
+
+ // different index -> returns false
+ assertFalse(standardCommand.equals(new EditLessonCommand(INDEX_SECOND, LESSON_DESC_AMY)));
+
+ // different descriptor -> returns false
+ assertFalse(standardCommand.equals(new EditLessonCommand(INDEX_FIRST, LESSON_DESC_BOB)));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index index = Index.fromOneBased(1);
+ EditLessonDescriptor editLessonDescriptor = new EditLessonDescriptor();
+ EditLessonCommand editCommand = new EditLessonCommand(index, editLessonDescriptor);
+ String expected = EditLessonCommand.class.getCanonicalName() + "{index=" + index + ", editLessonDescriptor="
+ + editLessonDescriptor + "}";
+ assertEquals(expected, editCommand.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/EditLessonDescriptorTest.java b/src/test/java/seedu/address/logic/commands/EditLessonDescriptorTest.java
new file mode 100644
index 00000000000..72a67d9be35
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/EditLessonDescriptorTest.java
@@ -0,0 +1,66 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.LESSON_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.LESSON_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_DATE_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_SUBJECT_HUSBAND;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TIME_BOB;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.EditLessonCommand.EditLessonDescriptor;
+import seedu.address.testutil.EditLessonDescriptorBuilder;
+
+public class EditLessonDescriptorTest {
+
+ @Test
+ public void equals() {
+ // same values -> returns true
+ EditLessonDescriptor descriptorWithSameValues = new EditLessonDescriptor(LESSON_DESC_AMY);
+ assertTrue(LESSON_DESC_AMY.equals(descriptorWithSameValues));
+
+ // same object -> returns true
+ assertTrue(LESSON_DESC_AMY.equals(LESSON_DESC_AMY));
+
+ // null -> returns false
+ assertFalse(LESSON_DESC_AMY.equals(null));
+
+ // different types -> returns false
+ assertFalse(LESSON_DESC_AMY.equals(5));
+
+ // different values -> returns false
+ assertFalse(LESSON_DESC_AMY.equals(LESSON_DESC_BOB));
+
+ // different name -> returns false
+ EditLessonDescriptor editedAmy = new EditLessonDescriptorBuilder(LESSON_DESC_AMY)
+ .withName(VALID_NAME_BOB).build();
+ assertFalse(LESSON_DESC_AMY.equals(editedAmy));
+
+ // different date -> returns false
+ editedAmy = new EditLessonDescriptorBuilder(LESSON_DESC_AMY).withDate(VALID_DATE_BOB).build();
+ assertFalse(LESSON_DESC_AMY.equals(editedAmy));
+
+ // different time -> returns false
+ editedAmy = new EditLessonDescriptorBuilder(LESSON_DESC_AMY).withTime(VALID_TIME_BOB).build();
+ assertFalse(LESSON_DESC_AMY.equals(editedAmy));
+
+ // different subjects -> returns false
+ editedAmy = new EditLessonDescriptorBuilder(LESSON_DESC_AMY).withSubject(VALID_SUBJECT_HUSBAND).build();
+ assertFalse(LESSON_DESC_AMY.equals(editedAmy));
+ }
+
+ @Test
+ public void toStringMethod() {
+ EditLessonDescriptor editLessonDescriptor = new EditLessonDescriptor();
+ String expected = EditLessonDescriptor.class.getCanonicalName() + "{subjects="
+ + editLessonDescriptor.getSubject().orElse(null) + ", name="
+ + editLessonDescriptor.getName().orElse(null) + ", date="
+ + editLessonDescriptor.getDate().orElse(null) + ", time="
+ + editLessonDescriptor.getTime().orElse(null) + "}";
+ assertEquals(expected, editLessonDescriptor.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java b/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
deleted file mode 100644
index b17c1f3d5c2..00000000000
--- a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package seedu.address.logic.commands;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
-
-import org.junit.jupiter.api.Test;
-
-import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
-import seedu.address.testutil.EditPersonDescriptorBuilder;
-
-public class EditPersonDescriptorTest {
-
- @Test
- public void equals() {
- // same values -> returns true
- EditPersonDescriptor descriptorWithSameValues = new EditPersonDescriptor(DESC_AMY);
- assertTrue(DESC_AMY.equals(descriptorWithSameValues));
-
- // same object -> returns true
- assertTrue(DESC_AMY.equals(DESC_AMY));
-
- // null -> returns false
- assertFalse(DESC_AMY.equals(null));
-
- // different types -> returns false
- assertFalse(DESC_AMY.equals(5));
-
- // different values -> returns false
- assertFalse(DESC_AMY.equals(DESC_BOB));
-
- // different name -> returns false
- EditPersonDescriptor editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withName(VALID_NAME_BOB).build();
- assertFalse(DESC_AMY.equals(editedAmy));
-
- // different phone -> returns false
- editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withPhone(VALID_PHONE_BOB).build();
- assertFalse(DESC_AMY.equals(editedAmy));
-
- // different email -> returns false
- editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withEmail(VALID_EMAIL_BOB).build();
- assertFalse(DESC_AMY.equals(editedAmy));
-
- // different address -> returns false
- editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withAddress(VALID_ADDRESS_BOB).build();
- assertFalse(DESC_AMY.equals(editedAmy));
-
- // different tags -> returns false
- editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withTags(VALID_TAG_HUSBAND).build();
- assertFalse(DESC_AMY.equals(editedAmy));
- }
-
- @Test
- public void toStringMethod() {
- EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
- String expected = EditPersonDescriptor.class.getCanonicalName() + "{name="
- + editPersonDescriptor.getName().orElse(null) + ", phone="
- + editPersonDescriptor.getPhone().orElse(null) + ", email="
- + editPersonDescriptor.getEmail().orElse(null) + ", address="
- + editPersonDescriptor.getAddress().orElse(null) + ", tags="
- + editPersonDescriptor.getTags().orElse(null) + "}";
- assertEquals(expected, editPersonDescriptor.toString());
- }
-}
diff --git a/src/test/java/seedu/address/logic/commands/EditStudentCommandTest.java b/src/test/java/seedu/address/logic/commands/EditStudentCommandTest.java
new file mode 100644
index 00000000000..446dd06d17b
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/EditStudentCommandTest.java
@@ -0,0 +1,238 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccessWithUpdate;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND;
+import static seedu.address.testutil.TypicalStudents.ALICE;
+import static seedu.address.testutil.TypicalStudents.BENSON;
+import static seedu.address.testutil.TypicalStudents.getTypicalAddressBook;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.student.Student;
+import seedu.address.testutil.EditStudentDescriptorBuilder;
+import seedu.address.testutil.StudentBuilder;
+
+public class EditStudentCommandTest {
+
+ private Model model;
+ private TestLogHandler testLogHandler;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ testLogHandler = new TestLogHandler();
+ Logger logger = LogsCenter.getLogger(EditStudentCommand.class);
+ logger.setUseParentHandlers(false); // Prevent default handlers from interfering
+ logger.addHandler(testLogHandler);
+ logger.setLevel(java.util.logging.Level.ALL); // Capture all log levels
+ }
+
+ @Test
+ public void execute_allFieldsSpecified_success() {
+ Student editedStudent = new StudentBuilder().build();
+ EditStudentCommand.EditStudentDescriptor descriptor = new EditStudentDescriptorBuilder(editedStudent).build();
+ EditStudentCommand editStudentCommand = new EditStudentCommand(INDEX_FIRST, descriptor);
+
+ String expectedMessage = String.format(EditStudentCommand.MESSAGE_EDIT_STUDENT_SUCCESS,
+ Messages.format(editedStudent));
+
+ Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ expectedModel.setStudent(model.getFilteredStudentList().get(0), editedStudent);
+
+ assertCommandSuccessWithUpdate(editStudentCommand, model, expectedMessage, expectedModel);
+
+ // Verify logging
+ assertTrue(testLogHandler.containsMessage("Executing EditStudentCommand for student index: 1"));
+ assertTrue(testLogHandler.containsMessage("Student successfully edited: " + editedStudent.getName()));
+ }
+
+ @Test
+ public void execute_someFieldsSpecified_success() {
+ Index indexLastStudent = Index.fromOneBased(model.getFilteredStudentList().size());
+ Student lastStudent = model.getFilteredStudentList().get(indexLastStudent.getZeroBased());
+
+ StudentBuilder studentInList = new StudentBuilder(lastStudent);
+ Student editedStudent = studentInList.withName("New Name").withPhone("12345678").build();
+
+ EditStudentCommand.EditStudentDescriptor descriptor = new EditStudentDescriptorBuilder()
+ .withName("New Name")
+ .withPhone("12345678")
+ .build();
+ EditStudentCommand editStudentCommand = new EditStudentCommand(indexLastStudent, descriptor);
+
+ String expectedMessage = String.format(EditStudentCommand.MESSAGE_EDIT_STUDENT_SUCCESS,
+ Messages.format(editedStudent));
+
+ Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ expectedModel.setStudent(lastStudent, editedStudent);
+
+ assertCommandSuccessWithUpdate(editStudentCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_noFieldSpecified_throwsCommandException() {
+ EditStudentCommand editStudentCommand = new EditStudentCommand(INDEX_FIRST,
+ new EditStudentCommand.EditStudentDescriptor());
+
+ assertCommandFailure(editStudentCommand, model, EditStudentCommand.MESSAGE_NOT_EDITED);
+ }
+
+ @Test
+ public void execute_duplicateStudent_throwsCommandException() {
+ Student firstStudent = model.getFilteredStudentList().get(INDEX_FIRST.getZeroBased());
+ EditStudentCommand.EditStudentDescriptor descriptor = new EditStudentDescriptorBuilder(firstStudent).build();
+ EditStudentCommand editStudentCommand = new EditStudentCommand(INDEX_SECOND, descriptor);
+
+ assertCommandFailure(editStudentCommand, model, EditStudentCommand.MESSAGE_DUPLICATE_STUDENT);
+ }
+
+ @Test
+ public void equals() {
+ final EditStudentCommand standardCommand = new EditStudentCommand(INDEX_FIRST,
+ new EditStudentDescriptorBuilder().withName("Alice").build());
+
+ // same values -> returns true
+ EditStudentCommand.EditStudentDescriptor copyDescriptor =
+ new EditStudentDescriptorBuilder().withName("Alice").build();
+ EditStudentCommand commandWithSameValues = new EditStudentCommand(INDEX_FIRST, copyDescriptor);
+ assertTrue(standardCommand.equals(commandWithSameValues));
+
+ // same object -> returns true
+ assertTrue(standardCommand.equals(standardCommand));
+
+ // null -> returns false
+ assertFalse(standardCommand.equals(null));
+
+ // different types -> returns false
+ assertFalse(standardCommand.equals(new ClearCommand()));
+
+ // different index -> returns false
+ assertFalse(standardCommand.equals(new EditStudentCommand(INDEX_SECOND, copyDescriptor)));
+
+ // different descriptor -> returns false
+ EditStudentCommand.EditStudentDescriptor differentDescriptor =
+ new EditStudentDescriptorBuilder().withName("Bob").build();
+ assertFalse(standardCommand.equals(new EditStudentCommand(INDEX_FIRST, differentDescriptor)));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index index = Index.fromOneBased(1);
+ EditStudentCommand.EditStudentDescriptor editStudentDescriptor =
+ new EditStudentDescriptorBuilder().withName("Alice").build();
+ EditStudentCommand editStudentCommand = new EditStudentCommand(index, editStudentDescriptor);
+ String expected = EditStudentCommand.class.getCanonicalName() + "{index=" + index
+ + ", editStudentDescriptor=" + editStudentDescriptor + "}";
+ assertEquals(expected, editStudentCommand.toString());
+ }
+
+ @Test
+ public void createEditedStudent_allFieldsEdited() throws CommandException {
+ Student original = ALICE;
+ EditStudentCommand.EditStudentDescriptor descriptor = new EditStudentDescriptorBuilder(BENSON).build();
+ Student edited = EditStudentCommand.createEditedStudent(original, descriptor);
+
+ assertEquals(BENSON.getName(), edited.getName());
+ assertEquals(BENSON.getPhone(), edited.getPhone());
+ assertEquals(BENSON.getEmail(), edited.getEmail());
+ assertEquals(BENSON.getAddress(), edited.getAddress());
+ assertEquals(BENSON.getSubjects(), edited.getSubjects());
+ }
+
+ @Test
+ public void editStudentDescriptor_isAnyFieldEdited() {
+ // No fields edited
+ assertFalse(new EditStudentCommand.EditStudentDescriptor().isAnyFieldEdited());
+
+ // Name edited
+ EditStudentCommand.EditStudentDescriptor nameDescriptor =
+ new EditStudentDescriptorBuilder().withName("New Name").build();
+ assertTrue(nameDescriptor.isAnyFieldEdited());
+
+ // Phone edited
+ EditStudentCommand.EditStudentDescriptor phoneDescriptor =
+ new EditStudentDescriptorBuilder().withPhone("12345678").build();
+ assertTrue(phoneDescriptor.isAnyFieldEdited());
+
+ // Email edited
+ EditStudentCommand.EditStudentDescriptor emailDescriptor =
+ new EditStudentDescriptorBuilder().withEmail("new@email.com").build();
+ assertTrue(emailDescriptor.isAnyFieldEdited());
+
+ // Address edited
+ EditStudentCommand.EditStudentDescriptor addressDescriptor =
+ new EditStudentDescriptorBuilder().withAddress("New Address").build();
+ assertTrue(addressDescriptor.isAnyFieldEdited());
+
+ EditStudentCommand.EditStudentDescriptor subjectsDescriptor =
+ new EditStudentDescriptorBuilder().withSubjects("Math").build();
+ assertTrue(subjectsDescriptor.isAnyFieldEdited());
+ }
+
+ @Test
+ public void editStudentDescriptor_getters() {
+ EditStudentCommand.EditStudentDescriptor descriptor = new EditStudentCommand.EditStudentDescriptor();
+
+ // Test all getters with empty descriptor
+ assertTrue(descriptor.getName().isEmpty());
+ assertTrue(descriptor.getPhone().isEmpty());
+ assertTrue(descriptor.getEmail().isEmpty());
+ assertTrue(descriptor.getAddress().isEmpty());
+ assertTrue(descriptor.getSubjects().isEmpty());
+ assertTrue(descriptor.getAssignments().isEmpty());
+
+ // Test with populated descriptor
+ Student student = ALICE;
+ descriptor = new EditStudentDescriptorBuilder(student).build();
+ assertEquals(Optional.of(student.getName()), descriptor.getName());
+ assertEquals(Optional.of(student.getPhone()), descriptor.getPhone());
+ assertEquals(Optional.of(student.getEmail()), descriptor.getEmail());
+ assertEquals(Optional.of(student.getAddress()), descriptor.getAddress());
+ assertEquals(Optional.of(student.getSubjects()), descriptor.getSubjects());
+ }
+
+ // Helper class to test logging
+ private static class TestLogHandler extends Handler {
+ private final List messages = new ArrayList<>();
+
+ @Override
+ public void publish(LogRecord record) {
+ messages.add(record.getLevel() + ": " + record.getMessage());
+ }
+
+ @Override
+ public void flush() {}
+
+ public boolean containsMessage(String expectedMessagePart) {
+ return messages.stream().anyMatch(message -> message.contains(expectedMessagePart));
+ }
+
+ @Override
+ public void close() throws SecurityException {}
+
+ public void printMessages() {
+ messages.forEach(System.out::println); // debugging purpose
+ }
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/EditStudentDescriptorTest.java b/src/test/java/seedu/address/logic/commands/EditStudentDescriptorTest.java
new file mode 100644
index 00000000000..63247edf00d
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/EditStudentDescriptorTest.java
@@ -0,0 +1,73 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.STUDENT_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.STUDENT_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_SUBJECT_HUSBAND;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.EditStudentCommand.EditStudentDescriptor;
+import seedu.address.testutil.EditStudentDescriptorBuilder;
+
+public class EditStudentDescriptorTest {
+
+ @Test
+ public void equals() {
+ // same values -> returns true
+ EditStudentDescriptor descriptorWithSameValues = new EditStudentDescriptor(STUDENT_DESC_AMY);
+ assertTrue(STUDENT_DESC_AMY.equals(descriptorWithSameValues));
+
+ // same object -> returns true
+ assertTrue(STUDENT_DESC_AMY.equals(STUDENT_DESC_AMY));
+
+ // null -> returns false
+ assertFalse(STUDENT_DESC_AMY.equals(null));
+
+ // different types -> returns false
+ assertFalse(STUDENT_DESC_AMY.equals(5));
+
+ // different values -> returns false
+ assertFalse(STUDENT_DESC_AMY.equals(STUDENT_DESC_BOB));
+
+ // different name -> returns false
+ EditStudentDescriptor editedAmy = new EditStudentDescriptorBuilder(STUDENT_DESC_AMY)
+ .withName(VALID_NAME_BOB).build();
+ assertFalse(STUDENT_DESC_AMY.equals(editedAmy));
+
+ // different phone -> returns false
+ editedAmy = new EditStudentDescriptorBuilder(STUDENT_DESC_AMY).withPhone(VALID_PHONE_BOB).build();
+ assertFalse(STUDENT_DESC_AMY.equals(editedAmy));
+
+ // different email -> returns false
+ editedAmy = new EditStudentDescriptorBuilder(STUDENT_DESC_AMY).withEmail(VALID_EMAIL_BOB).build();
+ assertFalse(STUDENT_DESC_AMY.equals(editedAmy));
+
+ // different address -> returns false
+ editedAmy = new EditStudentDescriptorBuilder(STUDENT_DESC_AMY).withAddress(VALID_ADDRESS_BOB).build();
+ assertFalse(STUDENT_DESC_AMY.equals(editedAmy));
+
+ // different subjects -> returns false
+ editedAmy = new EditStudentDescriptorBuilder(STUDENT_DESC_AMY).withSubjects(VALID_SUBJECT_HUSBAND).build();
+ assertFalse(STUDENT_DESC_AMY.equals(editedAmy));
+ }
+
+ @Test
+ public void toStringMethod() {
+ EditStudentDescriptor editStudentDescriptor = new EditStudentDescriptor();
+ String expected = EditStudentDescriptor.class.getCanonicalName() + "{name="
+ + editStudentDescriptor.getName().orElse(null) + ", phone="
+ + editStudentDescriptor.getPhone().orElse(null) + ", email="
+ + editStudentDescriptor.getEmail().orElse(null) + ", address="
+ + editStudentDescriptor.getAddress().orElse(null) + ", subjects="
+ + editStudentDescriptor.getSubjects().orElse(null) + ", assignments="
+ + editStudentDescriptor.getAssignments().orElse(null) + "}";
+ assertEquals(expected, editStudentDescriptor.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/FindCommandTest.java b/src/test/java/seedu/address/logic/commands/FindStudentCommandTest.java
similarity index 53%
rename from src/test/java/seedu/address/logic/commands/FindCommandTest.java
rename to src/test/java/seedu/address/logic/commands/FindStudentCommandTest.java
index b8b7dbba91a..f440217853d 100644
--- a/src/test/java/seedu/address/logic/commands/FindCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/FindStudentCommandTest.java
@@ -3,12 +3,12 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.logic.Messages.MESSAGE_PERSONS_LISTED_OVERVIEW;
+import static seedu.address.logic.Messages.MESSAGE_STUDENTS_LISTED_OVERVIEW;
import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
-import static seedu.address.testutil.TypicalPersons.CARL;
-import static seedu.address.testutil.TypicalPersons.ELLE;
-import static seedu.address.testutil.TypicalPersons.FIONA;
-import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import static seedu.address.testutil.TypicalStudents.CARL;
+import static seedu.address.testutil.TypicalStudents.ELLE;
+import static seedu.address.testutil.TypicalStudents.FIONA;
+import static seedu.address.testutil.TypicalStudents.getTypicalAddressBook;
import java.util.Arrays;
import java.util.Collections;
@@ -18,12 +18,12 @@
import seedu.address.model.Model;
import seedu.address.model.ModelManager;
import seedu.address.model.UserPrefs;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import seedu.address.model.student.NameContainsKeywordsPredicate;
/**
- * Contains integration tests (interaction with the Model) for {@code FindCommand}.
+ * Contains integration tests (interaction with the Model) for {@code FindStudentCommand}.
*/
-public class FindCommandTest {
+public class FindStudentCommandTest {
private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
private Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
@@ -34,14 +34,14 @@ public void equals() {
NameContainsKeywordsPredicate secondPredicate =
new NameContainsKeywordsPredicate(Collections.singletonList("second"));
- FindCommand findFirstCommand = new FindCommand(firstPredicate);
- FindCommand findSecondCommand = new FindCommand(secondPredicate);
+ FindStudentCommand findFirstCommand = new FindStudentCommand(firstPredicate);
+ FindStudentCommand findSecondCommand = new FindStudentCommand(secondPredicate);
// same object -> returns true
assertTrue(findFirstCommand.equals(findFirstCommand));
// same values -> returns true
- FindCommand findFirstCommandCopy = new FindCommand(firstPredicate);
+ FindStudentCommand findFirstCommandCopy = new FindStudentCommand(firstPredicate);
assertTrue(findFirstCommand.equals(findFirstCommandCopy));
// different types -> returns false
@@ -50,36 +50,38 @@ public void equals() {
// null -> returns false
assertFalse(findFirstCommand.equals(null));
- // different person -> returns false
+ // different student -> returns false
assertFalse(findFirstCommand.equals(findSecondCommand));
}
@Test
- public void execute_zeroKeywords_noPersonFound() {
- String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 0);
+ public void execute_zeroKeywords_noStudentFound() {
+ String expectedMessage = String.format(MESSAGE_STUDENTS_LISTED_OVERVIEW, 0);
+ CommandResult expectedResult = new CommandResult(expectedMessage, true);
NameContainsKeywordsPredicate predicate = preparePredicate(" ");
- FindCommand command = new FindCommand(predicate);
- expectedModel.updateFilteredPersonList(predicate);
- assertCommandSuccess(command, model, expectedMessage, expectedModel);
- assertEquals(Collections.emptyList(), model.getFilteredPersonList());
+ FindStudentCommand command = new FindStudentCommand(predicate);
+ expectedModel.updateFilteredStudentList(predicate);
+ assertCommandSuccess(command, model, expectedResult, expectedModel);
+ assertEquals(Collections.emptyList(), model.getFilteredStudentList());
}
@Test
- public void execute_multipleKeywords_multiplePersonsFound() {
- String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 3);
+ public void execute_multipleKeywords_multipleStudentsFound() {
+ String expectedMessage = String.format(MESSAGE_STUDENTS_LISTED_OVERVIEW, 3);
+ CommandResult expectedResult = new CommandResult(expectedMessage, true);
NameContainsKeywordsPredicate predicate = preparePredicate("Kurz Elle Kunz");
- FindCommand command = new FindCommand(predicate);
- expectedModel.updateFilteredPersonList(predicate);
- assertCommandSuccess(command, model, expectedMessage, expectedModel);
- assertEquals(Arrays.asList(CARL, ELLE, FIONA), model.getFilteredPersonList());
+ FindStudentCommand command = new FindStudentCommand(predicate);
+ expectedModel.updateFilteredStudentList(predicate);
+ assertCommandSuccess(command, model, expectedResult, expectedModel);
+ assertEquals(Arrays.asList(CARL, ELLE, FIONA), model.getFilteredStudentList());
}
@Test
public void toStringMethod() {
NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Arrays.asList("keyword"));
- FindCommand findCommand = new FindCommand(predicate);
- String expected = FindCommand.class.getCanonicalName() + "{predicate=" + predicate + "}";
- assertEquals(expected, findCommand.toString());
+ FindStudentCommand findStudentCommand = new FindStudentCommand(predicate);
+ String expected = FindStudentCommand.class.getCanonicalName() + "{predicate=" + predicate + "}";
+ assertEquals(expected, findStudentCommand.toString());
}
/**
diff --git a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java
index 4904fc4352e..17adf8ab6b4 100644
--- a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java
@@ -1,7 +1,7 @@
package seedu.address.logic.commands;
import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
-import static seedu.address.logic.commands.HelpCommand.SHOWING_HELP_MESSAGE;
+import static seedu.address.logic.commands.HelpCommand.GENERAL_HELP_MESSAGE;
import org.junit.jupiter.api.Test;
@@ -14,7 +14,7 @@ public class HelpCommandTest {
@Test
public void execute_help_success() {
- CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false);
+ CommandResult expectedCommandResult = new CommandResult(GENERAL_HELP_MESSAGE, true, false);
assertCommandSuccess(new HelpCommand(), model, expectedCommandResult, expectedModel);
}
}
diff --git a/src/test/java/seedu/address/logic/commands/ListLessonsCommandTest.java b/src/test/java/seedu/address/logic/commands/ListLessonsCommandTest.java
new file mode 100644
index 00000000000..29c282e2211
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/ListLessonsCommandTest.java
@@ -0,0 +1,120 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.testutil.TypicalLessons.getTypicalAddressBook;
+import static seedu.address.testutil.TypicalStudents.ALICE;
+import static seedu.address.testutil.TypicalStudents.BENSON;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.lesson.StudentNameLessonPredicate;
+import seedu.address.testutil.TypicalStudents;
+
+public class ListLessonsCommandTest {
+ private Model model;
+ private Model expectedModel;
+ private TestLogHandler testLogHandler;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager(TypicalStudents.getTypicalAddressBook(), new UserPrefs());
+ expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ testLogHandler = new TestLogHandler();
+ Logger logger = LogsCenter.getLogger(ListLessonsCommand.class);
+ logger.setUseParentHandlers(false); // Prevent default handlers from interfering
+ logger.addHandler(testLogHandler);
+ logger.setLevel(java.util.logging.Level.ALL); // Capture all log levels
+ }
+
+ @Test
+ public void equals() {
+ StudentNameLessonPredicate firstPredicate = new StudentNameLessonPredicate(ALICE.getName());
+ StudentNameLessonPredicate secondPredicate = new StudentNameLessonPredicate(BENSON.getName());
+
+ ListLessonsCommand listAliceLessonsCommand = new ListLessonsCommand(firstPredicate);
+ ListLessonsCommand listBensonLessonsCommand = new ListLessonsCommand(secondPredicate);
+
+ // same object -> returns true
+ assertTrue(listAliceLessonsCommand.equals(listAliceLessonsCommand));
+
+ // same values -> returns true
+ ListLessonsCommand listAliceLessonsCommandCopy = new ListLessonsCommand(firstPredicate);
+ assertTrue(listAliceLessonsCommand.equals(listAliceLessonsCommandCopy));
+
+ // different types -> returns false
+ assertFalse(listAliceLessonsCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(listAliceLessonsCommand.equals(null));
+
+ // different predicate -> returns false
+ assertFalse(listAliceLessonsCommand.equals(listBensonLessonsCommand));
+ }
+
+ @Test
+ public void equals_withStudentName() {
+ StudentNameLessonPredicate predicate = new StudentNameLessonPredicate(ALICE.getName());
+
+ ListLessonsCommand commandWithName = new ListLessonsCommand(predicate, ALICE.getName());
+ ListLessonsCommand commandWithoutName = new ListLessonsCommand(predicate);
+
+ // Different because one has student name and other doesn't
+ assertFalse(commandWithName.equals(commandWithoutName));
+ }
+
+ @Test
+ public void toStringMethod() {
+ StudentNameLessonPredicate predicate = new StudentNameLessonPredicate(ALICE.getName());
+ ListLessonsCommand command = new ListLessonsCommand(predicate);
+
+ String expected = ListLessonsCommand.class.getCanonicalName() + "{predicate=" + predicate + "}";
+ assertEquals(expected, command.toString());
+ }
+
+ @Test
+ public void toStringMethod_withStudentName() {
+ StudentNameLessonPredicate predicate = new StudentNameLessonPredicate(ALICE.getName());
+ ListLessonsCommand command = new ListLessonsCommand(predicate, ALICE.getName());
+
+ String expected = ListLessonsCommand.class.getCanonicalName() + "{predicate=" + predicate + "}";
+ assertEquals(expected, command.toString());
+ }
+
+ // Helper class to test logging
+ private static class TestLogHandler extends Handler {
+ private final List messages = new ArrayList<>();
+
+ @Override
+ public void publish(LogRecord record) {
+ messages.add(record.getLevel() + ": " + record.getMessage());
+ }
+
+ @Override
+ public void flush() {}
+
+ public boolean containsMessage(String expectedMessagePart) {
+ return messages.stream().anyMatch(message -> message.contains(expectedMessagePart));
+ }
+
+ @Override
+ public void close() throws SecurityException {}
+
+ public void printMessages() {
+ messages.forEach(System.out::println); // debugging purpose
+ }
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/ListCommandTest.java b/src/test/java/seedu/address/logic/commands/ListStudentsCommandTest.java
similarity index 52%
rename from src/test/java/seedu/address/logic/commands/ListCommandTest.java
rename to src/test/java/seedu/address/logic/commands/ListStudentsCommandTest.java
index 435ff1f7275..f75c355e053 100644
--- a/src/test/java/seedu/address/logic/commands/ListCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/ListStudentsCommandTest.java
@@ -1,9 +1,9 @@
package seedu.address.logic.commands;
import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
-import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex;
-import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
-import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import static seedu.address.logic.commands.CommandTestUtil.showStudentAtIndex;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST;
+import static seedu.address.testutil.TypicalStudents.getTypicalAddressBook;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -13,9 +13,9 @@
import seedu.address.model.UserPrefs;
/**
- * Contains integration tests (interaction with the Model) and unit tests for ListCommand.
+ * Contains integration tests (interaction with the Model) and unit tests for ListStudentsCommand.
*/
-public class ListCommandTest {
+public class ListStudentsCommandTest {
private Model model;
private Model expectedModel;
@@ -28,12 +28,14 @@ public void setUp() {
@Test
public void execute_listIsNotFiltered_showsSameList() {
- assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel);
+ CommandResult expectedResult = new CommandResult(ListStudentsCommand.MESSAGE_SUCCESS, true);
+ assertCommandSuccess(new ListStudentsCommand(), model, expectedResult, expectedModel);
}
@Test
public void execute_listIsFiltered_showsEverything() {
- showPersonAtIndex(model, INDEX_FIRST_PERSON);
- assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel);
+ showStudentAtIndex(model, INDEX_FIRST);
+ CommandResult expectedResult = new CommandResult(ListStudentsCommand.MESSAGE_SUCCESS, true);
+ assertCommandSuccess(new ListStudentsCommand(), model, expectedResult, expectedModel);
}
}
diff --git a/src/test/java/seedu/address/logic/commands/MarkAssignmentCommandTest.java b/src/test/java/seedu/address/logic/commands/MarkAssignmentCommandTest.java
new file mode 100644
index 00000000000..979a65fddfa
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/MarkAssignmentCommandTest.java
@@ -0,0 +1,144 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.testutil.TypicalAssignments.ASSIGNMENT1;
+import static seedu.address.testutil.TypicalAssignments.ASSIGNMENT2;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND;
+import static seedu.address.testutil.TypicalStudents.getTypicalAddressBook;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.student.Student;
+
+public class MarkAssignmentCommandTest {
+ private Model model;
+ private TestLogHandler testLogHandler;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ testLogHandler = new TestLogHandler();
+ Logger logger = LogsCenter.getLogger(MarkAssignmentCommand.class);
+ logger.setUseParentHandlers(false); // Prevent default handlers from interfering
+ logger.addHandler(testLogHandler);
+ logger.setLevel(java.util.logging.Level.ALL); // Capture all log levels
+ }
+
+ @Test
+ public void execute_invalidStudentIndex_throwsCommandException() {
+ Index outOfBoundIndex = Index.fromOneBased(model.getFilteredStudentList().size() + 1);
+ MarkAssignmentCommand markCommand = new MarkAssignmentCommand(outOfBoundIndex, "Assignment");
+
+ assertCommandFailure(markCommand, model, Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+
+ // Verify logging
+ assertTrue(testLogHandler.containsMessage(
+ String.format("Invalid student index: %d (list size: %d)",
+ outOfBoundIndex.getOneBased(), model.getFilteredStudentList().size())));
+ }
+
+ @Test
+ public void execute_emptyStudentList_throwsCommandException() {
+ Model emptyModel = new ModelManager();
+ MarkAssignmentCommand markCommand = new MarkAssignmentCommand(INDEX_FIRST, "Assignment");
+
+ assertCommandFailure(markCommand, emptyModel, MarkAssignmentCommand.MESSAGE_EMPTY_STUDENT_LIST);
+
+ // Verify logging
+ assertTrue(testLogHandler.containsMessage("Attempted to mark assignment in empty student list"));
+ }
+
+ @Test
+ public void execute_assignmentNotFound_throwsCommandException() {
+ Student student = model.getFilteredStudentList().get(INDEX_FIRST.getZeroBased());
+ String nonExistentAssignment = "Nonexistent Assignment";
+ MarkAssignmentCommand markCommand = new MarkAssignmentCommand(INDEX_FIRST, nonExistentAssignment);
+
+ String expectedMessage = String.format(Messages.MESSAGE_ASSIGNMENT_NOT_FOUND, nonExistentAssignment);
+ assertCommandFailure(markCommand, model, expectedMessage);
+
+ // Verify logging
+ assertTrue(testLogHandler.containsMessage(
+ String.format("Assignment not found: %s for student %s",
+ nonExistentAssignment, student.getName())));
+ }
+
+ @Test
+ public void equals() {
+ MarkAssignmentCommand markFirstCommand = new MarkAssignmentCommand(INDEX_FIRST,
+ ASSIGNMENT1.getAssignmentName());
+ MarkAssignmentCommand markSecondCommand = new MarkAssignmentCommand(INDEX_SECOND,
+ ASSIGNMENT1.getAssignmentName());
+ MarkAssignmentCommand markDifferentAssignmentCommand = new MarkAssignmentCommand(INDEX_FIRST,
+ ASSIGNMENT2.getAssignmentName());
+
+ // same object -> returns true
+ assertTrue(markFirstCommand.equals(markFirstCommand));
+
+ // same values -> returns true
+ MarkAssignmentCommand markFirstCommandCopy = new MarkAssignmentCommand(INDEX_FIRST,
+ ASSIGNMENT1.getAssignmentName());
+ assertTrue(markFirstCommand.equals(markFirstCommandCopy));
+
+ // different types -> returns false
+ assertFalse(markFirstCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(markFirstCommand.equals(null));
+
+ // different student index -> returns false
+ assertFalse(markFirstCommand.equals(markSecondCommand));
+
+ // different assignment name -> returns false
+ assertFalse(markFirstCommand.equals(markDifferentAssignmentCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ MarkAssignmentCommand markCommand = new MarkAssignmentCommand(INDEX_FIRST, ASSIGNMENT1.getAssignmentName());
+ String expected = MarkAssignmentCommand.class.getCanonicalName() + "{studentIndex=" + INDEX_FIRST
+ + ", assignmentName=" + ASSIGNMENT1.getAssignmentName() + "}";
+ assertEquals(expected, markCommand.toString());
+ }
+
+ // Helper class to test logging
+ private static class TestLogHandler extends Handler {
+ private final List messages = new ArrayList<>();
+
+ @Override
+ public void publish(LogRecord record) {
+ messages.add(record.getLevel() + ": " + record.getMessage());
+ }
+
+ @Override
+ public void flush() {}
+
+ public boolean containsMessage(String expectedMessagePart) {
+ return messages.stream().anyMatch(message -> message.contains(expectedMessagePart));
+ }
+
+ @Override
+ public void close() throws SecurityException {}
+
+ public void printMessages() {
+ messages.forEach(System.out::println); // debugging purpose
+ }
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/UnmarkAssignmentCommandTest.java b/src/test/java/seedu/address/logic/commands/UnmarkAssignmentCommandTest.java
new file mode 100644
index 00000000000..5a931bd5395
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/UnmarkAssignmentCommandTest.java
@@ -0,0 +1,146 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.testutil.TypicalAssignments.ASSIGNMENT1;
+import static seedu.address.testutil.TypicalAssignments.ASSIGNMENT2;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND;
+import static seedu.address.testutil.TypicalStudents.getTypicalAddressBook;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.student.Student;
+
+public class UnmarkAssignmentCommandTest {
+ private Model model;
+ private TestLogHandler testLogHandler;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ testLogHandler = new TestLogHandler();
+ Logger logger = LogsCenter.getLogger(UnmarkAssignmentCommand.class);
+ logger.setUseParentHandlers(false); // Prevent default handlers from interfering
+ logger.addHandler(testLogHandler);
+ logger.setLevel(java.util.logging.Level.ALL); // Capture all log levels
+ }
+
+ @Test
+ public void execute_invalidStudentIndex_throwsCommandException() {
+ Index outOfBoundIndex = Index.fromOneBased(model.getFilteredStudentList().size() + 1);
+ UnmarkAssignmentCommand unmarkCommand = new UnmarkAssignmentCommand(outOfBoundIndex, "Assignment");
+
+ assertCommandFailure(unmarkCommand, model, Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+
+ // Verify logging
+ assertTrue(testLogHandler.containsMessage(
+ String.format("Invalid student index: %d (list size: %d)",
+ outOfBoundIndex.getOneBased(), model.getFilteredStudentList().size())));
+ }
+
+ @Test
+ public void execute_emptyStudentList_throwsCommandException() {
+ Model emptyModel = new ModelManager();
+ UnmarkAssignmentCommand unmarkCommand = new UnmarkAssignmentCommand(INDEX_FIRST, "Assignment");
+
+ assertCommandFailure(unmarkCommand, emptyModel, UnmarkAssignmentCommand.MESSAGE_EMPTY_STUDENT_LIST);
+
+ // Verify logging
+ assertTrue(testLogHandler.containsMessage("Attempted to unmark assignment in empty student list"));
+ }
+
+ @Test
+ public void execute_assignmentNotFound_throwsCommandException() {
+ Student student = model.getFilteredStudentList().get(INDEX_FIRST.getZeroBased());
+ String nonExistentAssignment = "Nonexistent Assignment";
+ UnmarkAssignmentCommand unmarkCommand = new UnmarkAssignmentCommand(INDEX_FIRST, nonExistentAssignment);
+
+ String expectedMessage = String.format(Messages.MESSAGE_ASSIGNMENT_NOT_FOUND,
+ nonExistentAssignment);
+ assertCommandFailure(unmarkCommand, model, expectedMessage);
+
+ // Verify logging
+ assertTrue(testLogHandler.containsMessage(
+ String.format("Assignment not found: %s for student %s",
+ nonExistentAssignment, student.getName())));
+ }
+
+ @Test
+ public void equals() {
+ UnmarkAssignmentCommand unmarkFirstCommand = new UnmarkAssignmentCommand(INDEX_FIRST,
+ ASSIGNMENT1.getAssignmentName());
+ UnmarkAssignmentCommand unmarkSecondCommand = new UnmarkAssignmentCommand(INDEX_SECOND,
+ ASSIGNMENT1.getAssignmentName());
+ UnmarkAssignmentCommand unmarkDifferentAssignmentCommand = new UnmarkAssignmentCommand(INDEX_FIRST,
+ ASSIGNMENT2.getAssignmentName());
+
+ // same object -> returns true
+ assertTrue(unmarkFirstCommand.equals(unmarkFirstCommand));
+
+ // same values -> returns true
+ UnmarkAssignmentCommand unmarkFirstCommandCopy = new UnmarkAssignmentCommand(INDEX_FIRST,
+ ASSIGNMENT1.getAssignmentName());
+ assertTrue(unmarkFirstCommand.equals(unmarkFirstCommandCopy));
+
+ // different types -> returns false
+ assertFalse(unmarkFirstCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(unmarkFirstCommand.equals(null));
+
+ // different student index -> returns false
+ assertFalse(unmarkFirstCommand.equals(unmarkSecondCommand));
+
+ // different assignment name -> returns false
+ assertFalse(unmarkFirstCommand.equals(unmarkDifferentAssignmentCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ UnmarkAssignmentCommand unmarkCommand = new UnmarkAssignmentCommand(INDEX_FIRST,
+ ASSIGNMENT1.getAssignmentName());
+ String expected = UnmarkAssignmentCommand.class.getCanonicalName() + "{studentIndex=" + INDEX_FIRST
+ + ", assignmentName=" + ASSIGNMENT1.getAssignmentName() + "}";
+ assertEquals(expected, unmarkCommand.toString());
+ }
+
+ // Helper class to test logging
+ private static class TestLogHandler extends Handler {
+ private final List messages = new ArrayList<>();
+
+ @Override
+ public void publish(LogRecord record) {
+ messages.add(record.getLevel() + ": " + record.getMessage());
+ }
+
+ @Override
+ public void flush() {}
+
+ public boolean containsMessage(String expectedMessagePart) {
+ return messages.stream().anyMatch(message -> message.contains(expectedMessagePart));
+ }
+
+ @Override
+ public void close() throws SecurityException {}
+
+ public void printMessages() {
+ messages.forEach(System.out::println); // debugging purpose
+ }
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/AddAssignmentCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddAssignmentCommandParserTest.java
new file mode 100644
index 00000000000..5dd522196f6
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/AddAssignmentCommandParserTest.java
@@ -0,0 +1,85 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_DUPLICATE_FIELDS;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.commands.CommandTestUtil.ASSIGNMENT_NAME_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.ASSIGNMENT_NAME_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.DATE_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.DATE_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_ASSIGNMENT_NAME_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_DATE_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_PREAMBLE;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalAssignments.ASSIGNMENT_AMY;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.AddAssignmentCommand;
+import seedu.address.model.assignment.Assignment;
+import seedu.address.model.datetimeutil.Date;
+import seedu.address.testutil.AssignmentBuilder;
+
+public class AddAssignmentCommandParserTest {
+ private AddAssignmentCommandParser parser = new AddAssignmentCommandParser();
+
+ @Test
+ public void parse_allFieldsPresent_success() {
+ Assignment expectedAssignment = new AssignmentBuilder(ASSIGNMENT_AMY).build();
+
+ assertParseSuccess(parser, VALID_PREAMBLE + ASSIGNMENT_NAME_DESC_AMY + DATE_DESC_AMY,
+ new AddAssignmentCommand(Index.fromOneBased(1), expectedAssignment));
+ }
+
+ @Test
+ public void parse_compulsoryFieldMissing_failure() {
+ String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddAssignmentCommand.MESSAGE_USAGE);
+
+ // no parameters
+ assertParseFailure(parser, AddAssignmentCommand.COMMAND_WORD, expectedMessage);
+
+ // no index
+ assertParseFailure(parser, AddAssignmentCommand.COMMAND_WORD + ASSIGNMENT_NAME_DESC_BOB,
+ expectedMessage);
+
+ // missing assignment prefix
+ assertParseFailure(parser, AddAssignmentCommand.COMMAND_WORD + VALID_PREAMBLE + DATE_DESC_AMY,
+ expectedMessage);
+
+ // missing date prefix
+ assertParseFailure(parser, AddAssignmentCommand.COMMAND_WORD + VALID_PREAMBLE + ASSIGNMENT_NAME_DESC_AMY,
+ expectedMessage);
+ }
+
+ @Test
+ public void parse_invalidValue_failure() {
+ // invalid assignment
+ assertParseFailure(parser, VALID_PREAMBLE + INVALID_ASSIGNMENT_NAME_DESC + DATE_DESC_BOB,
+ Assignment.MESSAGE_CONSTRAINTS);
+
+ // invalid date
+ assertParseFailure(parser, VALID_PREAMBLE + ASSIGNMENT_NAME_DESC_AMY + INVALID_DATE_DESC,
+ Date.MESSAGE_CONSTRAINTS);
+ }
+
+ @Test
+ public void parse_duplicatePrefixes_failure() {
+ String expectedMessage = MESSAGE_DUPLICATE_FIELDS + CliSyntax.PREFIX_ASSIGNMENT;
+ assertParseFailure(parser,
+ VALID_PREAMBLE + ASSIGNMENT_NAME_DESC_AMY + ASSIGNMENT_NAME_DESC_BOB + DATE_DESC_AMY,
+ expectedMessage);
+ }
+
+ @Test
+ public void parse_emptyAssignmentValue_failure() {
+ assertParseFailure(parser, VALID_PREAMBLE + " as/ " + DATE_DESC_AMY,
+ Assignment.MESSAGE_CONSTRAINTS);
+ }
+
+ @Test
+ public void parse_emptyDateValue_failure() {
+ assertParseFailure(parser, VALID_PREAMBLE + ASSIGNMENT_NAME_DESC_AMY + " d/",
+ Date.MESSAGE_CONSTRAINTS);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java
deleted file mode 100644
index 5bc11d3cdaa..00000000000
--- a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java
+++ /dev/null
@@ -1,196 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY;
-import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE;
-import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND;
-import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
-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_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
-import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
-import static seedu.address.testutil.TypicalPersons.AMY;
-import static seedu.address.testutil.TypicalPersons.BOB;
-
-import org.junit.jupiter.api.Test;
-
-import seedu.address.logic.Messages;
-import seedu.address.logic.commands.AddCommand;
-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.tag.Tag;
-import seedu.address.testutil.PersonBuilder;
-
-public class AddCommandParserTest {
- private AddCommandParser parser = new AddCommandParser();
-
- @Test
- public void parse_allFieldsPresent_success() {
- Person expectedPerson = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND).build();
-
- // whitespace only preamble
- assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
- + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson));
-
-
- // multiple tags - all accepted
- Person expectedPersonMultipleTags = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND)
- .build();
- assertParseSuccess(parser,
- NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND,
- new AddCommand(expectedPersonMultipleTags));
- }
-
- @Test
- public void parse_repeatedNonTagValue_failure() {
- String validExpectedPersonString = NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
- + ADDRESS_DESC_BOB + TAG_DESC_FRIEND;
-
- // multiple names
- assertParseFailure(parser, NAME_DESC_AMY + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
-
- // multiple phones
- assertParseFailure(parser, PHONE_DESC_AMY + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
-
- // multiple emails
- assertParseFailure(parser, EMAIL_DESC_AMY + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_EMAIL));
-
- // multiple addresses
- assertParseFailure(parser, ADDRESS_DESC_AMY + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
-
- // multiple fields repeated
- assertParseFailure(parser,
- validExpectedPersonString + PHONE_DESC_AMY + EMAIL_DESC_AMY + NAME_DESC_AMY + ADDRESS_DESC_AMY
- + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME, PREFIX_ADDRESS, PREFIX_EMAIL, PREFIX_PHONE));
-
- // invalid value followed by valid value
-
- // invalid name
- assertParseFailure(parser, INVALID_NAME_DESC + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
-
- // invalid email
- assertParseFailure(parser, INVALID_EMAIL_DESC + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_EMAIL));
-
- // invalid phone
- assertParseFailure(parser, INVALID_PHONE_DESC + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
-
- // invalid address
- assertParseFailure(parser, INVALID_ADDRESS_DESC + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
-
- // valid value followed by invalid value
-
- // invalid name
- assertParseFailure(parser, validExpectedPersonString + INVALID_NAME_DESC,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
-
- // invalid email
- assertParseFailure(parser, validExpectedPersonString + INVALID_EMAIL_DESC,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_EMAIL));
-
- // invalid phone
- assertParseFailure(parser, validExpectedPersonString + INVALID_PHONE_DESC,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
-
- // invalid address
- assertParseFailure(parser, validExpectedPersonString + INVALID_ADDRESS_DESC,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
- }
-
- @Test
- public void parse_optionalFieldsMissing_success() {
- // zero tags
- Person expectedPerson = new PersonBuilder(AMY).withTags().build();
- assertParseSuccess(parser, NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY + ADDRESS_DESC_AMY,
- new AddCommand(expectedPerson));
- }
-
- @Test
- public void parse_compulsoryFieldMissing_failure() {
- String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE);
-
- // missing name prefix
- assertParseFailure(parser, VALID_NAME_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB,
- expectedMessage);
-
- // missing phone prefix
- assertParseFailure(parser, NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB,
- expectedMessage);
-
- // missing email prefix
- assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB + ADDRESS_DESC_BOB,
- expectedMessage);
-
- // missing address prefix
- assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + VALID_ADDRESS_BOB,
- expectedMessage);
-
- // all prefixes missing
- assertParseFailure(parser, VALID_NAME_BOB + VALID_PHONE_BOB + VALID_EMAIL_BOB + VALID_ADDRESS_BOB,
- expectedMessage);
- }
-
- @Test
- public void parse_invalidValue_failure() {
- // invalid name
- assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB
- + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Name.MESSAGE_CONSTRAINTS);
-
- // invalid phone
- assertParseFailure(parser, NAME_DESC_BOB + INVALID_PHONE_DESC + EMAIL_DESC_BOB + ADDRESS_DESC_BOB
- + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Phone.MESSAGE_CONSTRAINTS);
-
- // invalid email
- assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + INVALID_EMAIL_DESC + ADDRESS_DESC_BOB
- + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Email.MESSAGE_CONSTRAINTS);
-
- // invalid address
- assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC
- + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Address.MESSAGE_CONSTRAINTS);
-
- // invalid tag
- assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB
- + INVALID_TAG_DESC + VALID_TAG_FRIEND, Tag.MESSAGE_CONSTRAINTS);
-
- // two invalid values, only first invalid value reported
- assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC,
- Name.MESSAGE_CONSTRAINTS);
-
- // non-empty preamble
- assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
- + ADDRESS_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND,
- String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
- }
-}
diff --git a/src/test/java/seedu/address/logic/parser/AddLessonCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddLessonCommandParserTest.java
new file mode 100644
index 00000000000..4597e1029b3
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/AddLessonCommandParserTest.java
@@ -0,0 +1,98 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.commands.CommandTestUtil.DATE_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_DATE_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_SUBJECT_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_TIME_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY;
+import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE;
+import static seedu.address.logic.commands.CommandTestUtil.SUBJECT_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.TIME_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_DATE_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_SUBJECT_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TIME_BOB;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalLessons.BOB;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.AddLessonCommand;
+import seedu.address.model.datetimeutil.Date;
+import seedu.address.model.datetimeutil.Time;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.student.Name;
+import seedu.address.model.subject.Subject;
+import seedu.address.testutil.LessonBuilder;
+
+public class AddLessonCommandParserTest {
+ private AddLessonCommandParser parser = new AddLessonCommandParser();
+
+ @Test
+ public void parse_allFieldsPresent_success() {
+ Lesson expectedLesson = new LessonBuilder(BOB).build();
+
+ // whitespace only preamble
+ assertParseSuccess(parser, PREAMBLE_WHITESPACE + SUBJECT_DESC_BOB + NAME_DESC_BOB + DATE_DESC_BOB
+ + TIME_DESC_BOB , new AddLessonCommand(expectedLesson));
+
+
+ }
+
+ @Test
+ public void parse_compulsoryFieldMissing_failure() {
+ String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddLessonCommand.MESSAGE_USAGE);
+
+ // missing subject prefix
+ assertParseFailure(parser, VALID_SUBJECT_BOB + NAME_DESC_BOB + DATE_DESC_BOB
+ + TIME_DESC_BOB , expectedMessage);
+
+ // missing name prefix
+ assertParseFailure(parser, SUBJECT_DESC_BOB + VALID_NAME_BOB + DATE_DESC_BOB
+ + TIME_DESC_BOB , expectedMessage);
+
+ // missing date prefix
+ assertParseFailure(parser, SUBJECT_DESC_BOB + NAME_DESC_BOB + VALID_DATE_BOB
+ + TIME_DESC_BOB , expectedMessage);
+
+ // missing time prefix
+ assertParseFailure(parser, SUBJECT_DESC_BOB + NAME_DESC_BOB + DATE_DESC_BOB
+ + VALID_TIME_BOB , expectedMessage);
+
+ // all prefixes missing
+ assertParseFailure(parser, VALID_SUBJECT_BOB + VALID_NAME_BOB + VALID_DATE_BOB
+ + VALID_TIME_BOB , expectedMessage);
+ }
+
+ @Test
+ public void parse_invalidValue_failure() {
+ // invalid name
+ assertParseFailure(parser, INVALID_NAME_DESC + DATE_DESC_BOB + TIME_DESC_BOB + SUBJECT_DESC_BOB,
+ Name.MESSAGE_CONSTRAINTS);
+
+ // invalid date
+ assertParseFailure(parser, NAME_DESC_BOB + INVALID_DATE_DESC + TIME_DESC_BOB + SUBJECT_DESC_BOB,
+ Date.MESSAGE_CONSTRAINTS);
+
+ // invalid time
+ assertParseFailure(parser, NAME_DESC_BOB + DATE_DESC_BOB + INVALID_TIME_DESC + SUBJECT_DESC_BOB,
+ Time.MESSAGE_CONSTRAINTS);
+
+ // invalid subject
+ assertParseFailure(parser, NAME_DESC_BOB + DATE_DESC_BOB + TIME_DESC_BOB + INVALID_SUBJECT_DESC,
+ Subject.MESSAGE_CONSTRAINTS);
+
+ // two invalid values, only first invalid value reported
+ assertParseFailure(parser, INVALID_NAME_DESC + DATE_DESC_BOB + TIME_DESC_BOB + INVALID_SUBJECT_DESC,
+ Name.MESSAGE_CONSTRAINTS);
+
+ // non-empty preamble
+ assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + DATE_DESC_BOB + TIME_DESC_BOB
+ + SUBJECT_DESC_BOB,
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddLessonCommand.MESSAGE_USAGE));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/AddStudentCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddStudentCommandParserTest.java
new file mode 100644
index 00000000000..d551a2ba04b
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/AddStudentCommandParserTest.java
@@ -0,0 +1,194 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_SUBJECT_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY;
+import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE;
+import static seedu.address.logic.commands.CommandTestUtil.SUBJECT_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_SUBJECT_BOB;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalStudents.BOB;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.AddStudentCommand;
+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.testutil.StudentBuilder;
+
+public class AddStudentCommandParserTest {
+ private AddStudentCommandParser parser = new AddStudentCommandParser();
+
+ @Test
+ public void parse_allFieldsPresent_success() {
+ Student expectedStudent = new StudentBuilder(BOB).build();
+
+ // whitespace only preamble
+ assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
+ + SUBJECT_DESC_BOB + ADDRESS_DESC_BOB, new AddStudentCommand(expectedStudent));
+
+
+ // multiple tags - all accepted
+ // Student expectedStudentMultipleTags =
+ // new StudentBuilder(BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND)
+ // .build();
+ // assertParseSuccess(parser,
+ // NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
+ // + ADDRESS_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND,
+ // new AddStudentCommand(expectedStudentMultipleTags));
+ }
+
+ // @Test
+ // public void parse_repeatedNonTagValue_failure() {
+ // String validExpectedStudentString = NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
+ // + SUBJECT_DESC_BOB;
+ //
+ // // multiple names
+ // assertParseFailure(parser, NAME_DESC_AMY + validExpectedStudentString,
+ // Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
+ //
+ // // multiple phones
+ // assertParseFailure(parser, PHONE_DESC_AMY + validExpectedStudentString,
+ // Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
+ //
+ // // multiple emails
+ // assertParseFailure(parser, EMAIL_DESC_AMY + validExpectedStudentString,
+ // Messages.getErrorMessageForDuplicatePrefixes(PREFIX_EMAIL));
+ //
+ // // multiple addresses
+ // assertParseFailure(parser, ADDRESS_DESC_AMY + validExpectedStudentString,
+ // Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
+ //
+ // // multiple fields repeated
+ // assertParseFailure(parser,
+ // validExpectedStudentString + PHONE_DESC_AMY + EMAIL_DESC_AMY
+ // + NAME_DESC_AMY + ADDRESS_DESC_AMY
+ // + validExpectedStudentString,
+ // Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME,
+ // PREFIX_ADDRESS, PREFIX_EMAIL, PREFIX_PHONE));
+ //
+ // // invalid assignmentName followed by valid assignmentName
+ //
+ // // invalid name
+ // assertParseFailure(parser, INVALID_NAME_DESC + validExpectedStudentString,
+ // Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
+ //
+ // // invalid email
+ // assertParseFailure(parser, INVALID_EMAIL_DESC + validExpectedStudentString,
+ // Messages.getErrorMessageForDuplicatePrefixes(PREFIX_EMAIL));
+ //
+ // // invalid phone
+ // assertParseFailure(parser, INVALID_PHONE_DESC + validExpectedStudentString,
+ // Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
+ //
+ // // invalid address
+ // assertParseFailure(parser, INVALID_ADDRESS_DESC + validExpectedStudentString,
+ // Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
+ //
+ // // valid assignmentName followed by invalid assignmentName
+ //
+ // // invalid name
+ // assertParseFailure(parser, validExpectedStudentString + INVALID_NAME_DESC,
+ // Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
+ //
+ // // invalid email
+ // assertParseFailure(parser, validExpectedStudentString + INVALID_EMAIL_DESC,
+ // Messages.getErrorMessageForDuplicatePrefixes(PREFIX_EMAIL));
+ //
+ // // invalid phone
+ // assertParseFailure(parser, validExpectedStudentString + INVALID_PHONE_DESC,
+ // Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
+ //
+ // // invalid address
+ // assertParseFailure(parser, validExpectedStudentString + INVALID_ADDRESS_DESC,
+ // Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
+ // }
+
+ // @Test
+ // public void parse_optionalFieldsMissing_success() {
+ // // zero tags
+ // Student expectedStudent = new StudentBuilder(AMY).build();
+ // assertParseSuccess(parser, NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY + SUBJECT_DESC_AMY,
+ // new AddStudentCommand(expectedStudent));
+ // }
+
+ @Test
+ public void parse_compulsoryFieldMissing_failure() {
+ String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddStudentCommand.MESSAGE_USAGE);
+
+ // missing name prefix
+ assertParseFailure(parser, VALID_NAME_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + SUBJECT_DESC_BOB
+ + ADDRESS_DESC_BOB,
+ expectedMessage);
+
+ // missing phone prefix
+ assertParseFailure(parser, NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB + SUBJECT_DESC_BOB
+ + ADDRESS_DESC_BOB,
+ expectedMessage);
+
+ // missing email prefix
+ assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB + SUBJECT_DESC_BOB
+ + ADDRESS_DESC_BOB,
+ expectedMessage);
+
+ // missing address prefix
+ assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + VALID_SUBJECT_BOB
+ + VALID_ADDRESS_BOB,
+ expectedMessage);
+
+ // all prefixes missing
+ assertParseFailure(parser, VALID_NAME_BOB + VALID_PHONE_BOB + VALID_EMAIL_BOB + VALID_SUBJECT_BOB
+ + VALID_ADDRESS_BOB,
+ expectedMessage);
+ }
+
+ @Test
+ public void parse_invalidValue_failure() {
+ // invalid name
+ assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + SUBJECT_DESC_BOB
+ + ADDRESS_DESC_BOB,
+ Name.MESSAGE_CONSTRAINTS);
+
+ // invalid phone
+ assertParseFailure(parser, NAME_DESC_BOB + INVALID_PHONE_DESC + EMAIL_DESC_BOB + SUBJECT_DESC_BOB
+ + ADDRESS_DESC_BOB,
+ Phone.MESSAGE_CONSTRAINTS);
+
+ // invalid email
+ assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + INVALID_EMAIL_DESC + SUBJECT_DESC_BOB
+ + ADDRESS_DESC_BOB,
+ Email.MESSAGE_CONSTRAINTS);
+
+ // // invalid address
+ // assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC
+ // + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Address.MESSAGE_CONSTRAINTS);
+ //
+ // // invalid tag
+ // assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB
+ // + INVALID_TAG_DESC + VALID_TAG_FRIEND, Tag.MESSAGE_CONSTRAINTS);
+
+ // two invalid values, only first invalid assignmentName reported
+ assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_SUBJECT_DESC
+ + ADDRESS_DESC_BOB,
+ Name.MESSAGE_CONSTRAINTS);
+
+ // non-empty preamble
+ assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
+ + SUBJECT_DESC_BOB + ADDRESS_DESC_BOB,
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddStudentCommand.MESSAGE_USAGE));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
index 5a1ab3dbc0c..93e4a612088 100644
--- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
@@ -4,8 +4,10 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
+import static seedu.address.logic.commands.CommandTestUtil.ASSIGNMENT_NAME_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_ASSIGNMENT_NAME_AMY;
import static seedu.address.testutil.Assert.assertThrows;
-import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
import java.util.Arrays;
import java.util.List;
@@ -13,53 +15,148 @@
import org.junit.jupiter.api.Test;
-import seedu.address.logic.commands.AddCommand;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.AddAssignmentCommand;
+import seedu.address.logic.commands.AddLessonCommand;
+import seedu.address.logic.commands.AddStudentCommand;
import seedu.address.logic.commands.ClearCommand;
-import seedu.address.logic.commands.DeleteCommand;
-import seedu.address.logic.commands.EditCommand;
-import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
+import seedu.address.logic.commands.DeleteAssignmentCommand;
+import seedu.address.logic.commands.DeleteLessonCommand;
+import seedu.address.logic.commands.DeleteStudentCommand;
+import seedu.address.logic.commands.EditAssignmentCommand;
+import seedu.address.logic.commands.EditLessonCommand;
+import seedu.address.logic.commands.EditStudentCommand;
import seedu.address.logic.commands.ExitCommand;
-import seedu.address.logic.commands.FindCommand;
+import seedu.address.logic.commands.FindStudentCommand;
import seedu.address.logic.commands.HelpCommand;
-import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.ListLessonsCommand;
+import seedu.address.logic.commands.ListStudentsCommand;
+import seedu.address.logic.commands.MarkAssignmentCommand;
+import seedu.address.logic.commands.UnmarkAssignmentCommand;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
-import seedu.address.model.person.Person;
-import seedu.address.testutil.EditPersonDescriptorBuilder;
-import seedu.address.testutil.PersonBuilder;
-import seedu.address.testutil.PersonUtil;
+import seedu.address.model.assignment.Assignment;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.student.NameContainsKeywordsPredicate;
+import seedu.address.model.student.Student;
+import seedu.address.testutil.AssignmentBuilder;
+import seedu.address.testutil.AssignmentUtil;
+import seedu.address.testutil.EditAssignmentDescriptorBuilder;
+import seedu.address.testutil.EditLessonDescriptorBuilder;
+import seedu.address.testutil.EditStudentDescriptorBuilder;
+import seedu.address.testutil.LessonBuilder;
+import seedu.address.testutil.LessonUtil;
+import seedu.address.testutil.StudentBuilder;
+import seedu.address.testutil.StudentUtil;
public class AddressBookParserTest {
private final AddressBookParser parser = new AddressBookParser();
@Test
- public void parseCommand_add() throws Exception {
- Person person = new PersonBuilder().build();
- AddCommand command = (AddCommand) parser.parseCommand(PersonUtil.getAddCommand(person));
- assertEquals(new AddCommand(person), command);
+ public void parseCommand_addStudent() throws Exception {
+ Student student = new StudentBuilder().build();
+ AddStudentCommand command = (AddStudentCommand) parser.parseCommand(StudentUtil.getAddStudentCommand(student));
+ assertEquals(new AddStudentCommand(student), command);
}
@Test
- public void parseCommand_clear() throws Exception {
- assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD) instanceof ClearCommand);
- assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD + " 3") instanceof ClearCommand);
+ public void parseCommand_addLesson() throws Exception {
+ Lesson lesson = new LessonBuilder().build();
+ AddLessonCommand command = (AddLessonCommand) parser.parseCommand(LessonUtil.getAddLessonCommand(lesson));
+ assertEquals(new AddLessonCommand(lesson), command);
+ }
+
+ @Test
+ public void parseCommand_addAssignment() throws Exception {
+ Assignment assignment = new AssignmentBuilder().build();
+ AddAssignmentCommand command = (AddAssignmentCommand) parser
+ .parseCommand(AssignmentUtil.getAddAssignmentCommand(assignment));
+ assertEquals(new AddAssignmentCommand(Index.fromOneBased(1), assignment), command);
+ }
+
+ @Test
+ public void parseCommand_deleteStudent() throws Exception {
+ DeleteStudentCommand command = (DeleteStudentCommand) parser.parseCommand(
+ DeleteStudentCommand.COMMAND_WORD + " 1");
+ assertEquals(new DeleteStudentCommand(Index.fromOneBased(1)), command);
}
@Test
- public void parseCommand_delete() throws Exception {
- DeleteCommand command = (DeleteCommand) parser.parseCommand(
- DeleteCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased());
- assertEquals(new DeleteCommand(INDEX_FIRST_PERSON), command);
+ public void parseCommand_deleteLesson() throws Exception {
+ DeleteLessonCommand command = (DeleteLessonCommand) parser.parseCommand(
+ DeleteLessonCommand.COMMAND_WORD + " 1");
+ assertEquals(new DeleteLessonCommand(Index.fromOneBased(1)), command);
}
@Test
- public void parseCommand_edit() throws Exception {
- Person person = new PersonBuilder().build();
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(person).build();
- EditCommand command = (EditCommand) parser.parseCommand(EditCommand.COMMAND_WORD + " "
- + INDEX_FIRST_PERSON.getOneBased() + " " + PersonUtil.getEditPersonDescriptorDetails(descriptor));
- assertEquals(new EditCommand(INDEX_FIRST_PERSON, descriptor), command);
+ public void parseCommand_deleteAssignment() throws Exception {
+ DeleteAssignmentCommand command = (DeleteAssignmentCommand) parser.parseCommand(
+ DeleteAssignmentCommand.COMMAND_WORD + " 1" + ASSIGNMENT_NAME_DESC_AMY);
+ assertEquals(new DeleteAssignmentCommand(Index.fromOneBased(1), VALID_ASSIGNMENT_NAME_AMY.trim()),
+ command);
+ }
+
+ @Test
+ public void parseCommand_editStudent() throws Exception {
+ EditStudentCommand.EditStudentDescriptor descriptor = new EditStudentDescriptorBuilder()
+ .withName("New Name")
+ .withPhone("12345678")
+ .withEmail("new@email.com")
+ .withAddress("New Address")
+ .build();
+ EditStudentCommand command = (EditStudentCommand) parser.parseCommand(
+ EditStudentCommand.COMMAND_WORD + " 1 "
+ + StudentUtil.getEditStudentDescriptorDetails(descriptor));
+ assertEquals(new EditStudentCommand(Index.fromOneBased(1), descriptor), command);
+ }
+
+ @Test
+ public void parseCommand_editLesson() throws Exception {
+ Lesson lesson = new LessonBuilder().build();
+ EditLessonCommand.EditLessonDescriptor descriptor = new EditLessonDescriptorBuilder(lesson).build();
+ EditLessonCommand command = (EditLessonCommand) parser.parseCommand(EditLessonCommand.COMMAND_WORD
+ + " 1 "
+ + LessonUtil.getEditLessonDescriptorDetails(descriptor));
+ assertEquals(new EditLessonCommand(Index.fromOneBased(1), descriptor), command);
+ }
+
+ @Test
+ public void parseCommand_editAssignment() throws Exception {
+ Assignment assignment = new AssignmentBuilder().build();
+ EditAssignmentCommand.EditAssignmentDescriptor descriptor =
+ new EditAssignmentDescriptorBuilder(assignment).build();
+ EditAssignmentCommand command = (EditAssignmentCommand) parser.parseCommand(EditAssignmentCommand.COMMAND_WORD
+ + " 1 "
+ + ASSIGNMENT_NAME_DESC_AMY
+ + AssignmentUtil.getEditAssignmentDescriptorDetails(descriptor));
+ assertEquals(new EditAssignmentCommand(Index.fromOneBased(1), VALID_ASSIGNMENT_NAME_AMY.trim(), descriptor),
+ command);
+ }
+
+ @Test
+ public void parseCommand_markAssignment() throws Exception {
+ MarkAssignmentCommand command = (MarkAssignmentCommand) parser.parseCommand(
+ MarkAssignmentCommand.COMMAND_WORD + " 1 " + ASSIGNMENT_NAME_DESC_AMY);
+ assertEquals(new MarkAssignmentCommand(Index.fromOneBased(1), VALID_ASSIGNMENT_NAME_AMY.trim()), command);
+ }
+
+ @Test
+ public void parseCommand_unmarkAssignment() throws Exception {
+ UnmarkAssignmentCommand command = (UnmarkAssignmentCommand) parser.parseCommand(
+ UnmarkAssignmentCommand.COMMAND_WORD + " 1 " + ASSIGNMENT_NAME_DESC_AMY);
+ assertEquals(new UnmarkAssignmentCommand(Index.fromOneBased(1), VALID_ASSIGNMENT_NAME_AMY.trim()), command);
+ }
+
+ @Test
+ public void parseCommand_listLessons() throws Exception {
+ assertTrue(parser.parseCommand(ListLessonsCommand.COMMAND_WORD) instanceof ListLessonsCommand);
+ assertTrue(parser.parseCommand(ListLessonsCommand.COMMAND_WORD + NAME_DESC_AMY) instanceof ListLessonsCommand);
+ }
+
+ @Test
+ public void parseCommand_clear() throws Exception {
+ assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD) instanceof ClearCommand);
+ assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD + " 3") instanceof ClearCommand);
}
@Test
@@ -71,9 +168,9 @@ public void parseCommand_exit() throws Exception {
@Test
public void parseCommand_find() throws Exception {
List keywords = Arrays.asList("foo", "bar", "baz");
- FindCommand command = (FindCommand) parser.parseCommand(
- FindCommand.COMMAND_WORD + " " + keywords.stream().collect(Collectors.joining(" ")));
- assertEquals(new FindCommand(new NameContainsKeywordsPredicate(keywords)), command);
+ FindStudentCommand command = (FindStudentCommand) parser.parseCommand(
+ FindStudentCommand.COMMAND_WORD + " " + keywords.stream().collect(Collectors.joining(" ")));
+ assertEquals(new FindStudentCommand(new NameContainsKeywordsPredicate(keywords)), command);
}
@Test
@@ -83,9 +180,9 @@ public void parseCommand_help() throws Exception {
}
@Test
- public void parseCommand_list() throws Exception {
- assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD) instanceof ListCommand);
- assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD + " 3") instanceof ListCommand);
+ public void parseCommand_listStudent() throws Exception {
+ assertTrue(parser.parseCommand(ListStudentsCommand.COMMAND_WORD) instanceof ListStudentsCommand);
+ assertTrue(parser.parseCommand(ListStudentsCommand.COMMAND_WORD + " 3") instanceof ListStudentsCommand);
}
@Test
diff --git a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java b/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java
index c97308935f5..4179281f118 100644
--- a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java
+++ b/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java
@@ -55,7 +55,7 @@ private void assertArgumentAbsent(ArgumentMultimap argMultimap, Prefix prefix) {
@Test
public void tokenize_noPrefixes_allTakenAsPreamble() {
- String argsString = " some random string /t tag with leading and trailing spaces ";
+ String argsString = " some random string /s subject with leading and trailing spaces ";
ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString);
// Same string expected as preamble, but leading/trailing spaces should be trimmed
diff --git a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java
deleted file mode 100644
index 6a40e14a649..00000000000
--- a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
-import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
-import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
-
-import org.junit.jupiter.api.Test;
-
-import seedu.address.logic.commands.DeleteCommand;
-
-/**
- * As we are only doing white-box testing, our test cases do not cover path variations
- * outside of the DeleteCommand code. For example, inputs "1" and "1 abc" take the
- * same path through the DeleteCommand, and therefore we test only one of them.
- * The path variation for those two cases occur inside the ParserUtil, and
- * therefore should be covered by the ParserUtilTest.
- */
-public class DeleteCommandParserTest {
-
- private DeleteCommandParser parser = new DeleteCommandParser();
-
- @Test
- public void parse_validArgs_returnsDeleteCommand() {
- assertParseSuccess(parser, "1", new DeleteCommand(INDEX_FIRST_PERSON));
- }
-
- @Test
- public void parse_invalidArgs_throwsParseException() {
- assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE));
- }
-}
diff --git a/src/test/java/seedu/address/logic/parser/DeleteLessonCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteLessonCommandParserTest.java
new file mode 100644
index 00000000000..697de98f5ef
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/DeleteLessonCommandParserTest.java
@@ -0,0 +1,65 @@
+package seedu.address.logic.parser;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.DeleteLessonCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * As we are only doing white-box testing, our test cases do not cover path variations
+ * outside of the DeleteLessonCommand code. For example, inputs "1" and "1 abc" take the
+ * same path through the DeleteLessonCommand, and therefore we test only one of them.
+ * The path variation for those two cases occur inside the ParserUtil, and
+ * therefore should be covered by the ParserUtilTest.
+ */
+public class DeleteLessonCommandParserTest {
+
+ private DeleteLessonCommandParser parser = new DeleteLessonCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsDeleteCommand() {
+ assertParseSuccess(parser, "1", new DeleteLessonCommand(INDEX_FIRST));
+ }
+
+ @Test
+ public void parse_invalidArgs_throwsParseException() {
+ assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ DeleteLessonCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_zeroIndex_throwsParseException() {
+ assertParseFailure(parser, "0", String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ DeleteLessonCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_negativeIndex_throwsParseException() {
+ assertParseFailure(parser, "-1",
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteLessonCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_emptyString_throwsParseException() {
+ assertParseFailure(parser, " ",
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteLessonCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_nullInput_throwsParseException() {
+ try {
+ parser.parse(null);
+ } catch (ParseException e) {
+ assertEquals(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ DeleteLessonCommand.MESSAGE_USAGE), e.getMessage());
+ }
+ }
+
+}
+
diff --git a/src/test/java/seedu/address/logic/parser/DeleteStudentCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteStudentCommandParserTest.java
new file mode 100644
index 00000000000..cc795804f11
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/DeleteStudentCommandParserTest.java
@@ -0,0 +1,59 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.DeleteStudentCommand;
+
+/**
+ * As we are only doing white-box testing, our test cases do not cover path variations
+ * outside of the DeleteStudentCommand code. For example, inputs "1" and "1 abc" take the
+ * same path through the DeleteStudentCommand, and therefore we test only one of them.
+ * The path variation for those two cases occur inside the ParserUtil, and
+ * therefore should be covered by the ParserUtilTest.
+ */
+public class DeleteStudentCommandParserTest {
+
+ private DeleteStudentCommandParser parser = new DeleteStudentCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsDeleteStudentCommand() {
+ assertParseSuccess(parser, "1", new DeleteStudentCommand(INDEX_FIRST));
+ }
+
+ @Test
+ public void parse_invalidArgs_throwsParseException() {
+ assertParseFailure(parser, "a",
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteStudentCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_zeroIndex_throwsParseException() {
+ assertParseFailure(parser, "0",
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteStudentCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_negativeIndex_throwsParseException() {
+ assertParseFailure(parser, "-1",
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteStudentCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_nullArgs_throwsParseException() {
+ assertParseFailure(parser, null,
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteStudentCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_emptyString_throwsParseException() {
+ assertParseFailure(parser, " ",
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteStudentCommand.MESSAGE_USAGE));
+ }
+
+}
+
diff --git a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
deleted file mode 100644
index cc7175172d4..00000000000
--- a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
+++ /dev/null
@@ -1,208 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND;
-import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
-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_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
-import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
-import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
-import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON;
-import static seedu.address.testutil.TypicalIndexes.INDEX_THIRD_PERSON;
-
-import org.junit.jupiter.api.Test;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.logic.Messages;
-import seedu.address.logic.commands.EditCommand;
-import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
-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.tag.Tag;
-import seedu.address.testutil.EditPersonDescriptorBuilder;
-
-public class EditCommandParserTest {
-
- private static final String TAG_EMPTY = " " + PREFIX_TAG;
-
- private static final String MESSAGE_INVALID_FORMAT =
- String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE);
-
- private EditCommandParser parser = new EditCommandParser();
-
- @Test
- public void parse_missingParts_failure() {
- // no index specified
- assertParseFailure(parser, VALID_NAME_AMY, MESSAGE_INVALID_FORMAT);
-
- // no field specified
- assertParseFailure(parser, "1", EditCommand.MESSAGE_NOT_EDITED);
-
- // no index and no field specified
- assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT);
- }
-
- @Test
- public void parse_invalidPreamble_failure() {
- // negative index
- assertParseFailure(parser, "-5" + NAME_DESC_AMY, MESSAGE_INVALID_FORMAT);
-
- // zero index
- assertParseFailure(parser, "0" + NAME_DESC_AMY, MESSAGE_INVALID_FORMAT);
-
- // invalid arguments being parsed as preamble
- assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT);
-
- // invalid prefix being parsed as preamble
- assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT);
- }
-
- @Test
- public void parse_invalidValue_failure() {
- assertParseFailure(parser, "1" + INVALID_NAME_DESC, Name.MESSAGE_CONSTRAINTS); // invalid name
- assertParseFailure(parser, "1" + INVALID_PHONE_DESC, Phone.MESSAGE_CONSTRAINTS); // invalid phone
- assertParseFailure(parser, "1" + INVALID_EMAIL_DESC, Email.MESSAGE_CONSTRAINTS); // invalid email
- assertParseFailure(parser, "1" + INVALID_ADDRESS_DESC, Address.MESSAGE_CONSTRAINTS); // invalid address
- assertParseFailure(parser, "1" + INVALID_TAG_DESC, Tag.MESSAGE_CONSTRAINTS); // invalid tag
-
- // invalid phone followed by valid email
- assertParseFailure(parser, "1" + INVALID_PHONE_DESC + EMAIL_DESC_AMY, Phone.MESSAGE_CONSTRAINTS);
-
- // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Person} being edited,
- // parsing it together with a valid tag results in error
- assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_DESC_HUSBAND + TAG_EMPTY, Tag.MESSAGE_CONSTRAINTS);
- assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_EMPTY + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS);
- assertParseFailure(parser, "1" + TAG_EMPTY + TAG_DESC_FRIEND + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS);
-
- // multiple invalid values, but only the first invalid value is captured
- assertParseFailure(parser, "1" + INVALID_NAME_DESC + INVALID_EMAIL_DESC + VALID_ADDRESS_AMY + VALID_PHONE_AMY,
- Name.MESSAGE_CONSTRAINTS);
- }
-
- @Test
- public void parse_allFieldsSpecified_success() {
- Index targetIndex = INDEX_SECOND_PERSON;
- String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + TAG_DESC_HUSBAND
- + EMAIL_DESC_AMY + ADDRESS_DESC_AMY + NAME_DESC_AMY + TAG_DESC_FRIEND;
-
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY)
- .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY)
- .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build();
- EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
-
- assertParseSuccess(parser, userInput, expectedCommand);
- }
-
- @Test
- public void parse_someFieldsSpecified_success() {
- Index targetIndex = INDEX_FIRST_PERSON;
- String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + EMAIL_DESC_AMY;
-
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB)
- .withEmail(VALID_EMAIL_AMY).build();
- EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
-
- assertParseSuccess(parser, userInput, expectedCommand);
- }
-
- @Test
- public void parse_oneFieldSpecified_success() {
- // name
- Index targetIndex = INDEX_THIRD_PERSON;
- String userInput = targetIndex.getOneBased() + NAME_DESC_AMY;
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY).build();
- EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
- assertParseSuccess(parser, userInput, expectedCommand);
-
- // phone
- userInput = targetIndex.getOneBased() + PHONE_DESC_AMY;
- descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_AMY).build();
- expectedCommand = new EditCommand(targetIndex, descriptor);
- assertParseSuccess(parser, userInput, expectedCommand);
-
- // email
- userInput = targetIndex.getOneBased() + EMAIL_DESC_AMY;
- descriptor = new EditPersonDescriptorBuilder().withEmail(VALID_EMAIL_AMY).build();
- expectedCommand = new EditCommand(targetIndex, descriptor);
- assertParseSuccess(parser, userInput, expectedCommand);
-
- // address
- userInput = targetIndex.getOneBased() + ADDRESS_DESC_AMY;
- descriptor = new EditPersonDescriptorBuilder().withAddress(VALID_ADDRESS_AMY).build();
- expectedCommand = new EditCommand(targetIndex, descriptor);
- assertParseSuccess(parser, userInput, expectedCommand);
-
- // tags
- userInput = targetIndex.getOneBased() + TAG_DESC_FRIEND;
- descriptor = new EditPersonDescriptorBuilder().withTags(VALID_TAG_FRIEND).build();
- expectedCommand = new EditCommand(targetIndex, descriptor);
- assertParseSuccess(parser, userInput, expectedCommand);
- }
-
- @Test
- public void parse_multipleRepeatedFields_failure() {
- // More extensive testing of duplicate parameter detections is done in
- // AddCommandParserTest#parse_repeatedNonTagValue_failure()
-
- // valid followed by invalid
- Index targetIndex = INDEX_FIRST_PERSON;
- String userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + PHONE_DESC_BOB;
-
- assertParseFailure(parser, userInput, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
-
- // invalid followed by valid
- userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + INVALID_PHONE_DESC;
-
- assertParseFailure(parser, userInput, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
-
- // mulltiple valid fields repeated
- userInput = targetIndex.getOneBased() + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY
- + TAG_DESC_FRIEND + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY + TAG_DESC_FRIEND
- + PHONE_DESC_BOB + ADDRESS_DESC_BOB + EMAIL_DESC_BOB + TAG_DESC_HUSBAND;
-
- assertParseFailure(parser, userInput,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS));
-
- // multiple invalid values
- userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + INVALID_ADDRESS_DESC + INVALID_EMAIL_DESC
- + INVALID_PHONE_DESC + INVALID_ADDRESS_DESC + INVALID_EMAIL_DESC;
-
- assertParseFailure(parser, userInput,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS));
- }
-
- @Test
- public void parse_resetTags_success() {
- Index targetIndex = INDEX_THIRD_PERSON;
- String userInput = targetIndex.getOneBased() + TAG_EMPTY;
-
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withTags().build();
- EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
-
- assertParseSuccess(parser, userInput, expectedCommand);
- }
-}
diff --git a/src/test/java/seedu/address/logic/parser/EditLessonCommandParserTest.java b/src/test/java/seedu/address/logic/parser/EditLessonCommandParserTest.java
new file mode 100644
index 00000000000..cfca91d9ece
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/EditLessonCommandParserTest.java
@@ -0,0 +1,54 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_DUPLICATE_FIELDS;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.EditLessonCommand;
+import seedu.address.logic.commands.EditLessonCommand.EditLessonDescriptor;
+import seedu.address.model.datetimeutil.Date;
+import seedu.address.model.datetimeutil.Time;
+import seedu.address.model.student.Name;
+import seedu.address.model.subject.Subject;
+
+public class EditLessonCommandParserTest {
+
+ private final EditLessonCommandParser parser = new EditLessonCommandParser();
+
+ @Test
+ public void parse_allFieldsSpecified_success() {
+ EditLessonDescriptor descriptor = new EditLessonDescriptor();
+ descriptor.setName(new Name("Alice"));
+ descriptor.setDate(new Date("01-04-2030"));
+ descriptor.setTime(new Time("12:00"));
+ descriptor.setSubject(new Subject("Math"));
+
+ String userInput = "1 n/Alice d/01-04-2030 t/12:00 s/Math";
+
+ assertParseSuccess(parser, userInput, new EditLessonCommand(INDEX_FIRST, descriptor));
+ }
+
+ @Test
+ public void parse_invalidIndex_failure() {
+ assertParseFailure(parser, "a n/Alice", ParserUtil.MESSAGE_INVALID_INDEX);
+ }
+
+ @Test
+ public void parse_missingIndex_failure() {
+ assertParseFailure(parser, "n/Alice", ParserUtil.MESSAGE_INVALID_INDEX);
+ }
+
+ @Test
+ public void parse_noFieldsEdited_failure() {
+ assertParseFailure(parser, "1", EditLessonCommand.MESSAGE_NOT_EDITED);
+ }
+
+ @Test
+ public void parse_duplicatePrefixes_failure() {
+ String input = "1 n/Alice n/Bob";
+ assertParseFailure(parser, input, MESSAGE_DUPLICATE_FIELDS + "n/");
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/EditStudentCommandParserTest.java b/src/test/java/seedu/address/logic/parser/EditStudentCommandParserTest.java
new file mode 100644
index 00000000000..94ebe0a1eed
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/EditStudentCommandParserTest.java
@@ -0,0 +1,75 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_DUPLICATE_FIELDS;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.EditStudentCommand;
+import seedu.address.logic.commands.EditStudentCommand.EditStudentDescriptor;
+import seedu.address.testutil.EditStudentDescriptorBuilder;
+
+public class EditStudentCommandParserTest {
+
+ private final EditStudentCommandParser parser = new EditStudentCommandParser();
+
+ @Test
+ public void parse_allFieldsPresent_success() {
+ Index targetIndex = Index.fromOneBased(1);
+ String userInput = "1 n/Alice p/91234567 e/alice@example.com a/123, Main Street";
+
+ EditStudentDescriptor descriptor = new EditStudentDescriptorBuilder()
+ .withName("Alice")
+ .withPhone("91234567")
+ .withEmail("alice@example.com")
+ .withAddress("123, Main Street")
+ .build();
+
+ EditStudentCommand expectedCommand = new EditStudentCommand(targetIndex, descriptor);
+ assertParseSuccess(parser, userInput, expectedCommand);
+ }
+
+ @Test
+ public void parse_noFieldSpecified_failure() {
+ String userInput = "1";
+ assertParseFailure(parser, userInput, EditStudentCommand.MESSAGE_NOT_EDITED);
+ }
+
+ @Test
+ public void parse_missingIndex_failure() {
+ String userInput = "n/Bob";
+ String expectedMessage = ParserUtil.MESSAGE_INVALID_INDEX;
+ assertParseFailure(parser, userInput, expectedMessage);
+ }
+
+ @Test
+ public void parse_invalidIndex_failure() {
+ String userInput = "a n/Bob";
+ String expectedMessage = ParserUtil.MESSAGE_INVALID_INDEX;
+ assertParseFailure(parser, userInput, expectedMessage);
+ }
+
+
+ @Test
+ public void parse_duplicatePrefix_failure() {
+ String userInput = "1 n/Alice n/Bob";
+ assertParseFailure(parser, userInput, MESSAGE_DUPLICATE_FIELDS + "n/");
+ }
+
+ @Test
+ public void parse_editSubject_failure() {
+ String userInput = "1 s/Math";
+ assertParseFailure(parser, userInput,
+ EditStudentCommand.MESSAGE_EDIT_SUBJECT_DISALLOWED);
+ }
+
+ @Test
+ public void parse_emptyArgs_failure() {
+ String userInput = " ";
+ assertParseFailure(parser, userInput,
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditStudentCommand.MESSAGE_USAGE));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java
deleted file mode 100644
index d92e64d12f9..00000000000
--- a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
-import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
-
-import java.util.Arrays;
-
-import org.junit.jupiter.api.Test;
-
-import seedu.address.logic.commands.FindCommand;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
-
-public class FindCommandParserTest {
-
- private FindCommandParser parser = new FindCommandParser();
-
- @Test
- public void parse_emptyArg_throwsParseException() {
- assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
- }
-
- @Test
- public void parse_validArgs_returnsFindCommand() {
- // no leading and trailing whitespaces
- FindCommand expectedFindCommand =
- new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob")));
- assertParseSuccess(parser, "Alice Bob", expectedFindCommand);
-
- // multiple whitespaces between keywords
- assertParseSuccess(parser, " \n Alice \n \t Bob \t", expectedFindCommand);
- }
-
-}
diff --git a/src/test/java/seedu/address/logic/parser/FindStudentCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FindStudentCommandParserTest.java
new file mode 100644
index 00000000000..35d2094d2d8
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/FindStudentCommandParserTest.java
@@ -0,0 +1,35 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+
+import java.util.Arrays;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.FindStudentCommand;
+import seedu.address.model.student.NameContainsKeywordsPredicate;
+
+public class FindStudentCommandParserTest {
+
+ private FindStudentCommandParser parser = new FindStudentCommandParser();
+
+ @Test
+ public void parse_emptyArg_throwsParseException() {
+ assertParseFailure(parser, " ", String
+ .format(MESSAGE_INVALID_COMMAND_FORMAT, FindStudentCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_validArgs_returnsFindStudentCommand() {
+ // no leading and trailing whitespaces
+ FindStudentCommand expectedFindStudentCommand =
+ new FindStudentCommand(new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob")));
+ assertParseSuccess(parser, "Alice Bob", expectedFindStudentCommand);
+
+ // multiple whitespaces between keywords
+ assertParseSuccess(parser, " \n Alice \n \t Bob \t", expectedFindStudentCommand);
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/parser/ListLessonsCommandParserTest.java b/src/test/java/seedu/address/logic/parser/ListLessonsCommandParserTest.java
new file mode 100644
index 00000000000..15406f8bd66
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/ListLessonsCommandParserTest.java
@@ -0,0 +1,36 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_LESSONS;
+import static seedu.address.model.student.Name.MESSAGE_CONSTRAINTS;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.ListLessonsCommand;
+import seedu.address.model.lesson.StudentNameLessonPredicate;
+import seedu.address.model.student.Name;
+
+public class ListLessonsCommandParserTest {
+
+ private ListLessonsCommandParser parser = new ListLessonsCommandParser();
+
+ @Test
+ public void parse_emptyArg_showsAllLessons() {
+ ListLessonsCommand expectedFindStudentCommand =
+ new ListLessonsCommand(PREDICATE_SHOW_ALL_LESSONS);
+ assertParseSuccess(parser, " ", expectedFindStudentCommand);
+ }
+
+ @Test
+ public void parse_validArgs_returnsListLessonsCommand() {
+ // leading and trailing whitespaces
+ ListLessonsCommand expectedFindStudentCommand =
+ new ListLessonsCommand(new StudentNameLessonPredicate(new Name("Alice")), new Name("Alice"));
+ assertParseSuccess(parser, " n/ Alice ", expectedFindStudentCommand);
+
+ // blank keywords are not allowed
+ assertParseFailure(parser, " n/ ", MESSAGE_CONSTRAINTS);
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
index 4256788b1a7..c996d0172f9 100644
--- a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
+++ b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
@@ -4,7 +4,7 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static seedu.address.logic.parser.ParserUtil.MESSAGE_INVALID_INDEX;
import static seedu.address.testutil.Assert.assertThrows;
-import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST;
import java.util.Arrays;
import java.util.Collections;
@@ -14,25 +14,25 @@
import org.junit.jupiter.api.Test;
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.tag.Tag;
+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.subject.Subject;
public class ParserUtilTest {
private static final String INVALID_NAME = "R@chel";
private static final String INVALID_PHONE = "+651234";
private static final String INVALID_ADDRESS = " ";
private static final String INVALID_EMAIL = "example.com";
- private static final String INVALID_TAG = "#friend";
+ private static final String INVALID_SUBJECT = "#friend";
private static final String VALID_NAME = "Rachel Walker";
- private static final String VALID_PHONE = "123456";
+ private static final String VALID_PHONE = "12343856";
private static final String VALID_ADDRESS = "123 Main Street #0505";
private static final String VALID_EMAIL = "rachel@example.com";
- private static final String VALID_TAG_1 = "friend";
- private static final String VALID_TAG_2 = "neighbour";
+ private static final String VALID_SUBJECT_1 = "friend";
+ private static final String VALID_SUBJECT_2 = "neighbour";
private static final String WHITESPACE = " \t\r\n";
@@ -50,10 +50,10 @@ public void parseIndex_outOfRangeInput_throwsParseException() {
@Test
public void parseIndex_validInput_success() throws Exception {
// No whitespaces
- assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex("1"));
+ assertEquals(INDEX_FIRST, ParserUtil.parseIndex("1"));
// Leading and trailing whitespaces
- assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex(" 1 "));
+ assertEquals(INDEX_FIRST, ParserUtil.parseIndex(" 1 "));
}
@Test
@@ -149,48 +149,50 @@ public void parseEmail_validValueWithWhitespace_returnsTrimmedEmail() throws Exc
}
@Test
- public void parseTag_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> ParserUtil.parseTag(null));
+ public void parseSubject_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseSubject(null));
}
@Test
- public void parseTag_invalidValue_throwsParseException() {
- assertThrows(ParseException.class, () -> ParserUtil.parseTag(INVALID_TAG));
+ public void parseSubject_invalidValue_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseSubject(INVALID_SUBJECT));
}
@Test
- public void parseTag_validValueWithoutWhitespace_returnsTag() throws Exception {
- Tag expectedTag = new Tag(VALID_TAG_1);
- assertEquals(expectedTag, ParserUtil.parseTag(VALID_TAG_1));
+ public void parseSubject_validValueWithoutWhitespace_returnsSubject() throws Exception {
+ Subject expectedSubject = new Subject(VALID_SUBJECT_1);
+ assertEquals(expectedSubject, ParserUtil.parseSubject(VALID_SUBJECT_1));
}
@Test
- public void parseTag_validValueWithWhitespace_returnsTrimmedTag() throws Exception {
- String tagWithWhitespace = WHITESPACE + VALID_TAG_1 + WHITESPACE;
- Tag expectedTag = new Tag(VALID_TAG_1);
- assertEquals(expectedTag, ParserUtil.parseTag(tagWithWhitespace));
+ public void parseSubject_validValueWithWhitespace_returnsTrimmedSubject() throws Exception {
+ String subjectWithWhitespace = WHITESPACE + VALID_SUBJECT_1 + WHITESPACE;
+ Subject expectedSubject = new Subject(VALID_SUBJECT_1);
+ assertEquals(expectedSubject, ParserUtil.parseSubject(subjectWithWhitespace));
}
@Test
- public void parseTags_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> ParserUtil.parseTags(null));
+ public void parseSubjects_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseSubjects(null));
}
@Test
- public void parseTags_collectionWithInvalidTags_throwsParseException() {
- assertThrows(ParseException.class, () -> ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, INVALID_TAG)));
+ public void parseSubjects_collectionWithInvalidSubjects_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseSubjects(
+ Arrays.asList(VALID_SUBJECT_1, INVALID_SUBJECT)));
}
@Test
- public void parseTags_emptyCollection_returnsEmptySet() throws Exception {
- assertTrue(ParserUtil.parseTags(Collections.emptyList()).isEmpty());
+ public void parseSubjects_emptyCollection_returnsEmptySet() throws Exception {
+ assertTrue(ParserUtil.parseSubjects(Collections.emptyList()).isEmpty());
}
@Test
- public void parseTags_collectionWithValidTags_returnsTagSet() throws Exception {
- Set actualTagSet = ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, VALID_TAG_2));
- Set expectedTagSet = new HashSet(Arrays.asList(new Tag(VALID_TAG_1), new Tag(VALID_TAG_2)));
+ public void parseSubjects_collectionWithValidSubjects_returnsSubjectSet() throws Exception {
+ Set actualSubjectSet = ParserUtil.parseSubjects(Arrays.asList(VALID_SUBJECT_1, VALID_SUBJECT_2));
+ Set expectedSubjectSet = new HashSet(Arrays.asList(
+ new Subject(VALID_SUBJECT_1), new Subject(VALID_SUBJECT_2)));
- assertEquals(expectedTagSet, actualTagSet);
+ assertEquals(expectedSubjectSet, actualSubjectSet);
}
}
diff --git a/src/test/java/seedu/address/model/AddressBookTest.java b/src/test/java/seedu/address/model/AddressBookTest.java
index 68c8c5ba4d5..c4b79eaf77f 100644
--- a/src/test/java/seedu/address/model/AddressBookTest.java
+++ b/src/test/java/seedu/address/model/AddressBookTest.java
@@ -4,10 +4,10 @@
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_SUBJECT_HUSBAND;
import static seedu.address.testutil.Assert.assertThrows;
-import static seedu.address.testutil.TypicalPersons.ALICE;
-import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import static seedu.address.testutil.TypicalStudents.ALICE;
+import static seedu.address.testutil.TypicalStudents.getTypicalAddressBook;
import java.util.Arrays;
import java.util.Collection;
@@ -18,9 +18,10 @@
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.exceptions.DuplicatePersonException;
-import seedu.address.testutil.PersonBuilder;
+import seedu.address.model.lesson.Lesson;
+import seedu.address.model.student.Student;
+import seedu.address.model.student.exceptions.DuplicateStudentException;
+import seedu.address.testutil.StudentBuilder;
public class AddressBookTest {
@@ -28,7 +29,7 @@ public class AddressBookTest {
@Test
public void constructor() {
- assertEquals(Collections.emptyList(), addressBook.getPersonList());
+ assertEquals(Collections.emptyList(), addressBook.getStudentList());
}
@Test
@@ -44,64 +45,73 @@ public void resetData_withValidReadOnlyAddressBook_replacesData() {
}
@Test
- public void resetData_withDuplicatePersons_throwsDuplicatePersonException() {
- // Two persons with the same identity fields
- Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND)
+ public void resetData_withDuplicateStudents_throwsDuplicateStudentException() {
+ // Two students with the same identity fields
+ Student editedAlice = new StudentBuilder(ALICE).withAddress(VALID_ADDRESS_BOB)
+ .withSubjects(VALID_SUBJECT_HUSBAND)
.build();
- List newPersons = Arrays.asList(ALICE, editedAlice);
- AddressBookStub newData = new AddressBookStub(newPersons);
+ List newStudents = Arrays.asList(ALICE, editedAlice);
+ AddressBookStub newData = new AddressBookStub(newStudents);
- assertThrows(DuplicatePersonException.class, () -> addressBook.resetData(newData));
+ assertThrows(DuplicateStudentException.class, () -> addressBook.resetData(newData));
}
@Test
- public void hasPerson_nullPerson_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> addressBook.hasPerson(null));
+ public void hasStudent_nullStudent_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> addressBook.hasStudent(null));
}
@Test
- public void hasPerson_personNotInAddressBook_returnsFalse() {
- assertFalse(addressBook.hasPerson(ALICE));
+ public void hasStudent_studentNotInAddressBook_returnsFalse() {
+ assertFalse(addressBook.hasStudent(ALICE));
}
@Test
- public void hasPerson_personInAddressBook_returnsTrue() {
- addressBook.addPerson(ALICE);
- assertTrue(addressBook.hasPerson(ALICE));
+ public void hasStudent_studentInAddressBook_returnsTrue() {
+ addressBook.addStudent(ALICE);
+ assertTrue(addressBook.hasStudent(ALICE));
}
@Test
- public void hasPerson_personWithSameIdentityFieldsInAddressBook_returnsTrue() {
- addressBook.addPerson(ALICE);
- Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND)
+ public void hasStudent_studentWithSameIdentityFieldsInAddressBook_returnsTrue() {
+ addressBook.addStudent(ALICE);
+ Student editedAlice = new StudentBuilder(ALICE).withAddress(VALID_ADDRESS_BOB)
+ .withSubjects(VALID_SUBJECT_HUSBAND)
.build();
- assertTrue(addressBook.hasPerson(editedAlice));
+ assertTrue(addressBook.hasStudent(editedAlice));
}
@Test
- public void getPersonList_modifyList_throwsUnsupportedOperationException() {
- assertThrows(UnsupportedOperationException.class, () -> addressBook.getPersonList().remove(0));
+ public void getStudentList_modifyList_throwsUnsupportedOperationException() {
+ assertThrows(UnsupportedOperationException.class, () -> addressBook.getStudentList().remove(0));
}
@Test
public void toStringMethod() {
- String expected = AddressBook.class.getCanonicalName() + "{persons=" + addressBook.getPersonList() + "}";
+ String expected = AddressBook.class.getCanonicalName() + "{students=" + addressBook.getStudentList()
+ + ", lessons=" + addressBook.getLessonList() + "}";
assertEquals(expected, addressBook.toString());
}
/**
- * A stub ReadOnlyAddressBook whose persons list can violate interface constraints.
+ * A stub ReadOnlyAddressBook whose students list can violate interface constraints.
*/
private static class AddressBookStub implements ReadOnlyAddressBook {
- private final ObservableList persons = FXCollections.observableArrayList();
+ private final ObservableList students = FXCollections.observableArrayList();
+ private final ObservableList lessons = FXCollections.observableArrayList();
- AddressBookStub(Collection persons) {
- this.persons.setAll(persons);
+ AddressBookStub(Collection students) {
+ this.students.setAll(students);
}
@Override
- public ObservableList getPersonList() {
- return persons;
+ public ObservableList getStudentList() {
+ return students;
+ }
+
+ @Override
+ public ObservableList getLessonList() {
+ return lessons;
}
}
diff --git a/src/test/java/seedu/address/model/ModelManagerTest.java b/src/test/java/seedu/address/model/ModelManagerTest.java
index 2cf1418d116..1a28f2670aa 100644
--- a/src/test/java/seedu/address/model/ModelManagerTest.java
+++ b/src/test/java/seedu/address/model/ModelManagerTest.java
@@ -3,10 +3,11 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_LESSONS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
import static seedu.address.testutil.Assert.assertThrows;
-import static seedu.address.testutil.TypicalPersons.ALICE;
-import static seedu.address.testutil.TypicalPersons.BENSON;
+import static seedu.address.testutil.TypicalStudents.ALICE;
+import static seedu.address.testutil.TypicalStudents.BENSON;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -15,7 +16,7 @@
import org.junit.jupiter.api.Test;
import seedu.address.commons.core.GuiSettings;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import seedu.address.model.student.NameContainsKeywordsPredicate;
import seedu.address.testutil.AddressBookBuilder;
public class ModelManagerTest {
@@ -73,29 +74,39 @@ public void setAddressBookFilePath_validPath_setsAddressBookFilePath() {
}
@Test
- public void hasPerson_nullPerson_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> modelManager.hasPerson(null));
+ public void hasStudent_nullStudent_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> modelManager.hasStudent(null));
}
@Test
- public void hasPerson_personNotInAddressBook_returnsFalse() {
- assertFalse(modelManager.hasPerson(ALICE));
+ public void hasStudent_studentNotInAddressBook_returnsFalse() {
+ assertFalse(modelManager.hasStudent(ALICE));
}
@Test
- public void hasPerson_personInAddressBook_returnsTrue() {
- modelManager.addPerson(ALICE);
- assertTrue(modelManager.hasPerson(ALICE));
+ public void hasStudent_studentInAddressBook_returnsTrue() {
+ modelManager.addStudent(ALICE);
+ assertTrue(modelManager.hasStudent(ALICE));
}
@Test
- public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() {
- assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredPersonList().remove(0));
+ public void getFilteredStudentList_modifyList_throwsUnsupportedOperationException() {
+ assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredStudentList().remove(0));
}
+ @Test
+ public void getFilteredCurrList_afterUpdate_returnsCorrectList() {
+ modelManager.updateFilteredLessonList(PREDICATE_SHOW_ALL_LESSONS);
+ assertEquals(modelManager.getFilteredLessonList(), modelManager.getFilteredCurrList());
+
+ modelManager.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+ assertEquals(modelManager.getFilteredStudentList(), modelManager.getFilteredCurrList());
+ }
+
+
@Test
public void equals() {
- AddressBook addressBook = new AddressBookBuilder().withPerson(ALICE).withPerson(BENSON).build();
+ AddressBook addressBook = new AddressBookBuilder().withStudent(ALICE).withStudent(BENSON).build();
AddressBook differentAddressBook = new AddressBook();
UserPrefs userPrefs = new UserPrefs();
@@ -118,11 +129,11 @@ public void equals() {
// different filteredList -> returns false
String[] keywords = ALICE.getName().fullName.split("\\s+");
- modelManager.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(keywords)));
+ modelManager.updateFilteredStudentList(new NameContainsKeywordsPredicate(Arrays.asList(keywords)));
assertFalse(modelManager.equals(new ModelManager(addressBook, userPrefs)));
// resets modelManager to initial state for upcoming tests
- modelManager.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ modelManager.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
// different userPrefs -> returns false
UserPrefs differentUserPrefs = new UserPrefs();
diff --git a/src/test/java/seedu/address/model/assignment/AssignmentTest.java b/src/test/java/seedu/address/model/assignment/AssignmentTest.java
new file mode 100644
index 00000000000..47d1d67d490
--- /dev/null
+++ b/src/test/java/seedu/address/model/assignment/AssignmentTest.java
@@ -0,0 +1,117 @@
+package seedu.address.model.assignment;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.testutil.Assert.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.datetimeutil.Date;
+
+public class AssignmentTest {
+ private static final String FUTURE_DATE_1 = "10-04-2030";
+ private static final String FUTURE_DATE_2 = "11-04-2030";
+ private static final String PAST_DATE = "10-04-2020";
+ @Test
+ public void constructor_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new Assignment(null, null));
+ }
+
+ @Test
+ public void constructor_invalidAssignmentName_throwsIllegalArgumentException() {
+ String invalidName = "";
+ Date validDate = new Date("10-04-2025");
+ assertThrows(IllegalArgumentException.class, () -> new Assignment(invalidName, validDate));
+ }
+
+ @Test
+ public void isValidAssignmentName() {
+ assertThrows(NullPointerException.class, () -> Assignment.isValidAssignmentName(null));
+
+ // invalid name
+ assertFalse(Assignment.isValidAssignmentName("")); // empty string
+ assertFalse(Assignment.isValidAssignmentName(" ")); // spaces only
+ assertFalse(Assignment.isValidAssignmentName("^")); // only non-alphanumeric characters
+ assertFalse(Assignment.isValidAssignmentName("assignment*")); // contains non-alphanumeric characters
+
+ // valid name
+ assertTrue(Assignment.isValidAssignmentName("Some Assignment")); // alphabets only
+ assertTrue(Assignment.isValidAssignmentName("12345")); // numbers only
+ assertTrue(Assignment.isValidAssignmentName("Some Assignment 2")); // alphanumeric characters
+ assertTrue(Assignment.isValidAssignmentName("Some Assignment")); // with capital letters
+ assertTrue(Assignment.isValidAssignmentName("Some Assignment 123")); // with numbers and letters
+ }
+
+ @Test
+ public void isValidAssignmentDate() {
+ // null date
+ assertThrows(NullPointerException.class, () -> Assignment.isValidAssignmentDate(null));
+
+ // invalid date
+ assertFalse(Assignment.isValidAssignmentDate("")); // empty string
+ assertFalse(Assignment.isValidAssignmentDate(" ")); // spaces only
+ assertFalse(Assignment.isValidAssignmentDate("invalid date")); // non-date string
+ assertFalse(Assignment.isValidAssignmentDate(PAST_DATE)); // past date
+
+ // valid date
+ assertTrue(Assignment.isValidAssignmentDate(FUTURE_DATE_1)); // valid date
+ }
+
+ @Test
+ public void equals() {
+ Assignment assignment = new Assignment("Some Assignment", new Date(FUTURE_DATE_1));
+ Assignment assignmentCopy = new Assignment(assignment.assignmentName, assignment.dueDate);
+
+ // same values -> returns true
+ assertTrue(assignment.equals(assignmentCopy));
+
+ // same object -> returns true
+ assertTrue(assignment.equals(assignment));
+
+ // different types -> returns false
+ assertFalse(assignment.equals(1));
+
+ // null -> returns false
+ assertFalse(assignment.equals(null));
+
+ // different assignment -> returns false
+ Assignment differentAssignment = new Assignment("another assignment", new Date("19-04-2025"));
+ assertFalse(assignment.equals(differentAssignment));
+ }
+
+ @Test
+ public void hashCode_sameValues_returnsSameHashCode() {
+ Assignment assignment = new Assignment("Some Assignment", new Date(FUTURE_DATE_1));
+ Assignment assignmentCopy = new Assignment(assignment.assignmentName, assignment.dueDate);
+ assertEquals(assignment.hashCode(), assignmentCopy.hashCode());
+ }
+
+ @Test
+ public void setDone() {
+ Assignment assignment = new Assignment("Some Assignment", new Date(FUTURE_DATE_1));
+ assignment.setDone();
+ assertTrue(assignment.isDone());
+ }
+
+ @Test
+ public void setUndone() {
+ Assignment assignment = new Assignment("Some Assignment", new Date("10-04-2025"));
+ assignment.setDone();
+ assignment.setUndone();
+ assertFalse(assignment.isDone());
+ }
+
+ @Test
+ public void compareTo() {
+ Assignment assignment1 = new Assignment("assignment1", new Date(FUTURE_DATE_1));
+ Assignment assignment2 = new Assignment("assignment2", new Date(FUTURE_DATE_1));
+ Assignment assignment3 = new Assignment("assignment3", new Date(FUTURE_DATE_2));
+
+ // same date, different names
+ assertTrue(assignment1.compareTo(assignment2) < 0);
+
+ // different dates
+ assertTrue(assignment1.compareTo(assignment3) < 0);
+ }
+}
diff --git a/src/test/java/seedu/address/model/assignment/UniqueAssignmentListTest.java b/src/test/java/seedu/address/model/assignment/UniqueAssignmentListTest.java
new file mode 100644
index 00000000000..9745980c1a7
--- /dev/null
+++ b/src/test/java/seedu/address/model/assignment/UniqueAssignmentListTest.java
@@ -0,0 +1,192 @@
+package seedu.address.model.assignment;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.testutil.Assert.assertThrows;
+
+import java.util.Iterator;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.assignment.expections.AssignmentNotFoundException;
+import seedu.address.model.assignment.expections.DuplicateAssignmentException;
+import seedu.address.model.datetimeutil.Date;
+import seedu.address.testutil.AssignmentBuilder;
+
+public class UniqueAssignmentListTest {
+
+ private final UniqueAssignmentList uniqueAssignmentList = new UniqueAssignmentList();
+
+ @Test
+ public void contains_nullAssignment_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueAssignmentList.contains(null));
+ }
+
+ @Test
+ public void contains_assignmentNotInList_returnsFalse() {
+ Assignment assignment = new AssignmentBuilder().build();
+ assertFalse(uniqueAssignmentList.contains(assignment));
+ }
+
+ @Test
+ public void contains_assignmentInList_returnsTrue() {
+ Assignment assignment = new AssignmentBuilder().build();
+ uniqueAssignmentList.add(assignment);
+ assertTrue(uniqueAssignmentList.contains(assignment));
+ }
+
+ @Test
+ public void add_nullAssignment_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueAssignmentList.add(null));
+ }
+
+ @Test
+ public void add_duplicateAssignment_throwsDuplicateAssignmentException() {
+ Assignment assignment = new AssignmentBuilder().build();
+ uniqueAssignmentList.add(assignment);
+ assertThrows(DuplicateAssignmentException.class, () -> uniqueAssignmentList.add(assignment));
+ }
+
+ @Test
+ public void setAssignment_nullTargetAssignment_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueAssignmentList.setAssignment(
+ new AssignmentBuilder().build(), null));
+ }
+
+ @Test
+ public void setAssignment_nullEditedAssignment_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueAssignmentList.setAssignment(
+ new AssignmentBuilder().build(), null));
+ }
+
+ @Test
+ public void setAssignment_targetAssignmentNotInList_throwsAssignmentNotFoundException() {
+ assertThrows(AssignmentNotFoundException.class, () -> uniqueAssignmentList.setAssignment(
+ new AssignmentBuilder().build(), new AssignmentBuilder().build()));
+ }
+
+ @Test
+ public void setAssignment_editedAssignmentIsSameAssignment_success() {
+ Assignment assignment = new AssignmentBuilder().build();
+ uniqueAssignmentList.add(assignment);
+ uniqueAssignmentList.setAssignment(assignment, assignment);
+ UniqueAssignmentList expectedUniqueAssignmentList = new UniqueAssignmentList();
+ expectedUniqueAssignmentList.add(assignment);
+ assertEquals(expectedUniqueAssignmentList, uniqueAssignmentList);
+ }
+
+ @Test
+ public void setAssignment_editedAssignmentHasSameIdentity_success() {
+ Assignment assignment = new AssignmentBuilder().build();
+ uniqueAssignmentList.add(assignment);
+ Assignment editedAssignment = new AssignmentBuilder(assignment).withDueDate(new Date("11-04-2025")).build();
+ uniqueAssignmentList.setAssignment(assignment, editedAssignment);
+ UniqueAssignmentList expectedUniqueAssignmentList = new UniqueAssignmentList();
+ expectedUniqueAssignmentList.add(editedAssignment);
+ assertEquals(expectedUniqueAssignmentList, uniqueAssignmentList);
+ }
+
+ @Test
+ public void setAssignment_editedAssignmentHasDifferentIdentity_success() {
+ Assignment assignment = new AssignmentBuilder().build();
+ uniqueAssignmentList.add(assignment);
+ Assignment differentAssignment = new AssignmentBuilder().withAssignmentName("Different Assignment").build();
+ uniqueAssignmentList.setAssignment(assignment, differentAssignment);
+ UniqueAssignmentList expectedUniqueAssignmentList = new UniqueAssignmentList();
+ expectedUniqueAssignmentList.add(differentAssignment);
+ assertEquals(expectedUniqueAssignmentList, uniqueAssignmentList);
+ }
+
+ @Test
+ public void setAssignment_editedAssignmentHasNonUniqueIdentity_throwsDuplicateAssignmentException() {
+ Assignment assignment = new AssignmentBuilder().build();
+ uniqueAssignmentList.add(assignment);
+ Assignment duplicateAssignment = new AssignmentBuilder().withAssignmentName("Duplicate Assignment").build();
+ uniqueAssignmentList.add(duplicateAssignment);
+ assertThrows(DuplicateAssignmentException.class, () ->
+ uniqueAssignmentList.setAssignment(assignment, duplicateAssignment));
+ }
+
+ @Test
+ public void remove_existingAssignment_removesAssignment() {
+ Assignment assignment = new AssignmentBuilder().build();
+ uniqueAssignmentList.add(assignment);
+ uniqueAssignmentList.deleteAssignment(assignment.getAssignmentName());
+ UniqueAssignmentList expectedUniqueAssignmentList = new UniqueAssignmentList();
+ assertEquals(expectedUniqueAssignmentList, uniqueAssignmentList);
+ }
+
+ @Test
+ public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() {
+ assertThrows(UnsupportedOperationException.class, () ->
+ uniqueAssignmentList.asUnmodifiableObservableList().remove(0));
+ }
+
+ @Test
+ public void markAssignment_alreadyMarked_throwsCommandException() {
+ Assignment assignment = new AssignmentBuilder().build();
+ uniqueAssignmentList.add(assignment);
+ assertDoesNotThrow(() -> uniqueAssignmentList.markAssignment(assignment.getAssignmentName()));
+ assertThrows(CommandException.class, () -> uniqueAssignmentList.markAssignment(assignment.getAssignmentName()));
+ }
+
+ @Test
+ public void unmarkAssignment_alreadyUnmarked_throwsCommandException() {
+ Assignment assignment = new AssignmentBuilder().build();
+ uniqueAssignmentList.add(assignment);
+ assertThrows(CommandException.class, () ->
+ uniqueAssignmentList.unmarkAssignment(assignment.getAssignmentName()));
+ }
+
+ @Test
+ public void deleteAssignment_existingAssignment_deletesAssignment() {
+ Assignment assignment = new AssignmentBuilder().build();
+ uniqueAssignmentList.add(assignment);
+ uniqueAssignmentList.deleteAssignment(assignment.getAssignmentName());
+ assertFalse(uniqueAssignmentList.contains(assignment));
+ }
+
+ @Test
+ public void getAssignment_existingAssignment_returnsAssignment() {
+ Assignment assignment = new AssignmentBuilder().build();
+ uniqueAssignmentList.add(assignment);
+ assertEquals(assignment, uniqueAssignmentList.getAssignment(assignment.getAssignmentName()));
+ }
+
+ @Test
+ public void getAssignment_nonExistingAssignment_returnsNull() {
+ assertEquals(null, uniqueAssignmentList.getAssignment("Non-existing Assignment"));
+ }
+
+ @Test
+ public void compare_assignmentsByDueDate_worksCorrectly() {
+ Assignment a1 = new AssignmentBuilder().withDueDate(new Date("01-01-2030")).build();
+ Assignment a2 = new AssignmentBuilder().withDueDate(new Date("01-02-2030")).build();
+
+ assertTrue(uniqueAssignmentList.compare(a1, a2) < 0);
+ assertTrue(uniqueAssignmentList.compare(a2, a1) > 0);
+ assertEquals(0, uniqueAssignmentList.compare(a1, a1));
+ }
+
+ @Test
+ public void clear_assignmentListCleared_successfully() {
+ Assignment assignment = new AssignmentBuilder().build();
+ uniqueAssignmentList.add(assignment);
+ uniqueAssignmentList.clear();
+ assertEquals(0, uniqueAssignmentList.asUnmodifiableObservableList().size());
+ }
+
+ @Test
+ public void iterator_iteratesAssignmentsCorrectly() {
+ Assignment assignment = new AssignmentBuilder().build();
+ uniqueAssignmentList.add(assignment);
+ Iterator iterator = uniqueAssignmentList.iterator();
+ assertTrue(iterator.hasNext());
+ assertEquals(assignment, iterator.next());
+ }
+
+
+}
diff --git a/src/test/java/seedu/address/model/lesson/DateTest.java b/src/test/java/seedu/address/model/lesson/DateTest.java
new file mode 100644
index 00000000000..36dcdd6cc46
--- /dev/null
+++ b/src/test/java/seedu/address/model/lesson/DateTest.java
@@ -0,0 +1,54 @@
+package seedu.address.model.lesson;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.datetimeutil.Date;
+
+
+class DateTest {
+
+ @Test
+ void isValidDate() {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d-M-yyyy");
+
+ assertFalse(Date.isValidDate("12/07/2021")); // use of / is not allowed, only -
+ assertFalse(Date.isValidDate("14-5-20")); // year must be 4 digits
+ assertFalse(Date.isValidDate("12-07-2021")); // date cannot be in the past
+
+ LocalDate now = LocalDate.now();
+ assertFalse(Date.isValidDate(now.format(formatter))); // date cannot be today
+
+ now = now.plusDays(1);
+ assertTrue(Date.isValidDate(now.format(formatter))); //date is valid if its in the future
+
+ }
+
+ @Test
+ void testToString() {
+ assertEquals("10 Dec 2027", new Date("10-12-2027").toString()); //no change in the assignmentName
+ }
+
+ @Test
+ void testEquals() {
+ Date date = new Date("05-10-2026");
+
+ //null object -> return false
+ assertFalse(date.equals(null));
+
+ //different date -> return false
+ assertFalse(date.equals(new Date("05-11-2026")));
+
+ //same object -> return true
+ assertTrue(date.equals(date));
+
+ //different types -> return false
+ assertFalse(date.equals(5));
+ }
+}
diff --git a/src/test/java/seedu/address/model/lesson/LessonTest.java b/src/test/java/seedu/address/model/lesson/LessonTest.java
new file mode 100644
index 00000000000..6dfb14dc0cc
--- /dev/null
+++ b/src/test/java/seedu/address/model/lesson/LessonTest.java
@@ -0,0 +1,116 @@
+package seedu.address.model.lesson;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_DATE_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TIME_BOB;
+import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalLessons.ALICE;
+import static seedu.address.testutil.TypicalLessons.BENNY;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.testutil.LessonBuilder;
+
+public class LessonTest {
+ private static final String FUTURE_DATE_1 = "01-02-2030";
+ private static final String FUTURE_DATE_2 = "02-02-2030";
+ @Test
+ public void constructor_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new Lesson(null, null, null, null));
+ }
+
+ @Test
+ public void isConflict_sameDateAndTime_returnsTrue() {
+ Lesson lesson = new LessonBuilder().withDate(FUTURE_DATE_1).withTime("10:00").build();
+ Lesson conflictingLesson = new LessonBuilder().withDate(FUTURE_DATE_1).withTime("10:00").build();
+ assertTrue(lesson.isConflict(conflictingLesson));
+ }
+
+ @Test
+ public void isConflict_differentDate_returnsFalse() {
+ Lesson lesson = new LessonBuilder().withDate(FUTURE_DATE_1).withTime("10:00").build();
+ Lesson nonConflictingLesson = new LessonBuilder().withDate(FUTURE_DATE_2).withTime("10:00").build();
+ assertFalse(lesson.isConflict(nonConflictingLesson));
+ }
+
+ @Test
+ public void isConflict_differentTime_returnsFalse() {
+ Lesson lesson = new LessonBuilder().withDate(FUTURE_DATE_1).withTime("10:00").build();
+ Lesson nonConflictingLesson = new LessonBuilder().withDate(FUTURE_DATE_1).withTime("11:00").build();
+ assertFalse(lesson.isConflict(nonConflictingLesson));
+ }
+
+ @Test
+ public void equals_sameObject_returnsTrue() {
+ assertTrue(ALICE.equals(ALICE));
+ }
+
+ @Test
+ public void equals_null_returnsFalse() {
+ assertFalse(ALICE.equals(null));
+ }
+
+ @Test
+ public void equals_differentType_returnsFalse() {
+ assertFalse(ALICE.equals(5));
+ }
+
+ @Test
+ public void equals_differentLesson_returnsFalse() {
+ assertFalse(ALICE.equals(BENNY));
+ }
+
+ @Test
+ public void equals_sameValues_returnsTrue() {
+ Lesson aliceCopy = new LessonBuilder(ALICE).build();
+ assertTrue(ALICE.equals(aliceCopy));
+ }
+
+ @Test
+ public void hashCode_sameValues_returnsSameHashCode() {
+ Lesson aliceCopy = new LessonBuilder(ALICE).build();
+ assertEquals(ALICE.hashCode(), aliceCopy.hashCode());
+ }
+
+ @Test
+ public void equals() {
+ // same values -> returns true
+ Lesson aliceCopy = new LessonBuilder(ALICE).build();
+ assertTrue(ALICE.equals(aliceCopy));
+
+ // same object -> returns true
+ assertTrue(ALICE.equals(ALICE));
+
+ // null -> returns false
+ assertFalse(ALICE.equals(null));
+
+ // different type -> returns false
+ assertFalse(ALICE.equals(5));
+
+ // different lesson -> returns false
+ assertFalse(ALICE.equals(BENNY));
+
+ // different name -> returns false
+ Lesson editedAlice = new LessonBuilder(ALICE).withName(VALID_NAME_BOB).build();
+ assertFalse(ALICE.equals(editedAlice));
+
+ // different date -> returns false
+ editedAlice = new LessonBuilder(ALICE).withDate(VALID_DATE_BOB).build();
+ assertFalse(ALICE.equals(editedAlice));
+
+ // different time -> returns false
+ editedAlice = new LessonBuilder(ALICE).withTime(VALID_TIME_BOB).build();
+ assertFalse(ALICE.equals(editedAlice));
+ }
+
+ @Test
+ public void toStringMethod() {
+ String expected = Lesson.class.getCanonicalName() + "{subject=" + ALICE.getSubject()
+ + ", name=" + ALICE.getStudentName() + ", date=" + ALICE.getDate()
+ + ", time=" + ALICE.getTime() + "}";
+ assertEquals(expected, ALICE.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/model/lesson/StudentNameLessonPredicateTest.java b/src/test/java/seedu/address/model/lesson/StudentNameLessonPredicateTest.java
new file mode 100644
index 00000000000..b32f9ea1fc8
--- /dev/null
+++ b/src/test/java/seedu/address/model/lesson/StudentNameLessonPredicateTest.java
@@ -0,0 +1,72 @@
+package seedu.address.model.lesson;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.student.Name;
+import seedu.address.testutil.LessonBuilder;
+
+public class StudentNameLessonPredicateTest {
+
+ @Test
+ public void test_nameMatches_returnsTrue() {
+ StudentNameLessonPredicate predicate = new StudentNameLessonPredicate(new Name("Alice"));
+ Lesson lesson = new LessonBuilder().withName("Alice").build();
+ assertTrue(predicate.test(lesson));
+ }
+
+ @Test
+ public void test_nameDoesNotMatch_returnsFalse() {
+ StudentNameLessonPredicate predicate = new StudentNameLessonPredicate(new Name("Alice"));
+ Lesson lesson = new LessonBuilder().withName("Bob").build();
+ assertFalse(predicate.test(lesson));
+ }
+
+ @Test
+ public void test_namePartiallyMatches_returnsTrue() {
+ StudentNameLessonPredicate predicate = new StudentNameLessonPredicate(new Name("Ali"));
+ Lesson lesson = new LessonBuilder().withName("Alice").build();
+ assertTrue(predicate.test(lesson));
+ }
+
+ @Test
+ public void test_nameCaseInsensitiveMatch_returnsTrue() {
+ StudentNameLessonPredicate predicate = new StudentNameLessonPredicate(new Name("alice"));
+ Lesson lesson = new LessonBuilder().withName("Alice").build();
+ assertTrue(predicate.test(lesson));
+ }
+
+ @Test
+ public void equals_sameObject_returnsTrue() {
+ StudentNameLessonPredicate predicate = new StudentNameLessonPredicate(new Name("Alice"));
+ assertTrue(predicate.equals(predicate));
+ }
+
+ @Test
+ public void equals_null_returnsFalse() {
+ StudentNameLessonPredicate predicate = new StudentNameLessonPredicate(new Name("Alice"));
+ assertFalse(predicate.equals(null));
+ }
+
+ @Test
+ public void equals_differentType_returnsFalse() {
+ StudentNameLessonPredicate predicate = new StudentNameLessonPredicate(new Name("Alice"));
+ assertFalse(predicate.equals(5));
+ }
+
+ @Test
+ public void equals_differentName_returnsFalse() {
+ StudentNameLessonPredicate predicate1 = new StudentNameLessonPredicate(new Name("Alice"));
+ StudentNameLessonPredicate predicate2 = new StudentNameLessonPredicate(new Name("Bob"));
+ assertFalse(predicate1.equals(predicate2));
+ }
+
+ @Test
+ public void equals_sameName_returnsTrue() {
+ StudentNameLessonPredicate predicate1 = new StudentNameLessonPredicate(new Name("Alice"));
+ StudentNameLessonPredicate predicate2 = new StudentNameLessonPredicate(new Name("Alice"));
+ assertTrue(predicate1.equals(predicate2));
+ }
+}
diff --git a/src/test/java/seedu/address/model/lesson/TimeTest.java b/src/test/java/seedu/address/model/lesson/TimeTest.java
new file mode 100644
index 00000000000..576a9b648a6
--- /dev/null
+++ b/src/test/java/seedu/address/model/lesson/TimeTest.java
@@ -0,0 +1,45 @@
+package seedu.address.model.lesson;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.datetimeutil.Time;
+
+class TimeTest {
+
+ @Test
+ void isValidTime() {
+ assertFalse(Time.isValidTime("12.08")); // use of . is not allowed, only :
+ assertTrue(Time.isValidTime("7:00"));
+ assertTrue(Time.isValidTime("21:02"));
+
+ assertTrue(Time.isValidTime("20:00")); //valid and within hours
+ assertTrue(Time.isValidTime("21:00"));
+ assertTrue(Time.isValidTime("8:00"));
+ }
+
+ @Test
+ void testToString() {
+ assertEquals("10:15", new Time("10:15").toString()); //no change in the assignmentName
+ }
+
+ @Test
+ void testEquals() {
+ Time time = new Time("16:00");
+
+ //null object -> return false
+ assertFalse(time.equals(null));
+
+ //different date -> return false
+ assertFalse(time.equals(new Time("17:00")));
+
+ //same object -> return true
+ assertTrue(time.equals(time));
+
+ //different types -> return false
+ assertFalse(time.equals(7));
+ }
+}
diff --git a/src/test/java/seedu/address/model/lesson/UniqueLessonListTest.java b/src/test/java/seedu/address/model/lesson/UniqueLessonListTest.java
new file mode 100644
index 00000000000..d03f4096705
--- /dev/null
+++ b/src/test/java/seedu/address/model/lesson/UniqueLessonListTest.java
@@ -0,0 +1,116 @@
+package seedu.address.model.lesson;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalLessons.ALICE;
+
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.lesson.exceptions.DuplicateLessonException;
+import seedu.address.model.lesson.exceptions.LessonNotFoundException;
+import seedu.address.testutil.LessonBuilder;
+
+public class UniqueLessonListTest {
+
+ private final UniqueLessonList uniqueLessonList = new UniqueLessonList();
+
+ @Test
+ public void contains_nullLesson_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueLessonList.contains(null));
+ }
+
+ @Test
+ public void contains_lessonNotInList_returnsFalse() {
+ assertFalse(uniqueLessonList.contains(ALICE));
+ }
+
+ @Test
+ public void contains_lessonInList_returnsTrue() {
+ uniqueLessonList.add(ALICE);
+ assertTrue(uniqueLessonList.contains(ALICE));
+ }
+
+ @Test
+ public void contains_lessonWithSameIdentityFieldsInList_returnsTrue() {
+ uniqueLessonList.add(ALICE);
+ Lesson editedAlice = new LessonBuilder(ALICE).build();
+ assertTrue(uniqueLessonList.contains(editedAlice));
+ }
+
+ @Test
+ public void add_nullLesson_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueLessonList.add(null));
+ }
+
+ @Test
+ public void add_duplicateLesson_throwsDuplicateLessonException() {
+ uniqueLessonList.add(ALICE);
+ assertThrows(DuplicateLessonException.class, () -> uniqueLessonList.add(ALICE));
+ }
+
+ @Test
+ public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() {
+ assertThrows(UnsupportedOperationException.class, ()
+ -> uniqueLessonList.asUnmodifiableObservableList().remove(0));
+ }
+
+ @Test
+ public void clashes_conflictingLesson_returnsTrue() {
+ uniqueLessonList.add(ALICE);
+ Lesson conflicting = new LessonBuilder(ALICE).withTime(ALICE.getTime().toString()).build();
+ assertTrue(uniqueLessonList.clashes(conflicting));
+ }
+
+ @Test
+ public void clashes_nonConflictingLesson_returnsFalse() {
+ uniqueLessonList.add(ALICE);
+ Lesson nonConflicting = new LessonBuilder(ALICE).withTime("16:00").build(); // assume 16:00 doesn't clash
+ assertFalse(uniqueLessonList.clashes(nonConflicting));
+ }
+
+ @Test
+ public void remove_existingLesson_success() {
+ uniqueLessonList.add(ALICE);
+ uniqueLessonList.remove(ALICE);
+ assertFalse(uniqueLessonList.contains(ALICE));
+ }
+
+ @Test
+ public void remove_lessonNotInList_throwsLessonNotFoundException() {
+ Lesson notInList = new LessonBuilder().build();
+ assertThrows(LessonNotFoundException.class, () -> uniqueLessonList.remove(notInList));
+ }
+
+ @Test
+ public void setLessons_withDuplicateLessons_throwsDuplicateLessonException() {
+ Lesson lesson1 = new LessonBuilder().build();
+ List duplicateLessons = List.of(lesson1, lesson1);
+ assertThrows(DuplicateLessonException.class, () -> uniqueLessonList.setLessons(duplicateLessons));
+ }
+
+ @Test
+ public void setLessons_validList_replacesList() {
+ Lesson lesson1 = new LessonBuilder().build();
+ List newLessons = List.of(lesson1);
+ uniqueLessonList.setLessons(newLessons);
+ assertTrue(uniqueLessonList.contains(lesson1));
+ }
+
+
+ @Test
+ public void hashCode_consistentWithEquals() {
+ UniqueLessonList anotherList = new UniqueLessonList();
+ uniqueLessonList.add(ALICE);
+ anotherList.add(ALICE);
+ assertEquals(uniqueLessonList.hashCode(), anotherList.hashCode());
+ }
+
+ @Test
+ public void toStringMethod() {
+ assertEquals(uniqueLessonList.asUnmodifiableObservableList().toString(), uniqueLessonList.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/PersonTest.java b/src/test/java/seedu/address/model/person/PersonTest.java
deleted file mode 100644
index 31a10d156c9..00000000000
--- a/src/test/java/seedu/address/model/person/PersonTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package seedu.address.model.person;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
-import static seedu.address.testutil.Assert.assertThrows;
-import static seedu.address.testutil.TypicalPersons.ALICE;
-import static seedu.address.testutil.TypicalPersons.BOB;
-
-import org.junit.jupiter.api.Test;
-
-import seedu.address.testutil.PersonBuilder;
-
-public class PersonTest {
-
- @Test
- public void asObservableList_modifyList_throwsUnsupportedOperationException() {
- Person person = new PersonBuilder().build();
- assertThrows(UnsupportedOperationException.class, () -> person.getTags().remove(0));
- }
-
- @Test
- public void isSamePerson() {
- // same object -> returns true
- assertTrue(ALICE.isSamePerson(ALICE));
-
- // null -> returns false
- assertFalse(ALICE.isSamePerson(null));
-
- // same name, all other attributes different -> returns true
- Person editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB)
- .withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND).build();
- assertTrue(ALICE.isSamePerson(editedAlice));
-
- // different name, all other attributes same -> returns false
- editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build();
- assertFalse(ALICE.isSamePerson(editedAlice));
-
- // name differs in case, all other attributes same -> returns false
- Person editedBob = new PersonBuilder(BOB).withName(VALID_NAME_BOB.toLowerCase()).build();
- assertFalse(BOB.isSamePerson(editedBob));
-
- // name has trailing spaces, all other attributes same -> returns false
- String nameWithTrailingSpaces = VALID_NAME_BOB + " ";
- editedBob = new PersonBuilder(BOB).withName(nameWithTrailingSpaces).build();
- assertFalse(BOB.isSamePerson(editedBob));
- }
-
- @Test
- public void equals() {
- // same values -> returns true
- Person aliceCopy = new PersonBuilder(ALICE).build();
- assertTrue(ALICE.equals(aliceCopy));
-
- // same object -> returns true
- assertTrue(ALICE.equals(ALICE));
-
- // null -> returns false
- assertFalse(ALICE.equals(null));
-
- // different type -> returns false
- assertFalse(ALICE.equals(5));
-
- // different person -> returns false
- assertFalse(ALICE.equals(BOB));
-
- // different name -> returns false
- Person editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build();
- assertFalse(ALICE.equals(editedAlice));
-
- // different phone -> returns false
- editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).build();
- assertFalse(ALICE.equals(editedAlice));
-
- // different email -> returns false
- editedAlice = new PersonBuilder(ALICE).withEmail(VALID_EMAIL_BOB).build();
- assertFalse(ALICE.equals(editedAlice));
-
- // different address -> returns false
- editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).build();
- assertFalse(ALICE.equals(editedAlice));
-
- // different tags -> returns false
- editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND).build();
- assertFalse(ALICE.equals(editedAlice));
- }
-
- @Test
- public void toStringMethod() {
- String expected = Person.class.getCanonicalName() + "{name=" + ALICE.getName() + ", phone=" + ALICE.getPhone()
- + ", email=" + ALICE.getEmail() + ", address=" + ALICE.getAddress() + ", tags=" + ALICE.getTags() + "}";
- assertEquals(expected, ALICE.toString());
- }
-}
diff --git a/src/test/java/seedu/address/model/person/UniquePersonListTest.java b/src/test/java/seedu/address/model/person/UniquePersonListTest.java
deleted file mode 100644
index 17ae501df08..00000000000
--- a/src/test/java/seedu/address/model/person/UniquePersonListTest.java
+++ /dev/null
@@ -1,175 +0,0 @@
-package seedu.address.model.person;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
-import static seedu.address.testutil.Assert.assertThrows;
-import static seedu.address.testutil.TypicalPersons.ALICE;
-import static seedu.address.testutil.TypicalPersons.BOB;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import org.junit.jupiter.api.Test;
-
-import seedu.address.model.person.exceptions.DuplicatePersonException;
-import seedu.address.model.person.exceptions.PersonNotFoundException;
-import seedu.address.testutil.PersonBuilder;
-
-public class UniquePersonListTest {
-
- private final UniquePersonList uniquePersonList = new UniquePersonList();
-
- @Test
- public void contains_nullPerson_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> uniquePersonList.contains(null));
- }
-
- @Test
- public void contains_personNotInList_returnsFalse() {
- assertFalse(uniquePersonList.contains(ALICE));
- }
-
- @Test
- public void contains_personInList_returnsTrue() {
- uniquePersonList.add(ALICE);
- assertTrue(uniquePersonList.contains(ALICE));
- }
-
- @Test
- public void contains_personWithSameIdentityFieldsInList_returnsTrue() {
- uniquePersonList.add(ALICE);
- Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND)
- .build();
- assertTrue(uniquePersonList.contains(editedAlice));
- }
-
- @Test
- public void add_nullPerson_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> uniquePersonList.add(null));
- }
-
- @Test
- public void add_duplicatePerson_throwsDuplicatePersonException() {
- uniquePersonList.add(ALICE);
- assertThrows(DuplicatePersonException.class, () -> uniquePersonList.add(ALICE));
- }
-
- @Test
- public void setPerson_nullTargetPerson_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> uniquePersonList.setPerson(null, ALICE));
- }
-
- @Test
- public void setPerson_nullEditedPerson_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> uniquePersonList.setPerson(ALICE, null));
- }
-
- @Test
- public void setPerson_targetPersonNotInList_throwsPersonNotFoundException() {
- assertThrows(PersonNotFoundException.class, () -> uniquePersonList.setPerson(ALICE, ALICE));
- }
-
- @Test
- public void setPerson_editedPersonIsSamePerson_success() {
- uniquePersonList.add(ALICE);
- uniquePersonList.setPerson(ALICE, ALICE);
- UniquePersonList expectedUniquePersonList = new UniquePersonList();
- expectedUniquePersonList.add(ALICE);
- assertEquals(expectedUniquePersonList, uniquePersonList);
- }
-
- @Test
- public void setPerson_editedPersonHasSameIdentity_success() {
- uniquePersonList.add(ALICE);
- Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND)
- .build();
- uniquePersonList.setPerson(ALICE, editedAlice);
- UniquePersonList expectedUniquePersonList = new UniquePersonList();
- expectedUniquePersonList.add(editedAlice);
- assertEquals(expectedUniquePersonList, uniquePersonList);
- }
-
- @Test
- public void setPerson_editedPersonHasDifferentIdentity_success() {
- uniquePersonList.add(ALICE);
- uniquePersonList.setPerson(ALICE, BOB);
- UniquePersonList expectedUniquePersonList = new UniquePersonList();
- expectedUniquePersonList.add(BOB);
- assertEquals(expectedUniquePersonList, uniquePersonList);
- }
-
- @Test
- public void setPerson_editedPersonHasNonUniqueIdentity_throwsDuplicatePersonException() {
- uniquePersonList.add(ALICE);
- uniquePersonList.add(BOB);
- assertThrows(DuplicatePersonException.class, () -> uniquePersonList.setPerson(ALICE, BOB));
- }
-
- @Test
- public void remove_nullPerson_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> uniquePersonList.remove(null));
- }
-
- @Test
- public void remove_personDoesNotExist_throwsPersonNotFoundException() {
- assertThrows(PersonNotFoundException.class, () -> uniquePersonList.remove(ALICE));
- }
-
- @Test
- public void remove_existingPerson_removesPerson() {
- uniquePersonList.add(ALICE);
- uniquePersonList.remove(ALICE);
- UniquePersonList expectedUniquePersonList = new UniquePersonList();
- assertEquals(expectedUniquePersonList, uniquePersonList);
- }
-
- @Test
- public void setPersons_nullUniquePersonList_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((UniquePersonList) null));
- }
-
- @Test
- public void setPersons_uniquePersonList_replacesOwnListWithProvidedUniquePersonList() {
- uniquePersonList.add(ALICE);
- UniquePersonList expectedUniquePersonList = new UniquePersonList();
- expectedUniquePersonList.add(BOB);
- uniquePersonList.setPersons(expectedUniquePersonList);
- assertEquals(expectedUniquePersonList, uniquePersonList);
- }
-
- @Test
- public void setPersons_nullList_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((List) null));
- }
-
- @Test
- public void setPersons_list_replacesOwnListWithProvidedList() {
- uniquePersonList.add(ALICE);
- List personList = Collections.singletonList(BOB);
- uniquePersonList.setPersons(personList);
- UniquePersonList expectedUniquePersonList = new UniquePersonList();
- expectedUniquePersonList.add(BOB);
- assertEquals(expectedUniquePersonList, uniquePersonList);
- }
-
- @Test
- public void setPersons_listWithDuplicatePersons_throwsDuplicatePersonException() {
- List