diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
index 6ff220b5196..ca85dcec3c1 100644
--- a/.github/workflows/gradle.yml
+++ b/.github/workflows/gradle.yml
@@ -22,7 +22,7 @@ jobs:
run: git checkout --progress --force ${{ github.sha }}
- name: Run repository-wide tests
- if: runner.os == 'Linux'
+ if: runner.os == 'macOS'
working-directory: ${{ github.workspace }}/.github
run: ./run-checks.sh
@@ -39,7 +39,7 @@ jobs:
run: ./gradlew check coverage
- name: Upload coverage reports to Codecov
- if: runner.os == 'Linux'
+ if: runner.os == 'macOS'
uses: codecov/codecov-action@v3
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
diff --git a/README.md b/README.md
index 13f5c77403f..84e17b6bb15 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,26 @@
-[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions)
+[![CI Status](https://github.com/AY2324S1-CS2103T-F12-2/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2324S1-CS2103T-F12-2/tp/actions)
![Ui](docs/images/Ui.png)
-* This is **a sample project for Software Engineering (SE) students**.
- Example usages:
- * as a starting point of a course project (as opposed to writing everything from scratch)
- * as a case study
-* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details.
- * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big.
- * It comes with a **reasonable level of user and developer documentation**.
-* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...).
-* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**.
-* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info.
+### StudentConnect
+
+* This project was created for the **CS2101 Effective Communication for Computing Professionals and CS2103T Software Engineering modules**.
+* It is named `StudentConnect` because it was created to connect students in CS2103T and help with the formation of teams.
+* For the detailed documentation of this project, see the **[StudentConnect GitHub Website](https://github.com/AY2324S1-CS2103T-F12-2/tp)** and **[StudentConnect User Guide](https://ay2324s1-cs2103t-f12-2.github.io/tp/UserGuide.html)**.
+* Value proposition:
+ * `StudentConnect` helps with the **formation of project teams for CS2101/CS2103T**
+ * `StudentConnect` **stores** and **organises students’ contact details, and project group**
+ * Students can **easily search for the profiles of other students** and **connect with potential teammates**
+ * It offers tools for **tracking project progress/deadlines**, ensuring that the group stays on task
+ * There is no other application like `StudentConnect` for **CS2101/CS2103T students**
+
+### Other Links
+* StudentConnect [Main Website](https://ay2324s1-cs2103t-f12-2.github.io/tp/)
+* StudentConnect [User Guide](https://ay2324s1-cs2103t-f12-2.github.io/tp/UserGuide.html)
+* StudentConnect [Developer Guide](https://ay2324s1-cs2103t-f12-2.github.io/tp/DeveloperGuide.html)
+* StudentConnect [About Us Website](https://ay2324s1-cs2103t-f12-2.github.io/tp/AboutUs.html)
+* StudentConnect [Github Website](https://github.com/AY2324S1-CS2103T-F12-2/tp)
+
+### Acknowledgements
+This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org).
+
diff --git a/build.gradle b/build.gradle
index a2951cc709e..73b0640b2ad 100644
--- a/build.gradle
+++ b/build.gradle
@@ -20,7 +20,19 @@ checkstyle {
toolVersion = '10.2'
}
+run {
+ enableAssertions = true
+}
+
test {
+ doFirst {
+ if (System.getProperty("os.name").equals("Linux")) {
+ // FX tests do not successfully run on Ubuntu Runner
+ exclude '**/ui/**'
+ // Excluded because ClearCommand uses FX as well
+ exclude '**/ClearCommandTest.class'
+ }
+ }
useJUnitPlatform()
finalizedBy jacocoTestReport
}
@@ -56,17 +68,26 @@ dependencies {
implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'win'
implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'mac'
implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'linux'
+ implementation group: 'org.openjfx', name: 'javafx-swing', version: javaFxVersion, classifier: 'win'
+ implementation group: 'org.openjfx', name: 'javafx-swing', version: javaFxVersion, classifier: 'mac'
+ implementation group: 'org.openjfx', name: 'javafx-swing', version: javaFxVersion, classifier: 'linux'
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.7.0'
implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.7.4'
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: jUnitVersion
+ testImplementation 'org.testfx:testfx-core:4.0.16-alpha'
+ testImplementation 'org.testfx:testfx-junit5:4.0.16-alpha'
testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: jUnitVersion
}
+run {
+ enableAssertions = true
+}
+
shadowJar {
- archiveFileName = 'addressbook.jar'
+ archiveFileName = 'studentconnect.jar'
}
defaultTasks 'clean', 'test'
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 1c9514e966a..528c472c056 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -5,55 +5,55 @@ title: About Us
We are a team based in the [School of Computing, National University of Singapore](http://www.comp.nus.edu.sg).
-You can reach us at the email `seer[at]comp.nus.edu.sg`
+You can reach us from our [GitHub Website](https://github.com/AY2324S1-CS2103T-F12-2/tp).
## Project team
-### John Doe
+### Pearlynn Toh
-
+
-[[homepage](http://www.comp.nus.edu.sg/~damithch)]
-[[github](https://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](https://github.com/PearlynnT)]
+[[portfolio](team/pearlynnt.md)]
-* Role: Project Advisor
+* Role: Team Lead, Developer
-### Jane Doe
+### Yik Leong Loo
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](https://github.com/Bearypop)]
+[[portfolio](team/bearypop.md)]
-* Role: Team Lead
-* Responsibilities: UI
+* Role: Developer
+* Responsibilities: Testing + Integration
-### Johnny Doe
+### Chan Wei Ning
-
+
-[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)]
+[[github](https://github.com/wnchan)]
+[[portfolio](team/wnchan.md)]
* Role: Developer
-* Responsibilities: Data
+* Responsibilities: Documentation
-### Jean Doe
+### Alnaseri, Majedah Talal M
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](https://github.com/maj0-0)]
+[[portfolio](team/maj0-0.md)]
* Role: Developer
-* Responsibilities: Dev Ops + Threading
+* Responsibilities: Deliverables & Deadlines + Scheduling & Tracking
-### James Doe
+### Dewangan Neya Praveen
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/neyapraveen)]
+[[portfolio](team/neyapraveen.md)]
* Role: Developer
-* Responsibilities: UI
+* Responsibilities: Code Quality
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 8a861859bfd..eb508ed1594 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -9,135 +9,163 @@ title: Developer Guide
## **Acknowledgements**
-* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+This project is based on the AddressBook-Level3 project created by the [**SE-EDU initiative**](https://se-education.org).
--------------------------------------------------------------------------------------------------------------------
## **Setting up, getting started**
-Refer to the guide [_Setting up and getting started_](SettingUp.md).
+Refer to the guide [**_Setting up and getting started_**](SettingUp.md).
--------------------------------------------------------------------------------------------------------------------
+
-:bulb: **Tip:** The `.puml` files used to create diagrams in this document `docs/diagrams` folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams.
+:bulb: **Tip:** The `.puml` files used to create diagrams in this document `docs/diagrams` folder. Refer to the
+[_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams.
+:exclamation: **Important:**
+StudentConnect provides a set of general commands to make it more convenient for you to navigate the app.
+
+
+:exclamation: **Important:**
+StudentConnect provides a set of student information to make it more convenient for students to connect with others.
+
-### Adding a person: `add`
+:bulb: **Tip:**
+Including social media links and tutorial groups are optional.
+
:bulb: **Tip:**
-A person can have any number of tags (including 0)
+A student can include multiple tutorial groups they are interested in. Add multiple tutorial groups by using `t/` repeatedly.
-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`
+:bulb: **Tip:**
+A student can have any number of social media links. Add multiple social media links by using `sm/` repeatedly.
+
+
+#### Examples:
+* `add n/John Doe m/Computer Science y/2 e/johnd@u.nus.edu d/I’m a Frontend Developer t/06 t/19 sm/https://www.linkedin.com/in/john-doe-123456789 nt/local g/m`
+* `add n/Betsy Crowe m/Computer Science y/2 e/betsycrowe@u.nus.edu d/I’m adept at Backend technologies t/05 nt/foreigner g/f`
+
+#### Acceptable Values:
+* Name: Full names with alphabetical characters. Maximum 30 characters.
+* Major: Valid major names at NUS. View the :bulb: **Tip:**
+A student can edit to include multiple tutorial groups they are interested in. Add multiple tutorial groups by using `t/` repeatedly.
+
+
+:bulb: **Tip:**
+A student can edit to have any number of social media links. Add multiple social media links by using `sm/` repeatedly.
+
-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:
+* `edit johnd@u.nus.edu y/3 e/johndoe@u.nus.edu` Edits the year and email address of the student with the email `johnd@u.nus.edu` to be `3` and `johndoe@u.nus.edu` respectively.
+* `edit betsycrowe@u.nus.edu n/Betsy Crower sm/` Edits the name of the student with the email `betsycrowe@u.nus.edu` to be `Betsy Crower` and clears all existing social media.
-### Locating persons by name: `find`
+#### Acceptable Values:
+* EMAIL: a previously registered email address ending in `@u.nus.edu`.
-Finds persons whose names contain any of the given keywords.
+#### Expected Output (Success):
+* GUI: Student details updated in the student list.
+* Message: `Details edited successfully! Edited Student: [Updated data]`
+
+![sample result for 'edit'](images/edit.png)
-Format: `find KEYWORD [MORE_KEYWORDS]`
+#### Expected Output (Failure):
+* Case: No fields provided for edit.: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.
+* Deletes the student with the specified `EMAIL`.
+* The email must be registered in the system.
+
+#### Examples:
+* `list` followed by `delete alexy@u.nus.edu` deletes Alex Yeo from the system.
+
+#### Acceptable Values:
+* EMAIL: a previously registered email address ending in `u.nus.edu`.
+
+#### Expected Output (Success):
+* GUI: Student details removed from student list.
+* Message: `Student deleted successfully! [Deleted student's details]`
+
+![Delete feature](images/delete.png)
+
+#### Expected Output (Failure):
+* Case: Provided email not registered in system.
+ Message: `Student with the provided email not found.`
+* Case: Invalid command format e.g. `delete 02`.
+ Message: `Invalid command format!`
+ `delete: Deletes the student identified by the email address.`
+ `Parameters: EMAIL`
+ `Example: delete alexyeoh@u.nus.edu`
+
+>
Back to Table of Contents
+
+
+
+## Group Commands
+
+
:exclamation: **Important:**
+StudentConnect provides a set of group formation features to make it more convenient for students to find group mates. A group consists of 3 main components: a group number, a tutorial number and the group members.
+
**Group number:** Used to uniquely identify the group.
+
**Tutorial number:** This number is set by the creator of the group and serves as an indication of which tutorial class the members of the group are interested in enrolling in. It **does not** restrict students who do not have a matching tutorial number in their profile from joining the group. Running the
checkGroup command displays a warning if there are members whose tutorial numbers do not match the group's tutorial number.
+
**Group members:** The students who are members of this group.
-### Archiving data files `[coming in v2.0]`
+
+
+### Creating a New Group : `create`
+
+Creates a new empty group with the given tutorial number. The group number is automatically assigned and is used to uniquely identify each group. The tutorial number serves as an indication of which tutorial class the members of the group are interested in enrolling in. This can provide information about the tutorial preferences of the group members, to other students who are looking for a group to join.
+
+#### Format: `create t/TUTORIAL`
+
+#### Expected Output (Success):
+* GUI: A new empty group, with a group number, is created.
+* Message: `Group created successfully! Group number is [GROUP_NUMBER]`
+
+![Sample result for create](images/create.png)
+
+#### Expected Output (Failure):
+* Case: Invalid command format, e.g. `create`, `create 02`, etc.
+Message: `Invalid command format!`
+ `create: Creates a new empty group.`
+ `Parameters: t/TUTORIAL Example: create t/02`
+* Case: Invalid tutorial number is provided, e.g. `create t/0`, `create t/25`, etc.
+ Message: `Tutorials should be 2-digit numbers between 01 and 22.`
+
+
+
+### Listing All Groups : `listGroup`
+
+Displays a list of all groups. For each group, the group number, and the names and emails of the members are shown.
+
+#### Format: `listGroup`
+
+#### Expected Output (Success):
+* GUI: A list of all groups that are in the system is shown.
+* Message: `Viewing all groups`
+
+![Sample result for listGroup](images/listGroup.png)
+
+#### Expected Output (Failure):
+* Message: `Error: Unable to retrieve group entries. Please try again.`
+
+
+
+### Deleting a Group : `deleteGroup`
+
+Deletes a group from the system, based on group number.
+
+#### Format: `deleteGroup gr/GROUP_NUMBER`
+
+#### Examples:
+* `deleteGroup gr/2` deletes Group 2 from the system.
+* `deleteGroup gr/5` deletes Group 5 from the system.
+
+#### Acceptable Values:
+* GROUP_NUMBER: Must be a non-zero unsigned integer.
+
+
+
+#### Expected Output (Success):
+* GUI: Specified group is no longer visible.
+* Message: `Group deleted successfully! Deleted Group: [GROUP_NUMBER]`
+
+![result for 'deleteGroup gr/3'](images/deleteGroup.png)
+
+#### Expected Output (Failure):
+* Case: Group with specified number is not in the system.
+ Message: `Group with the provided group number not found.`
+
+
+
+### Joining a Group : `join`
+
+Adds a student to the specified group.
+
+#### Format: `join e/EMAIL gr/GROUP_NUMBER`
+
+#### Expected Output (Success):
+* GUI: The student's name and email are displayed in the specified group's card.
+* Message: `Join successful! [NAME] has joined Group [GROUP_NUMBER]!`
+
+#### Expected Output (Failure):
+* Case: Email not found in the system.
+Message: `Student with the provided email not found.`
+* Case: Group number not found in the system.
+Message: `Group with the provided group number not found.`
+* Case: Student has been added in the group already.
+Message: `The provided student is already a member of the provided group.`
+* Case: Group has 5 members and is full.
+Message: `Join failed as the group already has 5 members.`
+* Case: Student is found in another group already.
+Message: `The provided student is already in another group.`
+
+![Sample result for join](images/join.png)
+
+
+
+### Leaving a Specific Group : `leave`
+
+Deletes a member from a specific group, indicating that they have left.
+
+#### Format: `leave e/EMAIL gr/GROUP_NUMBER`
+
+* Removes student from specified group.
+
+
+
+#### Examples:
+* `leave e/johnd@u.nus.edu gr/1` Removes member with email `johnd@u.nus.edu` from Group 1.
+* `leave e/bettyc@u.nus.edu gr/11` Removes member with email `bettyc@u.nus.edu` from Group 11.
+
+#### Acceptable Values:
+* GROUP_NUMBER: Must be a non-zero unsigned integer.
+* EMAIL: Must be a valid NUS email registered in the system.
+
+#### Expected Output (Success):
+* GUI: Student details removed from specified group.
+* Message: `Leave successful! NAME has left group 1!`
+
+![sample result for 'leave'](images/leave.png)
+
+#### Expected Output (Failure):
+* Case: Email not found in the system.
+Message: `Person with the provided email not found.`
+* Case: Group number not found in the system.
+Message: `Group with the provided group number not found.`
+* Case: Student is not a member of the provided group.
+Message: `The above student is not a member of the provided group.`
+
+
+
+### Finding a Group by Group Number : `findGroup`
+
+Finds group(s) with group number(s) that matches any of the given keywords.
+
+#### Format: `findGroup KEYWORD [MORE_KEYWORDS]`
+
+* The order of the keywords does not matter. e.g. `5 12` will match `12 5`.
+* Only the group number is searched.
+* Only the full keywords will be matched. e.g. `1` will not match `12`.
+* Groups matching one keyword will be returned (i.e. `OR` search). e.g. `5 12` will return `5`, `12`.
+* The keyword(s) must be a non-zero unsigned integer.
+
+#### Examples:
+* `findGroup 7` returns Group `7`.
+* `findGroup 7 15` returns Group `7`, Group `15`.
+
+#### Expected Output (Success):
+* GUI: List of all group entries whose group number(s) match the keyword(s) in the system.
+
+![sample result for 'findGroup'](images/findGroup.png)
+
+#### Expected Output (Failure):
+* Case: Invalid command format is provided, e.g. `findGroup`.
+ Message: `Invalid command format!`
+ `findGroup: Finds all groups whose number contain any of the specified keywords and displays them as a list with index numbers.`
+ `Parameters: KEYWORD [MORE_KEYWORDS]...`
+ `Example: findGroup 1 5 10`
+* Case: Invalid keyword(s) is provided, e.g. `findGroup a`, `findGroup 0`, etc.
+ Message: `Group number is not a non-zero unsigned integer.`
+
+
+
+### Filtering Groups by Tutorial : `filterGroup`
+
+Filters the groups by tutorial based on the given slot.
+
+#### Format: `filterGroup SLOT`
+
+* The slot must be 2-digit numbers between `01` and `22` inclusive.
+* Tutorials are only accepted as 2-digits, i.e. `3` is not a valid tutorial, but `03` is.
+* Only the tutorial is searched.
+
+
+
+#### Examples:
+* `filterGroup 03` returns groups that belong to `T03`.
+
+#### Expected Output(Success):
+* GUI: List of all group entries with the tutorial that match the slot in the system.
+
+![sample result for 'filterGroup'](images/filterGroup.png)
-_Details coming soon ..._
+#### Expected Output (Failure):
+* Case: Invalid command format is provided, e.g. `filterGroup`.
+Message: `Invalid command format!`
+ `filterGroup: Filters all groups that belong to the specified tutorial slot (2-digit numbers between 01 and 22) and displays them as a list with index numbers.`
+ `Parameters: SLOT`
+ `Example: filterGroup 01"`
+* Case: Invalid slot is provided, e.g. `filterGroup 0`, `filterGroup 25`, etc.
+Message: `Tutorials should be 2-digit numbers between 01 and 22.`
+
+
+
+
+
+### Checking a Group : `checkGroup`
+
+Checks if a group fulfils the group requirements of the course.
+
+#### Format: `checkGroup GR0UP_NUMBER`
+
+* Checks the group with the specified `GROUP_NUMBER`.
+* The group number must come from a group that has been created in the system.
+* checkGroup does not restrict students from joining a group, instead, it provides helpful alerts to help groups adhere to the criteria set by CS2103T and CS2101.
+
+#### Examples:
+* `checkGroup 4` checks the group with a group number `4` if it is created in the system.
+
+#### Expected Output (Success):
+* Case: Group fulfils the group requirements.
+Message: `Group GROUP_NUMBER`
+ `Group fulfils the diversity requirements of CS2103T.`
+
+![sample result for 'checkGroup'](images/checkGroup.png)
+
+* Case: Group has no members.
+Message: `Group GROUP_NUMBER`
+ `Group does not have any members.`
+ `You can enter the help command for more information on group requirements.`
+* Case: Group has only 1 member.
+Message: `Group GROUP_NUMBER`
+ `Group has only one member.`
+ `You can enter the help command for more information on group requirements.`
+* Case: Group has more than 1 member and does not fulfil the group requirements. Possible messages include:
+Message: `Group GROUP_NUMBER`
+Message: `Group has less than 5 members.`
+Message: `Group size has exceeded limit with more than 5 members.`
+Message: `Group comprises of members of the same nationality.`
+Message: `Group comprises of members of the same gender.`
+Message: `Not every group member's tutorial matches the group's tutorial.`
+Message: `You can enter the help command for more information on group requirements.`
+
+#### Expected Output (Failure):
+* Case: Invalid command format is provided, e.g. `checkGroup`.
+Message: `Invalid command format!`
+ `checkGroup: Checks the group identified by its group number.`
+ `Parameters: GROUP_NUMBER"`
+ `Example: checkGroup 1`
+* Case: Group not found in the system.
+Message: `Group with the provided group number not found.`
+
+
+
+### Listing All Tasks : `tasks`
+
+Lists out all tasks for a specific group.
+
+#### Format: `tasks GROUP_NUMBER`
+
+* Lists out all tasks for the specified group.
+
+#### Examples:
+* `tasks 2` Lists out all tasks for group `2`.
+* `tasks 5` Lists out all tasks for group `5`.
+
+#### Acceptable Values:
+* GROUP_NUMBER: Must be a non-zero unsigned integer.
+
+
+
+#### Expected Output (Success):
+* GUI: List of all tasks for the specified group is displayed, the specified group and group members are shown.
+* Message: `Here are the tasks for group [GROUP_NUMBER]: [list of tasks]`
+
+![sample result for 'tasks'](images/tasks.png)
+
+#### Expected Output (Failure):
+* Case: Invalid tasks command, e.g. `tasks gr/3`, `tasks t`, etc.
+ Message: `Invalid command format!`
+ `tasks: Lists out all tasks for a specific group.`
+ `Parameters: GROUP_NUMBER`
+ `Example: tasks 3`
+* Case: Invalid group number as the group has not yet been created.
+ Message: `Group with the provided group number not found.`
+
+
+
+
+
+### Marking a Task as Done : `mark`
+
+Mark a task for a specified group as done.
+
+#### Format: `mark gr/GROUP_NUMBER ti/TASK_INDEX`
+
+* Marks the task of the specified group as done.
+
+#### Examples:
+* `mark gr/2 ti/1` Marks task `1` of group `2` as done.
+* `mark ti/2 gr/12` Marks task `2` of group `12` as done.
+
+#### Acceptable Values:
+* GROUP_NUMBER: Must be an integer value that is grater than 0 and a group number that is found in the group list.
+* TASK_INDEX: Must be an integer value that is greater than 0 and smaller than task size.
+
+#### Expected Output (Success):
+* GUI: The task in the specified group is marked as done and task list is updated.
+* Message: `Marked task number (ti) for group (gr) [and displays the updated task list]`
+
+![sample result for 'mark'](images/mark.png)
+
+#### Expected Output (Failure):
+* Case: Invalid mark command, e.g. `mark`, `mark t`, etc.
+Message: `Invalid command format! mark: Mark task specified as done. Parameters: gr/GROUP_NUMBER ti/TASK_INDEX Example: mark gr/2 ti/3`
+* Case: Invalid group number, a group number that is not found from the list in `listGroup` command.
+Message: `Group with the provided group number not found.`
+* Case: Invalid group number, e.g. zero or negative numbers.
+Message: `Group number is not a non-zero unsigned integer.`
+* Case: Invalid task index, e.g. zero or negative numbers.
+Message: `Task index must be a positive integer.`
+* Case: Invalid task index, e.g. task index greater than the number of tasks found from the list in `tasks` command.
+Message: `Invalid task index. Task not found.`
+
+
+
+
+
+### Marking a Task as Not Done : `unmark`
+
+Mark a task for a specified group as not done.
+
+#### Format: `unmark gr/GROUP_NUMBER ti/TASK_INDEX`
+
+* Marks the task of the specified group as not done.
+
+#### Examples:
+* `unmark gr/7 ti/3` Marks task `3` of group `7` as not done.
+* `unmark ti/5 gr/9` Marks task `5` of group `9` as not done.
+
+#### Acceptable Values:
+* GROUP_NUMBER: Must be an integer value that is grater than 0 and a group number that is found in the group list.
+* TASK_INDEX: Must be an integer value that is greater than 0 and smaller than task size.
+
+#### Expected Output (Success):
+* GUI: The task in the specified group is marked as not done and task list is updated.
+* Message: `Unmarked task number (ti) for group (gr) [and displays the updated task list]`
+
+![sample result for 'unmark'](images/unmark.png)
+
+#### Expected Output (Failure):
+* Case: Invalid unmark command, e.g. `unmark`, `unmark t`, etc.
+ Message: `Invalid command format! unmark: Mark task specified as not done. Parameters: gr/GROUP_NUMBER ti/TASK_INDEX Example: unmark gr/2 ti/3`
+* Case: Invalid group number, a group number that is not found from the list in `listGroup` command.
+Message: `Group with the provided group number not found.`
+* Case: Invalid group number, e.g. zero or negative numbers.
+Message: `Group number is not a non-zero unsigned integer.`
+* Case: Invalid task index, e.g. zero or negative numbers.
+Message: `Task index must be a positive integer.`
+* Case: Invalid task index, e.g. task index greater than the number of tasks found from the list in `tasks` command.
+Message: `Invalid task index. Task not found.`
+
+>
Back to Table of Contents
+
+
+
+### Saving The Data
+
+Students' data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
--------------------------------------------------------------------------------------------------------------------
+### Editing The Data File
+
+Students' 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.
+
+
:exclamation: **Caution:**
+If your changes to the data file makes its format invalid, StudentConnect 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.
+
+
+>
Back to Table of Contents
+
+
+
## 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.
+**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 StudentConnect
home folder .
---------------------------------------------------------------------------------------------------------------------
+**Q**: Where is my data being saved?
+**A**: It is saved in `[JAR file location]/data/addressbook.json`.
+>
Back to Table of Contents
+
+
-## Known issues
+## 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. **Clear Command**, if you use the clear command, but exit the application incorrectly using the red exit button instead of running the exit command, data will not be cleared. Kindly always use the exit command to leave the application.
+>
Back to Table of Contents
+
+
+
+## Command Summary
+
+| Action | Format, Examples |
+|------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| **Help** | `help` |
+| **Clear** | `clear` |
+| **Exit** | `exit` |
+| **Add** | `add n/NAME m/MAJOR y/YEAR e/EMAIL d/DESCRIPTION [t/TUTORIALS]… [sm/SOCIALMEDIA]… nt/NATIONALITY g/GENDER`
e.g., `add n/Betsy Crowe m/Computer Science y/2 e/betsycrowe@u.nus.edu t/05 d/I’m adept at Backend technologies nt/local g/f` |
+| **List** | `list` |
+| **Edit** | `edit EMAIL [n/NAME] [m/MAJOR] [y/YEAR] [e/EMAIL] [d/DESCRIPTION] [t/TUTORIALS]… [sm/SOCIALMEDIA]… [nt/NATIONALITY] [g/GENDER]`
e.g.,`edit jameslee@u.nus.edu n/James Lee e/jameslee@u.nus.edu` |
+| **Find** | `find KEYWORD [MORE_KEYWORDS]…`
e.g., `find James Jake` |
+| **Filter** | `filter SLOT [MORE_SLOTS]…`
e.g., `filter 05 11` |
+| **Delete** | `delete EMAIL`
e.g., `delete betsycrowe@u.nus.edu` |
+| **Create group** | `create t/[TUTORIAL]`
e.g., `create t/01` |
+| **List groups** | `listGroup` |
+| **Delete group** | `deleteGroup gr/[GROUP_NUMBER}`
e.g., `deleteGroup gr/1` |
+| **Join group** | `join e/[EMAIL] gr/[GROUP_NUMBER]`
e.g., `join e/johnd@u.nus.edu gr/1` |
+| **Leave group** | `leave e/[EMAIL] gr/[GROUP_NUMBER}`
e.g., `leave e/johnd@u.nus.edu gr/1` |
+| **Find group** | `findGroup KEYWORD [MORE_KEYWORDS]`
e.g., `findGroup 7 15` |
+| **Filter group** | `filter SLOT`
e.g., `filterGroup 3` |
+| **Check group** | `checkGroup GR0UP_NUMBER`
e.g., `checkGroup 4` |
+| **List Tasks** | `tasks GROUP_NUMBER`
e.g., `tasks 5` |
+| **Mark** | `mark gr/GROUP_NUMBER ti/TASK_INDEX`
e.g., `mark gr/2 ti/1` |
+| **Unmark** | `unmark gr/GROUP_NUMBER ti/TASK_INDEX`
e.g., `unmark gr/5 ti/1` |
+
+>
Back to Table of Contents
+
+
+
+## Appendix
+This section includes details on the requirements of each field in the StudentConnect system.
+
+### Name Format
+The name field indicates the student's name.
---------------------------------------------------------------------------------------------------------------------
-## 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`
+
:bulb: **Tip:**
+Ensure that the name is appropriately formatted with only letters and spaces, without exceeding the specified character limit below. Special characters and digits are not permitted.
+
+
+- **Type:** Alphabetic.
+- **Character Restrictions:** Only alphabetical characters (A-Z, a-z) and spaces are allowed.
+- **Length:** Must not exceed 30 characters.
+- **Blank Fields:** The field should not be left blank.
+
+**Examples of Valid Inputs:**
+- John Doe
+- Alice
+- La Niña
+- Élise DuPont
+
+**Examples of Invalid Inputs:**
+- John123 (Invalid due to numeric characters)
+- John_Doe (Invalid due to underscore)
+- [Blank Field] (Invalid as the field cannot be blank)
+- Elizabeth Alexandra Mary Windsor (Invalid as it exceeds 30 characters)
+
+
+
+
+### Valid Majors
+The majors field indicates the major that the student is enrolled in.
+
+
:bulb: **Tip:**
+If your major is not in the list below, you can simply write `Others` as your major.
+
+
+
+
+For now, we accept the following majors in National University of Singapore(NUS):
+
+| Major |
+|-------------------------------------------|
+| Accountancy |
+| Actuarial Studies |
+| Anthropology |
+| Architecture |
+| Biological Sciences |
+| Biomedical Engineering |
+| Business Administration |
+| Business Administration (Accountancy) |
+| Business Analytics |
+| Chemical Engineering |
+| Chemistry |
+| Chinese Language |
+| Chinese Studies |
+| Communications and New Media |
+| Civil Engineering |
+| Computer Engineering |
+| Computer Science |
+| Data Science and Analytics |
+| Data Science and Economics |
+| Dentistry |
+| Economics |
+| Electrical Engineering |
+| English Language |
+| English Literature |
+| Environmental Engineering |
+| Environmental Studies |
+| Food Science and Technology |
+| Geography |
+| Global Studies |
+| History |
+| Japanese Studies |
+| Industrial Design |
+| Industrial Engineering |
+| Industrial and Systems Engineering |
+| Information Systems |
+| Information Security |
+| Infrastructure and Project Management |
+| Landscape Architecture |
+| Law |
+| Life Sciences |
+| Malay Studies |
+| Management |
+| Marketing |
+| Materials Science and Engineering |
+| Mathematics |
+| Mechanical Engineering |
+| Medicine |
+| Pharmaceutical Science |
+| Philosophy |
+| Philosophy, Politics, and Economics |
+| Physics |
+| Political Science |
+| Psychology |
+| Quantitative Finance |
+| Real Estate |
+| Social Work |
+| Sociology |
+| South Asian Studies |
+| Southeast Asian Studies |
+| Statistics |
+| Systems Engineering |
+| Theatre Studies |
+| Urban Studies |
+| Visual Communications |
+
+
+
+
+### Valid Years
+The year field is used to represent the student's current year of study.
+
+
:bulb: **Tip:**
+This field strictly requires a single-digit number. Ensure there are no leading zeros or non-numeric characters.
+
+
+| Year |
+|------|
+| 1 |
+| 2 |
+| 3 |
+| 4 |
+| 5 |
+| 6 |
+
+
+
+
+### Email Formats
+The email field is used to represent the student's email, and is the unique identifier for each student.
+
+
:bulb: **Tip:**
+Ensure the email address includes the specified domain and the local-part conforms to the outlined character restrictions and length below.
+
+
+1. **Local-Part Format:**
+ - **Allowed Characters:** Alphanumeric characters (A-Z, a-z, 0-9) and special characters (+, _, ., -) only.
+ - **Character Limit:** Must not exceed 20 characters.
+ - **Positioning of Special Characters:** The local-part must not start or end with any of the special characters (+, _, ., -).
+ - **Structure:** It should be in the format of `local-part@u.nus.edu`.
+
+2. **Domain Name:**
+ - **Fixed Domain:** The domain name must be `u.nus.edu`.
+ - **Symbol:** The local-part and domain name should be separated by an '@' symbol.
+
+**Examples of Valid Inputs:**
+- johndoe@u.nus.edu
+- alice.bob@u.nus.edu
+- n_user123@u.nus.edu
+
+**Examples of Invalid Inputs:**
+- john@u.nus.edu (Invalid as it exceeds 20 characters in the local-part)
+- .johndoe@u.nus.edu (Invalid as the local-part starts with a special character)
+- johndoe@example.com (Invalid as the domain name is not 'u.nus.edu')
+- johndoe@u.nus (Invalid as the domain name is incomplete)
+
+
+
+### Description Formats
+The description field is to allow student's to put any fun facts or information they'd like to share.
+
+
:bulb: **Tip:**
+Descriptions that are blank or that exceed 150 characters are not accepted.
+
+
+**Length Constraints:**
+- **Minimum Length:** The description must not be blank.
+- **Maximum Length:** The description should not exceed 150 characters.
+
+**Examples of Valid Inputs:**
+- "Minoring in Mathematics"
+- "Backend Developer"
+- "In need of two more members!"
+
+
+
+
+### Valid Tutorials
+The tutorial field is used to represent tutorial slots students' are interested in or have been assigned.
+
+
:bulb: **Tip:**
+This field strictly requires a double-digit number. Ensure are no non-numeric characters.
+
+
+| Tutorial |
+|----------|
+| 01 |
+| 02 |
+| 03 |
+| 04 |
+| 05 |
+| 06 |
+| 07 |
+| 08 |
+| 09 |
+| 10 |
+| 11 |
+| 12 |
+| 13 |
+| 14 |
+| 15 |
+| 16 |
+| 17 |
+| 18 |
+| 19 |
+| 20 |
+| 21 |
+| 22 |
+
+
+
+### Valid Social Media Links
+The social media links field is used for entering URLs that direct to social media profiles or pages.
+
+
:bulb: **Tip:**
+ Ensure that the social media links conform to the standard URL format, with the correct protocol and a valid domain name. The link should be fully functional and direct to the intended social media page.
+
+- **Protocol Prefix:** Must begin with either "http://" or "https://".
+- **Domain Name:**
+ - **Allowed Characters:** Alphanumeric characters (A-Z, a-z, 0-9), dots (.), and hyphens (-).
+ - **Requirement:** The domain name must consist of one or more of the allowed characters.
+- **Structure:** The format should follow the standard URL structure, starting with the protocol prefix, followed by the domain name.
+
+**Examples of Valid Inputs:**
+- http://facebook.com/username
+- https://twitter.com/username
+- http://www.linkedin.com/in/username
+
+**Examples of Invalid Inputs:**
+- www.instagram.com/username (Invalid as it lacks the "http://" or "https://" prefix)
+- https://facebook_com/username (Invalid due to the use of an underscore in the domain name)
+- https:/twitter.com/username (Invalid due to incorrect protocol format)
+
+
+
+### Valid Nationalities
+Nationality field is used to display the nationality of the student.
+
:bulb: **Tip:**
+The nationality field is case-insensitive, but can only contain values `local` or `foreigner`.
+
+
+| Nationality |
+|-------------|
+| local |
+| foreigner |
+
+
+
+### Valid Genders
+Gender field is used to display the gender of the student.
+
:bulb: **Tip:**
+The gender field is case-insensitive, but can only contain values `M` or `F`.
+
+
+| Gender |
+|--------|
+| M |
+| F |
+
+
+
+### Valid Group Numbers
+The group number field is used to specify the numeric identifier of a group.
+
+
:bulb: **Tip:**
+Verify that the group number entered is for a group that exists in the system.
+Ensure the number is purely numeric without any alphabetic characters or special symbols.
+
+
+- **Type:** Numeric.
+- **Reference Requirement:** The number must correspond to a group that has already been created within the system.
+- **Uniqueness:** Each group number is unique. It should not duplicate the identifier of another group.
+
+
+
+### Valid Task Indexes
+The task index field is used to differentiate tasks.
+
+
:bulb: **Tip:**
+Verify that the task index entered is for a task that exists for that group.
+Ensure the number is purely numeric without any alphabetic characters or special symbols.
+
+
+- **Type:** Numeric
+- **Reference Requirement:** The number must correspond to a task within the system.
+- **Uniqueness:** Each task number is unique. It should not duplicate the identifier of another task .
+
+>
Back to Table of Contents
+
+
+
+## Glossary
+
+This glossary is intended to provide definitions for terms that may be unfamiliar to you. It is arranged in alphabetical order.
+
+- **CS2101**:
Effective Communication for Computing Professionals - An NUS course designed to equip computing professionals with essential communication skills, both in technical and non-technical contexts. It covers the creation of clear and comprehensible software documentation and effective communication strategies for diverse audiences.
+
+- **CS2103T**:
Software Engineering - An NUS course focusing on the systematic and rigorous development of software systems. It covers essential concepts and analytical tools necessary for software engineering.
+
+- **Case-Insensitive**:
Refers to the handling of text where uppercase and lowercase letters are treated as equivalent. For instance, in a case-insensitive search, searching for "Java" or "java" would yield the same results.
+
+- **Command Terminal**:
An interface in a computing environment where you can input text commands to perform specific tasks. Some examples include "Terminal" on macOS and "Powershell" on Windows.
+
+- **Double Degree**:
An academic program where a student earns two distinct degrees simultaneously, usually in different fields.
+
+- **Double Major**:
A type of academic degree where a student completes two sets of major requirements, although they receive just one degree.
+
+- **Major-Minor**:
An academic program where a student completes a major (primary focus) and a minor (secondary concentration) in different subjects.
+
+- **Extraneous Parameters**: These are additional or unnecessary parameters given in a command that do not affect its execution but are not required for the command to function properly.
+
+- **GUI (Graphical User Interface)**:
A type of user interface that allows you to interact with electronic devices through graphical icons and visual indicators, as opposed to text-based interfaces, typed command labels, or text navigation.
+
+- **Home Folder**:
In computing, this is a personal directory assigned to you in a file system, where they store personal files, settings, and configurations.
+
+- **Issue (GitHub)**:
A feature in GitHub used to track ideas, enhancements, tasks, or bugs for work on GitHub projects.
+
+- **Java 11**: A version of Java, a widely used programming language and computing platform. Java 11 includes various updates and features different from its predecessors.
+
+>
Back to Table of Contents
diff --git a/docs/_config.yml b/docs/_config.yml
index 6bd245d8f4e..e68586c9455 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -1,4 +1,4 @@
-title: "AB-3"
+title: "StudentConnect"
theme: minima
header_pages:
@@ -8,7 +8,7 @@ header_pages:
markdown: kramdown
-repository: "se-edu/addressbook-level3"
+repository: "nus-cs2103-AY2324S1/tp"
github_icon: "images/github-icon.png"
plugins:
diff --git a/docs/_includes/header.html b/docs/_includes/header.html
index 33badcd4f99..dbaeffe0f8e 100644
--- a/docs/_includes/header.html
+++ b/docs/_includes/header.html
@@ -25,7 +25,7 @@
{%- endif -%}
{%- endfor -%}
{%- if site.repository -%}
-
+
{%- endif -%}
diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss
index 0d3f6e80ced..49eb3e95305 100644
--- a/docs/_sass/minima/_base.scss
+++ b/docs/_sass/minima/_base.scss
@@ -288,8 +288,7 @@ table {
text-align: center;
}
.site-header:before {
- content: "AB-3";
+ content: "StudentConnect";
font-size: 32px;
}
}
-
diff --git a/docs/_sass/minima/skins/classic.scss b/docs/_sass/minima/skins/classic.scss
index 37ea9c5244c..a441ff0e667 100644
--- a/docs/_sass/minima/skins/classic.scss
+++ b/docs/_sass/minima/skins/classic.scss
@@ -8,7 +8,7 @@ $text-color: #111 !default;
$background-color: #fdfdfd !default;
$code-background-color: #eef !default;
-$link-base-color: #2a7ae2 !default;
+$link-base-color: #111754 !default;
$link-visited-color: darken($link-base-color, 15%) !default;
$table-text-color: lighten($text-color, 18%) !default;
diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml
index 48b6cc4333c..263af490c3b 100644
--- a/docs/diagrams/ArchitectureSequenceDiagram.puml
+++ b/docs/diagrams/ArchitectureSequenceDiagram.puml
@@ -8,10 +8,10 @@ Participant ":Logic" as logic LOGIC_COLOR
Participant ":Model" as model MODEL_COLOR
Participant ":Storage" as storage STORAGE_COLOR
-user -[USER_COLOR]> ui : "delete 1"
+user -[USER_COLOR]> ui : "delete johnd@u.nus.edu"
activate ui UI_COLOR
-ui -[UI_COLOR]> logic : execute("delete 1")
+ui -[UI_COLOR]> logic : execute("delete johnd@u.nus.edu")
activate logic LOGIC_COLOR
logic -[LOGIC_COLOR]> model : deletePerson(p)
diff --git a/docs/diagrams/CreateSequenceDiagram.puml b/docs/diagrams/CreateSequenceDiagram.puml
new file mode 100644
index 00000000000..4c6f2e57a46
--- /dev/null
+++ b/docs/diagrams/CreateSequenceDiagram.puml
@@ -0,0 +1,72 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic UI_COLOR_T2
+participant ":LogicManager" as LogicManager MODEL_COLOR
+participant ":AddressBookParser" as AddressBookParser MODEL_COLOR
+participant ":CreateCommandParser" as CreateCommandParser MODEL_COLOR
+participant "d:CreateCommand" as CreateCommand MODEL_COLOR
+participant ":CommandResult" as CommandResult MODEL_COLOR
+end box
+
+box Model LOGIC_COLOR_T2
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("create t/01")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("create t/01")
+activate AddressBookParser
+
+create CreateCommandParser
+AddressBookParser -> CreateCommandParser
+activate CreateCommandParser
+
+CreateCommandParser --> AddressBookParser
+deactivate CreateCommandParser
+
+AddressBookParser -> CreateCommandParser : parse("t/01")
+activate CreateCommandParser
+
+create CreateCommand
+CreateCommandParser -> CreateCommand
+activate CreateCommand
+
+CreateCommand --> CreateCommandParser : c
+deactivate CreateCommand
+
+CreateCommandParser --> AddressBookParser : c
+deactivate CreateCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+CreateCommandParser -[hidden]-> AddressBookParser
+destroy CreateCommandParser
+
+AddressBookParser --> LogicManager : c
+deactivate AddressBookParser
+
+LogicManager -> CreateCommand : execute()
+activate CreateCommand
+
+CreateCommand -> CreateCommand : generateGroupNumber()
+
+CreateCommand -> Model : addGroup(newGroup)
+activate Model
+
+Model --> CreateCommand
+deactivate Model
+
+create CommandResult
+CreateCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> CreateCommand
+deactivate CommandResult
+
+CreateCommand --> LogicManager : result
+deactivate CreateCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml
index 40ea6c9dc4c..329fba4985a 100644
--- a/docs/diagrams/DeleteSequenceDiagram.puml
+++ b/docs/diagrams/DeleteSequenceDiagram.puml
@@ -14,10 +14,10 @@ box Model MODEL_COLOR_T1
participant ":Model" as Model MODEL_COLOR
end box
-[-> LogicManager : execute("delete 1")
+[-> LogicManager : execute("delete johnd@u.nus.edu")
activate LogicManager
-LogicManager -> AddressBookParser : parseCommand("delete 1")
+LogicManager -> AddressBookParser : parseCommand("delete johnd@u.nus.edu")
activate AddressBookParser
create DeleteCommandParser
@@ -27,7 +27,7 @@ activate DeleteCommandParser
DeleteCommandParser --> AddressBookParser
deactivate DeleteCommandParser
-AddressBookParser -> DeleteCommandParser : parse("1")
+AddressBookParser -> DeleteCommandParser : parse("johnd@u.nus.edu")
activate DeleteCommandParser
create DeleteCommand
@@ -49,7 +49,7 @@ deactivate AddressBookParser
LogicManager -> DeleteCommand : execute()
activate DeleteCommand
-DeleteCommand -> Model : deletePerson(1)
+DeleteCommand -> Model : deletePerson(johnd@u.nus.edu)
activate Model
Model --> DeleteCommand
diff --git a/docs/diagrams/GroupModelClassDiagram.puml b/docs/diagrams/GroupModelClassDiagram.puml
new file mode 100644
index 00000000000..58729428a28
--- /dev/null
+++ b/docs/diagrams/GroupModelClassDiagram.puml
@@ -0,0 +1,49 @@
+@startuml
+!include style.puml
+skinparam arrowThickness 1.1
+skinparam arrowColor MODEL_COLOR
+skinparam classBackgroundColor MODEL_COLOR
+
+Package Model as ModelPackage <
>{
+Class "<>\nReadOnlyAddressBook" as ReadOnlyAddressBook
+Class "<>\nReadOnlyUserPrefs" as ReadOnlyUserPrefs
+Class "<>\nModel" as Model
+Class AddressBook
+Class ModelManager
+Class UserPrefs
+
+Class Person
+
+Class UniqueGroupList
+Class Group {
+- int number
+}
+Class Tutorial
+Class TaskList
+
+Class I #FFFFFF
+}
+Class HiddenOutside #FFFFFF
+HiddenOutside ..> Model
+
+AddressBook .up.|> ReadOnlyAddressBook
+
+ModelManager .up.|> Model
+Model .right.> ReadOnlyUserPrefs
+Model .left.> ReadOnlyAddressBook
+ModelManager -left-> "1" AddressBook
+ModelManager -right-> "1" UserPrefs
+UserPrefs .up.|> ReadOnlyUserPrefs
+
+AddressBook *--> "1" UniqueGroupList
+UniqueGroupList --> "~* all" Group
+Group *--> "1" Tutorial
+Group *-->"members *" Person
+Group *--> "1" TaskList
+
+Group -[hidden]up--> I
+UniqueGroupList -[hidden]right-> I
+
+ModelManager --> "~* filtered" Group
+
+@enduml
diff --git a/docs/diagrams/Person.puml b/docs/diagrams/Person.puml
new file mode 100644
index 00000000000..061a52e3706
--- /dev/null
+++ b/docs/diagrams/Person.puml
@@ -0,0 +1,27 @@
+@startuml
+
+class Person {
+ +Person(Name, Major, Year, Email, Description, Set, Set, Nationality, Gender)
+}
+
+class Name
+class Email
+class Major
+class Year
+class Description
+class Tutorial
+class SocialMediaLink
+class Nationality
+class Gender
+
+Person --|> Name
+Person --|> Email
+Person --|> Major
+Person --|> Year
+Person --|> Description
+Person --|> Tutorial
+Person --|> SocialMediaLink
+Person --|> Nationality
+Person --|> Gender
+
+@enduml
diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/PersonModelClassDiagram.puml
similarity index 78%
rename from docs/diagrams/ModelClassDiagram.puml
rename to docs/diagrams/PersonModelClassDiagram.puml
index 0de5673070d..003c0d76400 100644
--- a/docs/diagrams/ModelClassDiagram.puml
+++ b/docs/diagrams/PersonModelClassDiagram.puml
@@ -14,11 +14,15 @@ Class UserPrefs
Class UniquePersonList
Class Person
-Class Address
Class Email
Class Name
-Class Phone
-Class Tag
+Class Major
+Class Year
+Class Description
+Class SocialMediaLink
+Class Tutorial
+Class Nationality
+Class Gender
Class I #FFFFFF
}
@@ -38,17 +42,18 @@ UserPrefs .up.|> ReadOnlyUserPrefs
AddressBook *--> "1" UniquePersonList
UniquePersonList --> "~* all" Person
Person *--> Name
-Person *--> Phone
Person *--> Email
-Person *--> Address
-Person *--> "*" Tag
+Person *--> Major
+Person *--> Year
+Person *--> Description
+Person *--> "*" SocialMediaLink
+Person *--> "*" Tutorial
+Person *--> Nationality
+Person *--> Gender
Person -[hidden]up--> I
UniquePersonList -[hidden]right-> I
-Name -[hidden]right-> Phone
-Phone -[hidden]right-> Address
-Address -[hidden]right-> Email
-
ModelManager --> "~* filtered" Person
+
@enduml
diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml
index a821e06458c..a62f82615d1 100644
--- a/docs/diagrams/StorageClassDiagram.puml
+++ b/docs/diagrams/StorageClassDiagram.puml
@@ -19,7 +19,10 @@ Class "<>\nAddressBookStorage" as AddressBookStorage
Class JsonAddressBookStorage
Class JsonSerializableAddressBook
Class JsonAdaptedPerson
-Class JsonAdaptedTag
+Class JsonAdaptedGroup
+Class JsonAdaptedSocialMedia
+Class JsonAdaptedTutorial
+Class JsonAdaptedTask
}
}
@@ -38,6 +41,9 @@ JsonUserPrefsStorage .up.|> UserPrefsStorage
JsonAddressBookStorage .up.|> AddressBookStorage
JsonAddressBookStorage ..> JsonSerializableAddressBook
JsonSerializableAddressBook --> "*" JsonAdaptedPerson
-JsonAdaptedPerson --> "*" JsonAdaptedTag
+JsonSerializableAddressBook --> "*" JsonAdaptedGroup
+JsonAdaptedPerson --> "*" JsonAdaptedSocialMedia
+JsonAdaptedPerson --> "*" JsonAdaptedTutorial
+JsonAdaptedGroup --> "*" JsonAdaptedTask
@enduml
diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml
index 95473d5aa19..9a3def22fdb 100644
--- a/docs/diagrams/UiClassDiagram.puml
+++ b/docs/diagrams/UiClassDiagram.puml
@@ -13,8 +13,11 @@ Class HelpWindow
Class ResultDisplay
Class PersonListPanel
Class PersonCard
+Class GroupListPanel
+Class GroupCard
Class StatusBarFooter
Class CommandBox
+Class ConfirmationPopup
}
package Model <> {
@@ -31,12 +34,15 @@ HiddenOutside ..> Ui
UiManager .left.|> Ui
UiManager -down-> "1" MainWindow
MainWindow *-down-> "1" CommandBox
+MainWindow *-down-> "0..1" ConfirmationPopup
MainWindow *-down-> "1" ResultDisplay
-MainWindow *-down-> "1" PersonListPanel
+MainWindow *-down-> "0..1" PersonListPanel
+MainWindow *-down-> "0..1" GroupListPanel
MainWindow *-down-> "1" StatusBarFooter
MainWindow --> "0..1" HelpWindow
PersonListPanel -down-> "*" PersonCard
+GroupListPanel -down--> "*" GroupCard
MainWindow -left-|> UiPart
@@ -44,17 +50,24 @@ ResultDisplay --|> UiPart
CommandBox --|> UiPart
PersonListPanel --|> UiPart
PersonCard --|> UiPart
+GroupListPanel --|> UiPart
+GroupCard --|> UiPart
StatusBarFooter --|> UiPart
HelpWindow --|> UiPart
+ConfirmationPopup --|> UiPart
PersonCard ..> Model
+GroupCard ..> Model
UiManager -right-> Logic
MainWindow -left-> Logic
+GroupListPanel -[hidden]left- PersonListPanel
PersonListPanel -[hidden]left- HelpWindow
HelpWindow -[hidden]left- CommandBox
+HelpWindow -[hidden]left- CommandBox
CommandBox -[hidden]left- ResultDisplay
ResultDisplay -[hidden]left- StatusBarFooter
+StatusBarFooter -[hidden]left- ConfirmationPopup
MainWindow -[hidden]-|> UiPart
@enduml
diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png
index 37ad06a2803..c82aa2c350f 100644
Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ
diff --git a/docs/images/CreateSequenceDiagram.png b/docs/images/CreateSequenceDiagram.png
new file mode 100644
index 00000000000..f0409c99b7a
Binary files /dev/null and b/docs/images/CreateSequenceDiagram.png differ
diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png
index e186f7ba096..505076dd12a 100644
Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ
diff --git a/docs/images/GroupListUI.png b/docs/images/GroupListUI.png
new file mode 100644
index 00000000000..c54906f4d95
Binary files /dev/null and b/docs/images/GroupListUI.png differ
diff --git a/docs/images/GroupModelClassDiagram.png b/docs/images/GroupModelClassDiagram.png
new file mode 100644
index 00000000000..262b696c8ec
Binary files /dev/null and b/docs/images/GroupModelClassDiagram.png differ
diff --git a/docs/images/PersonClassDiagram.png b/docs/images/PersonClassDiagram.png
new file mode 100644
index 00000000000..56b6aec1953
Binary files /dev/null and b/docs/images/PersonClassDiagram.png differ
diff --git a/docs/images/PersonModelClassDiagram.png b/docs/images/PersonModelClassDiagram.png
new file mode 100644
index 00000000000..63042e0755e
Binary files /dev/null and b/docs/images/PersonModelClassDiagram.png differ
diff --git a/docs/images/StorageClassDiagramNew.png b/docs/images/StorageClassDiagramNew.png
new file mode 100644
index 00000000000..4c155069197
Binary files /dev/null and b/docs/images/StorageClassDiagramNew.png differ
diff --git a/docs/images/StudentConnectLogo.png b/docs/images/StudentConnectLogo.png
new file mode 100644
index 00000000000..a6ba0b2d38f
Binary files /dev/null and b/docs/images/StudentConnectLogo.png differ
diff --git a/docs/images/StudentListUI.png b/docs/images/StudentListUI.png
new file mode 100644
index 00000000000..8c329fca38f
Binary files /dev/null and b/docs/images/StudentListUI.png differ
diff --git a/docs/images/Ui.png b/docs/images/Ui.png
index 5bd77847aa2..d7b636490d3 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..4d6b229a1cf 100644
Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ
diff --git a/docs/images/add.png b/docs/images/add.png
new file mode 100644
index 00000000000..a4bf65f9608
Binary files /dev/null and b/docs/images/add.png differ
diff --git a/docs/images/bearypop.png b/docs/images/bearypop.png
new file mode 100644
index 00000000000..09f14c853d9
Binary files /dev/null and b/docs/images/bearypop.png differ
diff --git a/docs/images/checkGroup.png b/docs/images/checkGroup.png
new file mode 100644
index 00000000000..bcc6c17d61e
Binary files /dev/null and b/docs/images/checkGroup.png differ
diff --git a/docs/images/clear.png b/docs/images/clear.png
new file mode 100644
index 00000000000..0b5443f2480
Binary files /dev/null and b/docs/images/clear.png differ
diff --git a/docs/images/clearCancel.png b/docs/images/clearCancel.png
new file mode 100644
index 00000000000..2b8d5593382
Binary files /dev/null and b/docs/images/clearCancel.png differ
diff --git a/docs/images/clearPopUp.png b/docs/images/clearPopUp.png
new file mode 100644
index 00000000000..571940de972
Binary files /dev/null and b/docs/images/clearPopUp.png differ
diff --git a/docs/images/clearUI.png b/docs/images/clearUI.png
new file mode 100644
index 00000000000..68e76daf768
Binary files /dev/null and b/docs/images/clearUI.png differ
diff --git a/docs/images/create.png b/docs/images/create.png
new file mode 100644
index 00000000000..55ae81d3664
Binary files /dev/null and b/docs/images/create.png differ
diff --git a/docs/images/delete.png b/docs/images/delete.png
new file mode 100644
index 00000000000..b9304c5b2fb
Binary files /dev/null and b/docs/images/delete.png differ
diff --git a/docs/images/deleteGroup.png b/docs/images/deleteGroup.png
new file mode 100644
index 00000000000..7f43516dbb3
Binary files /dev/null and b/docs/images/deleteGroup.png differ
diff --git a/docs/images/desktop.ini b/docs/images/desktop.ini
new file mode 100644
index 00000000000..79f04ac40e7
--- /dev/null
+++ b/docs/images/desktop.ini
@@ -0,0 +1,2 @@
+[LocalizedFileNames]
+Ui.png=@Ui,0
diff --git a/docs/images/edit.png b/docs/images/edit.png
new file mode 100644
index 00000000000..9748bfc6b5f
Binary files /dev/null and b/docs/images/edit.png differ
diff --git a/docs/images/exit.png b/docs/images/exit.png
new file mode 100644
index 00000000000..777cf425a6a
Binary files /dev/null and b/docs/images/exit.png differ
diff --git a/docs/images/filter.png b/docs/images/filter.png
new file mode 100644
index 00000000000..7f3aea5bd2c
Binary files /dev/null and b/docs/images/filter.png differ
diff --git a/docs/images/filterGroup.png b/docs/images/filterGroup.png
new file mode 100644
index 00000000000..b21b77ec971
Binary files /dev/null and b/docs/images/filterGroup.png differ
diff --git a/docs/images/find.png b/docs/images/find.png
new file mode 100644
index 00000000000..6ff9007d574
Binary files /dev/null and b/docs/images/find.png differ
diff --git a/docs/images/findAlexResult.png b/docs/images/findAlexResult.png
new file mode 100644
index 00000000000..a7a96a42953
Binary files /dev/null and b/docs/images/findAlexResult.png differ
diff --git a/docs/images/findGroup.png b/docs/images/findGroup.png
new file mode 100644
index 00000000000..46336ce8236
Binary files /dev/null and b/docs/images/findGroup.png differ
diff --git a/docs/images/help.png b/docs/images/help.png
new file mode 100644
index 00000000000..692698b8551
Binary files /dev/null and b/docs/images/help.png differ
diff --git a/docs/images/helpMessage.png b/docs/images/helpMessage.png
index b1f70470137..3127050796c 100644
Binary files a/docs/images/helpMessage.png and b/docs/images/helpMessage.png differ
diff --git a/docs/images/helpWindow.png b/docs/images/helpWindow.png
new file mode 100644
index 00000000000..8c6a9f0e4ac
Binary files /dev/null and b/docs/images/helpWindow.png differ
diff --git a/docs/images/join.png b/docs/images/join.png
new file mode 100644
index 00000000000..eeb70944fb8
Binary files /dev/null and b/docs/images/join.png differ
diff --git a/docs/images/leave.png b/docs/images/leave.png
new file mode 100644
index 00000000000..1111243be78
Binary files /dev/null and b/docs/images/leave.png differ
diff --git a/docs/images/list.png b/docs/images/list.png
new file mode 100644
index 00000000000..3eefcd1b728
Binary files /dev/null and b/docs/images/list.png differ
diff --git a/docs/images/listGroup.png b/docs/images/listGroup.png
new file mode 100644
index 00000000000..241885415ba
Binary files /dev/null and b/docs/images/listGroup.png differ
diff --git a/docs/images/maj0-0.png b/docs/images/maj0-0.png
new file mode 100644
index 00000000000..ed0a06c263e
Binary files /dev/null and b/docs/images/maj0-0.png differ
diff --git a/docs/images/mark.png b/docs/images/mark.png
new file mode 100644
index 00000000000..87c7655c72c
Binary files /dev/null and b/docs/images/mark.png differ
diff --git a/docs/images/multipleLinks.png b/docs/images/multipleLinks.png
new file mode 100644
index 00000000000..c04989392c6
Binary files /dev/null and b/docs/images/multipleLinks.png differ
diff --git a/docs/images/neyapraveen.png b/docs/images/neyapraveen.png
new file mode 100644
index 00000000000..138d587afb5
Binary files /dev/null and b/docs/images/neyapraveen.png differ
diff --git a/docs/images/pearlynnt.png b/docs/images/pearlynnt.png
new file mode 100644
index 00000000000..34b634c3baa
Binary files /dev/null and b/docs/images/pearlynnt.png differ
diff --git a/docs/images/socialMediaLinks.png b/docs/images/socialMediaLinks.png
new file mode 100644
index 00000000000..c2014604efe
Binary files /dev/null and b/docs/images/socialMediaLinks.png differ
diff --git a/docs/images/tasks.png b/docs/images/tasks.png
new file mode 100644
index 00000000000..ae2320d1d89
Binary files /dev/null and b/docs/images/tasks.png differ
diff --git a/docs/images/unmark.png b/docs/images/unmark.png
new file mode 100644
index 00000000000..4f357187db4
Binary files /dev/null and b/docs/images/unmark.png differ
diff --git a/docs/images/wnchan.png b/docs/images/wnchan.png
new file mode 100644
index 00000000000..8f07e5ff775
Binary files /dev/null and b/docs/images/wnchan.png differ
diff --git a/docs/index.md b/docs/index.md
index 7601dbaad0d..9c4e30588be 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,19 +1,21 @@
---
layout: page
-title: AddressBook Level-3
+title: StudentConnect
---
-
-[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions)
-[![codecov](https://codecov.io/gh/se-edu/addressbook-level3/branch/master/graph/badge.svg)](https://codecov.io/gh/se-edu/addressbook-level3)
+[![CI Status](https://github.com/AY2324S1-CS2103T-F12-2/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2324S1-CS2103T-F12-2/tp/actions)
+[![codecov](https://codecov.io/gh/AY2324S1-CS2103T-F12-2/tp/branch/master/graph/badge.svg)](https://codecov.io/gh/AY2324S1-CS2103T-F12-2/tp)
![Ui](images/Ui.png)
-**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface).
-
-* If you are interested in using AddressBook, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start).
-* If you are interested about developing AddressBook, the [**Developer Guide**](DeveloperGuide.html) is a good place to start.
+**StudentConnect is a solution for hassle-free team formation for students to browse profiles and connect with others for the CS2103T and CS2101 group project.**
+
+While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface).
+* If you are interested in using StudentConnect, head over to the [_Quick Start_ section of the **User Guide**](https://ay2324s1-cs2103t-f12-2.github.io/tp/UserGuide.html#quick-start).
+* If you are interested about developing StudentConnect, the [**Developer Guide**](https://ay2324s1-cs2103t-f12-2.github.io/tp/DeveloperGuide.html) and [**GitHub Website**](https://github.com/AY2324S1-CS2103T-F12-2/tp) are good places to start.
+* If you are interested in finding out more about the team behind StudentConnect, head over to the [**About Us Page**](https://ay2324s1-cs2103t-f12-2.github.io/tp/AboutUs.html).
**Acknowledgements**
-* Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5)
+* This project is based on the AddressBook-Level3 project created by the [**SE-EDU initiative**](https://se-education.org).
+* Libraries used: [**JavaFX**](https://openjfx.io/), [**Jackson**](https://github.com/FasterXML/jackson), [**JUnit5**](https://github.com/junit-team/junit5).
diff --git a/docs/team/bearypop.md b/docs/team/bearypop.md
new file mode 100644
index 00000000000..87725fbf41f
--- /dev/null
+++ b/docs/team/bearypop.md
@@ -0,0 +1,42 @@
+---
+layout: page
+title: Yik Leong's Project Portfolio Page
+---
+
+### Project: StudentConnect
+
+StudentConnect is a solution for hassle-free team formation for students to browse profiles and connect with others for group projects. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
+
+Given below are my contributions to the project.
+
+### New Feature: Create group
+* Added a new feature that allows students to create groups in the app [\#77](https://github.com/AY2324S1-CS2103T-F12-2/tp/pull/77)
+* This feature makes the process of group formation much more convenient as the students can now form groups in the app itself, instead of having to use another channel such as social media to contact other students to form groups.
+* Implementing this feature required the creation of many new models and classes such as `Group` and `JsonAdaptedGroup`. The `Addressbook` class had to be modified significantly to support storing groups and the various group operations. Overall, adding this new feature was a complex process due to the large number of additions and changes that had to be made.
+
+### New Feature: List groups
+* Added a new feature that allows students to view the list of groups in the app [\#113](https://github.com/AY2324S1-CS2103T-F12-2/tp/pull/113)
+* This feature allows students to view the groups that have been created, as well as the members of each group. This will help students in finding suitable groups to join as they are able to see which groups are not yet full.
+* This feature was particularly time-consuming and challenging to implement as it involved the creation of various new components in the UI to display the group information. Something noteworthy to point out is that conditional rendering is used to display either the person UI or the group UI in the window, based on the last command that the user entered.
+
+### New Feature: Join group
+* Added a new feature that allows students to join a group in the app [\#97](https://github.com/AY2324S1-CS2103T-F12-2/tp/pull/97)
+* This feature is an essential part of the group formation service that our app provides.
+
+### Enhancements to existing features:
+* Updated the `exit` feature [\#15](https://github.com/AY2324S1-CS2103T-F12-2/tp/pull/15)
+
+### Testing:
+* Updated tests for `add` command [\#57](https://github.com/AY2324S1-CS2103T-F12-2/tp/pull/57)
+* Updated tests for `delete`, `edit` commands and Logic Manager [\#58](https://github.com/AY2324S1-CS2103T-F12-2/tp/pull/58)
+* Created tests for the `Group` class [\#174](https://github.com/AY2324S1-CS2103T-F12-2/tp/pull/174)
+* Created tests for `create`, `join` and `listGroup` commands [\#194](https://github.com/AY2324S1-CS2103T-F12-2/tp/pull/194), [\#208](https://github.com/AY2324S1-CS2103T-F12-2/tp/pull/208)
+
+### Code contributed: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=bearypop&breakdown=true)
+
+### Documentation:
+* User Guide:
+ * Added documentation for the features `exit` [\#15](https://github.com/AY2324S1-CS2103T-F12-2/tp/pull/15), [\#61](https://github.com/AY2324S1-CS2103T-F12-2/tp/pull/61)
+ * Added documentation for the features `create`, `join` and `listGroup` [\#137](https://github.com/AY2324S1-CS2103T-F12-2/tp/pull/137)
+* Developer Guide:
+ * Added documentation for the features `create` and the `UI` component [\#83](https://github.com/AY2324S1-CS2103T-F12-2/tp/pull/83), [\#200](https://github.com/AY2324S1-CS2103T-F12-2/tp/pull/200)
diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md
deleted file mode 100644
index 773a07794e2..00000000000
--- a/docs/team/johndoe.md
+++ /dev/null
@@ -1,46 +0,0 @@
----
-layout: page
-title: John Doe's Project Portfolio Page
----
-
-### Project: AddressBook Level 3
-
-AddressBook - Level 3 is a desktop address book application used for teaching Software Engineering principles. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
-
-Given below are my contributions to the project.
-
-* **New Feature**: Added the ability to undo/redo previous commands.
- * What it does: allows the user to undo all previous commands one at a time. Preceding undo commands can be reversed by using the redo command.
- * Justification: This feature improves the product significantly because a user can make mistakes in commands and the app should provide a convenient way to rectify them.
- * Highlights: This enhancement affects existing commands and commands to be added in future. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing commands.
- * Credits: *{mention here if you reused any code/ideas from elsewhere or if a third-party library is heavily used in the feature so that a reader can make a more accurate judgement of how much effort went into the feature}*
-
-* **New Feature**: Added a history command that allows the user to navigate to previous commands using up/down keys.
-
-* **Code contributed**: [RepoSense link]()
-
-* **Project management**:
- * Managed releases `v1.3` - `v1.5rc` (3 releases) on GitHub
-
-* **Enhancements to existing features**:
- * Updated the GUI color scheme (Pull requests [\#33](), [\#34]())
- * Wrote additional tests for existing features to increase coverage from 88% to 92% (Pull requests [\#36](), [\#38]())
-
-* **Documentation**:
- * User Guide:
- * Added documentation for the features `delete` and `find` [\#72]()
- * Did cosmetic tweaks to existing documentation of features `clear`, `exit`: [\#74]()
- * Developer Guide:
- * Added implementation details of the `delete` feature.
-
-* **Community**:
- * PRs reviewed (with non-trivial review comments): [\#12](), [\#32](), [\#19](), [\#42]()
- * Contributed to forum discussions (examples: [1](), [2](), [3](), [4]())
- * Reported bugs and suggestions for other teams in the class (examples: [1](), [2](), [3]())
- * Some parts of the history feature I added was adopted by several other class mates ([1](), [2]())
-
-* **Tools**:
- * Integrated a third party library (Natty) to the project ([\#42]())
- * Integrated a new Github plugin (CircleCI) to the team repo
-
-* _{you can add/remove categories in the list above}_
diff --git a/docs/team/maj0-0.md b/docs/team/maj0-0.md
new file mode 100644
index 00000000000..881e9846bef
--- /dev/null
+++ b/docs/team/maj0-0.md
@@ -0,0 +1,52 @@
+---
+layout: page
+title: Majedah's Project Portfolio Page
+---
+
+# Overview
+
+StudentConnect is the solution for hassle-free team formation for CS2103T students to browse profiles and connect with others for group projects.
+
+# Summary of Contributions
+
+## Code contributed
+* Link to my [RepoSense report](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=maj0-0&breakdown=false&sort=groupTitle%20dsc&sortWithin=title&since=2023-09-22&timeframe=commit&mergegroup=&groupSelect=groupByRepos).
+
+## Enhancements implemented
+* **New Feature**: Tasks Command
+ * What it does: Lists out the set of tasks for each group.
+ * Highlights: Since StudentConnect is catered towards CS2103T and CS2101, the tasks are pre-set and are automatically assigned to each group.
+ * Challenges: Our team had initially planned to have a separate UI list for tasks, but due to time constraints, the tasks now show up in the feedback panel instead.
+* **Enhancement**: Added Tutorial, Gender and Nationality fields
+ * Included the tutorial, nationality and gender fields to the person model.
+* **Enhancement**: Updated UI for nationality and gender fields.
+* **Enhancement**: List Command
+ * Updated the list students feature's success message to match StudentConnect context.
+
+## Contributions to the UG
+* Under student commands and command summary, the 'list' portion.
+* Under group commands and command summary, the 'tasks' portion.
+* Added the "How to Use This Guide" section.
+* Added the "Glossary" section.
+* Updated the "Appendix" section to include requirements for all Persons' fields.
+* Updated hyperlink format based on peer-review.
+* Added dividers throughout UG.
+* Made sure screenshots were up-to-date when features are added or updated.
+
+## Contributions to the DG
+* Updated the diagrams and description under Model Component section.
+* Under Use-cases, the 'tasks' feature.
+* Updated user stories for assigned tasks.
+* Under implementation section, the tutorial field and list tasks features.
+
+## Contributions to team-based tasks
+* Did equal share of tasks assigned.
+* Filmed and edited the product demo.
+* Brainstormed and suggested new features during each milestone.
+* Ensured all team deliverables were met in time.
+
+## Review / mentoring contributions
+* Reviewed teammates' PRs and gave suggestions when appropriate.
+
+## Contributions beyond the project team
+* Identified and assessed bugs in other teams' projects.
diff --git a/docs/team/neyapraveen.md b/docs/team/neyapraveen.md
new file mode 100644
index 00000000000..d072f10af9b
--- /dev/null
+++ b/docs/team/neyapraveen.md
@@ -0,0 +1,55 @@
+---
+layout: page
+title: Neya's Project Portfolio Page
+---
+# Overview
+
+StudentConnect is the solution for hassle-free team formation for CS2103T students to browse profiles and connect with others for group work.
+
+# Summary of Contributions
+
+## Code contributed
+* Link to my [Reposense Report](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=neya&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code&since=2023-09-22)
+
+## Enhancements implemented
+* **Enhancement**: Changed `delete` feature to delete by email rather than by index.
+* **Enhancement**: Redid the UI, making styling decisions such as colour scheme, fonts.
+* **Enhancement**: Edited UI for GroupListPanel, so it would resemble PersonListPanel.
+* **New Feature**: Added hyperlinks to the social media field, so users can open the urls in a browser.
+* **New Feature**: Implemented the `deleteGroup` command.
+* **New Feature**: Implemented the `leave` command.
+* **New Feature**: Added classes for `Todo`, `Deadlines` and `Tasklist` before implementation of task functions.
+* **New Feature**: Implemented and styled a confirmation pop-up for clear command (including css).
+
+## Contributions to testing
+* Changed test cases pertaining to `delete` feature, `clear` feature.
+* Created a `JavaFXInitialiser` to be used for fx and ui related tests.
+* Implemented `PersonCardTest`.`deleteGroupCommandTest`, `deleteGroupCommandParserTest`, `leaveCommandTest`, `leaveCommandParserTest`.
+
+## Contributions to the UG
+* Added documentation for the feature `delete`, `clear`, including the usage of the pop-up, `leave`, `deleteGroup` and social media hyperlinks.
+* Created Table of Contents.
+* Reformatted UG according to general commands, person commands and group commands.
+* Wrote the Introduction section and created the Appendix section.
+* Added page breaks for formatting and back to table of contents hyperlink at the end of pages.
+* Added relevant hyperlinks like download link for java 11 and warning messages.
+* Added hyperlinks of glossary terms.
+
+## Contributions to the DG
+* Updated initial contents in DG after discussion(target user profile, value proposition, user stories, use cases, NFRs, Glossary).
+* Under Use-cases, `Delete a student`, `Access social media` , `Clearing all data`, `Delete a group`, `Leave a group`.
+* Add relevant user stories for clear, delete group, leave group, social media links, delete.
+* Rearranged all use cases and numbered all for easy reference.
+* Added sequence diagram using PlantUML.
+
+## Contributions to team-based tasks
+* Did equal share of tasks assigned.
+* Wrote intro and Brainstormed ideas for features for further iterations.
+
+## Review/ mentoring contributions
+* Reviewed teammates' PRs and gave suggestions when appropriate.
+* Helped with teammates' UI and debugging, ensured code quality, helped in adding javadocs.
+
+## Contributions beyond the project team
+* Participated in load testing, helped in finding other group's bugs during PED.
+* Helped peer in forum.
diff --git a/docs/team/pearlynnt.md b/docs/team/pearlynnt.md
new file mode 100644
index 00000000000..72dbbf6aa6c
--- /dev/null
+++ b/docs/team/pearlynnt.md
@@ -0,0 +1,59 @@
+---
+layout: page
+title: Pearlynn Toh's Project Portfolio Page
+---
+
+### Overview
+StudentConnect is the solution for hassle-free team formation for CS2103T students to browse profiles and connect with others for group work.
+
+### Summary of Contributions
+Given below are my contributions to the project.
+
+**Code contributed**
+* Link to my [RepoSense report](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=pearlynnt&breakdown=false&sort=groupTitle%20dsc&sortWithin=title&since=2023-09-22&timeframe=commit&mergegroup=&groupSelect=groupByRepos)
+
+**Enhancements implemented**
+* **Edit Student Feature**: Added the ability to edit a student's personal details
+ * What it does: This feature allows the user to edit a student's personal details in the system.
+ * Justification: This feature helps users to keep their personal details up-to-date.
+ * Highlights: Modified the code to allow the new student fields to be editable.
+* **Find Students Feature**: Added the ability to find student(s) by name
+ * What it does: This feature allows the user to find student(s) by name with any combination of partial keyword(s).
+ * Justification: This feature helps users to find other students more quickly and easily.
+ * Highlights: Modified the code to check if each word in the students' name contains the keyword(s) instead of having to match the keyword(s).
+* **Filter Students Feature**: Added the ability to filter students by tutorial
+ * What it does: This feature allows the user to filter students by tutorial with any combination of tutorial slot(s).
+ * Justification: This feature helps users to search for other students who are also interested in attending or are assigned to the same tutorial slot.
+ * Highlights: A new Predicate class had to be written to support this feature. Invalid tutorial slot(s) provided by the user had to be handled gracefully.
+* **Find Group Feature**: Added the ability to find a group by group number
+ * What it does: This feature allows the user to find a project group by a group number.
+ * Justification: This feature helps users to find a specific group more quickly and easily.
+ * Highlights: A new Predicate class had to be written to support this feature. Invalid group number provided by the user had to be handled gracefully.
+* **Filter Groups Feature**: Added the ability to filter groups by tutorial
+ * What it does: This feature allows the user to filter groups by a tutorial slot.
+ * Justification: This feature helps users to search for groups that belong to a particular tutorial that they may prefer.
+ * Highlights: A new Predicate class had to be written to support this feature. Invalid tutorial slot provided by the user had to be handled gracefully.
+* **Check Group Feature**: Added the ability to check if a group fulfils the diversity requirements of the course
+ * What it does: This feature allows the user to check the composition of a group's members.
+ * Justification: This feature helps users to check if a group fulfils the diversity group formation requirements of the course with a single command.
+ * Highlights: Different warning messages were written to inform users of what the group may fall short of in terms of group formation. Extensive test cases and test data were written to rigorously test the different possible composition of the group members.
+
+**Contributions to the UG**
+* Added the 'Edit Command', 'Find Command', 'Filter Command', 'Find Group Command', 'Filter Group Command', 'Check Command' sections.
+* Ensured consistent formatting of the content in the User Guide.
+
+**Contributions to the DG**
+* Added the 'Edit a Student', 'Find a student', 'Filter students', 'Find a group', 'Filter groups', 'Check a group' use cases.
+* Updated the Storage Class Diagram using PlantUML.
+
+**Contributions to team-based tasks**
+* Set up the team's Github Repository, and project documentation.
+* Managed the milestones on Github.
+* Led the team's project meetings.
+
+**Review/mentoring contributions**
+* Reviewed the team's Github pull requests and gave comments where appropriate. (e.g., [#106](https://github.com/AY2324S1-CS2103T-F12-2/tp/pull/106), [#177](https://github.com/AY2324S1-CS2103T-F12-2/tp/pull/177), [#190](https://github.com/AY2324S1-CS2103T-F12-2/tp/pull/190))
+
+**Contributions beyond the project team**
+* Tested another team's product and reported bugs during PE-D. ([list of issues](https://github.com/PearlynnT/ped/issues))
+* Participated in load testing.
diff --git a/docs/team/wnchan.md b/docs/team/wnchan.md
new file mode 100644
index 00000000000..ccef70a82b4
--- /dev/null
+++ b/docs/team/wnchan.md
@@ -0,0 +1,51 @@
+---
+layout: page
+title: Wei Ning's Project Portfolio Page
+---
+# Overview
+
+StudentConnect is the solution for hassle-free team formation for CS2101/CS2103T students to browse profiles and connect with others for group work.
+
+# Summary of Contributions
+
+## Code contributed
+* Link to my [**RepoSense report**](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code&since=2023-09-22&chartGroupIndex=23&chartIndex=1)
+
+## Enhancements implemented
+* **Updated Feature**: Added the ability to `add` the student's personal details into the system.
+ * Highlights: This enhancement affects existing commands and commands to be added.
+ * Added the different field classes and other classes associated with the feature too.
+ * Challenges: Dealing with errors that occurred while changing the other classes associated.
+
+* **Updated Feature**: Helps the user to find the user guide if they need `help` and see requirements of CS2101/CS2103T groupings.
+ * Highlights: Updated help window to be more useful for our app and target audience.
+ * Added test cases, updated UI of the help window and added relevant information.
+ * Challenges: Adjusting the layout was quite challenging as a beginner in FXML.
+
+* **New Feature**: Added the ability to `mark` a specified task in a specified group.
+ * Highlights: This allows users to keep track of tasks and mark it as done.
+ * Added test cases for the feature.
+
+* **New Feature**: Added the ability to `unmark` a specified task in a specified group.
+ * Highlights: This allows users to keep track of tasks and mark it as not done.
+ * Added test cases for the feature.
+
+## Contributions to the UG
+* Under features and command summary, the `add`, `help`, `mark` and `unmark` portion.
+* Helped standardise messages, added logo and valid major list (in appendix) in the UG.
+
+## Contributions to the DG
+* Brainstormed as a group for the details included in the DG.
+* Under Use-cases, the `add`, `help`, `mark` and `unmark` feature.
+* Updated the sequence diagram under `Logic Component` section and `Appendix: Instructions for manual testing` to include all the commands for StudentConnect.
+
+## Contributions to team-based tasks
+* Did equal share of tasks assigned and brainstormed features that were implemented.
+* Created the StudentConnect logo, updated README.md and index.md.
+
+## Review/ mentoring contributions
+* Review other members' pull requests, gave ideas and suggestions.
+* Help members with debugging and test cases.
+
+## Contributions beyond the project team
+* Helped in the load testing, found bugs of other team's project during PE-D and PE.
diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/address/commons/core/GuiSettings.java
index a97a86ee8d7..873bae6a3f5 100644
--- a/src/main/java/seedu/address/commons/core/GuiSettings.java
+++ b/src/main/java/seedu/address/commons/core/GuiSettings.java
@@ -79,4 +79,5 @@ public String toString() {
.add("windowCoordinates", windowCoordinates)
.toString();
}
+
}
diff --git a/src/main/java/seedu/address/commons/util/JsonParserUtil.java b/src/main/java/seedu/address/commons/util/JsonParserUtil.java
new file mode 100644
index 00000000000..043d8e66dae
--- /dev/null
+++ b/src/main/java/seedu/address/commons/util/JsonParserUtil.java
@@ -0,0 +1,26 @@
+package seedu.address.commons.util;
+
+import java.io.IOException;
+
+import seedu.address.model.AddressBook;
+
+/**
+ * Utility class for parsing JSON data to an AddressBook object.
+ */
+public class JsonParserUtil {
+
+ /**
+ * Parses a JSON string into an AddressBook object.
+ *
+ * @param jsonString The JSON string to be parsed.
+ * @return The parsed AddressBook object.
+ * @throws IOException If there is an issue with the I/O operation during parsing.
+ */
+ public static AddressBook parseJsonToAddressBook(String jsonString) throws IOException {
+ try {
+ return JsonUtil.fromJsonString(jsonString, AddressBook.class);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/address/commons/util/StringUtil.java
index 61cc8c9a1cb..ffe8585301f 100644
--- a/src/main/java/seedu/address/commons/util/StringUtil.java
+++ b/src/main/java/seedu/address/commons/util/StringUtil.java
@@ -38,6 +38,56 @@ public static boolean containsWordIgnoreCase(String sentence, String word) {
.anyMatch(preppedWord::equalsIgnoreCase);
}
+ /**
+ * Returns true if the {@code sentence} contains the {@code word}.
+ * Ignores case, accepts a partial word match.
+ * examples:
+ * containsIgnoreCase("ABc def", "abc") == true
+ * containsIgnoreCase("ABc def", "DEF") == true
+ * containsIgnoreCase("ABc def", "AB") == true
+ * containsIgnoreCase("ABc def", "abcd") == false
+ *
+ *
+ * @param sentence cannot be null
+ * @param str cannot be null, cannot be empty, must be a single string
+ */
+ public static boolean containsIgnoreCase(String sentence, String str) {
+ requireNonNull(sentence);
+ requireNonNull(str);
+
+ String preppedStr = str.trim();
+ checkArgument(!preppedStr.isEmpty(), "Word parameter cannot be empty");
+ checkArgument(preppedStr.split("\\s+").length == 1, "Word parameter should be a single word");
+
+ String preppedSentence = sentence;
+ String[] wordsInPreppedSentence = preppedSentence.split("\\s+");
+
+ return Arrays.stream(wordsInPreppedSentence)
+ .anyMatch(word -> word.toLowerCase().contains(preppedStr.toLowerCase()));
+ }
+
+ /**
+ * Returns true if the {@code list} contains the {@code tut}.
+ * A full tutorial match is required.
+ *
+ * @param list cannot be null
+ * @param tut cannot be null, cannot be empty, must be a single number
+ */
+ public static boolean containsTutorial(String list, String tut) {
+ requireNonNull(list);
+ requireNonNull(tut);
+
+ String preppedTut = tut.trim();
+ checkArgument(!preppedTut.isEmpty(), "Tutorial parameter cannot be empty");
+ checkArgument(preppedTut.split("\\s+").length == 1, "Tutorial parameter should be a single number");
+
+ String preppedList = list;
+ String[] tutsInPreppedList = preppedList.split("\\s+");
+
+ return Arrays.stream(tutsInPreppedList)
+ .anyMatch(preppedTut::contentEquals);
+ }
+
/**
* Returns a detailed message of the t, including the stack trace.
*/
diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java
index 92cd8fa605a..663c9b94aea 100644
--- a/src/main/java/seedu/address/logic/Logic.java
+++ b/src/main/java/seedu/address/logic/Logic.java
@@ -8,6 +8,7 @@
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.group.Group;
import seedu.address.model.person.Person;
/**
@@ -33,6 +34,9 @@ public interface Logic {
/** Returns an unmodifiable view of the filtered list of persons */
ObservableList getFilteredPersonList();
+ /** Returns an unmodifiable view of the filtered list of groups */
+ ObservableList getFilteredGroupList();
+
/**
* 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..8b28747c12a 100644
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ b/src/main/java/seedu/address/logic/LogicManager.java
@@ -15,6 +15,7 @@
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.Model;
import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.group.Group;
import seedu.address.model.person.Person;
import seedu.address.storage.Storage;
@@ -71,6 +72,11 @@ public ObservableList getFilteredPersonList() {
return model.getFilteredPersonList();
}
+ @Override
+ public ObservableList getFilteredGroupList() {
+ return model.getFilteredGroupList();
+ }
+
@Override
public Path getAddressBookFilePath() {
return model.getAddressBookFilePath();
diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java
index ecd32c31b53..dbfa450f004 100644
--- a/src/main/java/seedu/address/logic/Messages.java
+++ b/src/main/java/seedu/address/logic/Messages.java
@@ -5,6 +5,7 @@
import java.util.stream.Stream;
import seedu.address.logic.parser.Prefix;
+import seedu.address.model.group.Group;
import seedu.address.model.person.Person;
/**
@@ -15,12 +16,17 @@ 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_INVALID_PERSON_DISPLAYED_EMAIL = "The email provided is invalid";
public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!";
+ public static final String MESSAGE_GROUPS_LISTED_OVERVIEW = "%1$d groups listed!";
public static final String MESSAGE_DUPLICATE_FIELDS =
"Multiple values specified for the following single-valued field(s): ";
/**
- * Returns an error message indicating the duplicate prefixes.
+ * Returns an error message indicating duplicate prefixes.
+ *
+ * @param duplicatePrefixes An array of Prefixes that are duplicated.
+ * @return Error message indicating duplicate prefixes.
*/
public static String getErrorMessageForDuplicatePrefixes(Prefix... duplicatePrefixes) {
assert duplicatePrefixes.length > 0;
@@ -32,19 +38,33 @@ public static String getErrorMessageForDuplicatePrefixes(Prefix... duplicatePref
}
/**
- * Formats the {@code person} for display to the user.
+ * Formats the given {@code person} for display to the user.
+ *
+ * @param person The Person object to be formatted.
+ * @return A formatted string representing the Person's information.
*/
public static String format(Person person) {
final StringBuilder builder = new StringBuilder();
- builder.append(person.getName())
- .append("; Phone: ")
- .append(person.getPhone())
- .append("; Email: ")
- .append(person.getEmail())
- .append("; Address: ")
- .append(person.getAddress())
- .append("; Tags: ");
- person.getTags().forEach(builder::append);
+ builder.append("Name: ").append(person.getName())
+ .append("; Major: ").append(person.getMajor())
+ .append("; Year: ").append(person.getYear())
+ .append("; Email: ").append(person.getEmail())
+ .append("; Description: ").append(person.getDescription())
+ .append("; Tutorial: ").append(person.getTutorials())
+ .append("; Social Media: ").append(person.getSocialMediaLinks());
+ return builder.toString();
+ }
+
+ /**
+ * Formats the given {@code group} for display to the user.
+ *
+ * @param group The Group object to be formatted.
+ * @return A formatted string representing the Group's information.
+ */
+ public static String format(Group group) {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("Group Number: ").append(group.getNumber())
+ .append("; Members: ").append(group.getMembers()); // todo: needs to be edited, formatting is wrong
return builder.toString();
}
diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java
index 5d7185a9680..79adc5dd98c 100644
--- a/src/main/java/seedu/address/logic/commands/AddCommand.java
+++ b/src/main/java/seedu/address/logic/commands/AddCommand.java
@@ -1,11 +1,15 @@
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_DESCRIPTION;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GENDER;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MAJOR;
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_NATIONALITY;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SOCIAL_MEDIA_LINK;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TUTORIAL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_YEAR;
import seedu.address.commons.util.ToStringBuilder;
import seedu.address.logic.Messages;
@@ -14,29 +18,37 @@
import seedu.address.model.person.Person;
/**
- * Adds a person to the address book.
+ * Adds a person to StudentConnect.
*/
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. "
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a student to StudentConnect. "
+ "Parameters: "
+ PREFIX_NAME + "NAME "
- + PREFIX_PHONE + "PHONE "
+ + PREFIX_MAJOR + "MAJOR "
+ + PREFIX_YEAR + "YEAR "
+ PREFIX_EMAIL + "EMAIL "
- + PREFIX_ADDRESS + "ADDRESS "
- + "[" + PREFIX_TAG + "TAG]...\n"
+ + PREFIX_DESCRIPTION + "DESCRIPTION "
+ + "[" + PREFIX_TUTORIAL + "TUTORIAL]... "
+ + "[" + PREFIX_SOCIAL_MEDIA_LINK + "SOCIAL_MEDIA_LINK]... "
+ + PREFIX_NATIONALITY + "NATIONALITY "
+ + PREFIX_GENDER + "GENDER \n"
+ "Example: " + COMMAND_WORD + " "
+ PREFIX_NAME + "John Doe "
- + PREFIX_PHONE + "98765432 "
- + PREFIX_EMAIL + "johnd@example.com "
- + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 "
- + PREFIX_TAG + "friends "
- + PREFIX_TAG + "owesMoney";
+ + PREFIX_MAJOR + "Computer Science "
+ + PREFIX_YEAR + "2 "
+ + PREFIX_EMAIL + "johnd@u.nus.edu "
+ + PREFIX_DESCRIPTION + "I love programming in my free time "
+ + PREFIX_TUTORIAL + "02 "
+ + PREFIX_SOCIAL_MEDIA_LINK + "https://www.linkedin.com/in/john-doe-123456789 "
+ + PREFIX_NATIONALITY + "local "
+ + PREFIX_GENDER + "M";
- public static final String MESSAGE_SUCCESS = "New person added: %1$s";
- public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book";
+ public static final String MESSAGE_SUCCESS = "Details added successfully! New student added: %1$s";
+ public static final String MESSAGE_DUPLICATE_PERSON = "This student is already on StudentConnect as this "
+ + "email has already been used.";
private final Person toAdd;
diff --git a/src/main/java/seedu/address/logic/commands/CheckCommand.java b/src/main/java/seedu/address/logic/commands/CheckCommand.java
new file mode 100644
index 00000000000..fd9c304ac0c
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/CheckCommand.java
@@ -0,0 +1,209 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_GROUPS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import java.util.Set;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.group.Group;
+import seedu.address.model.group.GroupContainsKeywordsPredicate;
+import seedu.address.model.person.Person;
+import seedu.address.model.tutorial.Tutorial;
+
+/**
+ * Checks a group requirement by its group number.
+ */
+public class CheckCommand extends Command {
+
+ public static final String COMMAND_WORD = "checkGroup";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Checks the group identified by its group number.\n"
+ + "Parameters: GROUP_NUMBER\n"
+ + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String GROUP_NUM = "Group %1$s\n";
+ public static final String MESSAGE_CHECK_GROUP_SUCCESS =
+ "Group fulfils the diversity requirements of CS2103T.";
+ public static final String MESSAGE_HELP =
+ "You can enter the `help` command for more information on group requirements.";
+ public static final String MESSAGE_CHECK_GROUP_SIZE_EMPTY =
+ "Group does not have any members.\n";
+ public static final String MESSAGE_CHECK_GROUP_SIZE_ONE =
+ "Group has only one member.\n";
+ public static final String MESSAGE_CHECK_GROUP_SIZE_UNDER =
+ "Group has less than 5 members.\n";
+ public static final String MESSAGE_CHECK_GROUP_SIZE_OVER =
+ "Group size has exceeded limit with more than 5 members.\n";
+ public static final String MESSAGE_CHECK_GROUP_NATIONALITY_WARNING =
+ "Group comprises of members of the same nationality.\n";
+ public static final String MESSAGE_CHECK_GROUP_GENDER_WARNING =
+ "Group comprises of members of the same gender.\n";
+ public static final String MESSAGE_CHECK_GROUP_TUTORIAL_WARNING =
+ "Not every group member's tutorial matches the group's tutorial.\n";
+ public static final String MESSAGE_CHECK_GROUP_NOT_FOUND = "Group with the provided group number not found.";
+ private final GroupContainsKeywordsPredicate predicate;
+
+
+ private final int groupNumber;
+
+ /**
+ * @param groupNumber unique identifier of the group
+ * @param predicate check group number
+ */
+ public CheckCommand(int groupNumber, GroupContainsKeywordsPredicate predicate) {
+ this.groupNumber = groupNumber;
+ this.predicate = predicate;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ model.updateFilteredGroupList(predicate);
+
+ // Check if the group with the provided group number exists
+ Group groupToCheck = model.getGroupWithNumber(groupNumber)
+ .orElseThrow(() -> new CommandException(MESSAGE_CHECK_GROUP_NOT_FOUND));
+
+ String message = "";
+ boolean isSuccess = true;
+ Set groupMembers = groupToCheck.getMembers();
+
+ // check group size
+ if (groupMembers.isEmpty()) {
+ isSuccess = false;
+ message = MESSAGE_CHECK_GROUP_SIZE_EMPTY;
+ } else if (groupMembers.size() == 1) {
+ isSuccess = false;
+ message = MESSAGE_CHECK_GROUP_SIZE_ONE;
+ } else if (groupMembers.size() > 1 && groupMembers.size() < 5) {
+ isSuccess = false;
+ message = MESSAGE_CHECK_GROUP_SIZE_UNDER;
+ } else if (groupMembers.size() > 5) {
+ isSuccess = false;
+ message = MESSAGE_CHECK_GROUP_SIZE_OVER;
+ }
+
+ if (groupMembers.size() > 1) {
+ String groupTutorial = groupToCheck.getTutorial().value;
+ isSuccess = hasMixNationality(groupMembers) && hasMixGender(groupMembers)
+ && hasGroupTutorial(groupMembers, groupTutorial);
+
+ if (!hasMixNationality(groupMembers)) {
+ message += MESSAGE_CHECK_GROUP_NATIONALITY_WARNING;
+ }
+ if (!hasMixGender(groupMembers)) {
+ message += MESSAGE_CHECK_GROUP_GENDER_WARNING;
+ }
+ if (!hasGroupTutorial(groupMembers, groupTutorial)) {
+ message += MESSAGE_CHECK_GROUP_TUTORIAL_WARNING;
+ }
+ }
+
+ if (isSuccess) {
+ return new CommandResult(String.format(GROUP_NUM + MESSAGE_CHECK_GROUP_SUCCESS, groupToCheck.getNumber()),
+ false, false, true, false);
+ } else {
+ return new CommandResult(String.format(GROUP_NUM + message + MESSAGE_HELP, groupToCheck.getNumber()),
+ false, false, true, false);
+ }
+ }
+
+ /**
+ * Checks if a group has a mix of local and foreigner members.
+ *
+ * @param groupMembers The set of persons representing the group members.
+ * @return True if there is a mix of local and foreigner members, false otherwise.
+ */
+ private boolean hasMixNationality(Set groupMembers) {
+ int localCount = 0;
+ int foreignerCount = 0;
+ for (Person member : groupMembers) {
+ if (member.getNationality().value.equals("local")) {
+ localCount++;
+ } else {
+ foreignerCount++;
+ }
+ }
+
+ if (localCount == 0 || foreignerCount == 0) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Checks if a group has a mix of male and female members.
+ *
+ * @param groupMembers The set of persons representing the group members.
+ * @return True if there is a mix of male and female members, false otherwise.
+ */
+ private boolean hasMixGender(Set groupMembers) {
+ int maleCount = 0;
+ int femaleCount = 0;
+ for (Person member : groupMembers) {
+ if (member.getGender().value.equals("M")) {
+ maleCount++;
+ } else {
+ femaleCount++;
+ }
+ }
+
+ if (maleCount == 0 || femaleCount == 0) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Checks if all members of a group belong to a specific tutorial.
+ *
+ * @param groupMembers The set of persons representing the group members.
+ * @param groupTutorial The tutorial code to check against.
+ * @return True if all members belong to the specified tutorial, false otherwise.
+ */
+ private boolean hasGroupTutorial(Set groupMembers, String groupTutorial) {
+ int count = 0;
+ for (Person member : groupMembers) {
+ for (Tutorial tut : member.getTutorials()) {
+ if (tut.value.equals((groupTutorial))) {
+ count++;
+ continue;
+ }
+ }
+ }
+
+ if (count != groupMembers.size()) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof CheckCommand)) {
+ return false;
+ }
+
+ CheckCommand otherCheckCommand = (CheckCommand) other;
+ return groupNumber == otherCheckCommand.groupNumber;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("groupNumber", groupNumber)
+ .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..ebc14d505ca 100644
--- a/src/main/java/seedu/address/logic/commands/ClearCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java
@@ -1,23 +1,48 @@
package seedu.address.logic.commands;
-import static java.util.Objects.requireNonNull;
-
+import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.AddressBook;
import seedu.address.model.Model;
+import seedu.address.ui.ConfirmationPopup;
/**
- * Clears the address book.
+ * Clears the address book with a confirmation popup.
*/
public class ClearCommand extends Command {
public static final String COMMAND_WORD = "clear";
- public static final String MESSAGE_SUCCESS = "Address book has been cleared!";
+ public static final String MESSAGE_SUCCESS = "All student data has been cleared.";
+
+ public static final String SHOWING_CONFIRMATION_MESSAGE = "Opened confirmation window. "
+ + "Please ensure you use the exit command when exiting StudentConnect for successful reset.";
+
+ private boolean isConfirmed = false;
@Override
- public CommandResult execute(Model model) {
- requireNonNull(model);
- model.setAddressBook(new AddressBook());
- return new CommandResult(MESSAGE_SUCCESS);
+ public CommandResult execute(Model model) throws CommandException {
+ ConfirmationPopup confirmationPopup = new ConfirmationPopup();
+ confirmationPopup.setConfirmationCallback(confirmed -> {
+ if (confirmed) {
+ this.isConfirmed = true;
+ model.setAddressBook(new AddressBook());
+ }
+ });
+ confirmationPopup.show();
+
+ if (isConfirmed) {
+ return new CommandResult(SHOWING_CONFIRMATION_MESSAGE, false, false, false, true);
+ } else {
+ return new CommandResult(SHOWING_CONFIRMATION_MESSAGE, false, false, false, false);
+ }
+ }
+
+ /**
+ * Sets the confirmation status of the popup.
+ *
+ * @param isConfirmed The confirmation status to be set. True if confirmed, false otherwise.
+ */
+ public void setConfirmed(boolean isConfirmed) {
+ this.isConfirmed = isConfirmed;
}
}
diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java
index 249b6072d0d..d9f2fadde2e 100644
--- a/src/main/java/seedu/address/logic/commands/CommandResult.java
+++ b/src/main/java/seedu/address/logic/commands/CommandResult.java
@@ -6,6 +6,7 @@
import seedu.address.commons.util.ToStringBuilder;
+
/**
* Represents the result of a command execution.
*/
@@ -13,19 +14,37 @@ public class CommandResult {
private final String feedbackToUser;
- /** Help information should be shown to the user. */
+ /**
+ * Help information should be shown to the user.
+ */
private final boolean showHelp;
- /** The application should exit. */
+ /**
+ * The application should exit.
+ */
private final boolean exit;
+ /**
+ * Group UI should be shown.
+ */
+ private final boolean groupCommand;
+
+ /**
+ * Data should be cleared.
+ */
+ private final boolean clear;
+
/**
* Constructs a {@code CommandResult} with the specified fields.
*/
- public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) {
+
+ public CommandResult(String feedbackToUser, boolean showHelp,
+ boolean exit, boolean groupCommand, boolean clear) {
this.feedbackToUser = requireNonNull(feedbackToUser);
this.showHelp = showHelp;
this.exit = exit;
+ this.groupCommand = groupCommand;
+ this.clear = clear;
}
/**
@@ -33,7 +52,7 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) {
* and other fields set to their default value.
*/
public CommandResult(String feedbackToUser) {
- this(feedbackToUser, false, false);
+ this(feedbackToUser, false, false, false, false);
}
public String getFeedbackToUser() {
@@ -48,6 +67,14 @@ public boolean isExit() {
return exit;
}
+ public boolean isGroupCommand() {
+ return groupCommand;
+ }
+
+ public boolean isClear() {
+ return clear;
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
@@ -62,12 +89,14 @@ public boolean equals(Object other) {
CommandResult otherCommandResult = (CommandResult) other;
return feedbackToUser.equals(otherCommandResult.feedbackToUser)
&& showHelp == otherCommandResult.showHelp
- && exit == otherCommandResult.exit;
+ && exit == otherCommandResult.exit
+ && clear == otherCommandResult.clear
+ && groupCommand == otherCommandResult.groupCommand;
}
@Override
public int hashCode() {
- return Objects.hash(feedbackToUser, showHelp, exit);
+ return Objects.hash(feedbackToUser, showHelp, exit, groupCommand, clear);
}
@Override
@@ -76,7 +105,8 @@ public String toString() {
.add("feedbackToUser", feedbackToUser)
.add("showHelp", showHelp)
.add("exit", exit)
+ .add("groupCommand", groupCommand)
+ .add("clear", clear)
.toString();
}
-
}
diff --git a/src/main/java/seedu/address/logic/commands/CreateCommand.java b/src/main/java/seedu/address/logic/commands/CreateCommand.java
new file mode 100644
index 00000000000..aa5cbd1dc7b
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/CreateCommand.java
@@ -0,0 +1,84 @@
+package seedu.address.logic.commands;
+
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TUTORIAL;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_GROUPS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javafx.collections.ObservableList;
+import seedu.address.model.Model;
+import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.group.Group;
+import seedu.address.model.tutorial.Tutorial;
+
+/**
+ * Creates a new empty group.
+ */
+public class CreateCommand extends Command {
+
+ public static final String COMMAND_WORD = "create";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Creates a new empty group.\n"
+ + "Parameters: "
+ + PREFIX_TUTORIAL + "TUTORIAL "
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_TUTORIAL + "02";
+
+ public static final String MESSAGE_SUCCESS = "Group created successfully! Group number is %1$s";
+
+ private final Tutorial tutorial;
+
+ public CreateCommand(Tutorial tutorial) {
+ this.tutorial = tutorial;
+ }
+
+ @Override
+ public CommandResult execute(Model model) {
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ int number = generateGroupNumber(model);
+ Group createdGroup = new Group(number, tutorial);
+ model.addGroup(createdGroup);
+
+ return new CommandResult(String.format(MESSAGE_SUCCESS, createdGroup.getNumber()),
+ false, false, true, false);
+ }
+
+ /**
+ * Generates the next group number to be used when creating a new group.
+ *
+ * @param model
+ */
+ public int generateGroupNumber(Model model) {
+ int number = 1;
+ ReadOnlyAddressBook addressBook = model.getAddressBook();
+ ObservableList groups = addressBook.getGroupList();
+
+ if (!groups.isEmpty()) {
+ List groupNumbers = groups.stream()
+ .map(Group::getNumber).collect(Collectors.toList());
+ while (groupNumbers.contains(number)) {
+ number++;
+ }
+ }
+
+ return number;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof CreateCommand)) {
+ return false;
+ }
+
+ CreateCommand otherCreateCommand = (CreateCommand) other;
+ return tutorial.equals(otherCreateCommand.tutorial);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
index 1135ac19b74..058594294d8 100644
--- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java
+++ b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
@@ -1,48 +1,64 @@
package seedu.address.logic.commands;
import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_GROUPS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
-import java.util.List;
+import java.util.Optional;
-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.group.Group;
+import seedu.address.model.person.Email;
import seedu.address.model.person.Person;
/**
- * Deletes a person identified using it's displayed index from the address book.
+ * Deletes a student 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";
+ + ": Deletes the student identified by the email address.\n"
+ + "Parameters: EMAIL\n"
+ + "Example: " + COMMAND_WORD + " alexyeoh@u.nus.edu";
- public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s";
+ public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Student deleted Successfully! Deleted Student: %1$s";
+ public static final String MESSAGE_DELETE_EMAIL_NOT_FOUND = "Student with the provided email not found.";
+ private final Email targetEmail;
- private final Index targetIndex;
-
- public DeleteCommand(Index targetIndex) {
- this.targetIndex = targetIndex;
+ public DeleteCommand(Email targetEmail) {
+ this.targetEmail = targetEmail;
}
@Override
public CommandResult execute(Model model) throws CommandException {
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
requireNonNull(model);
- List lastShownList = model.getFilteredPersonList();
- if (targetIndex.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ // Find the person with the provided email
+ Optional personToDelete = model.getPersonWithEmail(targetEmail);
+
+ if (personToDelete.isEmpty()) {
+ throw new CommandException(MESSAGE_DELETE_EMAIL_NOT_FOUND);
}
- Person personToDelete = lastShownList.get(targetIndex.getZeroBased());
- model.deletePerson(personToDelete);
- return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, Messages.format(personToDelete)));
+ // Check if the person is in any groups
+ for (Group group : model.getAddressBook().getGroupList()) {
+ if (group.hasMember(personToDelete.get())) {
+ // If the person is in a group, remove them from the group
+ model.removePersonFromGroup(personToDelete.get(), group);
+ }
+ }
+
+ // Delete the person from the model
+ model.deletePerson(personToDelete.get());
+
+ return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, Messages.format(personToDelete.get())));
}
@Override
@@ -57,13 +73,13 @@ public boolean equals(Object other) {
}
DeleteCommand otherDeleteCommand = (DeleteCommand) other;
- return targetIndex.equals(otherDeleteCommand.targetIndex);
+ return targetEmail.equals(otherDeleteCommand.targetEmail);
}
@Override
public String toString() {
return new ToStringBuilder(this)
- .add("targetIndex", targetIndex)
+ .add("targetEmail", targetEmail)
.toString();
}
}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteGroupCommand.java b/src/main/java/seedu/address/logic/commands/DeleteGroupCommand.java
new file mode 100644
index 00000000000..a41d454fb33
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/DeleteGroupCommand.java
@@ -0,0 +1,62 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_GROUPS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.group.Group;
+
+/**
+ * Deletes a group identified by its group number.
+ */
+public class DeleteGroupCommand extends Command {
+ public static final String COMMAND_WORD = "deleteGroup";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Deletes the group identified by its group number.\n"
+ + "Parameters: gr/GROUP_NUMBER\n"
+ + "Example: " + COMMAND_WORD + " gr/1";
+
+ public static final String MESSAGE_DELETE_GROUP_SUCCESS = "Group deleted successfully! Deleted Group: %1$s";
+ public static final String MESSAGE_DELETE_GROUP_NOT_FOUND = "Group with the provided group number not found.";
+
+ private final int groupNumber;
+
+ /**
+ * Creates a DeleteGroupCommand to delete the group with the specified group number.
+ *
+ * @param groupNumber The group number of the group to be deleted.
+ */
+ public DeleteGroupCommand(int groupNumber) {
+ this.groupNumber = groupNumber;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ requireNonNull(model);
+
+ // Check if the group with the provided group number exists
+ Group groupToDelete = model.getGroupWithNumber(groupNumber)
+ .orElseThrow(() -> new CommandException(MESSAGE_DELETE_GROUP_NOT_FOUND));
+
+ // Delete the group from the model
+ model.deleteGroup(groupToDelete);
+
+ return new CommandResult(String.format(MESSAGE_DELETE_GROUP_SUCCESS, groupToDelete.getNumber()),
+ false, false, true, false);
+ }
+
+ /**
+ * Gets the group number of the group to be deleted.
+ *
+ * @return The group number.
+ */
+ public int getGroupNumber() {
+ return groupNumber;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java
index 4b581c7331e..f50667d128c 100644
--- a/src/main/java/seedu/address/logic/commands/EditCommand.java
+++ b/src/main/java/seedu/address/logic/commands/EditCommand.java
@@ -1,90 +1,111 @@
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_DESCRIPTION;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GENDER;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MAJOR;
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_NATIONALITY;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SOCIAL_MEDIA_LINK;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TUTORIAL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_YEAR;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_GROUPS;
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.group.Group;
+import seedu.address.model.person.Description;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Gender;
+import seedu.address.model.person.Major;
import seedu.address.model.person.Name;
+import seedu.address.model.person.Nationality;
import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.person.Year;
+import seedu.address.model.socialmedialink.SocialMediaLink;
+import seedu.address.model.tutorial.Tutorial;
/**
- * Edits the details of an existing person in the address book.
+ * Edits the details of an existing student in StudentConnect.
*/
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. "
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the student identified "
+ + "by the email used in the displayed student list. "
+ "Existing values will be overwritten by the input values.\n"
- + "Parameters: INDEX (must be a positive integer) "
+ + "Parameters: EMAIL (must end with u.nus.edu) "
+ "[" + PREFIX_NAME + "NAME] "
- + "[" + PREFIX_PHONE + "PHONE] "
+ + "[" + PREFIX_MAJOR + "MAJOR] "
+ + "[" + PREFIX_YEAR + "YEAR] "
+ "[" + 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";
+ + "[" + PREFIX_DESCRIPTION + "DESCRIPTION] "
+ + "[" + PREFIX_TUTORIAL + "TUTORIAL]... "
+ + "[" + PREFIX_SOCIAL_MEDIA_LINK + "SOCIAL_MEDIA_LINK]... "
+ + "[" + PREFIX_NATIONALITY + "NATIONALITY] "
+ + "[" + PREFIX_GENDER + "GENDER] \n"
+ + "Example: " + COMMAND_WORD + " johnd@u.nus.edu "
+ + PREFIX_YEAR + "3 "
+ + PREFIX_EMAIL + "johndoe@u.nus.edu";
+
+ public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Details edited successfully! Edited Student: %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.";
+ public static final String MESSAGE_DUPLICATE_PERSON = "This student is already on StudentConnect as this "
+ + "email has already been used.";
+ public static final String MESSAGE_EMAIL_NOT_FOUND = "Student with the provided email not found.";
- private final Index index;
+ private final Email email;
private final EditPersonDescriptor editPersonDescriptor;
/**
- * @param index of the person in the filtered person list to edit
- * @param editPersonDescriptor details to edit the person with
+ * @param email of the person in the filtered student list to edit
+ * @param editPersonDescriptor details to edit the student with
*/
- public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) {
- requireNonNull(index);
+ public EditCommand(Email email, EditPersonDescriptor editPersonDescriptor) {
+ requireNonNull(email);
requireNonNull(editPersonDescriptor);
- this.index = index;
+ this.email = email;
this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor);
}
@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
- List lastShownList = model.getFilteredPersonList();
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ // Find the person with the provided email
+ Optional personToEdit = model.getPersonWithEmail(email);
- if (index.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ if (personToEdit.isEmpty()) {
+ throw new CommandException(MESSAGE_EMAIL_NOT_FOUND);
}
- Person personToEdit = lastShownList.get(index.getZeroBased());
- Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor);
+ Person editedPerson = createEditedPerson(personToEdit.get(), editPersonDescriptor);
- if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) {
+ if (!personToEdit.get().isSamePerson(editedPerson) && model.hasPerson(editedPerson)) {
throw new CommandException(MESSAGE_DUPLICATE_PERSON);
}
- model.setPerson(personToEdit, editedPerson);
+ model.setPerson(personToEdit.get(), editedPerson);
model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ if (model.personIsInAGroup(editedPerson)) {
+ Group targetGroup = model.getGroupThatPersonIsIn(personToEdit.get());
+ targetGroup.removeMember(personToEdit.get());
+ targetGroup.addMember(editedPerson);
+ }
return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson)));
}
@@ -96,12 +117,19 @@ private static Person createEditedPerson(Person personToEdit, EditPersonDescript
assert personToEdit != null;
Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName());
- Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone());
+ Major updatedMajor = editPersonDescriptor.getMajor().orElse(personToEdit.getMajor());
+ Year updatedYear = editPersonDescriptor.getYear().orElse(personToEdit.getYear());
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);
+ Description updatedDescription = editPersonDescriptor.getDescription().orElse(personToEdit.getDescription());
+ Set updatedTutorials = editPersonDescriptor.getTutorials()
+ .orElse(personToEdit.getTutorials());
+ Set updatedSocialMediaLinks = editPersonDescriptor.getSocialMediaLinks()
+ .orElse(personToEdit.getSocialMediaLinks());
+ Nationality updatedNationality = editPersonDescriptor.getNationality().orElse(personToEdit.getNationality());
+ Gender updatedGender = editPersonDescriptor.getGender().orElse(personToEdit.getGender());
+
+ return new Person(updatedName, updatedMajor, updatedYear, updatedEmail, updatedDescription,
+ updatedTutorials, updatedSocialMediaLinks, updatedNationality, updatedGender);
}
@Override
@@ -116,48 +144,57 @@ public boolean equals(Object other) {
}
EditCommand otherEditCommand = (EditCommand) other;
- return index.equals(otherEditCommand.index)
+ return email.equals(otherEditCommand.email)
&& editPersonDescriptor.equals(otherEditCommand.editPersonDescriptor);
}
@Override
public String toString() {
return new ToStringBuilder(this)
- .add("index", index)
+ .add("email", email)
.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.
+ * Stores the details to edit the student with. Each non-empty field value will replace the
+ * corresponding field value of the student.
*/
public static class EditPersonDescriptor {
private Name name;
- private Phone phone;
+ private Major major;
+ private Year year;
private Email email;
- private Address address;
- private Set tags;
+ private Description description;
+ private Set tutorials;
+ private Set socialMediaLinks;
+ private Nationality nationality;
+ private Gender gender;
public EditPersonDescriptor() {}
/**
* Copy constructor.
- * A defensive copy of {@code tags} is used internally.
+ * A defensive copy of {@code socialMediaLinks} is used internally.
*/
public EditPersonDescriptor(EditPersonDescriptor toCopy) {
setName(toCopy.name);
- setPhone(toCopy.phone);
+ setMajor(toCopy.major);
+ setYear(toCopy.year);
setEmail(toCopy.email);
- setAddress(toCopy.address);
- setTags(toCopy.tags);
+ setDescription(toCopy.description);
+ setTutorials(toCopy.tutorials);
+ setSocialMediaLinks(toCopy.socialMediaLinks);
+ setNationality(toCopy.nationality);
+ setGender(toCopy.gender);
}
/**
* Returns true if at least one field is edited.
*/
public boolean isAnyFieldEdited() {
- return CollectionUtil.isAnyNonNull(name, phone, email, address, tags);
+ return CollectionUtil.isAnyNonNull(name, major, year, email, description,
+ tutorials, socialMediaLinks, nationality, gender);
}
public void setName(Name name) {
@@ -168,12 +205,20 @@ public Optional getName() {
return Optional.ofNullable(name);
}
- public void setPhone(Phone phone) {
- this.phone = phone;
+ public void setMajor(Major major) {
+ this.major = major;
+ }
+
+ public Optional getMajor() {
+ return Optional.ofNullable(major);
}
- public Optional getPhone() {
- return Optional.ofNullable(phone);
+ public void setYear(Year year) {
+ this.year = year;
+ }
+
+ public Optional getYear() {
+ return Optional.ofNullable(year);
}
public void setEmail(Email email) {
@@ -184,29 +229,62 @@ public Optional getEmail() {
return Optional.ofNullable(email);
}
- public void setAddress(Address address) {
- this.address = address;
+ public void setDescription(Description description) {
+ this.description = description;
+ }
+
+ public Optional getDescription() {
+ return Optional.ofNullable(description);
+ }
+
+ /**
+ * Sets {@code tutorials} to this object's {@code tutorials}.
+ * A defensive copy of {@code tutorials} is used internally.
+ */
+ public void setTutorials(Set tutorials) {
+ this.tutorials = (tutorials != null) ? new HashSet<>(tutorials) : null;
+ }
+
+ public Optional getNationality() {
+ return Optional.ofNullable(nationality);
+ }
+
+ public void setNationality(Nationality nationality) {
+ this.nationality = nationality;
}
- public Optional getAddress() {
- return Optional.ofNullable(address);
+ public Optional getGender() {
+ return Optional.ofNullable(gender);
+ }
+
+ public void setGender(Gender gender) {
+ this.gender = gender;
+ }
+ /**
+ * Returns an unmodifiable tutorial set, which throws {@code UnsupportedOperationException}
+ * if modification is attempted.
+ * Returns {@code Optional#empty()} if {@code tutorials} is null.
+ */
+ public Optional> getTutorials() {
+ return (tutorials != null) ? Optional.of(Collections.unmodifiableSet(tutorials)) : Optional.empty();
}
/**
- * Sets {@code tags} to this object's {@code tags}.
- * A defensive copy of {@code tags} is used internally.
+ * Sets {@code socialMediaLinks} to this object's {@code socialMediaLinks}.
+ * A defensive copy of {@code socialMediaLinks} is used internally.
*/
- public void setTags(Set tags) {
- this.tags = (tags != null) ? new HashSet<>(tags) : null;
+ public void setSocialMediaLinks(Set socialMediaLinks) {
+ this.socialMediaLinks = (socialMediaLinks != null) ? new HashSet<>(socialMediaLinks) : null;
}
/**
- * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException}
+ * Returns an unmodifiable social media link set, which throws {@code UnsupportedOperationException}
* if modification is attempted.
- * Returns {@code Optional#empty()} if {@code tags} is null.
+ * Returns {@code Optional#empty()} if {@code socialMediaLinks} is null.
*/
- public Optional> getTags() {
- return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty();
+ public Optional> getSocialMediaLinks() {
+ return (socialMediaLinks != null) ? Optional.of(Collections.unmodifiableSet(socialMediaLinks))
+ : Optional.empty();
}
@Override
@@ -222,20 +300,28 @@ public boolean equals(Object other) {
EditPersonDescriptor otherEditPersonDescriptor = (EditPersonDescriptor) other;
return Objects.equals(name, otherEditPersonDescriptor.name)
- && Objects.equals(phone, otherEditPersonDescriptor.phone)
+ && Objects.equals(major, otherEditPersonDescriptor.major)
+ && Objects.equals(year, otherEditPersonDescriptor.year)
&& Objects.equals(email, otherEditPersonDescriptor.email)
- && Objects.equals(address, otherEditPersonDescriptor.address)
- && Objects.equals(tags, otherEditPersonDescriptor.tags);
+ && Objects.equals(description, otherEditPersonDescriptor.description)
+ && Objects.equals(tutorials, otherEditPersonDescriptor.tutorials)
+ && Objects.equals(socialMediaLinks, otherEditPersonDescriptor.socialMediaLinks)
+ && Objects.equals(nationality, otherEditPersonDescriptor.nationality)
+ && Objects.equals(gender, otherEditPersonDescriptor.gender);
}
@Override
public String toString() {
return new ToStringBuilder(this)
.add("name", name)
- .add("phone", phone)
+ .add("major", major)
+ .add("year", year)
.add("email", email)
- .add("address", address)
- .add("tags", tags)
+ .add("description", description)
+ .add("tutorials", tutorials)
+ .add("social media links", socialMediaLinks)
+ .add("nationality", nationality)
+ .add("gender", gender)
.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..6c2ab8933ea 100644
--- a/src/main/java/seedu/address/logic/commands/ExitCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java
@@ -9,11 +9,12 @@ public class ExitCommand extends Command {
public static final String COMMAND_WORD = "exit";
- public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ...";
+ public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Thank you for using StudentConnect!\n"
+ + "Exiting the application now…";
@Override
public CommandResult execute(Model model) {
- return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true);
+ return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, false, false);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/FilterCommand.java b/src/main/java/seedu/address/logic/commands/FilterCommand.java
new file mode 100644
index 00000000000..bf32a97b6d5
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/FilterCommand.java
@@ -0,0 +1,61 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_GROUPS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.tutorial.TutorialContainsSlotsPredicate;
+
+/**
+ * Filters all students in StudentConnect whose tutorial slots match the filter slots.
+ */
+public class FilterCommand extends Command {
+
+ public static final String COMMAND_WORD = "filter";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Filters all students whose tutorials match any of "
+ + "the specified slots (2-digit numbers between 01 and 22) and displays them as a list with index numbers."
+ + "\nParameters: SLOT [MORE_SLOTS]...\n"
+ + "Example: " + COMMAND_WORD + " 08 15";
+
+ private final TutorialContainsSlotsPredicate predicate;
+
+ public FilterCommand(TutorialContainsSlotsPredicate predicate) {
+ this.predicate = predicate;
+ }
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ 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 FilterCommand)) {
+ return false;
+ }
+
+ FilterCommand otherFilterCommand = (FilterCommand) other;
+ return predicate.equals(otherFilterCommand.predicate);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("predicate", predicate)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/FilterGroupCommand.java b/src/main/java/seedu/address/logic/commands/FilterGroupCommand.java
new file mode 100644
index 00000000000..9428a642023
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/FilterGroupCommand.java
@@ -0,0 +1,62 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_GROUPS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.group.GroupBelongsTutorialPredicate;
+
+/**
+ * Filters all groups in StudentConnect that belong to the filtered tutorial slot.
+ */
+public class FilterGroupCommand extends Command {
+
+ public static final String COMMAND_WORD = "filterGroup";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Filters all groups that belong to the specified "
+ + "tutorial slot (2-digit numbers between 01 and 22) and displays them as a list with index numbers."
+ + "\nParameters: SLOT\n"
+ + "Example: " + COMMAND_WORD + " 01";
+
+ private final GroupBelongsTutorialPredicate predicate;
+
+ public FilterGroupCommand(GroupBelongsTutorialPredicate predicate) {
+ this.predicate = predicate;
+ }
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ model.updateFilteredGroupList(predicate);
+ return new CommandResult(
+ String.format(Messages.MESSAGE_GROUPS_LISTED_OVERVIEW, model.getFilteredGroupList().size()),
+ false, false, true, false);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof FilterGroupCommand)) {
+ return false;
+ }
+
+ FilterGroupCommand otherFilterGroupCommand = (FilterGroupCommand) other;
+ return predicate.equals(otherFilterGroupCommand.predicate);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("predicate", predicate)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java
index 72b9eddd3a7..c21cd98c3d7 100644
--- a/src/main/java/seedu/address/logic/commands/FindCommand.java
+++ b/src/main/java/seedu/address/logic/commands/FindCommand.java
@@ -1,6 +1,8 @@
package seedu.address.logic.commands;
import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_GROUPS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
import seedu.address.commons.util.ToStringBuilder;
import seedu.address.logic.Messages;
@@ -8,14 +10,14 @@
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.
+ * Finds and lists all students in StudentConnect 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 "
+ 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";
@@ -29,6 +31,8 @@ public FindCommand(NameContainsKeywordsPredicate predicate) {
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
model.updateFilteredPersonList(predicate);
return new CommandResult(
String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size()));
diff --git a/src/main/java/seedu/address/logic/commands/FindGroupCommand.java b/src/main/java/seedu/address/logic/commands/FindGroupCommand.java
new file mode 100644
index 00000000000..f228f71491e
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/FindGroupCommand.java
@@ -0,0 +1,62 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_GROUPS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.group.GroupContainsKeywordsPredicate;
+
+/**
+ * Finds and lists all groups in StudentConnect whose group contains any of the argument keywords.
+ */
+public class FindGroupCommand extends Command {
+
+ public static final String COMMAND_WORD = "findGroup";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all groups whose number contain any of "
+ + "the specified keywords and displays them as a list with index numbers.\n"
+ + "Parameters: KEYWORD [MORE_KEYWORDS]...\n"
+ + "Example: " + COMMAND_WORD + " 1 5 10";
+
+ private final GroupContainsKeywordsPredicate predicate;
+
+ public FindGroupCommand(GroupContainsKeywordsPredicate predicate) {
+ this.predicate = predicate;
+ }
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ model.updateFilteredGroupList(predicate);
+ return new CommandResult(
+ String.format(Messages.MESSAGE_GROUPS_LISTED_OVERVIEW, model.getFilteredGroupList().size()),
+ false, false, true, false);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof FindGroupCommand)) {
+ return false;
+ }
+
+ FindGroupCommand otherFindGroupCommand = (FindGroupCommand) other;
+ return predicate.equals(otherFindGroupCommand.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..1faf8126726 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 static seedu.address.model.Model.PREDICATE_SHOW_ALL_GROUPS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
import seedu.address.model.Model;
/**
@@ -16,6 +19,8 @@ public class HelpCommand extends Command {
@Override
public CommandResult execute(Model model) {
- return new CommandResult(SHOWING_HELP_MESSAGE, true, false);
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ return new CommandResult(SHOWING_HELP_MESSAGE, true, false, false, false);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/JoinCommand.java b/src/main/java/seedu/address/logic/commands/JoinCommand.java
new file mode 100644
index 00000000000..b402052d307
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/JoinCommand.java
@@ -0,0 +1,118 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GROUP;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_GROUPS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import java.util.Optional;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.group.Group;
+import seedu.address.model.person.Email;
+import seedu.address.model.person.Person;
+
+/**
+ * Adds a student specified by email to a group specified by group number.
+ */
+public class JoinCommand extends Command {
+
+ public static final String COMMAND_WORD = "join";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Adds the student specified by email to the group specified by group number.\n"
+ + "Group number must be a positive integer. The maximum number of members a group can have is 5.\n"
+ + "Parameters: "
+ + PREFIX_EMAIL + "EMAIL "
+ + PREFIX_GROUP + "GROUP NUMBER\n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_EMAIL + "johnd@u.nus.edu "
+ + PREFIX_GROUP + "1";
+
+ public static final String MESSAGE_JOIN_SUCCESS = "Join successful! %1$s has joined Group %2$s!";
+ public static final String MESSAGE_JOIN_EMAIL_NOT_FOUND = "Student with the provided email not found.";
+ public static final String MESSAGE_JOIN_GROUP_NOT_FOUND = "Group with the provided group number not found.";
+ public static final String MESSAGE_PERSON_ALREADY_IN_GROUP = "The provided student is "
+ + "already a member of the provided group.";
+ public static final String MESSAGE_GROUP_FULL = "Join failed as the group already has 5 members.";
+ public static final String MESSAGE_PERSON_IN_ANOTHER_GROUP = "The provided student is already in another group.";
+
+ private final Email targetEmail;
+ private final int targetGroupNumber;
+
+ /**
+ * Constructs a {@code JoinCommand} with the specified email and group number to join a group.
+ *
+ * @param targetEmail of the student to be added to the group
+ * @param targetGroupNumber group number
+ */
+ public JoinCommand(Email targetEmail, int targetGroupNumber) {
+ this.targetEmail = targetEmail;
+ this.targetGroupNumber = targetGroupNumber;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ // Find the person with the provided email
+ Optional personToJoin = model.getPersonWithEmail(targetEmail);
+
+ if (personToJoin.isEmpty()) {
+ throw new CommandException(MESSAGE_JOIN_EMAIL_NOT_FOUND);
+ }
+
+ // Find the group with the provided email
+ Optional groupToJoin = model.getGroupWithNumber(targetGroupNumber);
+
+ if (groupToJoin.isEmpty()) {
+ throw new CommandException(MESSAGE_JOIN_GROUP_NOT_FOUND);
+ }
+
+ if (groupToJoin.get().hasMember(personToJoin.get())) {
+ throw new CommandException(MESSAGE_PERSON_ALREADY_IN_GROUP);
+ }
+
+ if (model.personIsInAGroup(personToJoin.get())) {
+ throw new CommandException(MESSAGE_PERSON_IN_ANOTHER_GROUP);
+ }
+
+ if (groupToJoin.get().isFull()) {
+ throw new CommandException(MESSAGE_GROUP_FULL);
+ }
+
+ // Add the person to the group
+ model.addPersonToGroup(personToJoin.get(), groupToJoin.get());
+
+ return new CommandResult(String.format(MESSAGE_JOIN_SUCCESS,
+ personToJoin.get().getName(), groupToJoin.get().getNumber()),
+ false, false, true, false);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof JoinCommand)) {
+ return false;
+ }
+
+ JoinCommand otherJoinCommand = (JoinCommand) other;
+ return targetEmail.equals(otherJoinCommand.targetEmail)
+ && targetGroupNumber == otherJoinCommand.targetGroupNumber;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetEmail", targetEmail)
+ .add("targetGroupNumber", targetGroupNumber)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/LeaveCommand.java b/src/main/java/seedu/address/logic/commands/LeaveCommand.java
new file mode 100644
index 00000000000..05fb9c71ec3
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/LeaveCommand.java
@@ -0,0 +1,96 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GROUP;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_GROUPS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.group.Group;
+import seedu.address.model.person.Email;
+import seedu.address.model.person.Person;
+
+/**
+ * Removes a person specified by email from a group specified by group number.
+ */
+public class LeaveCommand extends Command {
+
+ public static final String COMMAND_WORD = "leave";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Removes the person specified by email from the group specified by group number.\n"
+ + "Parameters: "
+ + PREFIX_EMAIL + "EMAIL "
+ + PREFIX_GROUP + "GROUP NUMBER\n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_EMAIL + "johnd@u.nus.edu "
+ + PREFIX_GROUP + "1";
+
+ public static final String MESSAGE_LEAVE_SUCCESS = "Leave successful! %1$s has left Group %2$s!";
+ public static final String MESSAGE_LEAVE_EMAIL_NOT_FOUND = "Person with the provided email not found.";
+ public static final String MESSAGE_LEAVE_GROUP_NOT_FOUND = "Group with the provided group number not found.";
+ public static final String MESSAGE_NOT_IN_GROUP = "The above student is not a member of the provided group.";
+
+ private final Email targetEmail;
+ private final int targetGroupNumber;
+
+ /**
+ * Constructs a {@code LeaveCommand} with the specified email and group number to leave a group.
+ *
+ * @param targetEmail The email of the person to leave the group.
+ * @param targetGroupNumber The number of the group to leave.
+ */
+ public LeaveCommand(Email targetEmail, int targetGroupNumber) {
+ this.targetEmail = targetEmail;
+ this.targetGroupNumber = targetGroupNumber;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ // Find the person with the provided email
+ Person personToLeave = model.getPersonWithEmail(targetEmail)
+ .orElseThrow(() -> new CommandException(MESSAGE_LEAVE_EMAIL_NOT_FOUND));
+
+ // Find the group with the provided group number
+ Group groupToLeave = model.getGroupWithNumber(targetGroupNumber)
+ .orElseThrow(() -> new CommandException(MESSAGE_LEAVE_GROUP_NOT_FOUND));
+
+ if (!groupToLeave.hasMember(personToLeave)) {
+ throw new CommandException(MESSAGE_NOT_IN_GROUP);
+ }
+ // Remove the person from the group
+ model.removePersonFromGroup(personToLeave, groupToLeave);
+
+ return new CommandResult(String.format(MESSAGE_LEAVE_SUCCESS,
+ personToLeave.getName(), groupToLeave.getNumber()), false, false, true, false);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof LeaveCommand)) {
+ return false;
+ }
+
+ LeaveCommand otherLeaveCommand = (LeaveCommand) other;
+ return targetEmail.equals(otherLeaveCommand.targetEmail)
+ && targetGroupNumber == otherLeaveCommand.targetGroupNumber;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetEmail", targetEmail)
+ .add("targetGroupNumber", targetGroupNumber)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java
index 84be6ad2596..f4e92e4b9ad 100644
--- a/src/main/java/seedu/address/logic/commands/ListCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ListCommand.java
@@ -1,23 +1,25 @@
package seedu.address.logic.commands;
import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_GROUPS;
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.
+ * Lists all students 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";
-
+ public static final String MESSAGE_SUCCESS = "Viewing all students";
+ public static final String MESSAGE_FAILURE = "Error: Unable to retrieve student entries. Please try again.";
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
return new CommandResult(MESSAGE_SUCCESS);
}
diff --git a/src/main/java/seedu/address/logic/commands/ListGroupCommand.java b/src/main/java/seedu/address/logic/commands/ListGroupCommand.java
new file mode 100644
index 00000000000..34e7d1d6d73
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ListGroupCommand.java
@@ -0,0 +1,26 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_GROUPS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import seedu.address.model.Model;
+
+/**
+ * Lists all groups in the address book to the user.
+ */
+public class ListGroupCommand extends Command {
+
+ public static final String COMMAND_WORD = "listGroup";
+
+ public static final String MESSAGE_SUCCESS = "Viewing all groups";
+ public static final String MESSAGE_FAILURE = "Error: Unable to retrieve group entries. Please try again.";
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ return new CommandResult(MESSAGE_SUCCESS, false, false, true, false);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/MarkCommand.java b/src/main/java/seedu/address/logic/commands/MarkCommand.java
new file mode 100644
index 00000000000..3f92bbc72df
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/MarkCommand.java
@@ -0,0 +1,94 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GROUP;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TASK_INDEX;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_GROUPS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import java.util.Optional;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.group.Group;
+import seedu.address.model.group.GroupContainsKeywordsPredicate;
+import seedu.address.model.group.tasks.Task;
+import seedu.address.model.group.tasks.TaskList;
+
+/**
+ * Marks a task as done within a specific group.
+ */
+public class MarkCommand extends Command {
+
+ public static final String COMMAND_WORD = "mark";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Mark task specified as done.\n"
+ + "Parameters: " + PREFIX_GROUP + "GROUP_NUMBER " + PREFIX_TASK_INDEX + "TASK_INDEX\n"
+ + "Example: " + COMMAND_WORD + " gr/2 ti/3";
+
+ public static final String MESSAGE_SUCCESS = "Marked task number %2$s for group %1$s";
+ public static final String MESSAGE_TASK_GROUP_NOT_FOUND = "Group with the provided group number not found.";
+ public static final String MESSAGE_INVALID_TASK_INDEX = "Invalid task index. Task not found.";
+ private final int groupId;
+ private final int taskIndex;
+ private final GroupContainsKeywordsPredicate predicate;
+
+ /**
+ * Creates a MarkCommand to mark a task as done within a specific group.
+ *
+ * @param groupId The group ID in which the task exists.
+ * @param taskIndex The index of the task to mark as done.
+ */
+ public MarkCommand(int groupId, int taskIndex, GroupContainsKeywordsPredicate predicate) {
+ this.groupId = groupId;
+ this.taskIndex = taskIndex;
+ this.predicate = predicate;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+
+ // Retrieve the group using the groupId
+ Optional optionalGroup = model.getGroupWithNumber(groupId);
+ if (optionalGroup.isEmpty()) {
+ throw new CommandException(MESSAGE_TASK_GROUP_NOT_FOUND);
+ }
+ Group group = optionalGroup.get();
+ TaskList taskList = group.getTasks();
+ if (isValidTaskIndex(taskIndex, taskList)) {
+ Task taskToMark = taskList.getTasks().get(taskIndex);
+ taskToMark.mark();
+ String displayedTasks = taskList.toString();
+ requireNonNull(model);
+ model.updateFilteredGroupList(predicate);
+ return new CommandResult(String.format(MESSAGE_SUCCESS, groupId, taskIndex + 1) + "\n" + displayedTasks,
+ false, false, true, false);
+ } else {
+ throw new CommandException(MESSAGE_INVALID_TASK_INDEX);
+ }
+
+ }
+
+ public boolean isValidTaskIndex(int taskIndex, TaskList taskList) {
+ return taskIndex >= 0 && taskIndex < taskList.getTasks().size();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // Basic checks
+ if (other == this) {
+ return true;
+ }
+ if (!(other instanceof MarkCommand)) {
+ return false;
+ }
+
+ // Property checks
+ MarkCommand otherMarkCommand = (MarkCommand) other;
+ return groupId == otherMarkCommand.groupId && taskIndex == otherMarkCommand.taskIndex;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/TasksCommand.java b/src/main/java/seedu/address/logic/commands/TasksCommand.java
new file mode 100644
index 00000000000..246e3a33781
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/TasksCommand.java
@@ -0,0 +1,93 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_GROUPS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import java.util.Optional;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.group.Group;
+import seedu.address.model.group.GroupContainsKeywordsPredicate;
+import seedu.address.model.group.exceptions.TaskException;
+import seedu.address.model.group.tasks.TaskInitializer;
+import seedu.address.model.group.tasks.TaskList;
+
+/**
+ * Lists out all tasks for a specific group.
+ */
+public class TasksCommand extends Command {
+
+ public static final String COMMAND_WORD = "tasks";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Lists out all tasks for a specific group.\n"
+ + "Parameters: GROUP_NUMBER\n"
+ + "Example: " + COMMAND_WORD + " 3";
+
+ public static final String MESSAGE_SUCCESS = "Listing out tasks for group %1$s";
+ public static final String MESSAGE_TASK_GROUP_NOT_FOUND = "Group with the provided group number not found.";
+
+ private final int groupId;
+ private final GroupContainsKeywordsPredicate predicate;
+
+
+ /**
+ * Creates a TasksCommand to list out all tasks for a specific group.
+ *
+ * @param groupId The group ID for which tasks should be listed.
+ */
+ public TasksCommand(int groupId, GroupContainsKeywordsPredicate predicate) {
+ this.groupId = groupId;
+ this.predicate = predicate;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+
+ // Retrieve the group using the groupId
+ Optional optionalGroup = model.getGroupWithNumber(groupId);
+ if (optionalGroup.isEmpty()) {
+ throw new CommandException(MESSAGE_TASK_GROUP_NOT_FOUND);
+ }
+
+ Group group = optionalGroup.get();
+
+ TaskList taskList = group.getTasks();
+
+ if (taskList.isEmpty()) {
+ try {
+ taskList = TaskInitializer.initializeTasks();
+ } catch (TaskException e) {
+ throw new RuntimeException(e);
+ }
+ group.addTasks(taskList);
+ }
+
+ String displayedTasks = taskList.toString();
+ requireNonNull(model);
+ model.updateFilteredGroupList(predicate);
+
+ return new CommandResult(String.format(MESSAGE_SUCCESS, groupId) + "\n" + displayedTasks,
+ false, false, true, false);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // basic checks
+ if (other == this) {
+ return true;
+ }
+ if (!(other instanceof TasksCommand)) {
+ return false;
+ }
+
+ // property checks
+ TasksCommand otherTasksCommand = (TasksCommand) other;
+ return groupId == otherTasksCommand.groupId;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/UnMarkCommand.java b/src/main/java/seedu/address/logic/commands/UnMarkCommand.java
new file mode 100644
index 00000000000..753af8cf976
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/UnMarkCommand.java
@@ -0,0 +1,95 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GROUP;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TASK_INDEX;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_GROUPS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import java.util.Optional;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.group.Group;
+import seedu.address.model.group.GroupContainsKeywordsPredicate;
+import seedu.address.model.group.tasks.Task;
+import seedu.address.model.group.tasks.TaskList;
+
+/**
+ * Marks a task as not done within a specific group.
+ */
+public class UnMarkCommand extends Command {
+
+ public static final String COMMAND_WORD = "unmark";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Mark task specified as not done.\n"
+ + "Parameters:" + PREFIX_GROUP + "GROUP_NUMBER " + PREFIX_TASK_INDEX + "TASK_INDEX\n"
+ + "Example: " + COMMAND_WORD + " gr/2 ti/3";
+
+ public static final String MESSAGE_SUCCESS = "Unmarked task number %2$s for group %1$s";
+ public static final String MESSAGE_TASK_GROUP_NOT_FOUND = "Group with the provided group number not found.";
+ public static final String MESSAGE_INVALID_TASK_INDEX = "Invalid task index.";
+ private final int groupId;
+ private final int taskIndex;
+ private final GroupContainsKeywordsPredicate predicate;
+
+
+ /**
+ * Creates an UnMarkCommand to mark a task as not done within a specific group.
+ *
+ * @param groupId The group ID in which the task exists.
+ * @param taskIndex The index of the task to mark as not done.
+ */
+ public UnMarkCommand(int groupId, int taskIndex, GroupContainsKeywordsPredicate predicate) {
+ this.groupId = groupId;
+ this.taskIndex = taskIndex;
+ this.predicate = predicate;
+
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+
+ // Retrieve the group using the groupId
+ Optional optionalGroup = model.getGroupWithNumber(groupId);
+ if (optionalGroup.isEmpty()) {
+ throw new CommandException(MESSAGE_TASK_GROUP_NOT_FOUND);
+ }
+ Group group = optionalGroup.get();
+ TaskList taskList = group.getTasks();
+ if (isValidTaskIndex(taskIndex, taskList)) {
+ Task taskToMark = taskList.getTasks().get(taskIndex);
+ taskToMark.unMark();
+ String displayedTasks = taskList.toString();
+ requireNonNull(model);
+ model.updateFilteredGroupList(predicate);
+ return new CommandResult(String.format(MESSAGE_SUCCESS, groupId, taskIndex + 1) + "\n"
+ + displayedTasks, false, false, true, false);
+ } else {
+ throw new CommandException(MESSAGE_INVALID_TASK_INDEX);
+ }
+ }
+
+ public boolean isValidTaskIndex(int taskIndex, TaskList taskList) {
+ return taskIndex >= 0 && taskIndex < taskList.getTasks().size();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // basic checks
+ if (other == this) {
+ return true;
+ }
+ if (!(other instanceof UnMarkCommand)) {
+ return false;
+ }
+
+ // Property checks
+ UnMarkCommand otherUnMarkCommand = (UnMarkCommand) other;
+ return groupId == otherUnMarkCommand.groupId && taskIndex == otherUnMarkCommand.taskIndex;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
index 4ff1a97ed77..b8ee8935bcd 100644
--- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
@@ -1,23 +1,31 @@
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_DESCRIPTION;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GENDER;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MAJOR;
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_NATIONALITY;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SOCIAL_MEDIA_LINK;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TUTORIAL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_YEAR;
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.Description;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Gender;
+import seedu.address.model.person.Major;
import seedu.address.model.person.Name;
+import seedu.address.model.person.Nationality;
import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.person.Year;
+import seedu.address.model.socialmedialink.SocialMediaLink;
+import seedu.address.model.tutorial.Tutorial;
/**
* Parses input arguments and creates a new AddCommand object
@@ -31,21 +39,31 @@ public class AddCommandParser implements Parser {
*/
public AddCommand parse(String args) throws ParseException {
ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_MAJOR, PREFIX_YEAR, PREFIX_EMAIL,
+ PREFIX_DESCRIPTION, PREFIX_TUTORIAL, PREFIX_SOCIAL_MEDIA_LINK, PREFIX_NATIONALITY, PREFIX_GENDER);
- if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL)
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_MAJOR, PREFIX_YEAR, PREFIX_EMAIL,
+ PREFIX_DESCRIPTION, PREFIX_NATIONALITY, PREFIX_GENDER)
|| !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);
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_MAJOR, PREFIX_YEAR,
+ PREFIX_EMAIL, PREFIX_DESCRIPTION, PREFIX_NATIONALITY, PREFIX_GENDER);
Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get());
- Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get());
+ Major major = ParserUtil.parseMajor(argMultimap.getValue(PREFIX_MAJOR).get());
+ Year year = ParserUtil.parseYear(argMultimap.getValue(PREFIX_YEAR).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));
+ Description description = ParserUtil.parseDescription(argMultimap.getValue(PREFIX_DESCRIPTION).get());
+ Set tutorialSet = ParserUtil.parseTutorials(argMultimap.getAllValues(PREFIX_TUTORIAL));
+ Set socialMediaLinkList = ParserUtil.parseSocialMediaLinks(
+ argMultimap.getAllValues(PREFIX_SOCIAL_MEDIA_LINK));
+ Nationality nationality = ParserUtil.parseNationality(argMultimap.getValue(PREFIX_NATIONALITY).get());
+ Gender gender = ParserUtil.parseGender(argMultimap.getValue(PREFIX_GENDER).get());
- Person person = new Person(name, phone, email, address, tagList);
+
+ Person person = new Person(name, major, year, email, description,
+ tutorialSet, socialMediaLinkList, nationality, gender);
return new AddCommand(person);
}
diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
index 3149ee07e0b..5ff056e51ee 100644
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
@@ -9,14 +9,26 @@
import seedu.address.commons.core.LogsCenter;
import seedu.address.logic.commands.AddCommand;
+import seedu.address.logic.commands.CheckCommand;
import seedu.address.logic.commands.ClearCommand;
import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CreateCommand;
import seedu.address.logic.commands.DeleteCommand;
+import seedu.address.logic.commands.DeleteGroupCommand;
import seedu.address.logic.commands.EditCommand;
import seedu.address.logic.commands.ExitCommand;
+import seedu.address.logic.commands.FilterCommand;
+import seedu.address.logic.commands.FilterGroupCommand;
import seedu.address.logic.commands.FindCommand;
+import seedu.address.logic.commands.FindGroupCommand;
import seedu.address.logic.commands.HelpCommand;
+import seedu.address.logic.commands.JoinCommand;
+import seedu.address.logic.commands.LeaveCommand;
import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.ListGroupCommand;
+import seedu.address.logic.commands.MarkCommand;
+import seedu.address.logic.commands.TasksCommand;
+import seedu.address.logic.commands.UnMarkCommand;
import seedu.address.logic.parser.exceptions.ParseException;
/**
@@ -68,15 +80,51 @@ public Command parseCommand(String userInput) throws ParseException {
case FindCommand.COMMAND_WORD:
return new FindCommandParser().parse(arguments);
+ case FilterCommand.COMMAND_WORD:
+ return new FilterCommandParser().parse(arguments);
+
case ListCommand.COMMAND_WORD:
return new ListCommand();
+ case CreateCommand.COMMAND_WORD:
+ return new CreateCommandParser().parse(arguments);
+
+ case CheckCommand.COMMAND_WORD:
+ return new CheckCommandParser().parse(arguments);
+
+ case JoinCommand.COMMAND_WORD:
+ return new JoinCommandParser().parse(arguments);
+
+ case ListGroupCommand.COMMAND_WORD:
+ return new ListGroupCommand();
+
+ case FindGroupCommand.COMMAND_WORD:
+ return new FindGroupCommandParser().parse(arguments);
+
+ case FilterGroupCommand.COMMAND_WORD:
+ return new FilterGroupCommandParser().parse(arguments);
+
case ExitCommand.COMMAND_WORD:
return new ExitCommand();
case HelpCommand.COMMAND_WORD:
return new HelpCommand();
+ case LeaveCommand.COMMAND_WORD:
+ return new LeaveCommandParser().parse(arguments);
+
+ case TasksCommand.COMMAND_WORD:
+ return new TasksCommandParser().parse(arguments);
+
+ case MarkCommand.COMMAND_WORD:
+ return new MarkCommandParser().parse(arguments);
+
+ case UnMarkCommand.COMMAND_WORD:
+ return new UnMarkCommandParser().parse(arguments);
+
+ case DeleteGroupCommand.COMMAND_WORD:
+ return new DeleteGroupCommandParser().parse(arguments);
+
default:
logger.finer("This user input caused a ParseException: " + userInput);
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
diff --git a/src/main/java/seedu/address/logic/parser/CheckCommandParser.java b/src/main/java/seedu/address/logic/parser/CheckCommandParser.java
new file mode 100644
index 00000000000..9ac21d5deeb
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/CheckCommandParser.java
@@ -0,0 +1,31 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Arrays;
+
+import seedu.address.logic.commands.CheckCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.group.GroupContainsKeywordsPredicate;
+
+/**
+ * Parses input arguments and creates a new CheckCommand object
+ */
+public class CheckCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the CheckCommand
+ * and returns a CheckCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+ public CheckCommand parse(String args) throws ParseException {
+ String trimmedArgs = args.trim();
+ if (trimmedArgs.isEmpty() || !trimmedArgs.matches("^\\d+$")) { // checks o\if args is an integer
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, CheckCommand.MESSAGE_USAGE));
+ }
+ return new CheckCommand(Integer.parseInt(trimmedArgs), new GroupContainsKeywordsPredicate(Arrays.asList(
+ String.valueOf(Integer.parseInt(trimmedArgs)))));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java
index 75b1a9bf119..bab4f44a929 100644
--- a/src/main/java/seedu/address/logic/parser/CliSyntax.java
+++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java
@@ -7,9 +7,15 @@ public class CliSyntax {
/* Prefix definitions */
public static final Prefix PREFIX_NAME = new Prefix("n/");
- public static final Prefix PREFIX_PHONE = new Prefix("p/");
+ public static final Prefix PREFIX_MAJOR = new Prefix("m/");
+ public static final Prefix PREFIX_YEAR = new Prefix("y/");
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_DESCRIPTION = new Prefix("d/");
+ public static final Prefix PREFIX_TUTORIAL = new Prefix("t/");
+ public static final Prefix PREFIX_SOCIAL_MEDIA_LINK = new Prefix("sm/");
+ public static final Prefix PREFIX_NATIONALITY = new Prefix("nt/");
+ public static final Prefix PREFIX_GENDER = new Prefix("g/");
+ public static final Prefix PREFIX_GROUP = new Prefix("gr/");
+ public static final Prefix PREFIX_TASK_INDEX = new Prefix("ti/");
}
diff --git a/src/main/java/seedu/address/logic/parser/CreateCommandParser.java b/src/main/java/seedu/address/logic/parser/CreateCommandParser.java
new file mode 100644
index 00000000000..4ee42d43118
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/CreateCommandParser.java
@@ -0,0 +1,53 @@
+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_TUTORIAL;
+
+import java.util.stream.Stream;
+
+import seedu.address.logic.commands.CreateCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.tutorial.Tutorial;
+
+/**
+ * Parses input arguments and creates a new CreateCommand object
+ */
+public class CreateCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the CreateCommand
+ * and returns a CreateCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+ public CreateCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_TUTORIAL);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_TUTORIAL)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, CreateCommand.MESSAGE_USAGE));
+ }
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_TUTORIAL);
+
+ try {
+ Tutorial tutorial = ParserUtil.parseTutorial(argMultimap.getValue(PREFIX_TUTORIAL).get());
+ return new CreateCommand(tutorial);
+ } catch (ParseException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ Tutorial.MESSAGE_CONSTRAINTS + "\n" + CreateCommand.MESSAGE_USAGE), pe);
+ }
+ }
+
+ /**
+ * 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
index 3527fe76a3e..5acfe7ca5ee 100644
--- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
@@ -2,9 +2,9 @@
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;
+import seedu.address.model.person.Email;
/**
* Parses input arguments and creates a new DeleteCommand object
@@ -14,16 +14,16 @@ 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);
+ Email email = ParserUtil.parseEmail(args);
+ return new DeleteCommand(email);
} 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/DeleteGroupCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteGroupCommandParser.java
new file mode 100644
index 00000000000..c241a5ddb00
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/DeleteGroupCommandParser.java
@@ -0,0 +1,39 @@
+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_GROUP;
+
+import seedu.address.logic.commands.DeleteGroupCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new DeleteGroupCommand object.
+ */
+public class DeleteGroupCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the DeleteGroupCommand
+ * and returns a DeleteGroupCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+ public DeleteGroupCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ // Parse the group number from the args using the group prefix
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_GROUP);
+
+ if (!argMultimap.getValue(PREFIX_GROUP).isPresent() || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteGroupCommand.MESSAGE_USAGE));
+ }
+
+ // Extract the group number from the argument map
+ try {
+ int groupNumber = ParserUtil.parseGroupNumber(argMultimap.getValue(PREFIX_GROUP).get());
+ return new DeleteGroupCommand(groupNumber);
+ } catch (ParseException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteGroupCommand.MESSAGE_USAGE), pe);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
index 46b3309a78b..15489c5bc3e 100644
--- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
@@ -2,22 +2,27 @@
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_DESCRIPTION;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GENDER;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MAJOR;
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_NATIONALITY;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SOCIAL_MEDIA_LINK;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TUTORIAL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_YEAR;
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;
+import seedu.address.model.person.Email;
+import seedu.address.model.socialmedialink.SocialMediaLink;
+import seedu.address.model.tutorial.Tutorial;
/**
* Parses input arguments and creates a new EditCommand object
@@ -32,54 +37,94 @@ public class EditCommandParser implements Parser {
public EditCommand parse(String args) throws ParseException {
requireNonNull(args);
ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_MAJOR, PREFIX_YEAR, PREFIX_EMAIL,
+ PREFIX_DESCRIPTION, PREFIX_TUTORIAL, PREFIX_SOCIAL_MEDIA_LINK, PREFIX_NATIONALITY, PREFIX_GENDER);
- Index index;
+ Email email;
try {
- index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ email = ParserUtil.parseEmail(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);
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_MAJOR, PREFIX_YEAR, PREFIX_EMAIL,
+ PREFIX_DESCRIPTION, PREFIX_NATIONALITY, PREFIX_GENDER);
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_MAJOR).isPresent()) {
+ editPersonDescriptor.setMajor(ParserUtil.parseMajor(argMultimap.getValue(PREFIX_MAJOR).get()));
+ }
+ if (argMultimap.getValue(PREFIX_YEAR).isPresent()) {
+ editPersonDescriptor.setYear(ParserUtil.parseYear(argMultimap.getValue(PREFIX_YEAR).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()));
+ if (argMultimap.getValue(PREFIX_DESCRIPTION).isPresent()) {
+ editPersonDescriptor.setDescription(
+ ParserUtil.parseDescription(argMultimap.getValue(PREFIX_DESCRIPTION).get()));
+ }
+ /*
+ List tutorialsStrings = argMultimap.getAllValues(PREFIX_TUTORIAL);
+ if (!tutorialsStrings.isEmpty()) {
+ Set tutorialList = ParserUtil.parseTutorials(tutorialsStrings);
+ editPersonDescriptor.setTutorials(tutorialList);
+ }
+ */
+ parseTutorialsForEdit(argMultimap.getAllValues(PREFIX_TUTORIAL))
+ .ifPresent(editPersonDescriptor::setTutorials);
+
+ parseSocialMediaLinksForEdit(argMultimap.getAllValues(PREFIX_SOCIAL_MEDIA_LINK))
+ .ifPresent(editPersonDescriptor::setSocialMediaLinks);
+
+ if (argMultimap.getValue(PREFIX_NATIONALITY).isPresent()) {
+ editPersonDescriptor.setNationality(
+ ParserUtil.parseNationality(argMultimap.getValue(PREFIX_NATIONALITY).get()));
+ }
+
+ if (argMultimap.getValue(PREFIX_GENDER).isPresent()) {
+ editPersonDescriptor.setGender(ParserUtil.parseGender(argMultimap.getValue(PREFIX_GENDER).get()));
}
- parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags);
if (!editPersonDescriptor.isAnyFieldEdited()) {
throw new ParseException(EditCommand.MESSAGE_NOT_EDITED);
}
- return new EditCommand(index, editPersonDescriptor);
+ return new EditCommand(email, editPersonDescriptor);
+ }
+
+ private Optional> parseTutorialsForEdit(Collection tutorials) throws ParseException {
+ assert tutorials != null;
+
+ if (tutorials.isEmpty()) {
+ return Optional.empty();
+ }
+ Collection tutorialSet =
+ tutorials.size() == 1 && tutorials.contains("") ? Collections.emptySet() : tutorials;
+ return Optional.of(ParserUtil.parseTutorials(tutorialSet));
}
/**
- * 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.
+ * Parses {@code Collection socialMediaLinks} into a {@code Set}
+ * if {@code socialMediaLinks} is non-empty.
+ * If {@code socialMediaLinks} 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;
+ private Optional> parseSocialMediaLinksForEdit(Collection socialMediaLinks)
+ throws ParseException {
+ assert socialMediaLinks != null;
- if (tags.isEmpty()) {
+ if (socialMediaLinks.isEmpty()) {
return Optional.empty();
}
- Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags;
- return Optional.of(ParserUtil.parseTags(tagSet));
+ Collection socialMediaLinkSet = socialMediaLinks.size() == 1 && socialMediaLinks.contains("")
+ ? Collections.emptySet() : socialMediaLinks;
+ return Optional.of(ParserUtil.parseSocialMediaLinks(socialMediaLinkSet));
}
}
diff --git a/src/main/java/seedu/address/logic/parser/FilterCommandParser.java b/src/main/java/seedu/address/logic/parser/FilterCommandParser.java
new file mode 100644
index 00000000000..1fb8389e328
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/FilterCommandParser.java
@@ -0,0 +1,35 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Arrays;
+
+import seedu.address.logic.commands.FilterCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.tutorial.TutorialContainsSlotsPredicate;
+
+/**
+ * Parses input arguments and creates a new FilterCommand object
+ */
+public class FilterCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the FilterCommand
+ * and returns a FilterCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public FilterCommand parse(String args) throws ParseException {
+ String trimmedArgs = args.trim();
+ if (trimmedArgs.isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterCommand.MESSAGE_USAGE));
+ }
+
+ String[] tutorialSlots = trimmedArgs.split("\\s+");
+ for (String tut : tutorialSlots) {
+ ParserUtil.parseTutorial(tut);
+ }
+
+ return new FilterCommand(new TutorialContainsSlotsPredicate(Arrays.asList(tutorialSlots)));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/FilterGroupCommandParser.java b/src/main/java/seedu/address/logic/parser/FilterGroupCommandParser.java
new file mode 100644
index 00000000000..eb72e23d1dd
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/FilterGroupCommandParser.java
@@ -0,0 +1,30 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.logic.commands.FilterGroupCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.group.GroupBelongsTutorialPredicate;
+
+/**
+ * Parses input arguments and creates a new FilterGroupCommand object
+ */
+public class FilterGroupCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the FilterGroupCommand
+ * and returns a FilterGroupCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public FilterGroupCommand parse(String args) throws ParseException {
+ String trimmedArgs = args.trim();
+ if (trimmedArgs.isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterGroupCommand.MESSAGE_USAGE));
+ }
+
+ ParserUtil.parseTutorial(trimmedArgs);
+
+ return new FilterGroupCommand(new GroupBelongsTutorialPredicate(trimmedArgs));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/FindGroupCommandParser.java b/src/main/java/seedu/address/logic/parser/FindGroupCommandParser.java
new file mode 100644
index 00000000000..a36b268bdb3
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/FindGroupCommandParser.java
@@ -0,0 +1,35 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Arrays;
+
+import seedu.address.logic.commands.FindGroupCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.group.GroupContainsKeywordsPredicate;
+
+/**
+ * Parses input arguments and creates a new FindGroupCommand object
+ */
+public class FindGroupCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the FindGroupCommand
+ * and returns a FindGroupCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public FindGroupCommand parse(String args) throws ParseException {
+ String trimmedArgs = args.trim();
+ if (trimmedArgs.isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindGroupCommand.MESSAGE_USAGE));
+ }
+
+ String[] groupKeywords = trimmedArgs.split("\\s+");
+ for (String gk : groupKeywords) {
+ ParserUtil.parseGroupNumber(gk);
+ }
+
+ return new FindGroupCommand(new GroupContainsKeywordsPredicate(Arrays.asList(groupKeywords)));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/JoinCommandParser.java b/src/main/java/seedu/address/logic/parser/JoinCommandParser.java
new file mode 100644
index 00000000000..2529530cf44
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/JoinCommandParser.java
@@ -0,0 +1,55 @@
+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_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GROUP;
+
+import java.util.stream.Stream;
+
+import seedu.address.logic.commands.JoinCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.Email;
+
+/**
+ * Parses input arguments and creates a new JoinCommand object
+ */
+public class JoinCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the JoinCommand
+ * and returns a JoinCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+ public JoinCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args,
+ PREFIX_EMAIL, PREFIX_GROUP);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_EMAIL, PREFIX_GROUP)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, JoinCommand.MESSAGE_USAGE));
+ }
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_EMAIL, PREFIX_GROUP);
+
+ try {
+ Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get());
+ int groupNumber = ParserUtil.parseGroupNumber(argMultimap.getValue(PREFIX_GROUP).get());
+ return new JoinCommand(email, groupNumber);
+ } catch (ParseException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, JoinCommand.MESSAGE_USAGE), pe);
+ }
+ }
+
+ /**
+ * 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/LeaveCommandParser.java b/src/main/java/seedu/address/logic/parser/LeaveCommandParser.java
new file mode 100644
index 00000000000..836bb5262dc
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/LeaveCommandParser.java
@@ -0,0 +1,56 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GROUP;
+
+import java.util.stream.Stream;
+
+import seedu.address.logic.commands.LeaveCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.Email;
+
+
+/**
+ * Parses input arguments and creates a new LeaveCommand object
+ */
+public class LeaveCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the LeaveCommand
+ * and returns a LeaveCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+ public LeaveCommand parse(String args) throws ParseException {
+ // Similar to JoinCommandParser, parse email and group number from args
+ // Example: "e/johnd@u.nus.edu gr/1"
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args,
+ PREFIX_EMAIL, PREFIX_GROUP);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_EMAIL, PREFIX_GROUP)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, LeaveCommand.MESSAGE_USAGE));
+ }
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_EMAIL, PREFIX_GROUP);
+
+ try {
+ Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get());
+ int groupNumber = ParserUtil.parseGroupNumber(argMultimap.getValue(PREFIX_GROUP).get());
+ return new LeaveCommand(email, groupNumber);
+ } catch (ParseException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, LeaveCommand.MESSAGE_USAGE), pe);
+ }
+ }
+
+ /**
+ * 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/MarkCommandParser.java b/src/main/java/seedu/address/logic/parser/MarkCommandParser.java
new file mode 100644
index 00000000000..71006336e69
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/MarkCommandParser.java
@@ -0,0 +1,45 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Arrays;
+
+import seedu.address.logic.commands.MarkCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.group.GroupContainsKeywordsPredicate;
+
+/**
+ * Parses input arguments and creates a new MarkCommand object
+ */
+public class MarkCommandParser implements Parser {
+ /**
+ * Parses the given {@code String} of arguments in the context of the MarkCommand
+ * and returns a MarkCommand object for execution.
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+ public MarkCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ try {
+ String[] argParts = args.trim().split(" ");
+ int groupNumber = -1;
+ int taskIndex = -1;
+
+ for (String part : argParts) {
+ if (part.startsWith("gr/")) {
+ groupNumber = ParserUtil.parseGroupNumber(part.substring(3));
+ } else if (part.startsWith("ti/")) {
+ taskIndex = ParserUtil.parseTaskIndex(part.substring(3)) - 1;
+ }
+ }
+
+ if (groupNumber == -1 || taskIndex == -1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MarkCommand.MESSAGE_USAGE));
+ }
+ return new MarkCommand(groupNumber, taskIndex, new GroupContainsKeywordsPredicate(Arrays.asList(
+ String.valueOf(groupNumber))));
+ } catch (NumberFormatException nfe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MarkCommand.MESSAGE_USAGE), nfe);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java
index b117acb9c55..3824d4b2101 100644
--- a/src/main/java/seedu/address/logic/parser/ParserUtil.java
+++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java
@@ -6,34 +6,24 @@
import java.util.HashSet;
import java.util.Set;
-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.group.Group;
+import seedu.address.model.person.Description;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Gender;
+import seedu.address.model.person.Major;
import seedu.address.model.person.Name;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.person.Nationality;
+import seedu.address.model.person.Year;
+import seedu.address.model.socialmedialink.SocialMediaLink;
+import seedu.address.model.tutorial.Tutorial;
/**
* 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.";
-
- /**
- * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be
- * trimmed.
- * @throws ParseException if the specified index is invalid (not non-zero unsigned integer).
- */
- public static Index parseIndex(String oneBasedIndex) throws ParseException {
- String trimmedIndex = oneBasedIndex.trim();
- if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) {
- throw new ParseException(MESSAGE_INVALID_INDEX);
- }
- return Index.fromOneBased(Integer.parseInt(trimmedIndex));
- }
+ public static final String MESSAGE_INVALID_GROUP_NUMBER = "Group number is not a non-zero unsigned integer.";
/**
* Parses a {@code String name} into a {@code Name}.
@@ -51,74 +41,187 @@ public static Name parseName(String name) throws ParseException {
}
/**
- * Parses a {@code String phone} into a {@code Phone}.
+ * Parses a {@code String email} into an {@code Email}.
* Leading and trailing whitespaces will be trimmed.
*
- * @throws ParseException if the given {@code phone} is invalid.
+ * @throws ParseException if the given {@code email} is invalid.
*/
- public static Phone parsePhone(String phone) throws ParseException {
- requireNonNull(phone);
- String trimmedPhone = phone.trim();
- if (!Phone.isValidPhone(trimmedPhone)) {
- throw new ParseException(Phone.MESSAGE_CONSTRAINTS);
+ public static Email parseEmail(String email) throws ParseException {
+ requireNonNull(email);
+ String trimmedEmail = email.trim();
+ if (!Email.isValidEmail(trimmedEmail)) {
+ throw new ParseException(Email.MESSAGE_CONSTRAINTS);
}
- return new Phone(trimmedPhone);
+ return new Email(trimmedEmail);
}
/**
- * Parses a {@code String address} into an {@code Address}.
+ * Parses a {@code String major} into a {@code Major}.
* Leading and trailing whitespaces will be trimmed.
*
- * @throws ParseException if the given {@code address} is invalid.
+ * @throws ParseException if the given {@code major} is invalid.
*/
- public static Address parseAddress(String address) throws ParseException {
- requireNonNull(address);
- String trimmedAddress = address.trim();
- if (!Address.isValidAddress(trimmedAddress)) {
- throw new ParseException(Address.MESSAGE_CONSTRAINTS);
+ public static Major parseMajor(String major) throws ParseException {
+ requireNonNull(major);
+ String trimmedMajor = major.trim();
+ if (!Major.isValidMajor(trimmedMajor)) {
+ throw new ParseException(Major.MESSAGE_CONSTRAINTS);
}
- return new Address(trimmedAddress);
+ return new Major(trimmedMajor);
}
/**
- * Parses a {@code String email} into an {@code Email}.
+ * Parses a {@code String year} into a {@code Year}.
* Leading and trailing whitespaces will be trimmed.
*
- * @throws ParseException if the given {@code email} is invalid.
+ * @throws ParseException if the given {@code year} is invalid.
*/
- public static Email parseEmail(String email) throws ParseException {
- requireNonNull(email);
- String trimmedEmail = email.trim();
- if (!Email.isValidEmail(trimmedEmail)) {
- throw new ParseException(Email.MESSAGE_CONSTRAINTS);
+ public static Year parseYear(String year) throws ParseException {
+ requireNonNull(year);
+ String trimmedYear = year.trim();
+ if (!Year.isValidYear(trimmedYear)) {
+ throw new ParseException(Year.MESSAGE_CONSTRAINTS);
}
- return new Email(trimmedEmail);
+ return new Year(trimmedYear);
+ }
+
+ /**
+ * Parses a {@code String description} into a {@code Description}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code description} is invalid.
+ */
+ public static Description parseDescription(String description) throws ParseException {
+ requireNonNull(description);
+ String trimmedDescription = description.trim();
+ if (!Description.isValidDescription(trimmedDescription)) {
+ throw new ParseException(Description.MESSAGE_CONSTRAINTS);
+ }
+ return new Description(trimmedDescription);
+ }
+
+ /**
+ * Parses a {@code String tutorial} into a {@code Tutorial}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code tutorial} is invalid.
+ */
+ public static Tutorial parseTutorial(String tutorial) throws ParseException {
+ requireNonNull(tutorial);
+ String trimmedTutorial = tutorial.trim();
+ if (!Tutorial.isValidTutorial(trimmedTutorial)) {
+ throw new ParseException(Tutorial.MESSAGE_CONSTRAINTS);
+ }
+ return new Tutorial(trimmedTutorial);
+ }
+
+ /**
+ * Parses {@code Collection tutorials} into a {@code Set}.
+ */
+ public static Set parseTutorials(Collection tutorials) throws ParseException {
+ requireNonNull(tutorials);
+ final Set tutorialSet = new HashSet<>();
+ for (String tut : tutorials) {
+ tutorialSet.add(parseTutorial(tut));
+ }
+ return tutorialSet;
+ }
+
+ /**
+ * Parses a {@code String socialMediaLink} into a {@code SocialMediaLink}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code socialMediaLink} is invalid.
+ */
+ public static SocialMediaLink parseSocialMediaLink(String socialMediaLink) throws ParseException {
+ requireNonNull(socialMediaLink);
+ String trimmedSocialMediaLink = socialMediaLink.trim();
+ if (!SocialMediaLink.isValidSocialMediaLink(trimmedSocialMediaLink)) {
+ throw new ParseException(SocialMediaLink.MESSAGE_CONSTRAINTS);
+ }
+ return new SocialMediaLink(trimmedSocialMediaLink);
+ }
+
+ /**
+ * Parses a collection of social media links into a set of SocialMediaLink objects.
+ *
+ * @param socialMediaLinks A collection of social media links as a Collection of strings.
+ * @return A Set of SocialMediaLink objects representing the parsed social media links.
+ * @throws ParseException If there is an issue parsing the social media links.
+ */
+ public static Set parseSocialMediaLinks(Collection socialMediaLinks)
+ throws ParseException {
+ requireNonNull(socialMediaLinks);
+ final Set socialMediaLinkSet = new HashSet<>();
+ for (String socialMedia : socialMediaLinks) {
+ socialMediaLinkSet.add(parseSocialMediaLink(socialMedia));
+ }
+ return socialMediaLinkSet;
}
/**
- * Parses a {@code String tag} into a {@code Tag}.
+ * Parses a {@code String nationality} into a {@code Nationality}.
* Leading and trailing whitespaces will be trimmed.
*
- * @throws ParseException if the given {@code tag} is invalid.
+ * @param nationality A string representing the nationality.
+ * @return A {@code Nationality} object.
+ * @throws ParseException If the given {@code nationality} 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 Nationality parseNationality(String nationality) throws ParseException {
+ requireNonNull(nationality);
+ String trimmedNationality = nationality.trim();
+ try {
+ return new Nationality(trimmedNationality);
+ } catch (IllegalArgumentException e) {
+ throw new ParseException(Nationality.MESSAGE_CONSTRAINTS);
}
- return new Tag(trimmedTag);
}
/**
- * Parses {@code Collection tags} into a {@code Set}.
+ * Parses a {@code String gender} into a {@code Gender}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code gender} is invalid.
+ */
+ public static Gender parseGender(String gender) throws ParseException {
+ requireNonNull(gender);
+ String trimmedGender = gender.trim().toLowerCase(); // Convert to lowercase for case-insensitive check
+ if (!Gender.isValidGender(trimmedGender)) {
+ throw new ParseException(Gender.MESSAGE_CONSTRAINTS);
+ }
+ return new Gender(trimmedGender);
+ }
+
+ /**
+ * Parses {@code groupNumber} into an {@code Integer} and returns it. Leading and trailing whitespaces will be
+ * trimmed.
+ * @throws ParseException if the specified group number is invalid (not non-zero unsigned integer).
+ */
+ public static int parseGroupNumber(String groupNumber) throws ParseException {
+ String trimmedIndex = groupNumber.trim();
+ if (!Group.isValidGroupNumber(trimmedIndex)) {
+ throw new ParseException(MESSAGE_INVALID_GROUP_NUMBER);
+ }
+ return Integer.parseInt(trimmedIndex);
+ }
+
+ /**
+ * Parses a string into an integer task index.
+ *
+ * @param taskIndexString The string representation of the task index.
+ * @return The parsed integer task index.
+ * @throws ParseException if the specified task index is invalid (not a positive integer).
*/
- 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 int parseTaskIndex(String taskIndexString) throws ParseException {
+ String trimmedIndex = taskIndexString.trim();
+ try {
+ int taskIndex = Integer.parseInt(trimmedIndex);
+ if (taskIndex <= 0) {
+ throw new ParseException("Task index must be a positive integer.");
+ }
+ return taskIndex;
+ } catch (NumberFormatException e) {
+ throw new ParseException("Invalid task index format.");
}
- return tagSet;
}
}
diff --git a/src/main/java/seedu/address/logic/parser/TasksCommandParser.java b/src/main/java/seedu/address/logic/parser/TasksCommandParser.java
new file mode 100644
index 00000000000..16b44d2efec
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/TasksCommandParser.java
@@ -0,0 +1,40 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Arrays;
+
+import seedu.address.logic.commands.TasksCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.group.GroupContainsKeywordsPredicate;
+
+/**
+ * Parses input arguments and creates a new TasksCommand object
+ */
+public class TasksCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the TasksCommand
+ * and returns a TasksCommand object for execution.
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+ public TasksCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ String trimmedArgs = args.trim();
+
+ // Check if the input is an unsigned, non-zero integer
+ if (!trimmedArgs.matches("\\d+") || trimmedArgs.equals("0")) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, TasksCommand.MESSAGE_USAGE));
+ }
+
+ try {
+ int groupNumber = ParserUtil.parseGroupNumber(trimmedArgs);
+ return new TasksCommand(groupNumber, new GroupContainsKeywordsPredicate(Arrays.asList(
+ String.valueOf(groupNumber))));
+ } catch (NumberFormatException nfe) {
+ // This should not happen as we have already validated the input
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, TasksCommand.MESSAGE_USAGE), nfe);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/UnMarkCommandParser.java b/src/main/java/seedu/address/logic/parser/UnMarkCommandParser.java
new file mode 100644
index 00000000000..0fec2b387c2
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/UnMarkCommandParser.java
@@ -0,0 +1,46 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Arrays;
+
+import seedu.address.logic.commands.UnMarkCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.group.GroupContainsKeywordsPredicate;
+
+/**
+ * Parses input arguments and creates a new UnMarkCommand object
+ */
+public class UnMarkCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the UnMarkCommand
+ * and returns a UnMarkCommand object for execution.
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+ public UnMarkCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ try {
+ String[] argParts = args.trim().split(" ");
+ int groupNumber = -1;
+ int taskIndex = -1;
+
+ for (String part : argParts) {
+ if (part.startsWith("gr/")) {
+ groupNumber = ParserUtil.parseGroupNumber(part.substring(3));
+ } else if (part.startsWith("ti/")) {
+ taskIndex = ParserUtil.parseTaskIndex(part.substring(3)) - 1;
+ }
+ }
+
+ if (groupNumber == -1 || taskIndex == -1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UnMarkCommand.MESSAGE_USAGE));
+ }
+ return new UnMarkCommand(groupNumber, taskIndex, new GroupContainsKeywordsPredicate(Arrays.asList(
+ String.valueOf(groupNumber))));
+ } catch (NumberFormatException nfe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UnMarkCommand.MESSAGE_USAGE), nfe);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java
index 73397161e84..ebac319170c 100644
--- a/src/main/java/seedu/address/model/AddressBook.java
+++ b/src/main/java/seedu/address/model/AddressBook.java
@@ -6,6 +6,8 @@
import javafx.collections.ObservableList;
import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.model.group.Group;
+import seedu.address.model.group.UniqueGroupList;
import seedu.address.model.person.Person;
import seedu.address.model.person.UniquePersonList;
@@ -16,6 +18,7 @@
public class AddressBook implements ReadOnlyAddressBook {
private final UniquePersonList persons;
+ private final UniqueGroupList groups;
/*
* The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication
@@ -26,12 +29,13 @@ public class AddressBook implements ReadOnlyAddressBook {
*/
{
persons = new UniquePersonList();
+ groups = new UniqueGroupList();
}
public AddressBook() {}
/**
- * Creates an AddressBook using the Persons in the {@code toBeCopied}
+ * Creates an AddressBook using the Persons and Groups in the {@code toBeCopied}
*/
public AddressBook(ReadOnlyAddressBook toBeCopied) {
this();
@@ -48,6 +52,14 @@ public void setPersons(List persons) {
this.persons.setPersons(persons);
}
+ /**
+ * Replaces the contents of the group list with {@code groups}.
+ * {@code groups} must not contain duplicate groups.
+ */
+ public void setGroups(List groups) {
+ this.groups.setGroups(groups);
+ }
+
/**
* Resets the existing data of this {@code AddressBook} with {@code newData}.
*/
@@ -55,6 +67,7 @@ public void resetData(ReadOnlyAddressBook newData) {
requireNonNull(newData);
setPersons(newData.getPersonList());
+ setGroups(newData.getGroupList());
}
//// person-level operations
@@ -94,6 +107,37 @@ public void removePerson(Person key) {
persons.remove(key);
}
+ /**
+ * Returns true if a group with the same identity as {@code group} exists in the address book.
+ */
+ public boolean hasGroup(Group group) {
+ requireNonNull(group);
+ return groups.contains(group);
+ }
+
+ /**
+ * Adds a group to the address book.
+ *
+ * @param group
+ */
+ public void addGroup(Group group) {
+ requireNonNull(group);
+ groups.add(group);
+ }
+
+ /**
+ * Adds the given {@code Person} to the give {@code Group}.
+ *
+ * @param person The person to be added.
+ * @param group The group that the person will be added to.
+ */
+ public void addPersonToGroup(Person person, Group group) {
+ requireNonNull(person);
+ requireNonNull(group);
+
+ group.addMember(person);
+ }
+
//// util methods
@Override
@@ -108,6 +152,17 @@ public ObservableList getPersonList() {
return persons.asUnmodifiableObservableList();
}
+ @Override
+ public ObservableList getGroupList() {
+ return groups.asUnmodifiableObservableList();
+ }
+
+ @Override
+ public void sortGroups() {
+ List sortedGroups = groups.getSortedList();
+ this.setGroups(sortedGroups);
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
@@ -127,4 +182,25 @@ public boolean equals(Object other) {
public int hashCode() {
return persons.hashCode();
}
+
+
+
+ /**
+ * Removes the given person from the specified group.
+ * @param person The person to be removed from the group.
+ */
+ public void removePersonFromGroup(Person person) {
+ groups.remove(person);
+ }
+
+ /**
+ * Removes the given group from the address book.
+ *
+ * @param group The group to be removed.
+ */
+ public void removeGroup(Group group) {
+ requireNonNull(group);
+ groups.remove(group);
+ }
+
}
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
index d54df471c1f..5b839de1b54 100644
--- a/src/main/java/seedu/address/model/Model.java
+++ b/src/main/java/seedu/address/model/Model.java
@@ -1,10 +1,14 @@
package seedu.address.model;
import java.nio.file.Path;
+import java.util.Optional;
import java.util.function.Predicate;
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
+import seedu.address.model.group.Group;
+import seedu.address.model.group.tasks.TaskList;
+import seedu.address.model.person.Email;
import seedu.address.model.person.Person;
/**
@@ -14,6 +18,9 @@ public interface Model {
/** {@code Predicate} that always evaluate to true */
Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true;
+ /** {@code Predicate} that always evaluate to true */
+ Predicate PREDICATE_SHOW_ALL_GROUPS = unused -> true;
+
/**
* Replaces user prefs data with the data in {@code userPrefs}.
*/
@@ -53,26 +60,26 @@ 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 person with the same identity as {@code person} exists in StudentConnect.
*/
boolean hasPerson(Person person);
/**
* Deletes the given person.
- * The person must exist in the address book.
+ * The person must exist in StudentConnect.
*/
void deletePerson(Person target);
/**
* Adds the given person.
- * {@code person} must not already exist in the address book.
+ * {@code person} must not already exist in StudentConnect.
*/
void addPerson(Person person);
/**
* Replaces the given person {@code target} with {@code editedPerson}.
- * {@code target} must exist in the address book.
- * The person identity of {@code editedPerson} must not be the same as another existing person in the address book.
+ * {@code target} must exist in StudentConnect.
+ * The person identity of {@code editedPerson} must not be the same as another existing person in StudentConnect.
*/
void setPerson(Person target, Person editedPerson);
@@ -84,4 +91,84 @@ public interface Model {
* @throws NullPointerException if {@code predicate} is null.
*/
void updateFilteredPersonList(Predicate predicate);
+
+ /**
+ * Adds the given group.
+ */
+ void addGroup(Group group);
+
+ /**
+ * Adds the given {@code Person} to the give {@code Group}.
+ *
+ * @param person The person to be added.
+ * @param group The group that the person will be added to.
+ */
+ void addPersonToGroup(Person person, Group group);
+
+ /** Returns an unmodifiable view of the filtered group list */
+ ObservableList getFilteredGroupList();
+
+ /**
+ * Updates the filter of the filtered group list to filter by the given {@code predicate}.
+ * @throws NullPointerException if {@code predicate} is null.
+ */
+ void updateFilteredGroupList(Predicate predicate);
+
+ /**
+ * Retrieves a {@code Person} with the specified email.
+ *
+ * @param email The email associated with the person.
+ * @return An {@code Optional} containing the {@code Person} if found, or empty otherwise.
+ */
+ Optional getPersonWithEmail(Email email);
+
+ /**
+ * Retrieves a {@code Group} with the specified group number.
+ *
+ * @param number The number associated with the group.
+ * @return An {@code Optional} containing the {@code Group} if found, or empty otherwise.
+ */
+ Optional getGroupWithNumber(int number);
+
+ /**
+ * Returns true if the given person is in a group.
+ *
+ * @param person The person to be checked.
+ */
+ boolean personIsInAGroup(Person person);
+
+ /**
+ * Returns the group that the given person is in.
+ *
+ * @param person
+ */
+ Group getGroupThatPersonIsIn(Person person);
+
+ /**
+ * Removes a person from a group.
+ *
+ * @param person The person to be removed from the group.
+ * @param group The group from which the person should be removed.
+ */
+ void removePersonFromGroup(Person person, Group group);
+
+ /**
+ * Adds the given {@code TaskList} to the give {@code Group}.
+ *
+ * @param taskList The taskList to be added.
+ * @param group The group that the task will be added to.
+ */
+ void addTasksToGroup(TaskList taskList, Group group);
+
+ /**
+ * Returns true if a group with the same identity as {@code group} exists in StudentConnect.
+ */
+ boolean hasGroup(Group group);
+
+ /**
+ * Deletes the given group.
+ * The group must exist in StudentConnect.
+ */
+ void deleteGroup(Group group);
+
}
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
index 57bc563fde6..4dbec81573c 100644
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ b/src/main/java/seedu/address/model/ModelManager.java
@@ -4,6 +4,7 @@
import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
import java.nio.file.Path;
+import java.util.Optional;
import java.util.function.Predicate;
import java.util.logging.Logger;
@@ -11,6 +12,9 @@
import javafx.collections.transformation.FilteredList;
import seedu.address.commons.core.GuiSettings;
import seedu.address.commons.core.LogsCenter;
+import seedu.address.model.group.Group;
+import seedu.address.model.group.tasks.TaskList;
+import seedu.address.model.person.Email;
import seedu.address.model.person.Person;
/**
@@ -22,9 +26,14 @@ public class ModelManager implements Model {
private final AddressBook addressBook;
private final UserPrefs userPrefs;
private final FilteredList filteredPersons;
+ private final FilteredList filteredGroups;
+
/**
* Initializes a ModelManager with the given addressBook and userPrefs.
+ *
+ * @param addressBook The initial address book data.
+ * @param userPrefs The user preferences.
*/
public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) {
requireAllNonNull(addressBook, userPrefs);
@@ -34,8 +43,12 @@ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs
this.addressBook = new AddressBook(addressBook);
this.userPrefs = new UserPrefs(userPrefs);
filteredPersons = new FilteredList<>(this.addressBook.getPersonList());
+ filteredGroups = new FilteredList<>(this.addressBook.getGroupList());
}
+ /**
+ * Default constructor for ModelManager.
+ */
public ModelManager() {
this(new AddressBook(), new UserPrefs());
}
@@ -111,6 +124,68 @@ public void setPerson(Person target, Person editedPerson) {
addressBook.setPerson(target, editedPerson);
}
+ @Override
+ public void addGroup(Group group) {
+ addressBook.addGroup(group);
+ updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ }
+
+ @Override
+ public void addPersonToGroup(Person person, Group group) {
+ addressBook.addPersonToGroup(person, group);
+ }
+
+ @Override
+ public boolean personIsInAGroup(Person person) {
+ for (Group group : addressBook.getGroupList()) {
+ if (group.hasMember(person)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Group getGroupThatPersonIsIn(Person person) {
+ assert personIsInAGroup(person) : "This person is not in a group.";
+ for (Group group : addressBook.getGroupList()) {
+ if (group.hasMember(person)) {
+ return group;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void removePersonFromGroup(Person person, Group group) {
+ requireAllNonNull(person, group);
+ addressBook.removePersonFromGroup(person);
+ }
+
+ /**
+ * Checks if the specified group exists in the address book.
+ *
+ * @param group The group to check for existence.
+ * @return True if the group exists, false otherwise.
+ * @throws NullPointerException if the given group is null.
+ */
+ public boolean hasGroup(Group group) {
+ requireNonNull(group);
+ return addressBook.hasGroup(group);
+ }
+
+ @Override
+ public void addTasksToGroup(TaskList taskList, Group group) {
+ requireAllNonNull(taskList, group);
+ group.addTasks(taskList);
+ }
+
+ @Override
+ public void deleteGroup(Group group) {
+ requireNonNull(group);
+ addressBook.removeGroup(group);
+ }
+
//=========== Filtered Person List Accessors =============================================================
/**
@@ -128,6 +203,23 @@ public void updateFilteredPersonList(Predicate predicate) {
filteredPersons.setPredicate(predicate);
}
+ //=========== Filtered Group List Accessors ==============================================================
+
+ /**
+ * Returns an unmodifiable view of the list of {@code Group} backed by the internal list of
+ * {@code versionedAddressBook}
+ */
+ @Override
+ public ObservableList getFilteredGroupList() {
+ return filteredGroups;
+ }
+
+ @Override
+ public void updateFilteredGroupList(Predicate predicate) {
+ requireNonNull(predicate);
+ filteredGroups.setPredicate(predicate);
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
@@ -142,7 +234,34 @@ public boolean equals(Object other) {
ModelManager otherModelManager = (ModelManager) other;
return addressBook.equals(otherModelManager.addressBook)
&& userPrefs.equals(otherModelManager.userPrefs)
- && filteredPersons.equals(otherModelManager.filteredPersons);
+ && filteredPersons.equals(otherModelManager.filteredPersons)
+ && filteredGroups.equals(otherModelManager.filteredGroups);
+ }
+
+ @Override
+ public Optional getPersonWithEmail(Email email) {
+ requireNonNull(email);
+
+ // Iterate through the filtered list of persons
+ for (Person person : filteredPersons) {
+ if (person.getEmail().equals(email)) {
+ return Optional.of(person);
+ }
+ }
+
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional getGroupWithNumber(int number) {
+ // Iterate through the filtered list of groups
+ for (Group group : filteredGroups) {
+ if (group.getNumber() == number) {
+ return Optional.of(group);
+ }
+ }
+
+ return Optional.empty();
}
}
diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
index 6ddc2cd9a29..403d063af9d 100644
--- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
+++ b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
@@ -1,6 +1,7 @@
package seedu.address.model;
import javafx.collections.ObservableList;
+import seedu.address.model.group.Group;
import seedu.address.model.person.Person;
/**
@@ -14,4 +15,14 @@ public interface ReadOnlyAddressBook {
*/
ObservableList getPersonList();
+ /**
+ * Returns an unmodifiable view of the groups list.
+ * This list will not contain any duplicate groups.
+ */
+ ObservableList getGroupList();
+
+ /**
+ * Sorts the address book's groups by group number.
+ */
+ public void sortGroups();
}
diff --git a/src/main/java/seedu/address/model/group/Group.java b/src/main/java/seedu/address/model/group/Group.java
new file mode 100644
index 00000000000..8baa849a9af
--- /dev/null
+++ b/src/main/java/seedu/address/model/group/Group.java
@@ -0,0 +1,171 @@
+package seedu.address.model.group;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.model.group.exceptions.TaskException;
+import seedu.address.model.group.tasks.TaskInitializer;
+import seedu.address.model.group.tasks.TaskList;
+import seedu.address.model.person.Person;
+import seedu.address.model.tutorial.Tutorial;
+
+/**
+ * Represents a Group in StudentConnect.
+ * Guarantees: details are present and not null, number is immutable.
+ */
+public class Group {
+
+ private static final int MAXIMUM_SIZE = 5;
+ private final int number;
+ private final Tutorial tutorial;
+ private Set members = new HashSet<>();
+ private TaskList tasks = new TaskList();
+
+ /**
+ * Constructs a {@code Group}.
+ *
+ * @param number A valid group number.
+ * @param tutorial A valid tutorial group.
+ */
+ public Group(int number, Tutorial tutorial) {
+ this.number = number;
+ this.tutorial = tutorial;
+ if (tasks.isEmpty()) {
+ try {
+ tasks = TaskInitializer.initializeTasks();
+ } catch (TaskException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ }
+
+ /**
+ * Constructs a {@code Group}.
+ *
+ * @param number A valid group number.
+ * @param tutorial A valid tutorial group.
+ * @param members The members of the group.
+ * @param tasks The initial tasks for the group.
+ */
+ public Group(int number, Tutorial tutorial, Set members, TaskList tasks) {
+ this.number = number;
+ this.tutorial = tutorial;
+ this.members = members;
+ this.tasks = tasks;
+ }
+
+ /**
+ * Adds a person to the group.
+ *
+ * @param person The person to be added as a member.
+ */
+ public void addMember(Person person) {
+ this.members.add(person);
+ }
+
+ public int getNumber() {
+ return this.number;
+ }
+
+ public Tutorial getTutorial() {
+ return this.tutorial;
+ }
+
+ public Set getMembers() {
+ return Collections.unmodifiableSet(members);
+ }
+
+ /**
+ * Returns true if the set contains an equivalent person as the given argument.
+ */
+ public boolean hasMember(Person person) {
+ return this.members.stream().anyMatch(person::isSamePerson);
+ }
+
+ /**
+ * Returns true if the group contains the maximum number of members.
+ */
+ public boolean isFull() {
+ return this.members.size() == MAXIMUM_SIZE;
+ }
+
+ /**
+ * Returns true if the group number is a positive integer.
+ */
+ public static boolean isValidGroupNumber(String groupNumber) {
+ requireNonNull(groupNumber);
+
+ try {
+ int value = Integer.parseInt(groupNumber);
+ return value > 0 && !groupNumber.startsWith("+"); // "+1" is successfully parsed by Integer#parseInt(String)
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns true if both groups have the same number.
+ * This defines a weaker notion of equality between two groups.
+ */
+ public boolean isSameGroup(Group otherGroup) {
+ if (otherGroup == this) {
+ return true;
+ }
+
+ return otherGroup != null
+ && otherGroup.getNumber() == this.number;
+ }
+
+ public TaskList getTasks() {
+ return tasks;
+ }
+
+ public void addTasks(TaskList tasks) {
+ this.tasks.addTasks(tasks.getTasks());
+ }
+
+ /**
+ * Returns true if both groups have the same number, tutorial and members.
+ * This defines a stronger notion of equality between two groups.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Group)) {
+ return false;
+ }
+
+ Group otherGroup = (Group) other;
+ return number == otherGroup.getNumber()
+ && tutorial.equals(otherGroup.tutorial)
+ && members.equals(otherGroup.members);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("group number", number)
+ .add("tutorial", tutorial)
+ .add("members", members)
+ .add("tasks", tasks)
+ .toString();
+ }
+
+ /**
+ * Removes a person from the group.
+ * @param person The person to be removed.
+ * @return True if the person was removed from the group, false if the person was not found in the group.
+ */
+ public boolean removeMember(Person person) {
+ return members.remove(person);
+ }
+}
diff --git a/src/main/java/seedu/address/model/group/GroupBelongsTutorialPredicate.java b/src/main/java/seedu/address/model/group/GroupBelongsTutorialPredicate.java
new file mode 100644
index 00000000000..4242085764e
--- /dev/null
+++ b/src/main/java/seedu/address/model/group/GroupBelongsTutorialPredicate.java
@@ -0,0 +1,43 @@
+package seedu.address.model.group;
+
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.StringUtil;
+import seedu.address.commons.util.ToStringBuilder;
+
+/**
+ * Tests that a {@code Group}'s {@code Tutorial} matches the tutorial given.
+ */
+public class GroupBelongsTutorialPredicate implements Predicate {
+ private final String tutorial;
+
+ public GroupBelongsTutorialPredicate(String tutorial) {
+ this.tutorial = tutorial;
+ }
+
+ @Override
+ public boolean test(Group group) {
+ String grpTutorial = group.getTutorial().value;
+ return StringUtil.containsTutorial(grpTutorial, tutorial);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof GroupBelongsTutorialPredicate)) {
+ return false;
+ }
+
+ GroupBelongsTutorialPredicate otherGroupBelongsTutorialPredicate = (GroupBelongsTutorialPredicate) other;
+ return tutorial.equals(otherGroupBelongsTutorialPredicate.tutorial);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add("tutorial", tutorial).toString();
+ }
+}
diff --git a/src/main/java/seedu/address/model/group/GroupContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/group/GroupContainsKeywordsPredicate.java
new file mode 100644
index 00000000000..38fb4473a94
--- /dev/null
+++ b/src/main/java/seedu/address/model/group/GroupContainsKeywordsPredicate.java
@@ -0,0 +1,45 @@
+package seedu.address.model.group;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.StringUtil;
+import seedu.address.commons.util.ToStringBuilder;
+
+/**
+ * Tests that a {@code Group}'s {@code Number} matches any of the keywords given.
+ */
+public class GroupContainsKeywordsPredicate implements Predicate {
+ private final List keywords;
+
+ public GroupContainsKeywordsPredicate(List keywords) {
+ this.keywords = keywords;
+ }
+
+ @Override
+ public boolean test(Group group) {
+ return keywords.stream()
+ .anyMatch(keyword ->
+ StringUtil.containsWordIgnoreCase(String.valueOf(new Integer(group.getNumber())), keyword));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof GroupContainsKeywordsPredicate)) {
+ return false;
+ }
+
+ GroupContainsKeywordsPredicate otherGroupContainsKeywordsPredicate = (GroupContainsKeywordsPredicate) other;
+ return keywords.equals(otherGroupContainsKeywordsPredicate.keywords);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add("keywords", keywords).toString();
+ }
+}
diff --git a/src/main/java/seedu/address/model/group/UniqueGroupList.java b/src/main/java/seedu/address/model/group/UniqueGroupList.java
new file mode 100644
index 00000000000..f450f91f478
--- /dev/null
+++ b/src/main/java/seedu/address/model/group/UniqueGroupList.java
@@ -0,0 +1,158 @@
+package seedu.address.model.group;
+
+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 java.util.stream.Collectors;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import seedu.address.model.group.exceptions.DuplicateGroupException;
+import seedu.address.model.group.exceptions.GroupNotFoundException;
+import seedu.address.model.person.Person;
+
+/**
+ * A list of groups that enforces uniqueness between its elements and does not allow nulls.
+ * A group is considered unique by comparing using {@code Group#isSameGroup(Group)}. As such, adding and updating of
+ * groups uses Group#isSameGroup(Group) for equality so as to ensure that the group being added or updated is
+ * unique in terms of identity in the UniqueGroupList. However, the removal of a group uses Group#equals(Object) so
+ * as to ensure that the group with exactly the same members will be removed.
+ *
+ * Supports a minimal set of list operations.
+ *
+ * @see Group#isSameGroup(Group)
+ */
+public class UniqueGroupList implements Iterable {
+
+ private final ObservableList internalList = FXCollections.observableArrayList();
+ private final ObservableList internalUnmodifiableList =
+ FXCollections.unmodifiableObservableList(internalList);
+
+ /**
+ * Returns true if the list contains an equivalent group as the given argument.
+ */
+ public boolean contains(Group toCheck) {
+ requireNonNull(toCheck);
+ return internalList.stream().anyMatch(toCheck::isSameGroup);
+ }
+
+ /**
+ * Adds a group to the list.
+ * The group must not already exist in the list.
+ */
+ public void add(Group toAdd) {
+ requireNonNull(toAdd);
+ if (contains(toAdd)) {
+ throw new DuplicateGroupException();
+ }
+ internalList.add(toAdd);
+ }
+
+ /**
+ * Removes the equivalent group from the list.
+ * The group must exist in the list.
+ */
+ public void remove(Group toRemove) {
+ requireNonNull(toRemove);
+ if (!internalList.remove(toRemove)) {
+ throw new GroupNotFoundException();
+ }
+ }
+
+ /**
+ * Removes the specified person from all groups in the list.
+ */
+ public void remove(Person person) {
+ requireNonNull(person);
+
+ for (Group group : internalList) {
+ if (group.hasMember(person)) {
+ group.removeMember(person);
+ }
+ }
+ }
+
+
+ public void setGroups(UniqueGroupList replacement) {
+ requireNonNull(replacement);
+ internalList.setAll(replacement.internalList);
+ }
+
+ /**
+ * Replaces the contents of this list with {@code groups}.
+ * {@code groups} must not contain duplicate groups.
+ */
+ public void setGroups(List groups) {
+ requireAllNonNull(groups);
+ if (!groupsAreUnique(groups)) {
+ throw new DuplicateGroupException();
+ }
+
+ internalList.setAll(groups);
+ }
+
+ /**
+ * Returns the backing list as an unmodifiable {@code ObservableList}.
+ */
+ public ObservableList asUnmodifiableObservableList() {
+ return internalUnmodifiableList;
+ }
+
+ /**
+ * Returns the list of {@code groups}, sorted by group number.
+ */
+ public List getSortedList() {
+ List sortedList = internalList.stream()
+ .sorted(Comparator.comparing(Group::getNumber))
+ .collect(Collectors.toList());
+
+ return sortedList;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return internalList.iterator();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof UniqueGroupList)) {
+ return false;
+ }
+
+ UniqueGroupList otherUniqueGroupList = (UniqueGroupList) other;
+ return internalList.equals(otherUniqueGroupList.internalList);
+ }
+
+ @Override
+ public int hashCode() {
+ return internalList.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return internalList.toString();
+ }
+
+ /**
+ * Returns true if {@code groups} contains only unique groups.
+ */
+ private boolean groupsAreUnique(List groups) {
+ for (int i = 0; i < groups.size() - 1; i++) {
+ for (int j = i + 1; j < groups.size(); j++) {
+ if (groups.get(i).isSameGroup(groups.get(j))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/seedu/address/model/group/exceptions/DuplicateGroupException.java b/src/main/java/seedu/address/model/group/exceptions/DuplicateGroupException.java
new file mode 100644
index 00000000000..20292d5c11d
--- /dev/null
+++ b/src/main/java/seedu/address/model/group/exceptions/DuplicateGroupException.java
@@ -0,0 +1,11 @@
+package seedu.address.model.group.exceptions;
+
+/**
+ * Signals that the operation will result in duplicate Groups (Persons are considered duplicates if they have the same
+ * identity).
+ */
+public class DuplicateGroupException extends RuntimeException {
+ public DuplicateGroupException() {
+ super("Operation would result in duplicate groups");
+ }
+}
diff --git a/src/main/java/seedu/address/model/group/exceptions/GroupNotFoundException.java b/src/main/java/seedu/address/model/group/exceptions/GroupNotFoundException.java
new file mode 100644
index 00000000000..e376cb1e7a0
--- /dev/null
+++ b/src/main/java/seedu/address/model/group/exceptions/GroupNotFoundException.java
@@ -0,0 +1,11 @@
+package seedu.address.model.group.exceptions;
+
+/**
+ * Signals that the operation is looking for a group that does not exist.
+ */
+
+public class GroupNotFoundException extends RuntimeException {
+ public GroupNotFoundException() {
+ super("Group not found.");
+ }
+}
diff --git a/src/main/java/seedu/address/model/group/exceptions/TaskException.java b/src/main/java/seedu/address/model/group/exceptions/TaskException.java
new file mode 100644
index 00000000000..2c20b3f342c
--- /dev/null
+++ b/src/main/java/seedu/address/model/group/exceptions/TaskException.java
@@ -0,0 +1,19 @@
+package seedu.address.model.group.exceptions;
+
+/**
+ * The `TaskException` class represents an exception specific to the Duke program.
+ * It is a subclass of the standard Java `Exception` class and is used
+ * to handle custom error messages.
+ */
+public class TaskException extends Exception {
+
+ /**
+ * Initializes a new instance of `TaskException` with the specified error message.
+ *
+ * @param message The error message associated with this exception.
+ */
+ public TaskException(String message) {
+ super(message);
+ }
+}
+
diff --git a/src/main/java/seedu/address/model/group/tasks/Deadline.java b/src/main/java/seedu/address/model/group/tasks/Deadline.java
new file mode 100644
index 00000000000..aa817f77ec0
--- /dev/null
+++ b/src/main/java/seedu/address/model/group/tasks/Deadline.java
@@ -0,0 +1,40 @@
+package seedu.address.model.group.tasks;
+
+import java.time.LocalDateTime;
+
+import seedu.address.model.group.exceptions.TaskException;
+
+/**
+ * The `Deadline` class represents a task with a specific deadline in the Duke program.
+ * It is a subclass of the `Task` class and provides functionality to handle tasks with deadlines.
+ */
+public class Deadline extends Task {
+
+ private String byStr;
+ private LocalDateTime by;
+
+ /**
+ * Initializes a new `Deadline` task with the specified description, status, module, and deadline.
+ *
+ * @param task The description of the task.
+ * @param status The status of the task (complete or incomplete).
+ * @param module The module the task is assigned to (CS2103T or CS2101).
+ * @param by The deadline of the task in string format (dd/MM/yyyy).
+ * @throws TaskException If there is an issue parsing the deadline format.
+ */
+ public Deadline(String task, TaskStatus status, TaskModule module, String by) throws TaskException {
+ super(task, status, module, "D", by);
+ try {
+ this.by = parseDateTime(by);
+ this.byStr = by;
+ } catch (Exception e) {
+ throw new TaskException("Invalid date format :< Please use dd/MM/yyyy\n");
+ }
+ }
+
+ @Override
+ public String getDeadline() {
+ return "(by " + this.byStr + ")";
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/group/tasks/Task.java b/src/main/java/seedu/address/model/group/tasks/Task.java
new file mode 100644
index 00000000000..452c86b6346
--- /dev/null
+++ b/src/main/java/seedu/address/model/group/tasks/Task.java
@@ -0,0 +1,215 @@
+package seedu.address.model.group.tasks;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Objects;
+
+
+/**
+ * The Task class represents a task in the StudentConnect application.
+ */
+public class Task {
+
+ private String task;
+ private TaskStatus status;
+ private TaskModule module;
+ private String type;
+ private String by;
+
+ /**
+ * Constructs a Task object with a task description, status, module, and save status.
+ *
+ * @param task The description of the task.
+ * @param status The status of the task (complete or incomplete).
+ * @param module The module the task is assigned to (CS2103T or CS2101).
+ * @param type The type of task, either a deadline or a todo.
+ * @param by The date the task must be completed by, empty for a todo.
+ */
+ public Task(String task, TaskStatus status, TaskModule module, String type, String by) {
+ this.task = task;
+ this.status = status;
+ this.module = module;
+ this.type = type;
+ this.by = by;
+
+ if (!task.isEmpty()) {
+ addTask(this.task);
+ }
+ }
+
+ /**
+ * Empty constructor for Task.
+ */
+ public Task() {
+
+ }
+
+ /**
+ * Returns a string representation of the task.
+ *
+ * @return A string representing the task's status and description.
+ */
+ @Override
+ public String toString() {
+ return this.type + " " + status.toString() + " " + this.module + " " + this.task + " " + this.by;
+ }
+
+ /**
+ * Adds a task to the list of tasks.
+ *
+ * @param task The task description to add.
+ */
+ public void addTask(String task) {
+ }
+
+ /**
+ * Gets the status of the task.
+ *
+ * @return The status of the task.
+ */
+ public TaskStatus getStatus() {
+ return this.status;
+ }
+
+ /**
+ * Sets the status of the task.
+ *
+ * @param taskStatus The status to set.
+ */
+ public void setStatus(TaskStatus taskStatus) {
+ this.status = taskStatus;
+ }
+
+ /**
+ * Gets the module of the task.
+ *
+ * @return The module the task is assigned to.
+ */
+ public TaskModule getModule() {
+ return module;
+ }
+
+ /**
+ * Gets the description of the task.
+ *
+ * @return The task description.
+ */
+ public String getTask() {
+ return this.task;
+ }
+
+ /**
+ * Gets the type of the task.
+ *
+ * @return The task type.
+ */
+ public String getType() {
+ return this.type;
+ }
+
+ /**
+ * Gets the deadline of the task.
+ *
+ * @return The task deadline.
+ */
+ public String getBy() {
+ return this.by;
+ }
+
+ /**
+ * Parses a date and time string to a LocalDateTime object.
+ *
+ * @param dateTimeString The date and time string in the format "dd/MM/yyyy HHmm".
+ * @return A LocalDateTime object representing the parsed date and time.
+ */
+ public LocalDateTime parseDateTime(String dateTimeString) {
+ // Split the input string into date and time parts
+ String[] parts = dateTimeString.split(" ", 2);
+
+ // Check if there are exactly two parts (date and time)
+ if (parts.length != 2) {
+ throw new IllegalArgumentException("Invalid date/time format: "
+ + dateTimeString);
+ }
+
+ String datePart = parts[0];
+ String timePart = parts[1];
+
+ // Define a formatter for the date part, e.g., "dd/MM/yyyy"
+ DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
+ // Parse the date part into a LocalDate object
+ LocalDate date = LocalDate.parse(datePart, dateFormatter);
+
+ // Define a formatter for the time part, e.g., "HHmm"
+ DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HHmm");
+ // Parse the time part into a LocalTime object
+ LocalTime time = LocalTime.parse(timePart, timeFormatter);
+
+ // Combine the date and time into a LocalDateTime object
+ return LocalDateTime.of(date, time);
+ }
+
+ /**
+ * Gets the type of the task.
+ *
+ * @return The string representing task type.
+ */
+ public String getTaskType() {
+ // Your logic to determine the task type based on the instance's actual class
+ if (this instanceof Todo) {
+ return "T";
+ } else if (this instanceof Deadline) {
+ return "D";
+ } else {
+ return ""; // Handle unknown task types or add appropriate logic
+ }
+ }
+
+ /**
+ * Marks the task as done.
+ */
+ public void mark() {
+ this.status = TaskStatus.DONE;
+ }
+
+ /**
+ * Marks the task as not done.
+ */
+ public void unMark() {
+ this.status = TaskStatus.NOT_DONE;
+ }
+
+ /**
+ * Gets the deadline of the task.
+ *
+ * @return string representation of deadline, or empty string for todo tasks.
+ */
+ public String getDeadline() {
+ if (Objects.equals(this.getTaskType(), "D")) {
+ return this.getDeadline();
+ } else {
+ return "";
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof Task)) {
+ return false;
+ }
+
+ Task task = (Task) other;
+ return Objects.equals(this.task, task.task)
+ && this.status == task.status
+ && this.module == task.module
+ && Objects.equals(this.type, task.type)
+ && Objects.equals(this.by, task.by);
+ }
+
+
+}
diff --git a/src/main/java/seedu/address/model/group/tasks/TaskInitializer.java b/src/main/java/seedu/address/model/group/tasks/TaskInitializer.java
new file mode 100644
index 00000000000..d2ec788d203
--- /dev/null
+++ b/src/main/java/seedu/address/model/group/tasks/TaskInitializer.java
@@ -0,0 +1,58 @@
+package seedu.address.model.group.tasks;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import seedu.address.model.group.exceptions.TaskException;
+
+/**
+ * A utility class for initializing tasks and creating a TaskList.
+ */
+public class TaskInitializer {
+ /**
+ * Initializes tasks for different modules and creates a TaskList.
+ *
+ * @return A TaskList with the initialized tasks.
+ * @throws TaskException If there is an issue with task initialization.
+ */
+ public static TaskList initializeTasks() throws TaskException {
+ // Create an empty TaskList
+ TaskList taskList = new TaskList();
+
+ List allTasks = new ArrayList<>();
+
+ // Create tasks for CS2101
+ allTasks.add(new Todo("Upload video of OP1.",
+ TaskStatus.NOT_DONE, TaskModule.CS2101));
+ allTasks.add(new Todo("Complete peer review for OP2.",
+ TaskStatus.NOT_DONE, TaskModule.CS2101));
+ allTasks.add(new Deadline("Submit slides for OP2.",
+ TaskStatus.NOT_DONE, TaskModule.CS2101, "29/10/2023 2359"));
+ allTasks.add(new Deadline("Complete peer review.",
+ TaskStatus.NOT_DONE, TaskModule.CS2101, "02/11/2023 2359"));
+ allTasks.add(new Todo("Research on the SCQA framework.",
+ TaskStatus.NOT_DONE, TaskModule.CS2101));
+ allTasks.add(new Deadline("Plan for OP2.",
+ TaskStatus.NOT_DONE, TaskModule.CS2101, "24/10/2023 2359"));
+ allTasks.add(new Deadline("Submit UG.",
+ TaskStatus.NOT_DONE, TaskModule.CS2101, "11/11/2023 2359"));
+
+ // Create tasks for CS2103T
+ allTasks.add(new Todo("Complete mid semester review form.",
+ TaskStatus.NOT_DONE, TaskModule.CS2103T));
+ allTasks.add(new Deadline("Add demo screenshots to project notes.",
+ TaskStatus.NOT_DONE, TaskModule.CS2103T, "20/11/2023 2359"));
+ allTasks.add(new Deadline("Release v1.3.trial jar file.",
+ TaskStatus.NOT_DONE, TaskModule.CS2103T, "27/10/2023 2359"));
+ allTasks.add(new Deadline("Wrap up milestone 1.3.",
+ TaskStatus.NOT_DONE, TaskModule.CS2103T, "03/11/2023 2359"));
+ allTasks.add(new Deadline("Finalise TP.",
+ TaskStatus.NOT_DONE, TaskModule.CS2103T, "17/11/2023 2359"));
+ allTasks.add(new Todo("Update DG for each feature.",
+ TaskStatus.NOT_DONE, TaskModule.CS2103T));
+
+ // Add the tasks to the TaskList
+ taskList.addTasks(allTasks);
+ return taskList;
+ }
+}
diff --git a/src/main/java/seedu/address/model/group/tasks/TaskList.java b/src/main/java/seedu/address/model/group/tasks/TaskList.java
new file mode 100644
index 00000000000..0414be34d1a
--- /dev/null
+++ b/src/main/java/seedu/address/model/group/tasks/TaskList.java
@@ -0,0 +1,128 @@
+package seedu.address.model.group.tasks;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a list of tasks in a group.
+ */
+public class TaskList {
+ private List tasks;
+
+ /**
+ * Constructs an empty TaskList.
+ */
+ public TaskList() {
+ tasks = new ArrayList<>();
+ }
+
+ /**
+ * Constructs a TaskList with the given initial tasks.
+ *
+ * @param initialTasks The initial tasks to populate the TaskList.
+ */
+ public TaskList(List initialTasks) {
+ tasks = new ArrayList<>(initialTasks);
+ }
+
+ /**
+ * Adds a task to the task list.
+ *
+ * @param task The task to add.
+ */
+ public void addTask(Task task) {
+ tasks.add(task);
+ }
+
+ /**
+ * Deletes a task from the task list at the specified index.
+ *
+ * @param taskIndex The index of the task to delete.
+ */
+ public void deleteTask(int taskIndex) {
+ if (taskIndex >= 0 && taskIndex < tasks.size()) {
+ tasks.remove(taskIndex);
+ }
+ }
+
+ /**
+ * Returns the list of tasks in the task list.
+ *
+ * @return The list of tasks in the task list.
+ */
+ public List getTasks() {
+ return tasks;
+ }
+
+ /**
+ * Returns the task at index in the task list.
+ *
+ * @param index The index of task to be returned.
+ * @return The task at the index.
+ */
+ public Task getTask(int index) {
+ return tasks.get(index);
+ }
+
+ /**
+ * Checks if a task with the specified task type and task description exists in the task list.
+ *
+ * @param taskType The type of the task.
+ * @param taskDescription The description of the task.
+ * @return True if a matching task exists, false otherwise.
+ */
+ public boolean isTaskInAllTasks(String taskType, String taskDescription) {
+ for (Task task : tasks) {
+ if (task.getTask().equals(taskDescription) && task.getTaskType().equals(taskType)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Adds a list of tasks to the task list.
+ *
+ * @param tasks The list of tasks to add.
+ */
+ public void addTasks(List tasks) {
+ this.tasks.addAll(tasks);
+ }
+
+ /**
+ * Returns true if the task list is empty.
+ *
+ * @return True if the task list is empty, false otherwise.
+ */
+ public boolean isEmpty() {
+ return tasks.isEmpty();
+ }
+
+ /**
+ * Returns a copy of the list of tasks in the task list.
+ *
+ * @return A copy of the list of tasks.
+ */
+ public List getTaskList() {
+ return new ArrayList<>(tasks);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder();
+ String tempStatus;
+ for (int i = 0; i < tasks.size(); i++) {
+ Task task = tasks.get(i);
+ if (task.getStatus() == TaskStatus.NOT_DONE) {
+ tempStatus = "❌";
+ } else {
+ tempStatus = "✅";
+ }
+ result.append(tempStatus).append(" ").append(task.getType()).append(" ").append(i + 1).append(".")
+ .append(" ").append(task.getModule()).append(" ").append(task.getTask())
+ .append(" ").append(task.getBy()).append("\n");
+
+ }
+ return String.valueOf(result);
+ }
+}
diff --git a/src/main/java/seedu/address/model/group/tasks/TaskModule.java b/src/main/java/seedu/address/model/group/tasks/TaskModule.java
new file mode 100644
index 00000000000..d0885e8d9d7
--- /dev/null
+++ b/src/main/java/seedu/address/model/group/tasks/TaskModule.java
@@ -0,0 +1,43 @@
+package seedu.address.model.group.tasks;
+
+/**
+ * The `TaskModule` enum represents the module for a task, which can be either CS2103T or CS2101.
+ * It provides symbolic representations for the task module.
+ */
+public enum TaskModule {
+ /**
+ * Represents the CS2103T module.
+ */
+ CS2103T,
+
+ /**
+ * Represents the CS2101 module.
+ */
+ CS2101;
+
+ public static final String MESSAGE_CONSTRAINTS = "Module must either be CS2103T or CS2101";
+
+ /**
+ * Returns the string representation of the task module.
+ *
+ * @return A string representation of the task module.
+ */
+ @Override
+ public String toString() {
+ return name(); // This returns the name of the enum constant (CS2103T or CS2101).
+ }
+ /**
+ * Checks if the given string is a valid task module.
+ *
+ * @param test The string to test.
+ * @return True if the string is a valid task module, false otherwise.
+ */
+ public static boolean isValidModule(String test) {
+ for (TaskModule module : TaskModule.values()) {
+ if (module.toString().equals(test)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/seedu/address/model/group/tasks/TaskStatus.java b/src/main/java/seedu/address/model/group/tasks/TaskStatus.java
new file mode 100644
index 00000000000..d44ca02cb13
--- /dev/null
+++ b/src/main/java/seedu/address/model/group/tasks/TaskStatus.java
@@ -0,0 +1,64 @@
+package seedu.address.model.group.tasks;
+
+/**
+ * The `TaskStatus` enum represents the status of a task, which can be either done or not done.
+ * It provides symbolic representations for task status and allows converting status to a string.
+ */
+public enum TaskStatus {
+ /**
+ * Represents a task that is marked as done.
+ */
+ DONE("DONE"),
+
+ /**
+ * Represents a task that is not marked as done.
+ */
+ NOT_DONE("NOT_DONE");
+
+
+ public static final String MESSAGE_CONSTRAINTS = "Status must either be DONE or NOT_DONE";
+ private final String symbol;
+
+ /**
+ * Constructs a `TaskStatus` enum value with the specified symbolic representation.
+ *
+ * @param symbol The symbolic representation of the task status.
+ */
+ TaskStatus(String symbol) {
+ this.symbol = symbol;
+ }
+
+ /**
+ * Returns the symbolic representation of the task status.
+ *
+ * @return A string representation of the task status.
+ */
+
+ @Override
+ public String toString() {
+ return symbol;
+ }
+
+ /**
+ * Checks if the given string is a valid task status.
+ *
+ * @param test The string to test.
+ * @return True if the string is a valid task status, false otherwise.
+ */
+ public static boolean isValidStatus(String test) {
+ for (TaskStatus status : TaskStatus.values()) {
+ if (status.toString().equals(test)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public String getSymbol() {
+ if (this == DONE) {
+ return "✅";
+ } else {
+ return "❌";
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/model/group/tasks/Todo.java b/src/main/java/seedu/address/model/group/tasks/Todo.java
new file mode 100644
index 00000000000..c722851b56a
--- /dev/null
+++ b/src/main/java/seedu/address/model/group/tasks/Todo.java
@@ -0,0 +1,21 @@
+package seedu.address.model.group.tasks;
+
+/**
+ * The `Todo` class represents a to-do task, which is a basic type of task with a description.
+ * It inherits from the `Task` class and provides specific implementations for to-do tasks.
+ */
+public class Todo extends Task {
+
+ /**
+ * Constructs a new `Todo` task with the given description, status, and module.
+ *
+ * @param task The description of the to-do task.
+ * @param status The status of the task (complete or incomplete).
+ * @param module The module the task is assigned to (CS2103T or CS2101).
+ */
+ public Todo(String task, TaskStatus status, TaskModule module) {
+ super(task, status, module, "T", "");
+
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/person/Address.java
deleted file mode 100644
index 469a2cc9a1e..00000000000
--- a/src/main/java/seedu/address/model/person/Address.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package seedu.address.model.person;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
-
-/**
- * Represents a Person's address in the address book.
- * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)}
- */
-public class Address {
-
- public static final String MESSAGE_CONSTRAINTS = "Addresses can take any values, and it should not be blank";
-
- /*
- * The first character of the address must not be a whitespace,
- * otherwise " " (a blank string) becomes a valid input.
- */
- public static final String VALIDATION_REGEX = "[^\\s].*";
-
- public final String value;
-
- /**
- * Constructs an {@code Address}.
- *
- * @param address A valid address.
- */
- public Address(String address) {
- requireNonNull(address);
- checkArgument(isValidAddress(address), MESSAGE_CONSTRAINTS);
- value = address;
- }
-
- /**
- * Returns true if a given string is a valid email.
- */
- public static boolean isValidAddress(String test) {
- return test.matches(VALIDATION_REGEX);
- }
-
- @Override
- public String toString() {
- return value;
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Address)) {
- return false;
- }
-
- Address otherAddress = (Address) other;
- return value.equals(otherAddress.value);
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/Description.java b/src/main/java/seedu/address/model/person/Description.java
new file mode 100644
index 00000000000..4e7fd084c93
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Description.java
@@ -0,0 +1,53 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents a Student's description in StudentConnect.
+ * Guarantees: immutable; is valid as declared in {@link #isValidDescription(String)}
+ */
+public class Description {
+
+ public static final String MESSAGE_CONSTRAINTS = "Descriptions should not be left blank or exceed 150 characters.";
+
+ public static final String VALIDATION_REGEX = "^.{1,150}$";
+
+ public final String value;
+
+
+ /**
+ * Constructs a {@code Description} with the specified description value.
+ *
+ * @param description The description value. Must not be null.
+ */
+ public Description(String description) {
+ requireNonNull(description);
+ checkArgument(isValidDescription(description), MESSAGE_CONSTRAINTS);
+ value = description;
+ }
+
+ /**
+ * Returns true if a given string is a valid description.
+ */
+ public static boolean isValidDescription(String test) {
+ return test.matches(VALIDATION_REGEX);
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof Description // instanceof handles nulls
+ && value.equals(((Description) other).value)); // state check
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/person/Email.java
index c62e512bc29..530b9bd6db3 100644
--- a/src/main/java/seedu/address/model/person/Email.java
+++ b/src/main/java/seedu/address/model/person/Email.java
@@ -4,7 +4,7 @@
import static seedu.address.commons.util.AppUtil.checkArgument;
/**
- * Represents a Person's email in the address book.
+ * Represents a Student's email in StudentConnect.
* Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)}
*/
public class Email {
@@ -14,22 +14,14 @@ public class Email {
+ "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"
- + "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"
- + " - have each domain label consist of alphanumeric characters, separated only by hyphens, if any.";
+ + "characters. It should also not exceed 20 characters\n"
+ + "2. This is followed by a '@' and then a domain name. The domain name should be 'u.nus.edu' ";
// alphanumeric and special characters
- private static final String ALPHANUMERIC_NO_UNDERSCORE = "[^\\W_]+"; // alphanumeric characters except underscore
+ private static final String ALPHANUMERIC_NO_UNDERSCORE = "[A-Za-z0-9" + SPECIAL_CHARACTERS + "]{1,20}";
private static final String LOCAL_PART_REGEX = "^" + ALPHANUMERIC_NO_UNDERSCORE + "([" + SPECIAL_CHARACTERS + "]"
+ ALPHANUMERIC_NO_UNDERSCORE + ")*";
- private static final String DOMAIN_PART_REGEX = ALPHANUMERIC_NO_UNDERSCORE
- + "(-" + ALPHANUMERIC_NO_UNDERSCORE + ")*";
- private static final String DOMAIN_LAST_PART_REGEX = "(" + DOMAIN_PART_REGEX + "){2,}$"; // At least two chars
- private static final String DOMAIN_REGEX = "(" + DOMAIN_PART_REGEX + "\\.)*" + DOMAIN_LAST_PART_REGEX;
- public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@" + DOMAIN_REGEX;
+ private static final String DOMAIN_PART_REGEX = "u\\.nus\\.edu";
+ public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@" + DOMAIN_PART_REGEX;
public final String value;
diff --git a/src/main/java/seedu/address/model/person/Gender.java b/src/main/java/seedu/address/model/person/Gender.java
new file mode 100644
index 00000000000..2d137bd42c2
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Gender.java
@@ -0,0 +1,61 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents a Student's gender.
+ * Guarantees: immutable; is valid as declared in {@link #isValidGender(String)}
+ */
+public class Gender {
+
+ public static final String MESSAGE_CONSTRAINTS =
+ "Gender should be 'M' or 'F' (case-insensitive).";
+
+ public static final String VALIDATION_REGEX = "(?i)[MF]"; // Case-insensitive regex for M or F
+
+ public final String value;
+
+ /**
+ * Constructs a {@code Gender}.
+ *
+ * @param gender A valid gender ('M' or 'F').
+ */
+ public Gender(String gender) {
+ requireNonNull(gender);
+ checkArgument(isValidGender(gender), MESSAGE_CONSTRAINTS);
+ value = gender.toUpperCase(); // Store as uppercase to ensure consistency
+ }
+
+ /**
+ * Returns true if a given string is a valid gender ('M' or 'F').
+ */
+ public static boolean isValidGender(String test) {
+ return test.matches(VALIDATION_REGEX);
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Gender)) {
+ return false;
+ }
+
+ Gender otherGender = (Gender) other;
+ return value.equals(otherGender.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Major.java b/src/main/java/seedu/address/model/person/Major.java
new file mode 100644
index 00000000000..bb200d76a44
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Major.java
@@ -0,0 +1,143 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents a Student's major in StudentConnect.
+ * Guarantees: immutable; is valid as declared in {@link #isValidMajor(String)}
+ */
+public class Major {
+
+ public static final String MESSAGE_CONSTRAINTS = "Majors should not be blank and must be a valid"
+ + " major offered at NUS.\n"
+ + "Valid major list can be found in the user guide.\n"
+ + "The user guide can be found by using the 'help' command.";
+
+ private static final String[] VALID_NUS_MAJORS = {
+ "Accountancy",
+ "Actuarial Studies",
+ "Anthropology",
+ "Architecture",
+ "Biological Sciences",
+ "Biomedical Engineering",
+ "Business Administration",
+ "Business Administration (Accountancy)",
+ "Business Analytics",
+ "Chemical Engineering",
+ "Chemistry",
+ "Chinese Language",
+ "Chinese Studies",
+ "Communications and New Media",
+ "Civil Engineering",
+ "Computer Engineering",
+ "Computer Science",
+ "Data Science and Analytics",
+ "Data Science and Economics",
+ "Dentistry",
+ "Economics",
+ "Electrical Engineering",
+ "English Language",
+ "English Literature",
+ "Environmental Engineering",
+ "Environmental Studies",
+ "Food Science and Technology",
+ "Geography",
+ "Global Studies",
+ "History",
+ "Japanese Studies",
+ "Industrial Design",
+ "Industrial Engineering",
+ "Industrial and Systems Engineering",
+ "Information Systems",
+ "Information Security",
+ "Infrastructure and Project Management",
+ "Landscape Architecture",
+ "Law",
+ "Life Sciences",
+ "Malay Studies",
+ "Management",
+ "Marketing",
+ "Materials Science and Engineering",
+ "Mathematics",
+ "Mechanical Engineering",
+ "Medicine",
+ "Pharmacy",
+ "Pharmaceutical Science",
+ "Philosophy",
+ "Philosophy, Politics, and Economics",
+ "Physics",
+ "Political Science",
+ "Psychology",
+ "Quantitative Finance",
+ "Real Estate",
+ "Social Work",
+ "Sociology",
+ "South Asian Studies",
+ "Southeast Asian Studies",
+ "Statistics",
+ "Systems Engineering",
+ "Theatre Studies",
+ "Urban Studies",
+ "Visual Communications",
+ "Others"
+ };
+
+ public final String value;
+
+ /**
+ * Constructs a {@code Major} with the specified major value.
+ *
+ * @param major The major value. Must not be null.
+ */
+ public Major(String major) {
+ requireNonNull(major);
+ checkArgument(isValidMajor(major), MESSAGE_CONSTRAINTS);
+ value = capitaliseFirstLetterOfEachWord(major);
+ }
+
+ private String capitaliseFirstLetterOfEachWord(String text) {
+ String[] words = text.split("\\s");
+ StringBuilder result = new StringBuilder();
+
+ for (String word : words) {
+ if (!word.isEmpty()) {
+ result.append(Character.toUpperCase(word.charAt(0))).append(word.substring(1).toLowerCase());
+ result.append(" "); // Add a space between words
+ }
+ }
+ if (result.length() > 0) {
+ result.setLength(result.length() - 1);
+ }
+ return result.toString();
+ }
+
+ /**
+ * Returns if a given string is a valid major offered at NUS.
+ */
+ public static boolean isValidMajor(String major) {
+ for (String validMajor : VALID_NUS_MAJORS) {
+ if (major.equalsIgnoreCase(validMajor)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof Major // instanceof handles nulls
+ && value.equals(((Major) other).value)); // state check
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/person/Name.java
index 173f15b9b00..855e05fd77a 100644
--- a/src/main/java/seedu/address/model/person/Name.java
+++ b/src/main/java/seedu/address/model/person/Name.java
@@ -4,19 +4,20 @@
import static seedu.address.commons.util.AppUtil.checkArgument;
/**
- * Represents a Person's name in the address book.
+ * Represents a Student's name in StudentConnect.
* Guarantees: immutable; is valid as declared in {@link #isValidName(String)}
*/
public class Name {
public static final String MESSAGE_CONSTRAINTS =
- "Names should only contain alphanumeric characters and spaces, and it should not be blank";
+ "Names should only contain alphabetical characters and spaces, and it should not be blank. "
+ + "It should also not exceed 30 characters.";
- /*
+ /**
* The first character of the address must not be a whitespace,
* otherwise " " (a blank string) becomes a valid input.
*/
- public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*";
+ public static final String VALIDATION_REGEX = "^(?=\\s*\\S)[A-Za-z ]{1,30}$";
public final String fullName;
@@ -28,7 +29,23 @@ public class Name {
public Name(String name) {
requireNonNull(name);
checkArgument(isValidName(name), MESSAGE_CONSTRAINTS);
- fullName = name;
+ fullName = capitaliseFirstLetterOfEachWord(name);
+ }
+
+ private String capitaliseFirstLetterOfEachWord(String text) {
+ String[] words = text.split("\\s");
+ StringBuilder result = new StringBuilder();
+
+ for (String word : words) {
+ if (!word.isEmpty()) {
+ result.append(Character.toUpperCase(word.charAt(0))).append(word.substring(1).toLowerCase());
+ result.append(" "); // Add a space between words
+ }
+ }
+ if (result.length() > 0) {
+ result.setLength(result.length() - 1);
+ }
+ return result.toString();
}
/**
@@ -63,5 +80,4 @@ public boolean equals(Object other) {
public int hashCode() {
return fullName.hashCode();
}
-
}
diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java
index 62d19be2977..a5acfab9cb9 100644
--- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java
+++ b/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java
@@ -19,7 +19,7 @@ public NameContainsKeywordsPredicate(List keywords) {
@Override
public boolean test(Person person) {
return keywords.stream()
- .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword));
+ .anyMatch(keyword -> StringUtil.containsIgnoreCase(person.getName().fullName, keyword));
}
@Override
diff --git a/src/main/java/seedu/address/model/person/Nationality.java b/src/main/java/seedu/address/model/person/Nationality.java
new file mode 100644
index 00000000000..fa1ed56a867
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Nationality.java
@@ -0,0 +1,61 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents a Student's nationality in StudentConnect.
+ * Guarantees: immutable; is valid as declared in {@link #isValidNationality(String)}
+ */
+public class Nationality {
+
+ public static final String MESSAGE_CONSTRAINTS =
+ "Nationality should be either 'local' or 'foreigner'";
+
+ public static final String VALIDATION_REGEX = "(?i)local|foreigner";
+
+ public final String value;
+
+ /**
+ * Constructs a {@code Nationality}.
+ *
+ * @param nationality A valid nationality.
+ */
+ public Nationality(String nationality) {
+ requireNonNull(nationality);
+ checkArgument(isValidNationality(nationality), MESSAGE_CONSTRAINTS);
+ value = nationality.toLowerCase(); // Store as lowercase to ensure consistency
+ }
+
+ /**
+ * Returns true if a given string is a valid nationality.
+ */
+ public static boolean isValidNationality(String test) {
+ return test.matches(VALIDATION_REGEX);
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Nationality)) {
+ return false;
+ }
+
+ Nationality otherNationality = (Nationality) other;
+ return value.equals(otherNationality.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java
index abe8c46b535..1af61e47cd9 100644
--- a/src/main/java/seedu/address/model/person/Person.java
+++ b/src/main/java/seedu/address/model/person/Person.java
@@ -8,61 +8,83 @@
import java.util.Set;
import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.socialmedialink.SocialMediaLink;
+import seedu.address.model.tutorial.Tutorial;
/**
- * Represents a Person in the address book.
+ * Represents a Person/Student in StudentConnect.
* 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<>();
+ private final Major major;
+ private final Year year;
+ private final Description description;
+ private final Set socialMediaLinks = new HashSet<>();
+ private final Set tutorials = new HashSet<>();
+ private final Nationality nationality;
+ private final Gender gender;
/**
* 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);
+ public Person(Name name, Major major, Year year, Email email, Description description, Set tutorials,
+ Set socialMediaLinks, Nationality nationality, Gender gender) {
+ requireAllNonNull(name, major, year, email, description, tutorials, socialMediaLinks, nationality, gender);
this.name = name;
- this.phone = phone;
+ this.major = major;
+ this.year = year;
this.email = email;
- this.address = address;
- this.tags.addAll(tags);
+ this.description = description;
+ this.tutorials.addAll(tutorials);
+ this.socialMediaLinks.addAll(socialMediaLinks);
+ this.nationality = nationality;
+ this.gender = gender;
}
public Name getName() {
return name;
}
- public Phone getPhone() {
- return phone;
+ public Major getMajor() {
+ return major;
+ }
+
+ public Year getYear() {
+ return year;
}
public Email getEmail() {
return email;
}
- public Address getAddress() {
- return address;
+ public Description getDescription() {
+ return description;
}
- /**
- * Returns an immutable tag set, which throws {@code UnsupportedOperationException}
- * if modification is attempted.
- */
- public Set getTags() {
- return Collections.unmodifiableSet(tags);
+ public Set getTutorials() {
+ return Collections.unmodifiableSet(tutorials);
+ }
+
+ public Set getSocialMediaLinks() {
+ return Collections.unmodifiableSet(socialMediaLinks);
+ }
+
+ public Nationality getNationality() {
+ return nationality;
+ }
+
+ public Gender getGender() {
+ return gender;
}
/**
- * Returns true if both persons have the same name.
+ * Returns true if both persons have the same email.
* This defines a weaker notion of equality between two persons.
*/
public boolean isSamePerson(Person otherPerson) {
@@ -71,7 +93,7 @@ public boolean isSamePerson(Person otherPerson) {
}
return otherPerson != null
- && otherPerson.getName().equals(getName());
+ && otherPerson.getEmail().equals(getEmail());
}
/**
@@ -91,27 +113,34 @@ public boolean equals(Object other) {
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);
+ && major.equals(otherPerson.major)
+ && year.equals(otherPerson.year)
+ && email.equals(otherPerson.email)
+ && description.equals(otherPerson.description)
+ && tutorials.equals(otherPerson.tutorials)
+ && socialMediaLinks.equals(otherPerson.socialMediaLinks)
+ && nationality.equals(otherPerson.nationality)
+ && gender.equals(otherPerson.gender);
}
@Override
public int hashCode() {
// use this method for custom fields hashing instead of implementing your own
- return Objects.hash(name, phone, email, address, tags);
+ return Objects.hash(name, major, year, email, description, tutorials, socialMediaLinks, nationality, gender);
}
@Override
public String toString() {
return new ToStringBuilder(this)
- .add("name", name)
- .add("phone", phone)
- .add("email", email)
- .add("address", address)
- .add("tags", tags)
- .toString();
+ .add("name", name)
+ .add("major", major)
+ .add("year", year)
+ .add("email", email)
+ .add("description", description)
+ .add("tutorials", tutorials)
+ .add("socialMediaLinks", socialMediaLinks)
+ .add("nationality", nationality)
+ .add("gender", gender)
+ .toString();
}
-
}
diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/person/Year.java
similarity index 51%
rename from src/main/java/seedu/address/model/person/Phone.java
rename to src/main/java/seedu/address/model/person/Year.java
index d733f63d739..f87a0ff8e8a 100644
--- a/src/main/java/seedu/address/model/person/Phone.java
+++ b/src/main/java/seedu/address/model/person/Year.java
@@ -4,32 +4,33 @@
import static seedu.address.commons.util.AppUtil.checkArgument;
/**
- * Represents a Person's phone number in the address book.
- * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)}
+ * Represents a Student's numeric year level.
+ * Guarantees: immutable; is valid as declared in {@link #isValidYear(String)}
*/
-public class Phone {
-
+public class Year {
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,}";
+ "Year should only contain numbers, and it should be 1 digit long between 1 and 6.";
+
+ public static final String VALIDATION_REGEX = "^[1-6]$";
+
public final String value;
/**
- * Constructs a {@code Phone}.
+ * Constructs a {@code Year}.
*
- * @param phone A valid phone number.
+ * @param year A valid year.
*/
- public Phone(String phone) {
- requireNonNull(phone);
- checkArgument(isValidPhone(phone), MESSAGE_CONSTRAINTS);
- value = phone;
+ public Year(String year) {
+ requireNonNull(year);
+ checkArgument(isValidYear(year), MESSAGE_CONSTRAINTS);
+ value = year;
}
/**
- * Returns true if a given string is a valid phone number.
+ * Returns true if a given string is a valid year.
*/
- public static boolean isValidPhone(String test) {
+ public static boolean isValidYear(String test) {
return test.matches(VALIDATION_REGEX);
}
@@ -45,17 +46,16 @@ public boolean equals(Object other) {
}
// instanceof handles nulls
- if (!(other instanceof Phone)) {
+ if (!(other instanceof Year)) {
return false;
}
- Phone otherPhone = (Phone) other;
- return value.equals(otherPhone.value);
+ Year otherYear = (Year) other;
+ return value.equals(otherYear.value);
}
@Override
public int hashCode() {
return value.hashCode();
}
-
}
diff --git a/src/main/java/seedu/address/model/socialmedialink/SocialMediaLink.java b/src/main/java/seedu/address/model/socialmedialink/SocialMediaLink.java
new file mode 100644
index 00000000000..87334b89284
--- /dev/null
+++ b/src/main/java/seedu/address/model/socialmedialink/SocialMediaLink.java
@@ -0,0 +1,62 @@
+package seedu.address.model.socialmedialink;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents the Social Media Links in StudentConnect.
+ * Guarantees: immutable; name is valid as declared in {@link #isValidSocialMediaLink(String)}
+ */
+public class SocialMediaLink {
+
+ public static final String MESSAGE_CONSTRAINTS = "Social media links should start with \"http://\", or "
+ + "\"https://\", followed by one or more alphanumeric characters, dots, or hyphens in the domain name";
+ public static final String VALIDATION_REGEX = "^(https?|ftp)://[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}(:[0-9]+)?(/.*)?$";
+
+ public final String socialMediaLink;
+
+ /**
+ * Constructs a {@code SocialMedia}.
+ *
+ * @param socialMediaLink A valid social media link.
+ */
+ public SocialMediaLink(String socialMediaLink) {
+ requireNonNull(socialMediaLink);
+ checkArgument(isValidSocialMediaLink(socialMediaLink), MESSAGE_CONSTRAINTS);
+ this.socialMediaLink = socialMediaLink;
+ }
+
+ /**
+ * Returns true if a given string is a valid social media link.
+ */
+ public static boolean isValidSocialMediaLink(String test) {
+ return test.matches(VALIDATION_REGEX);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof SocialMediaLink)) {
+ return false;
+ }
+
+ SocialMediaLink otherSocialMedia = (SocialMediaLink) other;
+ return socialMediaLink.equals(otherSocialMedia.socialMediaLink);
+ }
+
+ @Override
+ public int hashCode() {
+ return socialMediaLink.hashCode();
+ }
+
+ /**
+ * Format state as text for viewing.
+ */
+ public String toString() {
+ return '[' + socialMediaLink + ']';
+ }
+}
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/tutorial/Tutorial.java b/src/main/java/seedu/address/model/tutorial/Tutorial.java
new file mode 100644
index 00000000000..63356870627
--- /dev/null
+++ b/src/main/java/seedu/address/model/tutorial/Tutorial.java
@@ -0,0 +1,56 @@
+package seedu.address.model.tutorial;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents a Student's tutorial in StudentConnect.
+ * Guarantees: immutable; is valid as declared in {@link #isValidTutorial(String)}
+ */
+public class Tutorial {
+
+ public static final String MESSAGE_CONSTRAINTS = "Tutorials should be 2-digit numbers between 01 and 22.";
+
+ public static final String VALIDATION_REGEX = "^(0[1-9]|1\\d|2[0-2])$";
+
+ public final String value;
+
+ /**
+ * Constructs a {@code Tutorial} with the specified tutorial value.
+ *
+ * @param tutorial The tutorial value. Must not be null.
+ */
+ public Tutorial(String tutorial) {
+ requireNonNull(tutorial);
+ checkArgument(isValidTutorial(tutorial), MESSAGE_CONSTRAINTS);
+ value = tutorial;
+ }
+
+ /**
+ * Returns true if a given string is a valid tutorial number between 01 and 22.
+ */
+ public static boolean isValidTutorial(String test) {
+ return test.matches(VALIDATION_REGEX);
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return "T" + value;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof Tutorial // instanceof handles nulls
+ && value.equals(((Tutorial) other).value)); // state check
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/src/main/java/seedu/address/model/tutorial/TutorialContainsSlotsPredicate.java b/src/main/java/seedu/address/model/tutorial/TutorialContainsSlotsPredicate.java
new file mode 100644
index 00000000000..a70e4f496b2
--- /dev/null
+++ b/src/main/java/seedu/address/model/tutorial/TutorialContainsSlotsPredicate.java
@@ -0,0 +1,51 @@
+package seedu.address.model.tutorial;
+
+import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.StringUtil;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.model.person.Person;
+
+/**
+ * Tests that a {@code Person}'s {@code Tutorial} matches any of the slots given.
+ */
+public class TutorialContainsSlotsPredicate implements Predicate {
+ private final List slots;
+
+ public TutorialContainsSlotsPredicate(List slots) {
+ this.slots = slots;
+ }
+
+ @Override
+ public boolean test(Person person) {
+ Set tutorialSet = person.getTutorials();
+ StringBuilder result = new StringBuilder();
+ for (Tutorial tut : tutorialSet) {
+ result.append(tut.value).append(" ");
+ }
+ return slots.stream()
+ .anyMatch(slot -> StringUtil.containsTutorial(result.toString(), slot));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof TutorialContainsSlotsPredicate)) {
+ return false;
+ }
+
+ TutorialContainsSlotsPredicate otherTutorialContainsSlotsPredicate = (TutorialContainsSlotsPredicate) other;
+ return slots.equals(otherTutorialContainsSlotsPredicate.slots);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add("slots", slots).toString();
+ }
+}
diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java
index 1806da4facf..b5a994b830c 100644
--- a/src/main/java/seedu/address/model/util/SampleDataUtil.java
+++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java
@@ -6,55 +6,89 @@
import seedu.address.model.AddressBook;
import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Address;
+import seedu.address.model.group.Group;
+import seedu.address.model.person.Description;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Gender;
+import seedu.address.model.person.Major;
import seedu.address.model.person.Name;
+import seedu.address.model.person.Nationality;
import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.person.Year;
+import seedu.address.model.socialmedialink.SocialMediaLink;
+import seedu.address.model.tutorial.Tutorial;
/**
* 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"),
- 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"))
+ new Person(new Name("Alex Yeoh"), new Major("Computer Science"), new Year("2"),
+ new Email("alexyeoh@u.nus.edu"), new Description("Friendly person"),
+ getTutorialSet("01", "20"),
+ getSocialMediaLinkSet("https://example.com/alex"),
+ new Nationality("local"), new Gender("M")),
+ new Person(new Name("Bernice Yu"), new Major("Computer Science"), new Year("2"),
+ new Email("berniceyu@u.nus.edu"), new Description("Colleague from work"),
+ getTutorialSet("08", "19"),
+ getSocialMediaLinkSet("https://example.com/bernice"),
+ new Nationality("local"), new Gender("F")),
+ new Person(new Name("Charlotte Oliveiro"), new Major("Computer Science"), new Year("2"),
+ new Email("charlotte@u.nus.edu"), new Description("Neighbour"),
+ getTutorialSet("05", "06", "10"),
+ getSocialMediaLinkSet("https://example.com/charlotte"),
+ new Nationality("local"), new Gender("F")),
+ new Person(new Name("David Li"), new Major("Computer Science"), new Year("2"),
+ new Email("lidavid@u.nus.edu"), new Description("Family member"),
+ getTutorialSet("11"),
+ getSocialMediaLinkSet("https://example.com/david"),
+ new Nationality("foreigner"), new Gender("M")),
+ new Person(new Name("Irfan Ibrahim"), new Major("Computer Science"), new Year("2"),
+ new Email("irfan@u.nus.edu"), new Description("Classmate"),
+ getTutorialSet("07", "18"),
+ getSocialMediaLinkSet("https://example.com/irfan"),
+ new Nationality("local"), new Gender("M")),
+ new Person(new Name("Roy Balakrishnan"), new Major("Computer Science"), new Year("2"),
+ new Email("royb@u.nus.edu"), new Description("Colleague from work"),
+ getTutorialSet("11", "19", "22"),
+ getSocialMediaLinkSet("https://example.com/roy"),
+ new Nationality("foreigner"), new Gender("M"))
};
}
+ public static Group[] getSampleGroups() {
+ Group sampleGroup1 = new Group(1, new Tutorial("01"));
+ sampleGroup1.addMember(getSamplePersons()[0]);
+
+ Group sampleGroup2 = new Group(2, new Tutorial("11"));
+ sampleGroup2.addMember(getSamplePersons()[3]);
+ sampleGroup2.addMember(getSamplePersons()[5]);
+
+ return new Group[]{ sampleGroup1, sampleGroup2 };
+ }
+
public static ReadOnlyAddressBook getSampleAddressBook() {
AddressBook sampleAb = new AddressBook();
for (Person samplePerson : getSamplePersons()) {
sampleAb.addPerson(samplePerson);
}
+ for (Group sampleGroup : getSampleGroups()) {
+ sampleAb.addGroup(sampleGroup);
+ }
return sampleAb;
}
- /**
- * Returns a tag set containing the list of strings given.
- */
- public static Set getTagSet(String... strings) {
- return Arrays.stream(strings)
- .map(Tag::new)
- .collect(Collectors.toSet());
+ public static Set getTutorialSet(String... tutorials) {
+ return Arrays.stream(tutorials)
+ .map(Tutorial::new)
+ .collect(Collectors.toSet());
}
+ public static Set getSocialMediaLinkSet(String... strings) {
+ return Arrays.stream(strings)
+ .map(SocialMediaLink::new)
+ .collect(Collectors.toSet());
+ }
}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedGroup.java b/src/main/java/seedu/address/storage/JsonAdaptedGroup.java
new file mode 100644
index 00000000000..fe284f01f3a
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedGroup.java
@@ -0,0 +1,98 @@
+package seedu.address.storage;
+
+import static seedu.address.storage.JsonAdaptedPerson.MISSING_FIELD_MESSAGE_FORMAT;
+
+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.group.Group;
+import seedu.address.model.group.tasks.TaskList;
+import seedu.address.model.person.Person;
+import seedu.address.model.tutorial.Tutorial;
+
+/**
+ * Jackson-friendly version of {@link Group}.
+ */
+public class JsonAdaptedGroup {
+
+ public static final String INVALID_NUMBER_MESSAGE = "Group number is invalid!";
+
+ private final int number;
+ private final JsonAdaptedTutorial tutorial;
+ private final List members = new ArrayList<>();
+ private final List tasks = new ArrayList<>(); // add a list of JsonAdaptedTask
+
+ /**
+ * Constructs a {@code JsonAdaptedGroup} with the given group details.
+ */
+ @JsonCreator
+ public JsonAdaptedGroup(@JsonProperty("number") int number,
+ @JsonProperty("tutorial") JsonAdaptedTutorial tutorial,
+ @JsonProperty("members") List members,
+ @JsonProperty("tasks") List tasks) { // add tasks parameter
+
+ this.number = number;
+ this.tutorial = tutorial;
+ if (members != null) {
+ this.members.addAll(members);
+ }
+ if (tasks != null) {
+ this.tasks.addAll(tasks);
+ }
+ }
+
+ /**
+ * Converts a given {@code Group} into this class for Jackson use.
+ */
+ public JsonAdaptedGroup(Group source) {
+ number = source.getNumber();
+ tutorial = new JsonAdaptedTutorial(source.getTutorial());
+ members.addAll(source.getMembers().stream()
+ .map(JsonAdaptedPerson::new)
+ .collect(Collectors.toList()));
+ tasks.addAll(source.getTasks().getTaskList().stream()
+ .map(JsonAdaptedTask::new)
+ .collect(Collectors.toList()));
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted social media object into the model's {@code Group} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted group.
+ */
+ public Group toModelType() throws IllegalValueException {
+ final List groupMembers = new ArrayList<>();
+ final TaskList groupTasks = new TaskList();
+
+ for (JsonAdaptedPerson member : members) {
+ groupMembers.add(member.toModelType());
+ }
+ for (JsonAdaptedTask task : tasks) {
+ groupTasks.addTask(task.toModelType());
+ }
+
+ if (number < 1) {
+ throw new IllegalValueException(INVALID_NUMBER_MESSAGE);
+ }
+
+ if (tutorial == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ Tutorial.class.getSimpleName()));
+ }
+ if (!Tutorial.isValidTutorial(tutorial.getTutorial())) {
+ throw new IllegalValueException(Tutorial.MESSAGE_CONSTRAINTS);
+ }
+ final Tutorial modelTutorial = tutorial.toModelType();
+
+ final Set modelMembers = new HashSet<>(groupMembers);
+
+ return new Group(number, modelTutorial, modelMembers, groupTasks);
+ }
+}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
index bd1ca0f56c8..51cd4af3d37 100644
--- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
+++ b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
@@ -10,12 +10,17 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.person.Address;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.Description;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Gender;
+import seedu.address.model.person.Major;
import seedu.address.model.person.Name;
+import seedu.address.model.person.Nationality;
import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.person.Year;
+import seedu.address.model.socialmedialink.SocialMediaLink;
+import seedu.address.model.tutorial.Tutorial;
/**
* Jackson-friendly version of {@link Person}.
@@ -25,25 +30,41 @@ 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 major;
+ private final String year;
private final String email;
- private final String address;
- private final List tags = new ArrayList<>();
+ private final String description;
+ private final List tutorials = new ArrayList<>();
+ private final List socialMediaLinks = new ArrayList<>();
+ private final String nationality;
+ private final String gender;
/**
* 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) {
+ public JsonAdaptedPerson(@JsonProperty("name") String name,
+ @JsonProperty("major") String major,
+ @JsonProperty("year") String year,
+ @JsonProperty("email") String email,
+ @JsonProperty("description") String description,
+ @JsonProperty("tutorials") List tutorials,
+ @JsonProperty("socialMediaLinks") List socialMediaLinks,
+ @JsonProperty("nationality") String nationality,
+ @JsonProperty("gender") String gender) {
this.name = name;
- this.phone = phone;
+ this.major = major;
+ this.year = year;
this.email = email;
- this.address = address;
- if (tags != null) {
- this.tags.addAll(tags);
+ this.description = description;
+ if (tutorials != null) {
+ this.tutorials.addAll(tutorials);
}
+ if (socialMediaLinks != null) {
+ this.socialMediaLinks.addAll(socialMediaLinks);
+ }
+ this.nationality = nationality;
+ this.gender = gender;
}
/**
@@ -51,12 +72,18 @@ public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone
*/
public JsonAdaptedPerson(Person source) {
name = source.getName().fullName;
- phone = source.getPhone().value;
+ major = source.getMajor().value;
+ year = source.getYear().value;
email = source.getEmail().value;
- address = source.getAddress().value;
- tags.addAll(source.getTags().stream()
- .map(JsonAdaptedTag::new)
+ description = source.getDescription().value;
+ tutorials.addAll(source.getTutorials().stream()
+ .map(JsonAdaptedTutorial::new)
+ .collect(Collectors.toList()));
+ socialMediaLinks.addAll(source.getSocialMediaLinks().stream()
+ .map(JsonAdaptedSocialMedia::new)
.collect(Collectors.toList()));
+ nationality = source.getNationality().value;
+ gender = source.getGender().value;
}
/**
@@ -65,9 +92,15 @@ public JsonAdaptedPerson(Person source) {
* @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());
+ final List personTutorials = new ArrayList<>();
+ final List personSocialMediaLinks = new ArrayList<>();
+
+ for (JsonAdaptedTutorial tutorial : tutorials) {
+ personTutorials.add(tutorial.toModelType());
+ }
+
+ for (JsonAdaptedSocialMedia socialMediaLink : socialMediaLinks) {
+ personSocialMediaLinks.add(socialMediaLink.toModelType());
}
if (name == null) {
@@ -78,13 +111,21 @@ public Person toModelType() throws IllegalValueException {
}
final Name modelName = new Name(name);
- if (phone == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()));
+ if (major == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Major.class.getSimpleName()));
+ }
+ if (!Major.isValidMajor(major)) {
+ throw new ParseException(Major.MESSAGE_CONSTRAINTS);
+ }
+ final Major modelMajor = new Major(major);
+
+ if (year == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Year.class.getSimpleName()));
}
- if (!Phone.isValidPhone(phone)) {
- throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS);
+ if (!Year.isValidYear(year)) {
+ throw new IllegalValueException(Year.MESSAGE_CONSTRAINTS);
}
- final Phone modelPhone = new Phone(phone);
+ final Year modelYear = new Year(year);
if (email == null) {
throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName()));
@@ -94,16 +135,33 @@ public Person toModelType() throws IllegalValueException {
}
final Email modelEmail = new Email(email);
- if (address == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()));
+ if (description == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ Description.class.getSimpleName()));
}
- if (!Address.isValidAddress(address)) {
- throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS);
+ if (!Description.isValidDescription(description)) {
+ throw new ParseException(Description.MESSAGE_CONSTRAINTS);
+ }
+ if (nationality == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ Nationality.class.getSimpleName()));
+ }
+ if (!Nationality.isValidNationality(nationality)) {
+ throw new IllegalValueException(Nationality.MESSAGE_CONSTRAINTS);
}
- final Address modelAddress = new Address(address);
- final Set modelTags = new HashSet<>(personTags);
- return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags);
- }
+ if (!Gender.isValidGender(gender)) {
+ throw new IllegalValueException(Gender.MESSAGE_CONSTRAINTS);
+ }
+ final Nationality modelNationality = new Nationality(nationality);
+ final Gender modelGender = new Gender(gender);
+ final Description modelDescription = new Description(description);
+ final Set modelTutorials = new HashSet<>(personTutorials);
+ final Set modelSocialMediaLinks = new HashSet<>(personSocialMediaLinks);
+
+ return new Person(modelName, modelMajor, modelYear, modelEmail, modelDescription,
+ modelTutorials, modelSocialMediaLinks, modelNationality, modelGender);
+
+ }
}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedSocialMedia.java b/src/main/java/seedu/address/storage/JsonAdaptedSocialMedia.java
new file mode 100644
index 00000000000..bdb6714c05a
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedSocialMedia.java
@@ -0,0 +1,47 @@
+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.socialmedialink.SocialMediaLink;
+
+/**
+ * Jackson-friendly version of {@link SocialMediaLink}.
+ */
+public class JsonAdaptedSocialMedia {
+
+ private final String socialMediaLink;
+
+ /**
+ * Constructs a {@code JsonAdaptedSocialMedia} with the given {@code socialMediaLink}.
+ */
+ @JsonCreator
+ public JsonAdaptedSocialMedia(String socialMediaLink) {
+ this.socialMediaLink = socialMediaLink;
+ }
+
+ /**
+ * Converts a given {@code SocialMedia} into this class for Jackson use.
+ */
+ public JsonAdaptedSocialMedia(SocialMediaLink source) {
+ socialMediaLink = source.socialMediaLink;
+ }
+
+ @JsonValue
+ public String getSocialMediaLink() {
+ return socialMediaLink;
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted social media object into the model's {@code SocialMedia} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted social media.
+ */
+ public SocialMediaLink toModelType() throws IllegalValueException {
+ if (!SocialMediaLink.isValidSocialMediaLink(socialMediaLink)) {
+ throw new IllegalValueException(SocialMediaLink.MESSAGE_CONSTRAINTS);
+ }
+ return new SocialMediaLink(socialMediaLink);
+ }
+}
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/JsonAdaptedTask.java b/src/main/java/seedu/address/storage/JsonAdaptedTask.java
new file mode 100644
index 00000000000..b264edc7f6d
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedTask.java
@@ -0,0 +1,84 @@
+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.group.tasks.Task;
+import seedu.address.model.group.tasks.TaskModule;
+import seedu.address.model.group.tasks.TaskStatus;
+
+/**
+ * Jackson-friendly version of {@link Task}.
+ */
+public class JsonAdaptedTask {
+ private final String task;
+ private final String status;
+ private final String module;
+ private final String type;
+ private final String by;
+
+ /**
+ * Constructs a {@code JsonAdaptedTask} with the given task details.
+ */
+ @JsonCreator
+ public JsonAdaptedTask(@JsonProperty("task") String task,
+ @JsonProperty("status") String status,
+ @JsonProperty("module") String module,
+ @JsonProperty("type") String type,
+ @JsonProperty("by") String by) {
+ this.task = task;
+ this.status = status;
+ this.module = module;
+ this.type = type;
+ this.by = by;
+ }
+
+ /**
+ * Converts a given {@code Task} into this class for Jackson use.
+ */
+ public JsonAdaptedTask(Task source) {
+ task = source.getTask();
+ status = source.getStatus().toString();
+ module = source.getModule().toString();
+ type = source.getType();
+ by = source.getBy();
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted task object into the model's {@code Task} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted task.
+ */
+ public Task toModelType() throws IllegalValueException {
+ if (task == null) {
+ throw new IllegalValueException("Task's description is missing!");
+ }
+
+ if (status == null) {
+ throw new IllegalValueException("Task's status is missing!");
+ }
+ if (!TaskStatus.isValidStatus(status)) {
+ throw new IllegalValueException(TaskStatus.MESSAGE_CONSTRAINTS);
+ }
+ final TaskStatus modelStatus = TaskStatus.valueOf(status);
+
+ if (module == null) {
+ throw new IllegalValueException("Task's module is missing!");
+ }
+ if (!TaskModule.isValidModule(module)) {
+ throw new IllegalValueException(TaskModule.MESSAGE_CONSTRAINTS);
+ }
+ final TaskModule modelModule = TaskModule.valueOf(module);
+
+ if (type == null) {
+ throw new IllegalValueException("Task's type is missing!");
+ }
+
+ if (by == null) {
+ throw new IllegalValueException("Task's deadline is missing!");
+ }
+
+ return new Task(task, modelStatus, modelModule, type, by);
+ }
+}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTutorial.java b/src/main/java/seedu/address/storage/JsonAdaptedTutorial.java
new file mode 100644
index 00000000000..445d7fa4e69
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedTutorial.java
@@ -0,0 +1,57 @@
+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.tutorial.Tutorial;
+
+/**
+ * Jackson-friendly version of {@link Tutorial}.
+ */
+public class JsonAdaptedTutorial {
+
+ private final String tutorial;
+
+ /**
+ * Constructs a {@code JsonAdaptedTutorial} with the given {@code tutorial}.
+ *
+ * @param tutorial The tutorial value to be used in the adapted object.
+ */
+ @JsonCreator
+ public JsonAdaptedTutorial(String tutorial) {
+ this.tutorial = tutorial;
+ }
+
+ /**
+ * Converts a given {@code Tutorial} into this class for Jackson use.
+ *
+ * @param source The source Tutorial to be converted.
+ */
+ public JsonAdaptedTutorial(Tutorial source) {
+ tutorial = source.value;
+ }
+
+ /**
+ * Gets the tutorial value.
+ *
+ * @return The tutorial value as a string.
+ */
+ @JsonValue
+ public String getTutorial() {
+ return tutorial;
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted tutorial object into the model's {@code Tutorial} object.
+ *
+ * @return A Tutorial object.
+ * @throws IllegalValueException if there were any data constraints violated in the adapted tutorial.
+ */
+ public Tutorial toModelType() throws IllegalValueException {
+ if (!Tutorial.isValidTutorial(tutorial)) {
+ throw new IllegalValueException(Tutorial.MESSAGE_CONSTRAINTS);
+ }
+ return new Tutorial(tutorial);
+ }
+}
diff --git a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java b/src/main/java/seedu/address/storage/JsonAddressBookStorage.java
index 41e06f264e1..057bf0a1d6e 100644
--- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java
+++ b/src/main/java/seedu/address/storage/JsonAddressBookStorage.java
@@ -73,6 +73,7 @@ public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) thro
requireNonNull(addressBook);
requireNonNull(filePath);
+ addressBook.sortGroups();
FileUtil.createIfMissing(filePath);
JsonUtil.saveJsonFile(new JsonSerializableAddressBook(addressBook), filePath);
}
diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
index 5efd834091d..d176f6af5c7 100644
--- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
+++ b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
@@ -11,6 +11,7 @@
import seedu.address.commons.exceptions.IllegalValueException;
import seedu.address.model.AddressBook;
import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.group.Group;
import seedu.address.model.person.Person;
/**
@@ -20,15 +21,19 @@
class JsonSerializableAddressBook {
public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s).";
+ public static final String MESSAGE_DUPLICATE_GROUP = "Persons list contains duplicate group(s).";
private final List persons = new ArrayList<>();
+ private final List groups = new ArrayList<>();
/**
- * Constructs a {@code JsonSerializableAddressBook} with the given persons.
+ * Constructs a {@code JsonSerializableAddressBook} with the given persons and groups.
*/
@JsonCreator
- public JsonSerializableAddressBook(@JsonProperty("persons") List persons) {
+ public JsonSerializableAddressBook(@JsonProperty("persons") List persons,
+ @JsonProperty("groups") List groups) {
this.persons.addAll(persons);
+ this.groups.addAll(groups);
}
/**
@@ -38,6 +43,7 @@ public JsonSerializableAddressBook(@JsonProperty("persons") List {
+
+ private static final String FXML = "ClearConfirmationPopup.fxml";
+
+ @FXML
+ private Button yesButton;
+
+ @FXML
+ private Button cancelButton;
+
+ @FXML
+ private Label confirmationMessage;
+
+ private boolean isConfirmed = false;
+
+ private Consumer confirmationCallback;
+
+
+
+ /**
+ * Creates a new ConfirmationPopup with a new internal Stage.
+ */
+ public ConfirmationPopup() {
+ super(FXML, new Stage());
+ // Make the stage modal
+ getRoot().initModality(Modality.APPLICATION_MODAL);
+ initialize();
+ }
+
+ /**
+ * Sets the callback to be executed when the confirmation is received.
+ *
+ * @param callback The callback to be executed.
+ */
+ public void setConfirmationCallback(Consumer callback) {
+ this.confirmationCallback = callback;
+ }
+
+ /**
+ * Shows the confirmation popup with the specified message.
+ *
+ */
+ public void show() {
+ confirmationMessage.setText("Are you sure you would like to delete all the data? \n"
+ + "You will not be able to undo this action later.");
+ getLogger().fine("Showing confirmation popup.");
+ getRoot().show();
+ getRoot().centerOnScreen();
+ }
+
+ /**
+ * Initializes the confirmation popup, setting up event handlers for the "Yes" and "Cancel" buttons.
+ */
+ @FXML
+ private void initialize() {
+ yesButton.setOnAction(event -> {
+ isConfirmed = true;
+ getRoot().close();
+ if (confirmationCallback != null) {
+ confirmationCallback.accept(true);
+ }
+ });
+
+ cancelButton.setOnAction(event -> getRoot().close());
+ }
+
+ /**
+ * Gets the logger for the ConfirmationPopup class.
+ *
+ * @return The logger for the ConfirmationPopup class.
+ */
+ private static Logger getLogger() {
+ return LogsCenter.getLogger(ConfirmationPopup.class);
+ }
+
+ /**
+ * Checks if the confirmation popup is currently being shown.
+ *
+ * @return True if the confirmation popup is showing, false otherwise.
+ */
+ public boolean isShowing() {
+ return getRoot().isShowing();
+ }
+
+ /**
+ * Focuses on the confirmation popup.
+ */
+ public void focus() {
+ getRoot().requestFocus();
+ }
+
+ /**
+ * Checks if the confirmation was confirmed.
+ *
+ * @return True if the confirmation was confirmed, false otherwise.
+ */
+ public boolean isConfirmed() {
+ return isConfirmed;
+ }
+
+ /**
+ * Gets the "Yes" button of the confirmation popup.
+ *
+ * @return The "Yes" button.
+ */
+ public Button getYesButton() {
+ return yesButton;
+ }
+
+ /**
+ * Gets the "Cancel" button of the confirmation popup.
+ *
+ * @return The "Cancel" button.
+ */
+ public Button getCancelButton() {
+ return cancelButton;
+ }
+
+}
diff --git a/src/main/java/seedu/address/ui/GroupCard.java b/src/main/java/seedu/address/ui/GroupCard.java
new file mode 100644
index 00000000000..02668b0764f
--- /dev/null
+++ b/src/main/java/seedu/address/ui/GroupCard.java
@@ -0,0 +1,109 @@
+package seedu.address.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Label;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Region;
+import seedu.address.model.group.Group;
+import seedu.address.model.person.Person;
+
+/**
+ * An UI component that displays information of a {@code Group}.
+ */
+public class GroupCard extends UiPart {
+
+ private static final String FXML = "GroupListCard.fxml";
+ private static final Logger logger = Logger.getLogger(GroupCard.class.getName());
+ public final Group group;
+
+ @FXML
+ private HBox cardPane;
+ @FXML
+ private Label id; // No need to change the id label style
+ @FXML
+ private Label number;
+ @FXML
+ private HBox tutorialBox;
+ @FXML
+ private Label tutorial;
+ @FXML
+ private Label member1;
+ @FXML
+ private Label member2;
+ @FXML
+ private Label member3;
+ @FXML
+ private Label member4;
+ @FXML
+ private Label member5;
+ @FXML
+ private List members = List.of(member1, member2, member3, member4, member5);
+
+ /**
+ * Creates a {@code GroupCard} with the given {@code Group} and index to display.
+ */
+ public GroupCard(Group group, int displayedIndex) {
+ super(FXML);
+ this.group = group;
+ id.setText(displayedIndex + ". ");
+ id.setStyle("-fx-font-size: 17px; -fx-text-fill: #E7BE34; -fx-font-family: 'Arial'; -fx-font-weight: bold");
+
+ assert number != null : "Number is null";
+ number.setText("Group " + group.getNumber() + " ");
+ number.setStyle("-fx-font-size: 17px; -fx-text-fill: #E7BE34; -fx-font-family: 'Arial'; "
+ + "-fx-font-weight: bold;");
+ // Apply the same style as id
+
+ tutorialBox.setStyle("-fx-background-color: rgba(101,152,60,0.82); -fx-background-radius: 10;");
+ tutorial.setText("T" + group.getTutorial().getValue());
+ tutorial.setStyle("-fx-font-size: 14px; -fx-text-fill: white;");
+
+ int groupSize = group.getMembers().size();
+ List memberList = new ArrayList<>(group.getMembers());
+ for (int i = 0; i < 5; i++) {
+ if (i < groupSize) {
+ Person member = memberList.get(i);
+ members.get(i).setText((i + 1) + ". " + member.getName() + " (" + member.getEmail() + ")");
+ } else {
+ members.get(i).setText("");
+ }
+ }
+ }
+
+ // Getter methods for testing
+ public String getId() {
+ return id.getId();
+ }
+
+ public String getNumber() {
+ return number.getText();
+ }
+
+ public String getTutorial() {
+ return tutorial.getText();
+ }
+
+ public String getMember1() {
+ return member1.getText();
+ }
+
+ public String getMember2() {
+ return member2.getText();
+ }
+
+ public String getMember3() {
+ return member3.getText();
+ }
+
+ public String getMember4() {
+ return member4.getText();
+ }
+
+ public String getMember5() {
+ return member5.getText();
+ }
+}
diff --git a/src/main/java/seedu/address/ui/GroupListPanel.java b/src/main/java/seedu/address/ui/GroupListPanel.java
new file mode 100644
index 00000000000..95fab5c9835
--- /dev/null
+++ b/src/main/java/seedu/address/ui/GroupListPanel.java
@@ -0,0 +1,49 @@
+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.group.Group;
+
+/**
+ * Panel containing the list of groups.
+ */
+public class GroupListPanel extends UiPart {
+ private static final String FXML = "GroupListPanel.fxml";
+ private final Logger logger = LogsCenter.getLogger(GroupListPanel.class);
+
+ @FXML
+ private ListView groupListView;
+
+ /**
+ * Creates a {@code GroupListPanel} with the given {@code ObservableList}.
+ */
+ public GroupListPanel(ObservableList groupList) {
+ super(FXML);
+ groupListView.setItems(groupList);
+ groupListView.setCellFactory(listView -> new GroupListViewCell());
+ }
+
+ /**
+ * Custom {@code ListCell} that displays the graphics of a {@code Group} using a {@code GroupCard}.
+ */
+ class GroupListViewCell extends ListCell {
+ @Override
+ protected void updateItem(Group group, boolean empty) {
+ super.updateItem(group, empty);
+
+ if (empty || group == null) {
+ setGraphic(null);
+ setText(null);
+ } else {
+ setGraphic(new GroupCard(group, getIndex() + 1).getRoot());
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/address/ui/HelpWindow.java
index 3f16b2fcf26..0ff15527d02 100644
--- a/src/main/java/seedu/address/ui/HelpWindow.java
+++ b/src/main/java/seedu/address/ui/HelpWindow.java
@@ -15,8 +15,16 @@
*/
public class HelpWindow extends UiPart {
- public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html";
- public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL;
+ public static final String USERGUIDE_URL = "https://ay2324s1-cs2103t-f12-2.github.io/tp/UserGuide.html";
+
+ public static final String HELP_MESSAGE = "If you need help, please refer to StudentConnect User Guide: "
+ + USERGUIDE_URL;
+
+ public static final String REQUIREMENTS_MESSAGE = "Requirements for CS2101/CS2103T groupings:\n"
+ + "~ Default team size: 5\n"
+ + "~ All team members must be assigned to the same tutorial.\n"
+ + "~ Teams composed entirely of members from a single nationality are prohibited.\n"
+ + "~ Same gender teams are allowed, but are discouraged.";
private static final Logger logger = LogsCenter.getLogger(HelpWindow.class);
private static final String FXML = "HelpWindow.fxml";
@@ -27,6 +35,9 @@ public class HelpWindow extends UiPart {
@FXML
private Label helpMessage;
+ @FXML
+ private Label requirementsMessage;
+
/**
* Creates a new HelpWindow.
*
@@ -35,6 +46,7 @@ public class HelpWindow extends UiPart {
public HelpWindow(Stage root) {
super(FXML, root);
helpMessage.setText(HELP_MESSAGE);
+ requirementsMessage.setText(REQUIREMENTS_MESSAGE);
}
/**
@@ -93,10 +105,20 @@ public void focus() {
* Copies the URL to the user guide to the clipboard.
*/
@FXML
- private void copyUrl() {
+ void copyUrl() {
+ logger.info("Student copied the URL to the clipboard: " + USERGUIDE_URL);
final Clipboard clipboard = Clipboard.getSystemClipboard();
final ClipboardContent url = new ClipboardContent();
url.putString(USERGUIDE_URL);
clipboard.setContent(url);
}
+
+ /**
+ * Gets the "Copy" button of the help window.
+ *
+ * @return The "Copy" button.
+ */
+ public Button getCopyButton() {
+ return copyButton;
+ }
}
diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java
index 79e74ef37c0..0bd163a88a7 100644
--- a/src/main/java/seedu/address/ui/MainWindow.java
+++ b/src/main/java/seedu/address/ui/MainWindow.java
@@ -2,6 +2,7 @@
import java.util.logging.Logger;
+import javafx.animation.PauseTransition;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.MenuItem;
@@ -10,9 +11,11 @@
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
+import javafx.util.Duration;
import seedu.address.commons.core.GuiSettings;
import seedu.address.commons.core.LogsCenter;
import seedu.address.logic.Logic;
+import seedu.address.logic.commands.ClearCommand;
import seedu.address.logic.commands.CommandResult;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.logic.parser.exceptions.ParseException;
@@ -32,18 +35,25 @@ public class MainWindow extends UiPart {
// Independent Ui parts residing in this Ui container
private PersonListPanel personListPanel;
+ private GroupListPanel groupListPanel;
private ResultDisplay resultDisplay;
private HelpWindow helpWindow;
+ private ConfirmationPopup confirmationPopup;
@FXML
private StackPane commandBoxPlaceholder;
@FXML
private MenuItem helpMenuItem;
+ @FXML
+ private MenuItem clearMenuItem;
@FXML
private StackPane personListPanelPlaceholder;
+ @FXML
+ private StackPane groupListPanelPlaceholder;
+
@FXML
private StackPane resultDisplayPlaceholder;
@@ -66,6 +76,7 @@ public MainWindow(Stage primaryStage, Logic logic) {
setAccelerators();
helpWindow = new HelpWindow();
+ confirmationPopup = new ConfirmationPopup();
}
public Stage getPrimaryStage() {
@@ -74,6 +85,7 @@ public Stage getPrimaryStage() {
private void setAccelerators() {
setAccelerator(helpMenuItem, KeyCombination.valueOf("F1"));
+ setAccelerator(clearMenuItem, KeyCombination.valueOf("F2"));
}
/**
@@ -113,6 +125,8 @@ void fillInnerParts() {
personListPanel = new PersonListPanel(logic.getFilteredPersonList());
personListPanelPlaceholder.getChildren().add(personListPanel.getRoot());
+ groupListPanel = new GroupListPanel(logic.getFilteredGroupList());
+
resultDisplay = new ResultDisplay();
resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot());
@@ -147,6 +161,22 @@ public void handleHelp() {
}
}
+ /**
+ * Opens the confirmation popup for clearing data.
+ */
+ @FXML
+ public void handleClear() {
+ // You can directly execute the ClearCommand logic here
+ try {
+ CommandResult result = logic.execute(ClearCommand.COMMAND_WORD);
+ // Check the result if needed
+ } catch (CommandException | ParseException e) {
+ // Handle exceptions if any
+ e.printStackTrace(); // You might want to log or display an error message
+ }
+ }
+
+
void show() {
primaryStage.show();
}
@@ -156,17 +186,47 @@ void show() {
*/
@FXML
private void handleExit() {
- GuiSettings guiSettings = new GuiSettings(primaryStage.getWidth(), primaryStage.getHeight(),
- (int) primaryStage.getX(), (int) primaryStage.getY());
- logic.setGuiSettings(guiSettings);
- helpWindow.hide();
- primaryStage.hide();
+ PauseTransition pause = new PauseTransition(Duration.seconds(1.6));
+ pause.setOnFinished(event -> {
+ GuiSettings guiSettings = new GuiSettings(primaryStage.getWidth(), primaryStage.getHeight(),
+ (int) primaryStage.getX(), (int) primaryStage.getY());
+ logic.setGuiSettings(guiSettings);
+ helpWindow.hide();
+ primaryStage.hide();
+ });
+ pause.play();
+ }
+
+ /**
+ * Displays the group UI.
+ */
+ @FXML
+ public void handleGroupCommand() {
+ personListPanelPlaceholder.getChildren().remove(personListPanel.getRoot());
+ if (!groupListPanelPlaceholder.getChildren().contains(groupListPanel.getRoot())) {
+ groupListPanelPlaceholder.getChildren().add(groupListPanel.getRoot());
+ }
+ }
+
+ /**
+ * Displays the normal UI.
+ */
+ @FXML
+ public void handlePersonCommand() {
+ groupListPanelPlaceholder.getChildren().remove(groupListPanel.getRoot());
+ if (!personListPanelPlaceholder.getChildren().contains(personListPanel.getRoot())) {
+ personListPanelPlaceholder.getChildren().add(personListPanel.getRoot());
+ }
}
public PersonListPanel getPersonListPanel() {
return personListPanel;
}
+ public GroupListPanel getGroupListPanel() {
+ return groupListPanel;
+ }
+
/**
* Executes the command and returns the result.
*
@@ -176,7 +236,6 @@ private CommandResult executeCommand(String commandText) throws CommandException
try {
CommandResult commandResult = logic.execute(commandText);
logger.info("Result: " + commandResult.getFeedbackToUser());
- resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser());
if (commandResult.isShowHelp()) {
handleHelp();
@@ -186,6 +245,19 @@ private CommandResult executeCommand(String commandText) throws CommandException
handleExit();
}
+ if (commandResult.isGroupCommand()) {
+ handleGroupCommand();
+ } else {
+ handlePersonCommand();
+ }
+
+ if (commandResult.isClear()) {
+ handleClear();
+ } else {
+ // If it's not a clear command, set the feedback message in ResultDisplay
+ resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser());
+ }
+
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
index 094c42cda82..35d9d33cf6a 100644
--- a/src/main/java/seedu/address/ui/PersonCard.java
+++ b/src/main/java/seedu/address/ui/PersonCard.java
@@ -1,13 +1,21 @@
package seedu.address.ui;
import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
import javafx.fxml.FXML;
+import javafx.scene.Node;
+import javafx.scene.control.Hyperlink;
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;
+import seedu.address.model.socialmedialink.SocialMediaLink;
/**
* An UI component that displays information of a {@code Person}.
@@ -15,15 +23,7 @@
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
- */
-
+ private static final Logger logger = Logger.getLogger(PersonCard.class.getName());
public final Person person;
@FXML
@@ -33,13 +33,25 @@ public class PersonCard extends UiPart {
@FXML
private Label id;
@FXML
- private Label phone;
+ private Label major;
@FXML
- private Label address;
+ private Label year;
@FXML
private Label email;
@FXML
- private FlowPane tags;
+ private Label description;
+ @FXML
+ private Label tutorials;
+ @FXML
+ private FlowPane socialMediaLinks;
+ @FXML
+ private Label nationality;
+ @FXML
+ private HBox nationalityBox;
+ @FXML
+ private Label gender;
+ @FXML
+ private HBox genderBox;
/**
* Creates a {@code PersonCode} with the given {@code Person} and index to display.
@@ -48,12 +60,110 @@ public PersonCard(Person person, int displayedIndex) {
super(FXML);
this.person = person;
id.setText(displayedIndex + ". ");
+ id.setStyle("-fx-font-size: 17px; -fx-text-fill: #E7BE34; -fx-font-family: 'Arial';");
name.setText(person.getName().fullName);
- phone.setText(person.getPhone().value);
- address.setText(person.getAddress().value);
+ name.setStyle("-fx-font-size: 20px");
+ if (person.getNationality().value.equals("local")) {
+ nationality.setText("Local");
+ nationalityBox.setStyle("-fx-background-color: rgba(101,152,60,0.82); -fx-background-radius: 10;");
+ } else {
+ nationality.setText("Foreigner");
+ nationalityBox.setStyle("-fx-background-color: rgba(17,63,3,0.68); -fx-background-radius: 10;");
+ }
+ nationality.setStyle("-fx-font-size: 14px");
+ if (person.getGender().value.equals("M")) {
+ gender.setText("Male");
+ genderBox.setStyle("-fx-background-color: rgb(78,88,136); -fx-background-radius: 10;");
+ } else {
+ gender.setText("Female");
+ genderBox.setStyle("-fx-background-color: rgb(93,81,87); -fx-background-radius: 10;");
+ }
+ gender.setStyle("-fx-font-size: 14px; -fx-text-fill: white");
+ major.setText(person.getMajor().value);
+ year.setText("Y" + person.getYear().value);
email.setText(person.getEmail().value);
- person.getTags().stream()
- .sorted(Comparator.comparing(tag -> tag.tagName))
- .forEach(tag -> tags.getChildren().add(new Label(tag.tagName)));
+ description.setText(person.getDescription().value);
+ String tutorialsText = person.getTutorials().stream()
+ .map(t -> "T" + t.getValue())
+ .sorted()
+ .collect(Collectors.joining(", "));
+ tutorials.setText(tutorialsText);
+
+ int initialHyperlinksCount = socialMediaLinks.getChildren().size(); // Get the initial count
+
+ if (!person.getSocialMediaLinks().isEmpty()) {
+ person.getSocialMediaLinks().stream()
+ .sorted(Comparator.comparing(sm -> sm.socialMediaLink))
+ .forEach(sm -> {
+ Hyperlink hyperlink = new Hyperlink(sm.socialMediaLink);
+ hyperlink.setStyle(
+ "-fx-font-size: 13px; -fx-text-fill: white; -fx-font-family: 'Segoe UI Semibold';");
+ hyperlink.setOnAction(event -> openWebBrowser(sm.socialMediaLink));
+ socialMediaLinks.getChildren().add(hyperlink);
+ });
+
+ int finalHyperlinksCount = socialMediaLinks.getChildren().size(); // Get the final count
+
+ // Use an assertion to check the condition
+ assert finalHyperlinksCount >= initialHyperlinksCount
+ : "No hyperlinks were added. Please check the code that adds hyperlinks.";
+ }
+
+ }
+
+ /**
+ * Opens a web browser with the specified URL.
+ *
+ * @param link The URL to be opened in the web browser.
+ * @throws UnsupportedOperationException if the platform does not support the {@code Desktop} class.
+ * @throws java.io.IOException If the default browser is not found or it fails to be launched.
+ * @throws java.net.URISyntaxException If the specified link is not a valid URI.
+ */
+ public void openWebBrowser(String link) {
+ try {
+ java.awt.Desktop.getDesktop().browse(new java.net.URI(link));
+ logger.info("Opened web browser for link: " + link);
+ } catch (java.io.IOException | java.net.URISyntaxException e) {
+ // Exceptions handled in other classes
+ logger.log(Level.WARNING, "Failed to open the web browser with link: " + link, e);
+ }
+ }
+
+ // Getter methods for testing
+ public String getName() {
+ return name.getText();
+ }
+
+ public String getMajor() {
+ return major.getText();
+ }
+
+ public String getYear() {
+ return year.getText();
}
+
+ public String getEmail() {
+ return email.getText();
+ }
+
+ public String getId() {
+ return id.getId();
+ }
+
+ public String getTutorials() {
+ return tutorials.getText();
+ }
+
+ public Set getSocialMediaLinks() {
+ Set links = new HashSet<>();
+ for (Node node : socialMediaLinks.getChildren()) {
+ if (node instanceof Hyperlink) {
+ Hyperlink hyperlink = (Hyperlink) node;
+ SocialMediaLink socialMediaLink = new SocialMediaLink(hyperlink.getText());
+ links.add(socialMediaLink);
+ }
+ }
+ return links;
+ }
+
}
diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/address/ui/UiManager.java
index fdf024138bc..78aa1d637d4 100644
--- a/src/main/java/seedu/address/ui/UiManager.java
+++ b/src/main/java/seedu/address/ui/UiManager.java
@@ -20,7 +20,7 @@ public class UiManager implements Ui {
public static final String ALERT_DIALOG_PANE_FIELD_ID = "alertDialogPane";
private static final Logger logger = LogsCenter.getLogger(UiManager.class);
- private static final String ICON_APPLICATION = "/images/address_book_32.png";
+ private static final String ICON_APPLICATION = "/images/student_connect_32.png";
private Logic logic;
private MainWindow mainWindow;
diff --git a/src/main/resources/images/student_connect_32.png b/src/main/resources/images/student_connect_32.png
new file mode 100644
index 00000000000..0d069778d49
Binary files /dev/null and b/src/main/resources/images/student_connect_32.png differ
diff --git a/src/main/resources/view/ClearConfirmationPopup.css b/src/main/resources/view/ClearConfirmationPopup.css
new file mode 100644
index 00000000000..aaa176cbf84
--- /dev/null
+++ b/src/main/resources/view/ClearConfirmationPopup.css
@@ -0,0 +1,18 @@
+#yesButton, #cancelButton, #confirmationMessage {
+ -fx-text-fill: white;
+}
+
+#yesButton, #cancelButton {
+ -fx-background-color: dimgray;
+}
+
+#yesButton:hover, #cancelButton:hover {
+ -fx-background-color: gray;
+}
+
+#MessageContainer {
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
+.message-label {
+ -fx-font-size: 15px; /* Adjust the font size as needed */
+}
diff --git a/src/main/resources/view/ClearConfirmationPopup.fxml b/src/main/resources/view/ClearConfirmationPopup.fxml
new file mode 100644
index 00000000000..9f4731d9eb5
--- /dev/null
+++ b/src/main/resources/view/ClearConfirmationPopup.fxml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css
index 36e6b001cd8..17f92205dd2 100644
--- a/src/main/resources/view/DarkTheme.css
+++ b/src/main/resources/view/DarkTheme.css
@@ -1,26 +1,33 @@
.background {
- -fx-background-color: derive(#1d1d1d, 20%);
- background-color: #383838; /* Used in the default.html file */
+ -fx-background-color: #0e365c; /* Darker navy blue background */
+ background-color: #0e365c; /* Used in the default.html file */
}
.label {
-fx-font-size: 11pt;
-fx-font-family: "Segoe UI Semibold";
- -fx-text-fill: #555555;
+ -fx-text-fill: #757575; /* Light gray text */
-fx-opacity: 0.9;
}
+.label-bold {
+ -fx-font-weight: bold;
+ -fx-font-family: "Arial";
+ -fx-text-fill: #E7BE34; /* Mustard color */
+
+}
+
.label-bright {
-fx-font-size: 11pt;
-fx-font-family: "Segoe UI Semibold";
- -fx-text-fill: white;
+ -fx-text-fill: white; /* White text */
-fx-opacity: 1;
}
.label-header {
-fx-font-size: 32pt;
-fx-font-family: "Segoe UI Light";
- -fx-text-fill: white;
+ -fx-text-fill: white; /* White text */
-fx-opacity: 1;
}
@@ -40,11 +47,13 @@
}
.table-view {
- -fx-base: #1d1d1d;
- -fx-control-inner-background: #1d1d1d;
- -fx-background-color: #1d1d1d;
- -fx-table-cell-border-color: transparent;
- -fx-table-header-border-color: transparent;
+ -fx-base: #0e365c; /* Darker navy blue background */
+ -fx-control-inner-background: #0e365c; /* Darker navy blue background */
+ -fx-background-color: #0e365c; /* Darker navy blue background */
+ -fx-table-cell-border-color: #E7BE34; /* Mustard yellow border color */
+ -fx-table-header-border-color: #E7BE34; /* Mustard yellow border color */
+ -fx-table-cell-border-width: 1; /* Decrease border thickness */
+ -fx-table-header-border-width: 1; /* Decrease border thickness */
-fx-padding: 5;
}
@@ -59,71 +68,73 @@
-fx-border-color:
transparent
transparent
- derive(-fx-base, 80%)
- transparent;
+ #E7BE34; /* Mustard yellow border color */
-fx-border-insets: 0 10 1 0;
}
.table-view .column-header .label {
-fx-font-size: 20pt;
-fx-font-family: "Segoe UI Light";
- -fx-text-fill: white;
+ -fx-text-fill: white; /* White text */
-fx-alignment: center-left;
-fx-opacity: 1;
}
.table-view:focused .table-row-cell:filled:focused:selected {
- -fx-background-color: -fx-focus-color;
+ -fx-background-color: #536DFE; /* A brighter blue color */
}
.split-pane:horizontal .split-pane-divider {
- -fx-background-color: derive(#1d1d1d, 20%);
- -fx-border-color: transparent transparent transparent #4d4d4d;
+ -fx-background-color: #0e365c; /* Darker navy blue background */
+ -fx-border-color: transparent transparent transparent #E7BE34; /* Mustard yellow border color */
+ -fx-border-width: 1; /* Decrease border thickness */
}
.split-pane {
-fx-border-radius: 1;
-fx-border-width: 1;
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: #0e365c; /* Darker navy blue background */
}
.list-view {
-fx-background-insets: 0;
-fx-padding: 0;
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: #0e365c; /* Darker navy blue background */
}
.list-cell {
-fx-label-padding: 0 0 0 0;
- -fx-graphic-text-gap : 0;
+ -fx-graphic-text-gap: 0;
-fx-padding: 0 0 0 0;
}
.list-cell:filled:even {
- -fx-background-color: #3c3e3f;
+ -fx-background-color: #396987; /* Slightly brighter blue */
}
.list-cell:filled:odd {
- -fx-background-color: #515658;
+ -fx-background-color: #4781a5; /* Another shade of blue */
}
.list-cell:filled:selected {
- -fx-background-color: #424d5f;
+ -fx-background-color: #6b9ebf; /* A brighter blue color */
}
.list-cell:filled:selected #cardPane {
- -fx-border-color: #3e7b91;
+ -fx-border-color: #E7BE34; /* Mustard yellow border color */
-fx-border-width: 1;
}
.list-cell .label {
- -fx-text-fill: white;
+ -fx-text-fill: white; /* White text */
}
+
.cell_big_label {
- -fx-font-family: "Segoe UI Semibold";
- -fx-font-size: 16px;
- -fx-text-fill: #010504;
+ -fx-font-family: "Arial";
+ -fx-font-size: 17px;
+ -fx-text-fill: #E7BE34;
+ -fx-font-weight: bold
}
.cell_small_label {
@@ -133,24 +144,24 @@
}
.stack-pane {
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: #0e365c; /* Darker navy blue background */
}
.pane-with-border {
- -fx-background-color: derive(#1d1d1d, 20%);
- -fx-border-color: derive(#1d1d1d, 10%);
- -fx-border-top-width: 1px;
+ -fx-background-color: #0e365c; /* Darker navy blue background */
+ -fx-border-color: #E7BE34; /* Mustard yellow border color */
+ -fx-border-top-width: 1px;
}
.status-bar {
- -fx-background-color: derive(#1d1d1d, 30%);
+ -fx-background-color: #0e365c; /* Slightly brighter blue background */
}
.result-display {
-fx-background-color: transparent;
- -fx-font-family: "Segoe UI Light";
- -fx-font-size: 13pt;
- -fx-text-fill: white;
+ -fx-font-family: "Courier New";
+ -fx-font-size: 11pt;
+ -fx-text-fill: white; /* White text */
}
.result-display .label {
@@ -159,78 +170,73 @@
.status-bar .label {
-fx-font-family: "Segoe UI Light";
- -fx-text-fill: white;
+ -fx-text-fill: white; /* White text */
-fx-padding: 4px;
-fx-pref-height: 30px;
}
.status-bar-with-border {
- -fx-background-color: derive(#1d1d1d, 30%);
- -fx-border-color: derive(#1d1d1d, 25%);
+ -fx-background-color: #3F51B5; /* Slightly brighter blue background */
+ -fx-border-color: #3949AB; /* Another shade of blue border color */
-fx-border-width: 1px;
}
.status-bar-with-border .label {
- -fx-text-fill: white;
+ -fx-text-fill: white; /* White text */
}
.grid-pane {
- -fx-background-color: derive(#1d1d1d, 30%);
- -fx-border-color: derive(#1d1d1d, 30%);
+ -fx-background-color: #3F51B5; /* Slightly brighter blue background */
+ -fx-border-color: #3F51B5; /* Slightly brighter blue border color */
-fx-border-width: 1px;
}
.grid-pane .stack-pane {
- -fx-background-color: derive(#1d1d1d, 30%);
+ -fx-background-color: #3F51B5; /* Slightly brighter blue background */
}
.context-menu {
- -fx-background-color: derive(#1d1d1d, 50%);
+ -fx-background-color: #3949AB; /* Another shade of blue background */
}
.context-menu .label {
- -fx-text-fill: white;
+ -fx-text-fill: white; /* White text */
}
.menu-bar {
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: #0e365c; /* Darker navy blue background */
}
.menu-bar .label {
-fx-font-size: 14pt;
-fx-font-family: "Segoe UI Light";
- -fx-text-fill: white;
+ -fx-text-fill: white; /* White text */
-fx-opacity: 0.9;
}
.menu .left-container {
- -fx-background-color: black;
+ -fx-background-color: #1A237E; /* Darker blue background */
}
-/*
- * Metro style Push Button
- * Author: Pedro Duque Vieira
- * http://pixelduke.wordpress.com/2012/10/23/jmetro-windows-8-controls-on-java/
- */
.button {
-fx-padding: 5 22 5 22;
- -fx-border-color: #e2e2e2;
+ -fx-border-color: #E7BE34; /* Mustard yellow border color */
-fx-border-width: 2;
-fx-background-radius: 0;
- -fx-background-color: #1d1d1d;
+ -fx-background-color: #3F51B5; /* Slightly brighter blue background */
-fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
-fx-font-size: 11pt;
- -fx-text-fill: #d8d8d8;
+ -fx-text-fill: white; /* White text */
-fx-background-insets: 0 0 0 0, 0, 1, 2;
}
.button:hover {
- -fx-background-color: #3a3a3a;
+ -fx-background-color: #536DFE; /* A brighter blue color on hover */
}
.button:pressed, .button:default:hover:pressed {
- -fx-background-color: white;
- -fx-text-fill: #1d1d1d;
+ -fx-background-color: white; /* White background on click */
+ -fx-text-fill: #303F9F; /* Dark blue text on click */
}
.button:focused {
@@ -243,50 +249,50 @@
.button:disabled, .button:default:disabled {
-fx-opacity: 0.4;
- -fx-background-color: #1d1d1d;
- -fx-text-fill: white;
+ -fx-background-color: #3F51B5; /* Slightly brighter blue background */
+ -fx-text-fill: white; /* White text */
}
.button:default {
- -fx-background-color: -fx-focus-color;
- -fx-text-fill: #ffffff;
+ -fx-background-color: #536DFE; /* A brighter blue color */
+ -fx-text-fill: white; /* White text */
}
.button:default:hover {
- -fx-background-color: derive(-fx-focus-color, 30%);
+ -fx-background-color: #304FFE; /* Bright blue color on hover */
}
.dialog-pane {
- -fx-background-color: #1d1d1d;
+ -fx-background-color: #0e365c; /* Darker navy blue background */
}
.dialog-pane > *.button-bar > *.container {
- -fx-background-color: #1d1d1d;
+ -fx-background-color: #0e365c; /* Darker navy blue background */
}
.dialog-pane > *.label.content {
-fx-font-size: 14px;
-fx-font-weight: bold;
- -fx-text-fill: white;
+ -fx-text-fill: white; /* White text */
}
.dialog-pane:header *.header-panel {
- -fx-background-color: derive(#1d1d1d, 25%);
+ -fx-background-color: #3949AB; /* Another shade of blue background */
}
.dialog-pane:header *.header-panel *.label {
-fx-font-size: 18px;
-fx-font-style: italic;
-fx-fill: white;
- -fx-text-fill: white;
+ -fx-text-fill: #E7BE34;
}
.scroll-bar {
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: #0e365c; /* Darker navy blue background */
}
.scroll-bar .thumb {
- -fx-background-color: derive(#1d1d1d, 50%);
+ -fx-background-color: #807e7e;
-fx-background-insets: 3;
}
@@ -314,18 +320,19 @@
#commandTypeLabel {
-fx-font-size: 11px;
- -fx-text-fill: #F70D1A;
+ -fx-text-fill: #F70D1A; /* Reddish text for the command type label */
}
#commandTextField {
- -fx-background-color: transparent #383838 transparent #383838;
+ -fx-background-color: transparent #000080 transparent #0e365c;
-fx-background-insets: 0;
- -fx-border-color: #383838 #383838 #ffffff #383838;
+ -fx-border-color: #0e365c #0e365c #0e365c #0e365c;
-fx-border-insets: 0;
-fx-border-width: 1;
- -fx-font-family: "Segoe UI Light";
+ -fx-font-family: "Courier New";
+ -fx-font-weight: bold;
-fx-font-size: 13pt;
- -fx-text-fill: white;
+ -fx-text-fill: white; /* White text */
}
#filterField, #personListPanel, #personWebpage {
@@ -333,7 +340,7 @@
}
#resultDisplay .content {
- -fx-background-color: transparent, #383838, transparent, #383838;
+ -fx-background-color: transparent, #0e365c, transparent, #0e365c;
-fx-background-radius: 0;
}
@@ -343,10 +350,14 @@
}
#tags .label {
- -fx-text-fill: white;
- -fx-background-color: #3e7b91;
+ -fx-text-fill: white; /* White text */
+ -fx-background-color: #536DFE; /* A brighter blue background */
-fx-padding: 1 3 1 3;
-fx-border-radius: 2;
-fx-background-radius: 2;
-fx-font-size: 11;
}
+
+#name-label {
+ -fx-text-fill: #E7BE34 /* Mustard text color for the "name" label */
+}
diff --git a/src/main/resources/view/Extensions.css b/src/main/resources/view/Extensions.css
index bfe82a85964..c97bee1b547 100644
--- a/src/main/resources/view/Extensions.css
+++ b/src/main/resources/view/Extensions.css
@@ -5,7 +5,7 @@
.list-cell:empty {
/* Empty cells will not have alternating colours */
- -fx-background: #383838;
+ -fx-background: #0e365c;
}
.tag-selector {
diff --git a/src/main/resources/view/GroupListCard.fxml b/src/main/resources/view/GroupListCard.fxml
new file mode 100644
index 00000000000..eac43fc44ac
--- /dev/null
+++ b/src/main/resources/view/GroupListCard.fxml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/GroupListPanel.fxml b/src/main/resources/view/GroupListPanel.fxml
new file mode 100644
index 00000000000..055e9a9b13b
--- /dev/null
+++ b/src/main/resources/view/GroupListPanel.fxml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/HelpWindow.css b/src/main/resources/view/HelpWindow.css
index 17e8a8722cd..81a72f78c1a 100644
--- a/src/main/resources/view/HelpWindow.css
+++ b/src/main/resources/view/HelpWindow.css
@@ -11,7 +11,7 @@
}
#copyButton:armed {
- -fx-background-color: darkgray;
+ -fx-background-color: #0e365c;
}
#helpMessageContainer {
diff --git a/src/main/resources/view/HelpWindow.fxml b/src/main/resources/view/HelpWindow.fxml
index e01f330de33..a8b00c5af14 100644
--- a/src/main/resources/view/HelpWindow.fxml
+++ b/src/main/resources/view/HelpWindow.fxml
@@ -7,9 +7,10 @@
+
-
+
@@ -19,18 +20,23 @@
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
@@ -38,7 +44,7 @@
-
+
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
index 7778f666a0a..f92d9b3a70b 100644
--- a/src/main/resources/view/MainWindow.fxml
+++ b/src/main/resources/view/MainWindow.fxml
@@ -6,15 +6,14 @@
-
+ title="StudentConnect" minWidth="450" minHeight="600" onCloseRequest="#handleExit">
-
+
@@ -31,6 +30,9 @@
+
+
+
@@ -53,6 +55,13 @@
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml
index f5e812e25e6..c30f8f74863 100644
--- a/src/main/resources/view/PersonListCard.fxml
+++ b/src/main/resources/view/PersonListCard.fxml
@@ -1,5 +1,4 @@
-
@@ -25,12 +24,46 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Major:
+
+
+
+ Year:
+
+
+
+ Email:
+
+
+
+ Description:
+
+
+
+ Tutorials:
+
+
+
+ Social Media:
+
-
-
-
-
diff --git a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
index a7427fe7aa2..c0b918e0250 100644
--- a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
+++ b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
@@ -1,14 +1,26 @@
{
- "persons": [ {
- "name": "Alice Pauline",
- "phone": "94351253",
- "email": "alice@example.com",
- "address": "123, Jurong West Ave 6, #08-111",
- "tags": [ "friends" ]
- }, {
- "name": "Alice Pauline",
- "phone": "94351253",
- "email": "pauline@example.com",
- "address": "4th street"
- } ]
+ "persons": [
+ {
+ "name": "Alice Pauline",
+ "major": "Computer Science",
+ "year": "2",
+ "email": "pauline@u.nus.edu",
+ "description": "Friendly person",
+ "tutorials": ["01", "02"],
+ "socialMediaLinks": ["https://www.linkedin.com/in/alice"],
+ "gender" : "F",
+ "nationality": "foreigner"
+ },
+ {
+ "name": "Alice Pauline",
+ "major": "Computer Science",
+ "year": "2",
+ "email": "pauline@u.nus.edu",
+ "description": "Friendly person",
+ "tutorials": ["01", "02"],
+ "socialMediaLinks": ["https://www.linkedin.com/in/alice"],
+ "gender" : "F",
+ "nationality": "foreigner"
+ }
+ ], "groups" : []
}
diff --git a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
index ad3f135ae42..2d40fec51ba 100644
--- a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
+++ b/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
@@ -4,5 +4,5 @@
"phone": "9482424",
"email": "invalid@email!3e",
"address": "4th street"
- } ]
+ } ], "groups" : []
}
diff --git a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
index 72262099d35..669d5da685c 100644
--- a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
+++ b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
@@ -1,46 +1,82 @@
{
- "_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" : [ ]
- } ]
+ "_comment": "StudentConnect save file which contains the same Person values as in TypicalPersons#getTypicalAddressBook",
+ "persons": [
+ {
+ "name": "Alice Pauline",
+ "major": "Computer Science",
+ "year": "2",
+ "email": "alice@u.nus.edu",
+ "description": "web dev",
+ "tutorials": ["01", "02"],
+ "socialMediaLinks": ["https://www.linkedin.com/in/alice"],
+ "nationality": "foreigner",
+ "gender" : "F"
+ },
+ {
+ "name": "Benson Meier",
+ "major": "Computer Science",
+ "year": "2",
+ "email": "johnd@u.nus.edu",
+ "description": "mobile dev",
+ "tutorials": ["03", "04"],
+ "socialMediaLinks": ["https://github.com/benson", "https://www.linkedin.com/in/benson"],
+ "nationality": "local",
+ "gender" : "M"
+ },
+ {
+ "name": "Carl Kurz",
+ "major": "Computer Science",
+ "year": "2",
+ "email": "heinz@u.nus.edu",
+ "description": "fe dev",
+ "tutorials": ["05", "06"],
+ "socialMediaLinks": [],
+ "nationality": "local",
+ "gender" : "M"
+ },
+ {
+ "name": "Daniel Meier",
+ "major": "Computer Science",
+ "year": "2",
+ "email": "cornelia@u.nus.edu",
+ "description": "be dev",
+ "tutorials": ["07", "08"],
+ "socialMediaLinks": ["https://www.linkedin.com/in/daniel"],
+ "nationality": "local",
+ "gender" : "M"
+ },
+ {
+ "name": "Elle Meyer",
+ "major": "Computer Science",
+ "year": "2",
+ "email": "werner@u.nus.edu",
+ "description": "fe dev",
+ "tutorials": ["09", "10"],
+ "socialMediaLinks": [],
+ "nationality": "foreigner",
+ "gender" : "F"
+ },
+ {
+ "name": "Fiona Kunz",
+ "major": "Computer Science",
+ "year": "2",
+ "email": "lydia@u.nus.edu",
+ "description": "be dev",
+ "tutorials": ["11", "12"],
+ "socialMediaLinks": [],
+ "nationality": "foreigner",
+ "gender" : "F"
+ },
+ {
+ "name": "George Best",
+ "major": "Computer Science",
+ "year": "2",
+ "email": "anna@u.nus.edu",
+ "description": "fe dev",
+ "tutorials": ["13", "14"],
+ "socialMediaLinks": [],
+ "nationality": "foreigner",
+ "gender" : "F"
+ }
+ ], "groups" : []
}
diff --git a/src/test/java/seedu/address/commons/util/AppUtilTest.java b/src/test/java/seedu/address/commons/util/AppUtilTest.java
index 594de1e6365..60d9148a13a 100644
--- a/src/test/java/seedu/address/commons/util/AppUtilTest.java
+++ b/src/test/java/seedu/address/commons/util/AppUtilTest.java
@@ -9,7 +9,7 @@ public class AppUtilTest {
@Test
public void getImage_exitingImage() {
- assertNotNull(AppUtil.getImage("/images/address_book_32.png"));
+ assertNotNull(AppUtil.getImage("/images/student_connect_32.png"));
}
@Test
diff --git a/src/test/java/seedu/address/commons/util/SampleDataUtilTest.java b/src/test/java/seedu/address/commons/util/SampleDataUtilTest.java
new file mode 100644
index 00000000000..e128c9dd0dc
--- /dev/null
+++ b/src/test/java/seedu/address/commons/util/SampleDataUtilTest.java
@@ -0,0 +1,44 @@
+package seedu.address.commons.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.group.Group;
+import seedu.address.model.person.Person;
+import seedu.address.model.util.SampleDataUtil;
+
+public class SampleDataUtilTest {
+
+ @Test
+ public void getSamplePersons_validData_returnsSamplePersonsArray() {
+ Person[] samplePersons = SampleDataUtil.getSamplePersons();
+ assertEquals(6, samplePersons.length); // Check if the correct number of sample persons is returned
+ }
+
+ @Test
+ public void getSampleGroups_validData_returnsSampleGroupsArray() {
+ Group[] sampleGroups = SampleDataUtil.getSampleGroups();
+ assertEquals(2, sampleGroups.length); // Check if the correct number of sample groups is returned
+ }
+
+ @Test
+ public void getSampleAddressBook_validData_returnsReadOnlyAddressBook() {
+ assertEquals(6, SampleDataUtil.getSampleAddressBook().getPersonList().size());
+ // Check if the address book contains the correct number of sample persons
+ }
+
+ @Test
+ public void getTutorialSet_validData_returnsSetOfTutorials() {
+ String[] tutorialStrings = {"01", "02", "03"};
+ assertEquals(3, SampleDataUtil.getTutorialSet(tutorialStrings).size());
+ // Check if the correct number of tutorials is returned
+ }
+
+ @Test
+ public void getSocialMediaLinkSet_validData_returnsSetOfSocialMediaLinks() {
+ String[] socialMediaLinkStrings = {"https://example.com/1", "https://example.com/2"};
+ assertEquals(2, SampleDataUtil.getSocialMediaLinkSet(socialMediaLinkStrings).size());
+ // Check if the correct number of social media links is returned
+ }
+}
diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/address/logic/LogicManagerTest.java
index baf8ce336a2..2782b41a465 100644
--- a/src/test/java/seedu/address/logic/LogicManagerTest.java
+++ b/src/test/java/seedu/address/logic/LogicManagerTest.java
@@ -1,12 +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_UNKNOWN_COMMAND;
-import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.DESCRIPTION_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.GENDER_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.MAJOR_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.NATIONALITY_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.TUTORIAL_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.YEAR_DESC_AMY;
+import static seedu.address.logic.commands.DeleteCommand.MESSAGE_DELETE_EMAIL_NOT_FOUND;
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalPersons.AMY;
@@ -60,8 +64,8 @@ public void execute_invalidCommandFormat_throwsParseException() {
@Test
public void execute_commandExecutionError_throwsCommandException() {
- String deleteCommand = "delete 9";
- assertCommandException(deleteCommand, MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ String deleteCommand = "delete notFound@u.nus.edu";
+ assertCommandException(deleteCommand, MESSAGE_DELETE_EMAIL_NOT_FOUND);
}
@Test
@@ -165,9 +169,10 @@ 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 addCommand = AddCommand.COMMAND_WORD + NAME_DESC_AMY + MAJOR_DESC_AMY + YEAR_DESC_AMY
+ + EMAIL_DESC_AMY + DESCRIPTION_DESC_AMY + TUTORIAL_DESC_AMY
+ + GENDER_DESC_AMY + NATIONALITY_DESC_AMY;
+ Person expectedPerson = new PersonBuilder(AMY).withSocialMediaLinks().build();
ModelManager expectedModel = new ModelManager();
expectedModel.addPerson(expectedPerson);
assertCommandFailure(addCommand, CommandException.class, expectedMessage, expectedModel);
diff --git a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java b/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java
index 162a0c86031..a05e432bc77 100644
--- a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java
+++ b/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java
@@ -35,7 +35,7 @@ public void execute_newPerson_success() {
assertCommandSuccess(new AddCommand(validPerson), model,
String.format(AddCommand.MESSAGE_SUCCESS, Messages.format(validPerson)),
- expectedModel);
+ expectedModel, false);
}
@Test
diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java
index 90e8253f48e..c903b8d4007 100644
--- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/AddCommandTest.java
@@ -10,6 +10,8 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
import java.util.function.Predicate;
import org.junit.jupiter.api.Test;
@@ -22,6 +24,9 @@
import seedu.address.model.Model;
import seedu.address.model.ReadOnlyAddressBook;
import seedu.address.model.ReadOnlyUserPrefs;
+import seedu.address.model.group.Group;
+import seedu.address.model.group.tasks.TaskList;
+import seedu.address.model.person.Email;
import seedu.address.model.person.Person;
import seedu.address.testutil.PersonBuilder;
@@ -33,11 +38,12 @@ public void constructor_nullPerson_throwsNullPointerException() {
}
@Test
- public void execute_personAcceptedByModel_addSuccessful() throws Exception {
+ public void execute_personAcceptedByModel_addSuccessful() throws CommandException {
ModelStubAcceptingPersonAdded modelStub = new ModelStubAcceptingPersonAdded();
Person validPerson = new PersonBuilder().build();
- CommandResult commandResult = new AddCommand(validPerson).execute(modelStub);
+ AddCommand addCommand = new AddCommand(validPerson);
+ CommandResult commandResult = addCommand.execute(modelStub);
assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, Messages.format(validPerson)),
commandResult.getFeedbackToUser());
@@ -88,11 +94,28 @@ public void toStringMethod() {
* A default model stub that have all of the methods failing.
*/
private class ModelStub implements Model {
+ private Group group;
+
+ private List personsAdded = new ArrayList<>();
+ private Person person;
+
+ @Override
+ public void addPerson(Person person) {
+ requireNonNull(person);
+ personsAdded.add(person);
+ }
+
@Override
public void setUserPrefs(ReadOnlyUserPrefs userPrefs) {
throw new AssertionError("This method should not be called.");
}
+ @Override
+ public void addGroup(Group group) {
+ this.group = group;
+ throw new AssertionError("This method should not be called.");
+ }
+
@Override
public ReadOnlyUserPrefs getUserPrefs() {
throw new AssertionError("This method should not be called.");
@@ -118,11 +141,6 @@ 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.");
@@ -157,6 +175,63 @@ public ObservableList getFilteredPersonList() {
public void updateFilteredPersonList(Predicate predicate) {
throw new AssertionError("This method should not be called.");
}
+
+ @Override
+ public ObservableList getFilteredGroupList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void updateFilteredGroupList(Predicate predicate) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public Optional getPersonWithEmail(Email email) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public Optional getGroupWithNumber(int number) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void addPersonToGroup(Person person, Group group) {
+ this.person = person;
+ this.group = group;
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public boolean personIsInAGroup(Person person) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public Group getGroupThatPersonIsIn(Person person) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void removePersonFromGroup(Person person, Group group) {
+
+ }
+
+ @Override
+ public void addTasksToGroup(TaskList taskList, Group group) {
+
+ }
+
+ @Override
+ public boolean hasGroup(Group group) {
+ return false;
+ }
+
+ @Override
+ public void deleteGroup(Group group) {
+
+ }
}
/**
@@ -178,27 +253,31 @@ public boolean hasPerson(Person person) {
}
/**
- * A Model stub that always accept the person being added.
+ * A Model stub that always accepts the person being added.
*/
private class ModelStubAcceptingPersonAdded extends ModelStub {
- final ArrayList personsAdded = new ArrayList<>();
+ final ArrayList personsAdded = new ArrayList();
+ final ArrayList groupsAdded = new ArrayList();
@Override
- public boolean hasPerson(Person person) {
+ public void addPerson(Person person) {
requireNonNull(person);
- return personsAdded.stream().anyMatch(person::isSamePerson);
+ personsAdded.add(person);
}
@Override
- public void addPerson(Person person) {
+ public boolean hasPerson(Person person) {
requireNonNull(person);
- personsAdded.add(person);
+ return personsAdded.stream().anyMatch(person::isSamePerson);
}
@Override
public ReadOnlyAddressBook getAddressBook() {
- return new AddressBook();
+ AddressBook addressBook = new AddressBook();
+ for (Person person : personsAdded) {
+ addressBook.addPerson(person);
+ }
+ return addressBook;
}
}
-
}
diff --git a/src/test/java/seedu/address/logic/commands/CheckCommandTest.java b/src/test/java/seedu/address/logic/commands/CheckCommandTest.java
new file mode 100644
index 00000000000..45102c6be28
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/CheckCommandTest.java
@@ -0,0 +1,214 @@
+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.assertCommandSuccess;
+import static seedu.address.testutil.TypicalGroups.GROUP10;
+import static seedu.address.testutil.TypicalGroups.GROUP11;
+import static seedu.address.testutil.TypicalGroups.GROUP12;
+import static seedu.address.testutil.TypicalGroups.GROUP13;
+import static seedu.address.testutil.TypicalGroups.GROUP14;
+import static seedu.address.testutil.TypicalGroups.GROUP15;
+import static seedu.address.testutil.TypicalGroups.GROUP4;
+import static seedu.address.testutil.TypicalGroups.GROUP5;
+import static seedu.address.testutil.TypicalGroups.GROUP6;
+import static seedu.address.testutil.TypicalGroups.GROUP7;
+import static seedu.address.testutil.TypicalGroups.GROUP8;
+import static seedu.address.testutil.TypicalGroups.GROUP9;
+import static seedu.address.testutil.TypicalGroups.getTypicalAddressBook;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.group.GroupContainsKeywordsPredicate;
+
+public class CheckCommandTest {
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ private Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void equals() {
+ GroupContainsKeywordsPredicate firstPredicate =
+ new GroupContainsKeywordsPredicate(Collections.singletonList(String.valueOf(1)));
+ GroupContainsKeywordsPredicate secondPredicate =
+ new GroupContainsKeywordsPredicate(Collections.singletonList(String.valueOf(2)));
+
+ CheckCommand firstCheckCommand = new CheckCommand(1, firstPredicate);
+ CheckCommand secondCheckCommand = new CheckCommand(2, secondPredicate);
+
+ // same object -> returns true
+ assertTrue(firstCheckCommand.equals(firstCheckCommand));
+
+ // same values -> returns true
+ CheckCommand firstCheckCommandCopy = new CheckCommand(1, firstPredicate);
+ assertTrue(firstCheckCommand.equals(firstCheckCommandCopy));
+
+ // different types -> returns false
+ assertFalse(firstCheckCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(firstCheckCommand.equals(null));
+
+ // different check -> returns false
+ assertFalse(firstCheckCommand.equals(secondCheckCommand));
+ }
+
+ @Test
+ public void execute_emptyGroup_showMessage() {
+ String expectedMessage = String.format(CheckCommand.GROUP_NUM + CheckCommand.MESSAGE_CHECK_GROUP_SIZE_EMPTY
+ + CheckCommand.MESSAGE_HELP, 5);
+ GroupContainsKeywordsPredicate predicate = preparePredicate("5");
+ CheckCommand command = new CheckCommand(5, predicate);
+ expectedModel.updateFilteredGroupList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, true);
+ assertEquals(Arrays.asList(GROUP5), model.getFilteredGroupList());
+ }
+
+ @Test
+ public void execute_oneMemberGroup_showMessage() {
+ String expectedMessage = String.format(CheckCommand.GROUP_NUM + CheckCommand.MESSAGE_CHECK_GROUP_SIZE_ONE
+ + CheckCommand.MESSAGE_HELP, 4);
+ GroupContainsKeywordsPredicate predicate = preparePredicate("4");
+ CheckCommand command = new CheckCommand(4, predicate);
+ expectedModel.updateFilteredGroupList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, true);
+ assertEquals(Arrays.asList(GROUP4), model.getFilteredGroupList());
+ }
+
+ @Test
+ public void execute_groupLessThanFiveSameNationalitySameGenderMismatchTutorial_showMessage() {
+ String expectedMessage = String.format(CheckCommand.GROUP_NUM + CheckCommand.MESSAGE_CHECK_GROUP_SIZE_UNDER
+ + CheckCommand.MESSAGE_CHECK_GROUP_NATIONALITY_WARNING + CheckCommand.MESSAGE_CHECK_GROUP_GENDER_WARNING
+ + CheckCommand.MESSAGE_CHECK_GROUP_TUTORIAL_WARNING + CheckCommand.MESSAGE_HELP, 6);
+ GroupContainsKeywordsPredicate predicate = preparePredicate("6");
+ CheckCommand command = new CheckCommand(6, predicate);
+ expectedModel.updateFilteredGroupList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, true);
+ assertEquals(Arrays.asList(GROUP6), model.getFilteredGroupList());
+ }
+
+ @Test
+ public void execute_groupLessThanFiveSameGenderMismatchTutorial_showMessage() {
+ String expectedMessage = String.format(CheckCommand.GROUP_NUM + CheckCommand.MESSAGE_CHECK_GROUP_SIZE_UNDER
+ + CheckCommand.MESSAGE_CHECK_GROUP_GENDER_WARNING + CheckCommand.MESSAGE_CHECK_GROUP_TUTORIAL_WARNING
+ + CheckCommand.MESSAGE_HELP, 7);
+ GroupContainsKeywordsPredicate predicate = preparePredicate("7");
+ CheckCommand command = new CheckCommand(7, predicate);
+ expectedModel.updateFilteredGroupList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, true);
+ assertEquals(Arrays.asList(GROUP7), model.getFilteredGroupList());
+ }
+
+ @Test
+ public void execute_groupLessThanFiveSameNationalityMismatchTutorial_showMessage() {
+ String expectedMessage = String.format(CheckCommand.GROUP_NUM + CheckCommand.MESSAGE_CHECK_GROUP_SIZE_UNDER
+ + CheckCommand.MESSAGE_CHECK_GROUP_NATIONALITY_WARNING
+ + CheckCommand.MESSAGE_CHECK_GROUP_TUTORIAL_WARNING + CheckCommand.MESSAGE_HELP, 8);
+ GroupContainsKeywordsPredicate predicate = preparePredicate("8");
+ CheckCommand command = new CheckCommand(8, predicate);
+ expectedModel.updateFilteredGroupList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, true);
+ assertEquals(Arrays.asList(GROUP8), model.getFilteredGroupList());
+ }
+
+ @Test
+ public void execute_groupLessThanFiveSameNationalitySameGender_showMessage() {
+ String expectedMessage = String.format(CheckCommand.GROUP_NUM + CheckCommand.MESSAGE_CHECK_GROUP_SIZE_UNDER
+ + CheckCommand.MESSAGE_CHECK_GROUP_NATIONALITY_WARNING + CheckCommand.MESSAGE_CHECK_GROUP_GENDER_WARNING
+ + CheckCommand.MESSAGE_HELP, 9);
+ GroupContainsKeywordsPredicate predicate = preparePredicate("9");
+ CheckCommand command = new CheckCommand(9, predicate);
+ expectedModel.updateFilteredGroupList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, true);
+ assertEquals(Arrays.asList(GROUP9), model.getFilteredGroupList());
+ }
+
+ @Test
+ public void execute_groupMoreThanFiveSameNationalitySameGenderMismatchTutorial_showMessage() {
+ String expectedMessage = String.format(CheckCommand.GROUP_NUM + CheckCommand.MESSAGE_CHECK_GROUP_SIZE_OVER
+ + CheckCommand.MESSAGE_CHECK_GROUP_NATIONALITY_WARNING + CheckCommand.MESSAGE_CHECK_GROUP_GENDER_WARNING
+ + CheckCommand.MESSAGE_CHECK_GROUP_TUTORIAL_WARNING + CheckCommand.MESSAGE_HELP, 10);
+ GroupContainsKeywordsPredicate predicate = preparePredicate("10");
+ CheckCommand command = new CheckCommand(10, predicate);
+ expectedModel.updateFilteredGroupList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, true);
+ assertEquals(Arrays.asList(GROUP10), model.getFilteredGroupList());
+ }
+
+ @Test
+ public void execute_groupSameNationalitySameGenderMismatchTutorial_showMessage() {
+ String expectedMessage = String.format(CheckCommand.GROUP_NUM
+ + CheckCommand.MESSAGE_CHECK_GROUP_NATIONALITY_WARNING + CheckCommand.MESSAGE_CHECK_GROUP_GENDER_WARNING
+ + CheckCommand.MESSAGE_CHECK_GROUP_TUTORIAL_WARNING + CheckCommand.MESSAGE_HELP, 11);
+ GroupContainsKeywordsPredicate predicate = preparePredicate("11");
+ CheckCommand command = new CheckCommand(11, predicate);
+ expectedModel.updateFilteredGroupList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, true);
+ assertEquals(Arrays.asList(GROUP11), model.getFilteredGroupList());
+ }
+
+ @Test
+ public void execute_groupSameNationalitySameGender_showMessage() {
+ String expectedMessage = String.format(CheckCommand.GROUP_NUM
+ + CheckCommand.MESSAGE_CHECK_GROUP_NATIONALITY_WARNING + CheckCommand.MESSAGE_CHECK_GROUP_GENDER_WARNING
+ + CheckCommand.MESSAGE_HELP, 12);
+ GroupContainsKeywordsPredicate predicate = preparePredicate("12");
+ CheckCommand command = new CheckCommand(12, predicate);
+ expectedModel.updateFilteredGroupList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, true);
+ assertEquals(Arrays.asList(GROUP12), model.getFilteredGroupList());
+ }
+
+ @Test
+ public void execute_groupSameGender_showMessage() {
+ String expectedMessage = String.format(CheckCommand.GROUP_NUM + CheckCommand.MESSAGE_CHECK_GROUP_GENDER_WARNING
+ + CheckCommand.MESSAGE_HELP, 13);
+ GroupContainsKeywordsPredicate predicate = preparePredicate("13");
+ CheckCommand command = new CheckCommand(13, predicate);
+ expectedModel.updateFilteredGroupList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, true);
+ assertEquals(Arrays.asList(GROUP13), model.getFilteredGroupList());
+ }
+
+ @Test
+ public void execute_groupSameNationality_showMessage() {
+ String expectedMessage = String.format(CheckCommand.GROUP_NUM
+ + CheckCommand.MESSAGE_CHECK_GROUP_NATIONALITY_WARNING + CheckCommand.MESSAGE_HELP, 14);
+ GroupContainsKeywordsPredicate predicate = preparePredicate("14");
+ CheckCommand command = new CheckCommand(14, predicate);
+ expectedModel.updateFilteredGroupList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, true);
+ assertEquals(Arrays.asList(GROUP14), model.getFilteredGroupList());
+ }
+
+ @Test
+ public void execute_successGroup_showMessage() {
+ String expectedMessage = String.format(CheckCommand.GROUP_NUM + CheckCommand.MESSAGE_CHECK_GROUP_SUCCESS, 15);
+ GroupContainsKeywordsPredicate predicate = preparePredicate("15");
+ CheckCommand command = new CheckCommand(15, predicate);
+ expectedModel.updateFilteredGroupList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, true);
+ assertEquals(Arrays.asList(GROUP15), model.getFilteredGroupList());
+ }
+
+ @Test
+ public void toStringMethod() {
+ GroupContainsKeywordsPredicate predicate = new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(1)));
+ CheckCommand checkCommand = new CheckCommand(1, predicate);
+ String expected = CheckCommand.class.getCanonicalName() + "{groupNumber=" + 1 + "}";
+ assertEquals(expected, checkCommand.toString());
+ }
+
+ /**
+ * Parses {@code userInput} into a {@code GroupContainsKeywordsPredicate}.
+ */
+ private GroupContainsKeywordsPredicate preparePredicate(String userInput) {
+ return new GroupContainsKeywordsPredicate(Arrays.asList(userInput.split("\\s+")));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java b/src/test/java/seedu/address/logic/commands/ClearCommandTest.java
index 80d9110c03a..7ffa24800d8 100644
--- a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/ClearCommandTest.java
@@ -1,32 +1,230 @@
package seedu.address.logic.commands;
-import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
-import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+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.nio.file.Path;
+import java.util.Optional;
+import java.util.function.Predicate;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
+import javafx.application.Platform;
+import javafx.collections.ObservableList;
+import seedu.address.commons.core.GuiSettings;
+import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.AddressBook;
import seedu.address.model.Model;
-import seedu.address.model.ModelManager;
-import seedu.address.model.UserPrefs;
+import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyUserPrefs;
+import seedu.address.model.group.Group;
+import seedu.address.model.group.tasks.TaskList;
+import seedu.address.model.person.Email;
+import seedu.address.model.person.Person;
+import seedu.address.ui.JavaFxInitializer;
+
public class ClearCommandTest {
+ @BeforeAll
+ public static void init() {
+ JavaFxInitializer.initialize();
+ }
+
+ @AfterAll
+ public static void cleanup() {
+ JavaFxInitializer.cleanup();
+ }
+
@Test
- public void execute_emptyAddressBook_success() {
- Model model = new ModelManager();
- Model expectedModel = new ModelManager();
+ public void execute_clearCommandConfirmed_success() throws CommandException {
+ // Arrange
+ ModelStub modelStub = new ModelStub();
+ ClearCommand clearCommand = new ClearCommand();
+ clearCommand.setConfirmed(true);
+
+ // Act
+ Platform.runLater(() -> {
+ try {
+ CommandResult commandResult = clearCommand.execute(modelStub);
- assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel);
+ // Assert
+ assertEquals(ClearCommand.SHOWING_CONFIRMATION_MESSAGE, commandResult.getFeedbackToUser());
+ assertFalse(commandResult.isShowHelp());
+ assertFalse(commandResult.isExit());
+ assertFalse(commandResult.isGroupCommand());
+ assertTrue(commandResult.isClear());
+ assertEquals(0, modelStub.getAddressBook().getPersonList().size());
+ } catch (CommandException e) {
+ // Handle exception
+ }
+ });
}
@Test
- public void execute_nonEmptyAddressBook_success() {
- Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
- Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
- expectedModel.setAddressBook(new AddressBook());
+ public void execute_clearCommandNotConfirmed_success() throws CommandException {
+ // Arrange
+ ModelStub modelStub = new ModelStub();
+ ClearCommand clearCommand = new ClearCommand();
+ clearCommand.setConfirmed(false);
+
+ // Act
+ Platform.runLater(() -> {
+ try {
+ CommandResult commandResult = clearCommand.execute(modelStub);
- assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel);
+ // Assert
+ assertEquals(ClearCommand.SHOWING_CONFIRMATION_MESSAGE, commandResult.getFeedbackToUser());
+ assertFalse(commandResult.isShowHelp());
+ assertFalse(commandResult.isExit());
+ assertFalse(commandResult.isGroupCommand());
+ assertFalse(commandResult.isClear());
+ assertEquals(0, modelStub.getAddressBook().getPersonList().size());
+ } catch (CommandException e) {
+ // Handle exception if needed
+ }
+ });
}
+ // Rest of the class remains unchanged
+ private static class ModelStub implements Model {
+ private final AddressBook addressBook = new AddressBook();
+
+ @Override
+ public void setUserPrefs(ReadOnlyUserPrefs userPrefs) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ReadOnlyUserPrefs getUserPrefs() {
+ return null;
+ }
+
+ @Override
+ public GuiSettings getGuiSettings() {
+ return null;
+ }
+
+ @Override
+ public void setGuiSettings(GuiSettings guiSettings) {
+
+ }
+
+ @Override
+ public Path getAddressBookFilePath() {
+ // Implement if needed
+ return null;
+ }
+
+ @Override
+ public void setAddressBookFilePath(Path addressBookFilePath) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void setAddressBook(ReadOnlyAddressBook addressBook) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ReadOnlyAddressBook getAddressBook() {
+ return addressBook;
+ }
+
+ @Override
+ public boolean hasPerson(Person person) {
+ return addressBook.hasPerson(person);
+ }
+
+ @Override
+ public void deletePerson(Person target) {
+ addressBook.removePerson(target);
+ }
+
+ @Override
+ public void addPerson(Person person) {
+ addressBook.addPerson(person);
+ }
+
+ @Override
+ public void setPerson(Person target, Person editedPerson) {
+ addressBook.setPerson(target, editedPerson);
+ }
+
+ @Override
+ public ObservableList getFilteredPersonList() {
+ return null;
+ }
+
+ @Override
+ public void updateFilteredPersonList(Predicate predicate) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+
+ @Override
+ public void addGroup(Group group) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void addPersonToGroup(Person person, Group group) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ObservableList getFilteredGroupList() {
+ return null;
+ }
+
+ @Override
+ public void updateFilteredGroupList(Predicate predicate) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public Optional getPersonWithEmail(Email email) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional getGroupWithNumber(int number) {
+ return Optional.empty();
+ }
+
+
+ @Override
+ public boolean personIsInAGroup(Person person) {
+ // Implement if needed
+ return false;
+ }
+
+ @Override
+ public Group getGroupThatPersonIsIn(Person person) {
+ return null;
+ }
+
+ @Override
+ public void removePersonFromGroup(Person person, Group group) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void addTasksToGroup(TaskList taskList, Group group) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public boolean hasGroup(Group group) {
+ return false;
+ }
+
+ @Override
+ public void deleteGroup(Group group) {
+ throw new AssertionError("This method should not be called.");
+ }
+ }
}
diff --git a/src/test/java/seedu/address/logic/commands/CommandResultTest.java b/src/test/java/seedu/address/logic/commands/CommandResultTest.java
index 7b8c7cd4546..41be01fd5e4 100644
--- a/src/test/java/seedu/address/logic/commands/CommandResultTest.java
+++ b/src/test/java/seedu/address/logic/commands/CommandResultTest.java
@@ -14,7 +14,7 @@ public void equals() {
// same values -> returns true
assertTrue(commandResult.equals(new CommandResult("feedback")));
- assertTrue(commandResult.equals(new CommandResult("feedback", false, false)));
+ assertTrue(commandResult.equals(new CommandResult("feedback", false, false, false, false)));
// same object -> returns true
assertTrue(commandResult.equals(commandResult));
@@ -29,10 +29,10 @@ public void equals() {
assertFalse(commandResult.equals(new CommandResult("different")));
// different showHelp value -> returns false
- assertFalse(commandResult.equals(new CommandResult("feedback", true, false)));
+ assertFalse(commandResult.equals(new CommandResult("feedback", true, false, false, false)));
// different exit value -> returns false
- assertFalse(commandResult.equals(new CommandResult("feedback", false, true)));
+ assertFalse(commandResult.equals(new CommandResult("feedback", false, true, false, false)));
}
@Test
@@ -46,10 +46,10 @@ public void hashcode() {
assertNotEquals(commandResult.hashCode(), new CommandResult("different").hashCode());
// different showHelp value -> returns different hashcode
- assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false).hashCode());
+ assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false, false, false).hashCode());
// different exit value -> returns different hashcode
- assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true).hashCode());
+ assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true, false, false).hashCode());
}
@Test
@@ -57,7 +57,8 @@ public void toStringMethod() {
CommandResult commandResult = new CommandResult("feedback");
String expected = CommandResult.class.getCanonicalName() + "{feedbackToUser="
+ commandResult.getFeedbackToUser() + ", showHelp=" + commandResult.isShowHelp()
- + ", exit=" + commandResult.isExit() + "}";
+ + ", exit=" + commandResult.isExit() + ", groupCommand=" + commandResult.isGroupCommand()
+ + ", clear=" + commandResult.isClear() + "}";
assertEquals(expected, commandResult.toString());
}
}
diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
index 643a1d08069..aa4ac1d0af8 100644
--- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
+++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
@@ -2,12 +2,22 @@
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_DESCRIPTION;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GENDER;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MAJOR;
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_NATIONALITY;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SOCIAL_MEDIA_LINK;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TUTORIAL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_YEAR;
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.TypicalPersons.CARL;
+import static seedu.address.testutil.TypicalPersons.DANIEL;
+import static seedu.address.testutil.TypicalPersons.ELLE;
+import static seedu.address.testutil.TypicalPersons.FIONA;
import java.util.ArrayList;
import java.util.Arrays;
@@ -17,6 +27,12 @@
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.AddressBook;
import seedu.address.model.Model;
+import seedu.address.model.group.Group;
+import seedu.address.model.group.GroupContainsKeywordsPredicate;
+import seedu.address.model.group.tasks.Task;
+import seedu.address.model.group.tasks.TaskModule;
+import seedu.address.model.group.tasks.TaskStatus;
+import seedu.address.model.group.tasks.Todo;
import seedu.address.model.person.NameContainsKeywordsPredicate;
import seedu.address.model.person.Person;
import seedu.address.testutil.EditPersonDescriptorBuilder;
@@ -28,31 +44,80 @@ public class CommandTestUtil {
public static final String VALID_NAME_AMY = "Amy Bee";
public static final String VALID_NAME_BOB = "Bob Choo";
- public static final String VALID_PHONE_AMY = "11111111";
- public static final String VALID_PHONE_BOB = "22222222";
- public static final String VALID_EMAIL_AMY = "amy@example.com";
- 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_MAJOR_AMY = "Computer Science";
+ public static final String VALID_MAJOR_BOB = "Information Systems";
+ public static final String VALID_YEAR_AMY = "2";
+ public static final String VALID_YEAR_BOB = "3";
+ public static final String VALID_EMAIL_AMY = "amy@u.nus.edu";
+ public static final String VALID_EMAIL_BOB = "bob@u.nus.edu";
+ public static final String VALID_DESCRIPTION_AMY = "Frontend Developer";
+ public static final String VALID_DESCRIPTION_BOB = "Backend Developer";
+ public static final String VALID_TUT_FIRST_AMY = "01";
+ public static final String VALID_TUT_SECOND_AMY = "02";
+ public static final String VALID_TUT_FIRST_BOB = "03";
+ public static final String VALID_TUT_SECOND_BOB = "04";
+ public static final String VALID_SM_LINKEDIN_AMY = "https://www.linkedin.com/in/amy";
+ public static final String VALID_SM_GITHUB_AMY = "https://github.com/amy";
+ public static final String VALID_SM_LINKEDIN_BOB = "https://www.linkedin.com/in/bob";
+ public static final String VALID_SM_GITHUB_BOB = "https://github.com/bob";
+ public static final String VALID_NATIONALITY_AMY = "local";
+ public static final String VALID_NATIONALITY_BOB = "foreigner";
+ public static final String VALID_GENDER_AMY = "F";
+ public static final String VALID_GENDER_BOB = "M";
public static final String NAME_DESC_AMY = " " + PREFIX_NAME + VALID_NAME_AMY;
public static final String NAME_DESC_BOB = " " + PREFIX_NAME + VALID_NAME_BOB;
- public static final String PHONE_DESC_AMY = " " + PREFIX_PHONE + VALID_PHONE_AMY;
- public static final String PHONE_DESC_BOB = " " + PREFIX_PHONE + VALID_PHONE_BOB;
+ public static final String MAJOR_DESC_AMY = " " + PREFIX_MAJOR + VALID_MAJOR_AMY;
+ public static final String MAJOR_DESC_BOB = " " + PREFIX_MAJOR + VALID_MAJOR_BOB;
+ public static final String YEAR_DESC_AMY = " " + PREFIX_YEAR + VALID_YEAR_AMY;
+ public static final String YEAR_DESC_BOB = " " + PREFIX_YEAR + VALID_YEAR_BOB;
public static final String EMAIL_DESC_AMY = " " + PREFIX_EMAIL + VALID_EMAIL_AMY;
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 DESCRIPTION_DESC_AMY = " " + PREFIX_DESCRIPTION + VALID_DESCRIPTION_AMY;
+ public static final String DESCRIPTION_DESC_BOB = " " + PREFIX_DESCRIPTION + VALID_DESCRIPTION_BOB;
+ public static final String TUTORIAL_DESC_AMY = " " + PREFIX_TUTORIAL + VALID_TUT_FIRST_AMY + " "
+ + PREFIX_TUTORIAL + VALID_TUT_SECOND_AMY;
+ public static final String TUTORIAL_DESC_BOB = " " + PREFIX_TUTORIAL + VALID_TUT_FIRST_BOB + " "
+ + PREFIX_TUTORIAL + VALID_TUT_SECOND_BOB;
+ public static final String SM_DESC_AMY = " " + PREFIX_SOCIAL_MEDIA_LINK + VALID_SM_LINKEDIN_AMY + " "
+ + PREFIX_SOCIAL_MEDIA_LINK + VALID_SM_GITHUB_AMY;
+ public static final String SM_DESC_BOB = " " + PREFIX_SOCIAL_MEDIA_LINK + VALID_SM_LINKEDIN_BOB + " "
+ + PREFIX_SOCIAL_MEDIA_LINK + VALID_SM_GITHUB_BOB;
+ public static final String NATIONALITY_DESC_AMY = " " + PREFIX_NATIONALITY + VALID_NATIONALITY_AMY;
+ public static final String NATIONALITY_DESC_BOB = " " + PREFIX_NATIONALITY + VALID_NATIONALITY_BOB;
+ public static final String GENDER_DESC_AMY = " " + PREFIX_GENDER + VALID_GENDER_AMY;
+ public static final String GENDER_DESC_BOB = " " + PREFIX_GENDER + VALID_GENDER_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_MAJOR_DESC = " " + PREFIX_MAJOR + "Computer Games"; // not in list of majors
+ public static final String INVALID_YEAR_DESC = " " + PREFIX_YEAR + "-1"; // negative integer not allowed for year
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_DESCRIPTION_DESC = " "
+ + PREFIX_DESCRIPTION; // empty string not allowed for description
+ public static final String INVALID_TUTORIAL_DESC = " " + PREFIX_TUTORIAL + "1"; // not a 2 digit number
+ public static final String INVALID_SM_DESC = " " + PREFIX_SOCIAL_MEDIA_LINK
+ + "example.com"; // does not start with https://
+ public static final String INVALID_NATIONALITY_DESC = " " + PREFIX_NATIONALITY + "Singaporean";
+ public static final String INVALID_GENDER_DESC = " " + PREFIX_GENDER + "MALE";
+
+
+ public static final String VALID_NUMBER_GROUP1 = "1";
+ public static final String VALID_NUMBER_GROUP2 = "2";
+ public static final String VALID_TUTORIAL_GROUP1 = "01";
+ public static final String VALID_TUTORIAL_GROUP2 = "02";
+ public static final Person VALID_MEMBER1_GROUP1 = ALICE;
+ public static final Person VALID_MEMBER2_GROUP1 = BENSON;
+ public static final Person VALID_MEMBER3_GROUP1 = CARL;
+ public static final Person VALID_MEMBER1_GROUP2 = DANIEL;
+ public static final Person VALID_MEMBER2_GROUP2 = ELLE;
+ public static final Person VALID_MEMBER3_GROUP2 = FIONA;
+
+ public static final Task VALID_TASK1 = new Todo("Upload video of OP1.",
+ TaskStatus.NOT_DONE, TaskModule.CS2101);
+ public static final Task VALID_TASK2 = new Todo("Complete mid semester review form.",
+ TaskStatus.NOT_DONE, TaskModule.CS2103T);
+ public static final Task VALID_TASK3 = new Todo("Attend tutorials",
+ TaskStatus.NOT_DONE, TaskModule.CS2103T);
+
public static final String PREAMBLE_WHITESPACE = "\t \r \n";
public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble";
@@ -61,12 +126,16 @@ public class CommandTestUtil {
public static final EditCommand.EditPersonDescriptor DESC_BOB;
static {
- DESC_AMY = new EditPersonDescriptorBuilder().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)
- .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB)
- .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build();
+ DESC_AMY = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY).withMajor(VALID_MAJOR_AMY)
+ .withYear(VALID_YEAR_AMY).withEmail(VALID_EMAIL_AMY).withDescription(VALID_DESCRIPTION_AMY)
+ .withTutorials(VALID_TUT_FIRST_AMY, VALID_TUT_SECOND_AMY)
+ .withSocialMediaLinks(VALID_SM_LINKEDIN_AMY, VALID_SM_GITHUB_AMY)
+ .withNationality(VALID_NATIONALITY_BOB).withGender(VALID_GENDER_BOB).build();
+ DESC_BOB = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).withMajor(VALID_MAJOR_BOB)
+ .withYear(VALID_YEAR_BOB).withEmail(VALID_EMAIL_BOB).withDescription(VALID_DESCRIPTION_BOB)
+ .withTutorials(VALID_TUT_FIRST_BOB, VALID_TUT_SECOND_BOB)
+ .withSocialMediaLinks(VALID_SM_LINKEDIN_BOB, VALID_SM_GITHUB_BOB)
+ .withNationality(VALID_NATIONALITY_BOB).withGender(VALID_GENDER_BOB).build();
}
/**
@@ -90,8 +159,13 @@ public static void assertCommandSuccess(Command command, Model actualModel, Comm
* that takes a string {@code expectedMessage}.
*/
public static void assertCommandSuccess(Command command, Model actualModel, String expectedMessage,
- Model expectedModel) {
- CommandResult expectedCommandResult = new CommandResult(expectedMessage);
+ Model expectedModel, boolean groupCommand) {
+ CommandResult expectedCommandResult;
+ if (groupCommand) {
+ expectedCommandResult = new CommandResult(expectedMessage, false, false, true, false);
+ } else {
+ expectedCommandResult = new CommandResult(expectedMessage);
+ }
assertCommandSuccess(command, actualModel, expectedCommandResult, expectedModel);
}
@@ -111,6 +185,7 @@ public static void assertCommandFailure(Command command, Model actualModel, Stri
assertEquals(expectedAddressBook, actualModel.getAddressBook());
assertEquals(expectedFilteredList, actualModel.getFilteredPersonList());
}
+
/**
* Updates {@code model}'s filtered list to show only the person at the given {@code targetIndex} in the
* {@code model}'s address book.
@@ -125,4 +200,18 @@ public static void showPersonAtIndex(Model model, Index targetIndex) {
assertEquals(1, model.getFilteredPersonList().size());
}
+ /**
+ * Updates {@code model}'s filtered list to show only the group at the given {@code targetIndex} in the
+ * {@code model}'s address book.
+ */
+ public static void showGroupAtIndex(Model model, Index targetIndex) {
+ assertTrue(targetIndex.getZeroBased() < model.getFilteredGroupList().size());
+
+ Group group = model.getFilteredGroupList().get(targetIndex.getZeroBased());
+ int groupNumber = group.getNumber();
+ model.updateFilteredGroupList(new GroupContainsKeywordsPredicate(List.of(String.valueOf(groupNumber))));
+
+ assertEquals(1, model.getFilteredGroupList().size());
+ }
+
}
diff --git a/src/test/java/seedu/address/logic/commands/CreateCommandTest.java b/src/test/java/seedu/address/logic/commands/CreateCommandTest.java
new file mode 100644
index 00000000000..a017633849c
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/CreateCommandTest.java
@@ -0,0 +1,67 @@
+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 org.junit.jupiter.api.Test;
+
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.tutorial.Tutorial;
+import seedu.address.testutil.TypicalGroups;
+
+/**
+ * Contains integration tests (interaction with the Model) and unit tests for
+ * {@code CreateCommand}.
+ */
+public class CreateCommandTest {
+
+ @Test
+ public void execute_validTutorialNumber_success() {
+ Model model = new ModelManager(TypicalGroups.getTypicalAddressBook(), new UserPrefs());
+ CreateCommand createCommand = new CreateCommand(new Tutorial("01"));
+ String expectedMessage = String.format(CreateCommand.MESSAGE_SUCCESS,
+ createCommand.generateGroupNumber(model));
+
+ CommandResult commandResult = createCommand.execute(model);
+
+ assertEquals(expectedMessage, commandResult.getFeedbackToUser());
+ }
+
+ @Test
+ public void execute_invalidTutorialNumber_throwsCommandException() {
+ Model model = new ModelManager(TypicalGroups.getTypicalAddressBook(), new UserPrefs());
+ assertThrows(IllegalArgumentException.class, () -> new CreateCommand(new Tutorial("40")).execute(model));
+ }
+
+ @Test
+ public void generateGroupNumberTest() {
+ Model model = new ModelManager(TypicalGroups.getTypicalAddressBook(), new UserPrefs());
+ CreateCommand command = new CreateCommand(new Tutorial("01"));
+ assertEquals(command.generateGroupNumber(model), model.getFilteredGroupList().size() + 1);
+ }
+
+ @Test
+ public void equals() {
+ CreateCommand firstCreateCommand = new CreateCommand(new Tutorial("01"));
+ CreateCommand secondCreateCommand = new CreateCommand(new Tutorial("02"));
+
+ // same object -> returns true
+ assertTrue(firstCreateCommand.equals(firstCreateCommand));
+
+ // same values -> returns true
+ assertTrue(firstCreateCommand.equals(new CreateCommand(new Tutorial("01"))));
+
+ // different types -> returns false
+ assertFalse(firstCreateCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(firstCreateCommand.equals(null));
+
+ // different tutorial number -> returns false
+ assertFalse(firstCreateCommand.equals(secondCreateCommand));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
index b6f332eabca..6c866609fd2 100644
--- a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
@@ -5,18 +5,15 @@
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.Email;
import seedu.address.model.person.Person;
/**
@@ -28,9 +25,10 @@ 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);
+ public void execute_validEmailUnfilteredList_success() {
+ Person personToDelete = model.getFilteredPersonList().get(0); // Assuming the first person in the list is used
+ Email emailToDelete = personToDelete.getEmail();
+ DeleteCommand deleteCommand = new DeleteCommand(emailToDelete);
String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS,
Messages.format(personToDelete));
@@ -38,57 +36,32 @@ public void execute_validIndexUnfilteredList_success() {
ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
expectedModel.deletePerson(personToDelete);
- assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel);
+ assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel, false);
}
@Test
- public void execute_invalidIndexUnfilteredList_throwsCommandException() {
- Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
- DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex);
+ public void execute_invalidEmailUnfilteredList_throwsCommandException() {
+ Email invalidEmail = new Email("invalid@u.nus.edu"); // Use an email that doesn't exist in the test data
+ DeleteCommand deleteCommand = new DeleteCommand(invalidEmail);
- 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);
+ assertCommandFailure(deleteCommand, model, "Student with the provided email not found.");
}
@Test
public void equals() {
- DeleteCommand deleteFirstCommand = new DeleteCommand(INDEX_FIRST_PERSON);
- DeleteCommand deleteSecondCommand = new DeleteCommand(INDEX_SECOND_PERSON);
+ Person person = model.getFilteredPersonList().get(0); // Assuming the first person in the list is used
+ Email email = person.getEmail();
+ DeleteCommand deleteFirstCommand = new DeleteCommand(email);
+
+ Email email2 = model.getFilteredPersonList().get(1)
+ .getEmail(); // Assuming the second person in the list is used
+ DeleteCommand deleteSecondCommand = new DeleteCommand(email2);
// same object -> returns true
assertTrue(deleteFirstCommand.equals(deleteFirstCommand));
// same values -> returns true
- DeleteCommand deleteFirstCommandCopy = new DeleteCommand(INDEX_FIRST_PERSON);
+ DeleteCommand deleteFirstCommandCopy = new DeleteCommand(email);
assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy));
// different types -> returns false
@@ -97,15 +70,16 @@ public void equals() {
// null -> returns false
assertFalse(deleteFirstCommand.equals(null));
- // different person -> returns false
+ // different email -> 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 + "}";
+ Person person = model.getFilteredPersonList().get(0); // Assuming the first person in the list is used
+ Email email = person.getEmail();
+ DeleteCommand deleteCommand = new DeleteCommand(email);
+ String expected = DeleteCommand.class.getCanonicalName() + "{targetEmail=" + email + "}";
assertEquals(expected, deleteCommand.toString());
}
diff --git a/src/test/java/seedu/address/logic/commands/DeleteGroupCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteGroupCommandTest.java
new file mode 100644
index 00000000000..01272ecfa05
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/DeleteGroupCommandTest.java
@@ -0,0 +1,69 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+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.testutil.TypicalGroups;
+
+public class DeleteGroupCommandTest {
+
+ @Test
+ public void execute_validGroupNumber_success() throws CommandException {
+ // Create a model with typical groups and user preferences
+ Model model = new ModelManager(TypicalGroups.getTypicalAddressBook(), new UserPrefs());
+
+ // Create a DeleteGroupCommand for a valid group number
+ DeleteGroupCommand deleteGroupCommand = new DeleteGroupCommand(1);
+
+ // Build the expected success message
+ String expectedMessage = String.format(DeleteGroupCommand.MESSAGE_DELETE_GROUP_SUCCESS, 1);
+
+ // Execute the DeleteGroupCommand and get the result
+ CommandResult commandResult = deleteGroupCommand.execute(model);
+
+ // Assert that the feedback message matches the expected success message
+ assertEquals(expectedMessage, commandResult.getFeedbackToUser());
+ }
+
+ @Test
+ public void execute_invalidGroupNumber_throwsCommandException() {
+ // Create a model with typical groups and user preferences
+ Model model = new ModelManager(TypicalGroups.getTypicalAddressBook(), new UserPrefs());
+
+ // Choose a group number that is known to be non-existent (e.g., 5000)
+ int nonExistentGroupNumber = 5000;
+
+ // Ensure that the chosen group number is non-existent
+ assertTrue(model.getGroupWithNumber(nonExistentGroupNumber).isEmpty(),
+ "Precondition: Group with the chosen number should not exist in the model.");
+
+ // Create a DeleteGroupCommand for the non-existent group number
+ DeleteGroupCommand deleteGroupCommand = new DeleteGroupCommand(nonExistentGroupNumber);
+
+ // Use assertThrows to verify that executing the command throws the expected exception
+ assertThrows(CommandException.class, () -> deleteGroupCommand.execute(model),
+ DeleteGroupCommand.MESSAGE_DELETE_GROUP_NOT_FOUND);
+
+ // Ensure that the model remains unchanged after the command execution attempt
+ assertEquals(TypicalGroups.getTypicalGroups().size(), model.getFilteredGroupList().size());
+ }
+
+ @Test
+ public void execute_zeroGroupNumber_throwsCommandException() {
+ // Create a model with typical groups and user preferences
+ Model model = new ModelManager(TypicalGroups.getTypicalAddressBook(), new UserPrefs());
+
+ // Create a DeleteGroupCommand for a group number of zero
+ DeleteGroupCommand deleteGroupCommand = new DeleteGroupCommand(0);
+
+ // Use assertThrows to verify that executing the command throws the expected exception
+ assertThrows(CommandException.class, () -> deleteGroupCommand.execute(model));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/EditCommandTest.java b/src/test/java/seedu/address/logic/commands/EditCommandTest.java
index 469dd97daa7..0bd8206c7ba 100644
--- a/src/test/java/seedu/address/logic/commands/EditCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/EditCommandTest.java
@@ -6,24 +6,25 @@
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.VALID_SM_GITHUB_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_SM_LINKEDIN_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_YEAR_BOB;
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.logic.commands.EditCommand.MESSAGE_EMAIL_NOT_FOUND;
+import static seedu.address.testutil.TypicalEmails.EMAIL_FIRST_PERSON;
+import static seedu.address.testutil.TypicalEmails.EMAIL_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.Email;
import seedu.address.model.person.Person;
import seedu.address.testutil.EditPersonDescriptorBuilder;
import seedu.address.testutil.PersonBuilder;
@@ -39,56 +40,59 @@ public class EditCommandTest {
public void execute_allFieldsSpecifiedUnfilteredList_success() {
Person editedPerson = new PersonBuilder().build();
EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(editedPerson).build();
- EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, descriptor);
+ Email firstPersonEmail = model.getFilteredPersonList().get(0).getEmail();
+ EditCommand editCommand = new EditCommand(firstPersonEmail, 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);
+ assertCommandSuccess(editCommand, model, expectedMessage, expectedModel, false);
}
@Test
public void execute_someFieldsSpecifiedUnfilteredList_success() {
- Index indexLastPerson = Index.fromOneBased(model.getFilteredPersonList().size());
- Person lastPerson = model.getFilteredPersonList().get(indexLastPerson.getZeroBased());
+ Person firstPerson = model.getFilteredPersonList().get(0);
- PersonBuilder personInList = new PersonBuilder(lastPerson);
- Person editedPerson = personInList.withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB)
- .withTags(VALID_TAG_HUSBAND).build();
+ PersonBuilder personInList = new PersonBuilder(firstPerson);
+ Person editedPerson = personInList.withName(VALID_NAME_BOB).withYear(VALID_YEAR_BOB)
+ .withSocialMediaLinks(VALID_SM_LINKEDIN_BOB, VALID_SM_GITHUB_BOB).build();
EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB)
- .withPhone(VALID_PHONE_BOB).withTags(VALID_TAG_HUSBAND).build();
- EditCommand editCommand = new EditCommand(indexLastPerson, descriptor);
+ .withYear(VALID_YEAR_BOB)
+ .withSocialMediaLinks(VALID_SM_LINKEDIN_BOB, VALID_SM_GITHUB_BOB).build();
+
+ Email firstPersonEmail = model.getFilteredPersonList().get(0).getEmail();
+ EditCommand editCommand = new EditCommand(firstPersonEmail, 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);
+ expectedModel.setPerson(firstPerson, editedPerson);
- assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
+ assertCommandSuccess(editCommand, model, expectedMessage, expectedModel, false);
}
@Test
public void execute_noFieldSpecifiedUnfilteredList_success() {
- EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, new EditPersonDescriptor());
- Person editedPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Email firstPersonEmail = model.getFilteredPersonList().get(0).getEmail();
+ EditCommand editCommand = new EditCommand(firstPersonEmail, new EditPersonDescriptor());
+ Person editedPerson = model.getFilteredPersonList().get(0);
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);
+ assertCommandSuccess(editCommand, model, expectedMessage, expectedModel, false);
}
@Test
public void execute_filteredList_success() {
- showPersonAtIndex(model, INDEX_FIRST_PERSON);
-
- Person personInFilteredList = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Person personInFilteredList = model.getFilteredPersonList().get(0);
Person editedPerson = new PersonBuilder(personInFilteredList).withName(VALID_NAME_BOB).build();
- EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON,
+ Email firstPersonEmail = model.getFilteredPersonList().get(0).getEmail();
+ EditCommand editCommand = new EditCommand(firstPersonEmail,
new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build());
String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson));
@@ -96,63 +100,48 @@ public void execute_filteredList_success() {
Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson);
- assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
+ assertCommandSuccess(editCommand, model, expectedMessage, expectedModel, false);
}
@Test
public void execute_duplicatePersonUnfilteredList_failure() {
- Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Person firstPerson = model.getFilteredPersonList().get(0);
EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(firstPerson).build();
- EditCommand editCommand = new EditCommand(INDEX_SECOND_PERSON, descriptor);
+ Email secondPersonEmail = model.getFilteredPersonList().get(1).getEmail();
+ EditCommand editCommand = new EditCommand(secondPersonEmail, 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,
+ Email firstPersonEmail = model.getFilteredPersonList().get(0).getEmail();
+ Person personInList = model.getAddressBook().getPersonList().get(1);
+ EditCommand editCommand = new EditCommand(firstPersonEmail,
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);
+ public void execute_invalidPersonEmailUnfilteredList_failure() {
+ Email invalidEmail = new Email("invalid@u.nus.edu");
EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build();
- EditCommand editCommand = new EditCommand(outOfBoundIndex, descriptor);
+ EditCommand editCommand = new EditCommand(invalidEmail, 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);
+ assertCommandFailure(editCommand, model, MESSAGE_EMAIL_NOT_FOUND);
}
@Test
public void equals() {
- final EditCommand standardCommand = new EditCommand(INDEX_FIRST_PERSON, DESC_AMY);
+ Email amyEmail = new Email("amy@u.nus.edu");
+
+ final EditCommand standardCommand = new EditCommand(amyEmail, DESC_AMY);
// same values -> returns true
EditPersonDescriptor copyDescriptor = new EditPersonDescriptor(DESC_AMY);
- EditCommand commandWithSameValues = new EditCommand(INDEX_FIRST_PERSON, copyDescriptor);
+ EditCommand commandWithSameValues = new EditCommand(amyEmail, copyDescriptor);
assertTrue(standardCommand.equals(commandWithSameValues));
// same object -> returns true
@@ -164,21 +153,32 @@ public void equals() {
// different types -> returns false
assertFalse(standardCommand.equals(new ClearCommand()));
- // different index -> returns false
- assertFalse(standardCommand.equals(new EditCommand(INDEX_SECOND_PERSON, DESC_AMY)));
+ // different email -> returns false
+ assertFalse(standardCommand.equals(new EditCommand(EMAIL_SECOND_PERSON, DESC_AMY)));
// different descriptor -> returns false
- assertFalse(standardCommand.equals(new EditCommand(INDEX_FIRST_PERSON, DESC_BOB)));
+ assertFalse(standardCommand.equals(new EditCommand(EMAIL_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 + "}";
+ EditCommand editCommand = new EditCommand(EMAIL_FIRST_PERSON, editPersonDescriptor);
+ String expected = EditCommand.class.getCanonicalName() + "{email=" + EMAIL_FIRST_PERSON
+ + ", editPersonDescriptor=" + editPersonDescriptor + "}";
assertEquals(expected, editCommand.toString());
}
+ @Test
+ public void equals_sameTutorials_returnsTrue() {
+ // Create two edit descriptors with the same tutorials
+ EditPersonDescriptor descriptorWithTutorials = new EditPersonDescriptorBuilder()
+ .withTutorials("01", "02").build();
+ EditPersonDescriptor descriptorCopyWithTutorials = new EditPersonDescriptorBuilder()
+ .withTutorials("01", "02").build();
+
+ // They should be considered equal even if other fields are different
+ assertTrue(descriptorWithTutorials.equals(descriptorCopyWithTutorials));
+ }
+
}
diff --git a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java b/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
index b17c1f3d5c2..3dd2ccd48ac 100644
--- a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
+++ b/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
@@ -5,11 +5,13 @@
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_DESCRIPTION_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_MAJOR_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.VALID_SM_LINKEDIN_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TUT_FIRST_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_YEAR_BOB;
import org.junit.jupiter.api.Test;
@@ -40,20 +42,28 @@ public void equals() {
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();
+ // different major -> returns false
+ editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withMajor(VALID_MAJOR_BOB).build();
+ assertFalse(DESC_AMY.equals(editedAmy));
+
+ // different year -> returns false
+ editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withYear(VALID_YEAR_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();
+ // different description -> returns false
+ editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withDescription(VALID_DESCRIPTION_BOB).build();
assertFalse(DESC_AMY.equals(editedAmy));
- // different tags -> returns false
- editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withTags(VALID_TAG_HUSBAND).build();
+ // different tutorial -> returns false
+ editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withTutorials(VALID_TUT_FIRST_BOB).build();
+ assertFalse(DESC_AMY.equals(editedAmy));
+
+ // different social media links -> returns false
+ editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withSocialMediaLinks(VALID_SM_LINKEDIN_BOB).build();
assertFalse(DESC_AMY.equals(editedAmy));
}
@@ -61,11 +71,16 @@ public void equals() {
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) + "}";
+ + editPersonDescriptor.getName().orElse(null) + ", major="
+ + editPersonDescriptor.getMajor().orElse(null) + ", year="
+ + editPersonDescriptor.getYear().orElse(null) + ", email="
+ + editPersonDescriptor.getEmail().orElse(null) + ", description="
+ + editPersonDescriptor.getTutorials().orElse(null) + ", tutorials="
+ + editPersonDescriptor.getDescription().orElse(null) + ", social media links="
+ + editPersonDescriptor.getSocialMediaLinks().orElse(null) + ", nationality="
+ + editPersonDescriptor.getNationality().orElse(null) + ", gender="
+ + editPersonDescriptor.getGender().orElse(null) + "}";
+
assertEquals(expected, editPersonDescriptor.toString());
}
}
diff --git a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java b/src/test/java/seedu/address/logic/commands/ExitCommandTest.java
index 9533c473875..168aea11f72 100644
--- a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/ExitCommandTest.java
@@ -14,7 +14,8 @@ public class ExitCommandTest {
@Test
public void execute_exit_success() {
- CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true);
+ CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true,
+ false, false);
assertCommandSuccess(new ExitCommand(), model, expectedCommandResult, expectedModel);
}
}
diff --git a/src/test/java/seedu/address/logic/commands/FilterCommandTest.java b/src/test/java/seedu/address/logic/commands/FilterCommandTest.java
new file mode 100644
index 00000000000..4856a33fe39
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/FilterCommandTest.java
@@ -0,0 +1,91 @@
+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.Messages.MESSAGE_PERSONS_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 java.util.Arrays;
+import java.util.Collections;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.tutorial.TutorialContainsSlotsPredicate;
+
+/**
+ * Contains integration tests (interaction with the Model) for {@code FilterCommand}.
+ */
+public class FilterCommandTest {
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ private Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void equals() {
+ TutorialContainsSlotsPredicate firstPredicate =
+ new TutorialContainsSlotsPredicate(Collections.singletonList("01"));
+ TutorialContainsSlotsPredicate secondPredicate =
+ new TutorialContainsSlotsPredicate(Collections.singletonList("02"));
+
+ FilterCommand filterFirstCommand = new FilterCommand(firstPredicate);
+ FilterCommand filterSecondCommand = new FilterCommand(secondPredicate);
+
+ // same object -> returns true
+ assertTrue(filterFirstCommand.equals(filterFirstCommand));
+
+ // same values -> returns true
+ FilterCommand filterFirstCommandCopy = new FilterCommand(firstPredicate);
+ assertTrue(filterFirstCommand.equals(filterFirstCommandCopy));
+
+ // different types -> returns false
+ assertFalse(filterFirstCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(filterFirstCommand.equals(null));
+
+ // different tutorial -> returns false
+ assertFalse(filterFirstCommand.equals(filterSecondCommand));
+ }
+
+ @Test
+ public void execute_zeroSlots_noPersonFound() {
+ String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 0);
+ TutorialContainsSlotsPredicate predicate = preparePredicate(" ");
+ FilterCommand command = new FilterCommand(predicate);
+ expectedModel.updateFilteredPersonList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, false);
+ assertEquals(Collections.emptyList(), model.getFilteredPersonList());
+ }
+
+ @Test
+ public void execute_multipleSlots_multiplePersonsFound() {
+ String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 3);
+ TutorialContainsSlotsPredicate predicate = preparePredicate("05 09 11");
+ FilterCommand command = new FilterCommand(predicate);
+ expectedModel.updateFilteredPersonList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, false);
+ assertEquals(Arrays.asList(CARL, ELLE, FIONA), model.getFilteredPersonList());
+ }
+
+ @Test
+ public void toStringMethod() {
+ TutorialContainsSlotsPredicate predicate = new TutorialContainsSlotsPredicate(Arrays.asList("01"));
+ FilterCommand filterCommand = new FilterCommand(predicate);
+ String expected = FilterCommand.class.getCanonicalName() + "{predicate=" + predicate + "}";
+ assertEquals(expected, filterCommand.toString());
+ }
+
+ /**
+ * Parses {@code userInput} into a {@code TutorialContainsSlotsPredicate}.
+ */
+ private TutorialContainsSlotsPredicate preparePredicate(String userInput) {
+ return new TutorialContainsSlotsPredicate(Arrays.asList(userInput.split("\\s+")));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/FilterGroupCommandTest.java b/src/test/java/seedu/address/logic/commands/FilterGroupCommandTest.java
new file mode 100644
index 00000000000..295f6293a2d
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/FilterGroupCommandTest.java
@@ -0,0 +1,88 @@
+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.Messages.MESSAGE_GROUPS_LISTED_OVERVIEW;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.testutil.TypicalGroups.GROUP2;
+import static seedu.address.testutil.TypicalGroups.GROUP3;
+import static seedu.address.testutil.TypicalGroups.GROUP4;
+import static seedu.address.testutil.TypicalGroups.getTypicalAddressBook;
+
+import java.util.Arrays;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.group.GroupBelongsTutorialPredicate;
+
+/**
+ * Contains integration tests (interaction with the Model) for {@code FilterGroupCommand}.
+ */
+public class FilterGroupCommandTest {
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ private Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void equals() {
+ GroupBelongsTutorialPredicate firstPredicate = new GroupBelongsTutorialPredicate("01");
+ GroupBelongsTutorialPredicate secondPredicate = new GroupBelongsTutorialPredicate("02");
+
+ FilterGroupCommand filterGroupFirstCommand = new FilterGroupCommand(firstPredicate);
+ FilterGroupCommand filterGroupSecondCommand = new FilterGroupCommand(secondPredicate);
+
+ // same object -> returns true
+ assertTrue(filterGroupFirstCommand.equals(filterGroupFirstCommand));
+
+ // same values -> returns true
+ FilterGroupCommand filterGroupFirstCommandCopy = new FilterGroupCommand(firstPredicate);
+ assertTrue(filterGroupFirstCommand.equals(filterGroupFirstCommandCopy));
+
+ // different types -> returns false
+ assertFalse(filterGroupFirstCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(filterGroupFirstCommand.equals(null));
+
+ // different tutorial -> returns false
+ assertFalse(filterGroupFirstCommand.equals(filterGroupSecondCommand));
+ }
+
+ @Test
+ public void execute_oneSlot_oneGroupFound() {
+ String expectedMessage = String.format(MESSAGE_GROUPS_LISTED_OVERVIEW, 1);
+ GroupBelongsTutorialPredicate predicate = preparePredicate("03");
+ FilterGroupCommand command = new FilterGroupCommand(predicate);
+ expectedModel.updateFilteredGroupList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, true);
+ assertEquals(Arrays.asList(GROUP3), model.getFilteredGroupList());
+ }
+
+ @Test
+ public void execute_oneSlot_multipleGroupsFound() {
+ String expectedMessage = String.format(MESSAGE_GROUPS_LISTED_OVERVIEW, 2);
+ GroupBelongsTutorialPredicate predicate = preparePredicate("02");
+ FilterGroupCommand command = new FilterGroupCommand(predicate);
+ expectedModel.updateFilteredGroupList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, true);
+ assertEquals(Arrays.asList(GROUP2, GROUP4), model.getFilteredGroupList());
+ }
+
+ @Test
+ public void toStringMethod() {
+ GroupBelongsTutorialPredicate predicate = new GroupBelongsTutorialPredicate("01");
+ FilterGroupCommand filterGroupCommand = new FilterGroupCommand(predicate);
+ String expected = FilterGroupCommand.class.getCanonicalName() + "{predicate=" + predicate + "}";
+ assertEquals(expected, filterGroupCommand.toString());
+ }
+
+ /**
+ * Parses {@code userInput} into a {@code GroupBelongsTutorialPredicate}.
+ */
+ private GroupBelongsTutorialPredicate preparePredicate(String userInput) {
+ return new GroupBelongsTutorialPredicate(userInput);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/FindCommandTest.java b/src/test/java/seedu/address/logic/commands/FindCommandTest.java
index b8b7dbba91a..6213afd0936 100644
--- a/src/test/java/seedu/address/logic/commands/FindCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/FindCommandTest.java
@@ -60,7 +60,7 @@ public void execute_zeroKeywords_noPersonFound() {
NameContainsKeywordsPredicate predicate = preparePredicate(" ");
FindCommand command = new FindCommand(predicate);
expectedModel.updateFilteredPersonList(predicate);
- assertCommandSuccess(command, model, expectedMessage, expectedModel);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, false);
assertEquals(Collections.emptyList(), model.getFilteredPersonList());
}
@@ -70,7 +70,17 @@ public void execute_multipleKeywords_multiplePersonsFound() {
NameContainsKeywordsPredicate predicate = preparePredicate("Kurz Elle Kunz");
FindCommand command = new FindCommand(predicate);
expectedModel.updateFilteredPersonList(predicate);
- assertCommandSuccess(command, model, expectedMessage, expectedModel);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, false);
+ assertEquals(Arrays.asList(CARL, ELLE, FIONA), model.getFilteredPersonList());
+ }
+
+ @Test
+ public void execute_multiplePartialKeywords_multiplePersonsFound() {
+ String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 3);
+ NameContainsKeywordsPredicate predicate = preparePredicate("Kur Ell Kun");
+ FindCommand command = new FindCommand(predicate);
+ expectedModel.updateFilteredPersonList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, false);
assertEquals(Arrays.asList(CARL, ELLE, FIONA), model.getFilteredPersonList());
}
diff --git a/src/test/java/seedu/address/logic/commands/FindGroupCommandTest.java b/src/test/java/seedu/address/logic/commands/FindGroupCommandTest.java
new file mode 100644
index 00000000000..390087c1b0a
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/FindGroupCommandTest.java
@@ -0,0 +1,88 @@
+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.Messages.MESSAGE_GROUPS_LISTED_OVERVIEW;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.testutil.TypicalGroups.GROUP1;
+import static seedu.address.testutil.TypicalGroups.GROUP2;
+import static seedu.address.testutil.TypicalGroups.GROUP3;
+import static seedu.address.testutil.TypicalGroups.getTypicalAddressBook;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.group.GroupContainsKeywordsPredicate;
+
+public class FindGroupCommandTest {
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ private Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void equals() {
+ GroupContainsKeywordsPredicate firstPredicate =
+ new GroupContainsKeywordsPredicate(Collections.singletonList(String.valueOf(1)));
+ GroupContainsKeywordsPredicate secondPredicate =
+ new GroupContainsKeywordsPredicate(Collections.singletonList(String.valueOf(2)));
+
+ FindGroupCommand firstFindGroupCommand = new FindGroupCommand(firstPredicate);
+ FindGroupCommand secondFindGroupCommand = new FindGroupCommand(secondPredicate);
+
+ // same object -> returns true
+ assertTrue(firstFindGroupCommand.equals(firstFindGroupCommand));
+
+ // same values -> returns true
+ FindGroupCommand firstFindGroupCommandCopy = new FindGroupCommand(firstPredicate);
+ assertTrue(firstFindGroupCommand.equals(firstFindGroupCommandCopy));
+
+ // different types -> returns false
+ assertFalse(firstFindGroupCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(firstFindGroupCommand.equals(null));
+
+ // different group -> returns false
+ assertFalse(firstFindGroupCommand.equals(secondFindGroupCommand));
+ }
+
+ @Test
+ public void execute_zeroKeywords_noGroupFound() {
+ String expectedMessage = String.format(MESSAGE_GROUPS_LISTED_OVERVIEW, 0);
+ GroupContainsKeywordsPredicate predicate = preparePredicate(" ");
+ FindGroupCommand command = new FindGroupCommand(predicate);
+ expectedModel.updateFilteredGroupList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, true);
+ assertEquals(Collections.emptyList(), model.getFilteredGroupList());
+ }
+
+ @Test
+ public void execute_multipleKeywords_multipleGroupsFound() {
+ String expectedMessage = String.format(MESSAGE_GROUPS_LISTED_OVERVIEW, 3);
+ GroupContainsKeywordsPredicate predicate = preparePredicate("1 2 3");
+ FindGroupCommand command = new FindGroupCommand(predicate);
+ expectedModel.updateFilteredGroupList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, true);
+ assertEquals(Arrays.asList(GROUP1, GROUP2, GROUP3), model.getFilteredGroupList());
+ }
+
+ @Test
+ public void toStringMethod() {
+ GroupContainsKeywordsPredicate predicate = new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(1)));
+ FindGroupCommand findGroupCommand = new FindGroupCommand(predicate);
+ String expected = FindGroupCommand.class.getCanonicalName() + "{predicate=" + predicate + "}";
+ assertEquals(expected, findGroupCommand.toString());
+ }
+
+ /**
+ * Parses {@code userInput} into a {@code GroupContainsKeywordsPredicate}.
+ */
+ private GroupContainsKeywordsPredicate preparePredicate(String userInput) {
+ return new GroupContainsKeywordsPredicate(Arrays.asList(userInput.split("\\s+")));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java
index 4904fc4352e..f5822f5da6b 100644
--- a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java
@@ -1,5 +1,6 @@
package seedu.address.logic.commands;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
import static seedu.address.logic.commands.HelpCommand.SHOWING_HELP_MESSAGE;
@@ -14,7 +15,12 @@ public class HelpCommandTest {
@Test
public void execute_help_success() {
- CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false);
+ CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false, false, false);
assertCommandSuccess(new HelpCommand(), model, expectedCommandResult, expectedModel);
}
+
+ @Test
+ public void execute_nullModel_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new HelpCommand().execute(null));
+ }
}
diff --git a/src/test/java/seedu/address/logic/commands/JoinCommandTest.java b/src/test/java/seedu/address/logic/commands/JoinCommandTest.java
new file mode 100644
index 00000000000..fcfe1be4dd5
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/JoinCommandTest.java
@@ -0,0 +1,108 @@
+package seedu.address.logic.commands;
+
+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.testutil.TypicalGroups.GROUP1;
+import static seedu.address.testutil.TypicalGroups.GROUP11;
+import static seedu.address.testutil.TypicalGroups.GROUP2;
+import static seedu.address.testutil.TypicalGroups.GROUP3;
+import static seedu.address.testutil.TypicalPersons.ALICE;
+import static seedu.address.testutil.TypicalPersons.AMY;
+import static seedu.address.testutil.TypicalPersons.BOB;
+import static seedu.address.testutil.TypicalPersons.DANIEL;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.testutil.TypicalGroups;
+
+/**
+ * Contains integration tests (interaction with the Model) and unit tests for
+ * {@code JoinCommand}.
+ */
+public class JoinCommandTest {
+ private Model model;
+ private Model expectedModel;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager(TypicalGroups.getTypicalAddressBook(), new UserPrefs());
+ expectedModel = new ModelManager(TypicalGroups.getTypicalAddressBook(), new UserPrefs());
+ model.addPerson(AMY);
+ expectedModel.addPerson(AMY);
+ }
+
+ @Test
+ public void execute_join_success() {
+ // person exists, group exists, group is not full, person is not in a group
+ JoinCommand command = new JoinCommand(AMY.getEmail(), GROUP3.getNumber());
+ String expectedMessage = String.format(JoinCommand.MESSAGE_JOIN_SUCCESS,
+ AMY.getName(), GROUP3.getNumber());
+
+ expectedModel.addPersonToGroup(AMY, expectedModel.getGroupWithNumber(GROUP3.getNumber()).get());
+
+ assertCommandSuccess(command, model, expectedMessage, expectedModel, true);
+ }
+
+ @Test
+ public void execute_personDoesNotExist_throwsCommandException() {
+ JoinCommand command = new JoinCommand(BOB.getEmail(), GROUP1.getNumber());
+ assertCommandFailure(command, model, JoinCommand.MESSAGE_JOIN_EMAIL_NOT_FOUND);
+ }
+
+ @Test
+ public void execute_groupDoesNotExist_throwsCommandException() {
+ JoinCommand command = new JoinCommand(AMY.getEmail(), 100);
+ assertCommandFailure(command, model, JoinCommand.MESSAGE_JOIN_GROUP_NOT_FOUND);
+ }
+
+ @Test
+ public void execute_personAlreadyInTheGroup_throwsCommandException() {
+ JoinCommand command = new JoinCommand(ALICE.getEmail(), GROUP1.getNumber());
+ assertCommandFailure(command, model, JoinCommand.MESSAGE_PERSON_ALREADY_IN_GROUP);
+ }
+
+ @Test
+ public void execute_personInAnotherGroup_throwsCommandException() {
+ JoinCommand command = new JoinCommand(DANIEL.getEmail(), GROUP1.getNumber());
+ assertCommandFailure(command, model, JoinCommand.MESSAGE_PERSON_IN_ANOTHER_GROUP);
+ }
+
+ @Test
+ public void execute_fullGroup_throwsCommandException() {
+ JoinCommand command = new JoinCommand(AMY.getEmail(), GROUP11.getNumber());
+ assertCommandFailure(command, model, JoinCommand.MESSAGE_GROUP_FULL);
+ }
+
+ @Test
+ public void equals() {
+ JoinCommand firstJoinCommand = new JoinCommand(AMY.getEmail(), GROUP1.getNumber());
+ JoinCommand secondJoinCommand = new JoinCommand(AMY.getEmail(), GROUP2.getNumber());
+ JoinCommand thirdJoinCommand = new JoinCommand(BOB.getEmail(), GROUP1.getNumber());
+
+ // same object -> returns true
+ assertTrue(firstJoinCommand.equals(firstJoinCommand));
+
+ // same values -> returns true
+ JoinCommand firstJoinCommandCopy = new JoinCommand(AMY.getEmail(), GROUP1.getNumber());
+ assertTrue(firstJoinCommand.equals(firstJoinCommandCopy));
+
+ // different types -> returns false
+ assertFalse(firstJoinCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(firstJoinCommand.equals(null));
+
+ // different email -> returns false
+ assertFalse(firstJoinCommand.equals(thirdJoinCommand));
+
+ // different group number -> returns false
+ assertFalse(firstJoinCommand.equals(secondJoinCommand));
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/commands/LeaveCommandTest.java b/src/test/java/seedu/address/logic/commands/LeaveCommandTest.java
new file mode 100644
index 00000000000..7eecc66580b
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/LeaveCommandTest.java
@@ -0,0 +1,135 @@
+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 org.junit.jupiter.api.Assertions.fail;
+import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalGroups.GROUP1;
+import static seedu.address.testutil.TypicalGroups.GROUP2;
+import static seedu.address.testutil.TypicalPersons.ALICE;
+import static seedu.address.testutil.TypicalPersons.BENSON;
+import static seedu.address.testutil.TypicalPersons.DANIEL;
+import static seedu.address.testutil.TypicalPersons.ELLE;
+import static seedu.address.testutil.TypicalPersons.FIONA;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.person.Email;
+
+
+/**
+ * Contains integration tests (interaction with the Model) and unit tests for LeaveCommand.
+ */
+public class LeaveCommandTest {
+
+ private Model model;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager();
+ model.addPerson(ALICE);
+ model.addPerson(BENSON);
+ model.addPerson(DANIEL);
+ model.addPerson(ELLE);
+ model.addPerson(FIONA);
+ }
+
+ @Test
+ public void execute_leavePersonFromNonexistentGroup_throwsCommandException() {
+ LeaveCommand leaveCommand = new LeaveCommand(ALICE.getEmail(), GROUP1.getNumber());
+
+ assertCommandFailure(leaveCommand, model, LeaveCommand.MESSAGE_LEAVE_GROUP_NOT_FOUND);
+ }
+
+ @Test
+ public void execute_leaveNonexistentPersonFromGroup_throwsCommandException() {
+ model.addGroup(GROUP1);
+
+ LeaveCommand leaveCommand = new LeaveCommand(DANIEL.getEmail(), GROUP1.getNumber());
+
+ assertCommandFailure(leaveCommand, model, LeaveCommand.MESSAGE_NOT_IN_GROUP);
+ }
+
+ @Test
+ public void execute_leavePersonNotInGroup_throwsCommandException() {
+ model.addGroup(GROUP2);
+
+ LeaveCommand leaveCommand = new LeaveCommand(FIONA.getEmail(), GROUP2.getNumber());
+
+ try {
+ leaveCommand.execute(model);
+ fail("Expected CommandException was not thrown.");
+ } catch (UnsupportedOperationException e) {
+ // Catch the UnsupportedOperationException and convert it into a CommandException
+ assertThrows(CommandException.class, () -> {
+ throw new CommandException(LeaveCommand.MESSAGE_NOT_IN_GROUP);
+ });
+ } catch (CommandException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ @Test
+ public void equals() {
+ LeaveCommand leaveAliceGroup1Command = new LeaveCommand(ALICE.getEmail(), GROUP1.getNumber());
+ LeaveCommand leaveAliceGroup2Command = new LeaveCommand(ALICE.getEmail(), GROUP2.getNumber());
+ LeaveCommand leaveBensonGroup1Command = new LeaveCommand(BENSON.getEmail(), GROUP1.getNumber());
+ LeaveCommand leaveDanielGroup1Command = new LeaveCommand(DANIEL.getEmail(), GROUP1.getNumber());
+
+ // same object -> returns true
+ assertTrue(leaveAliceGroup1Command.equals(leaveAliceGroup1Command));
+
+ // same values -> returns true
+ LeaveCommand leaveAliceGroup1CommandCopy = new LeaveCommand(ALICE.getEmail(), GROUP1.getNumber());
+ assertTrue(leaveAliceGroup1Command.equals(leaveAliceGroup1CommandCopy));
+
+ // different types -> returns false
+ assertFalse(leaveAliceGroup1Command.equals(1));
+
+ // null -> returns false
+ assertFalse(leaveAliceGroup1Command.equals(null));
+
+ // different email -> returns false
+ assertFalse(leaveAliceGroup1Command.equals(leaveBensonGroup1Command));
+
+ // different group number -> returns false
+ assertFalse(leaveAliceGroup1Command.equals(leaveAliceGroup2Command));
+
+ // different person -> returns false
+ assertFalse(leaveAliceGroup1Command.equals(leaveDanielGroup1Command));
+ }
+
+ @Test
+ public void toString_validLeaveCommand_returnsExpectedString() {
+ LeaveCommand leaveCommand = new LeaveCommand(new Email("example@u.nus.edu"), 1);
+
+ String expectedString = new ToStringBuilder(leaveCommand)
+ .add("targetEmail", "example@u.nus.edu")
+ .add("targetGroupNumber", 1)
+ .toString();
+
+ assertEquals(expectedString, leaveCommand.toString());
+ }
+
+ private void assertCommandSuccess(LeaveCommand command, Model actualModel, String expectedMessage,
+ Model expectedModel) {
+ try {
+ CommandResult result = command.execute(actualModel);
+ assertEquals(expectedMessage, result.getFeedbackToUser());
+ assertEquals(expectedModel, actualModel);
+ } catch (CommandException ce) {
+ throw new AssertionError("Execution of command should not fail.", ce);
+ }
+ }
+
+ private void assertCommandFailure(LeaveCommand command, Model actualModel, String expectedMessage) {
+ assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/ListCommandTest.java b/src/test/java/seedu/address/logic/commands/ListCommandTest.java
index 435ff1f7275..f63e4cdc42c 100644
--- a/src/test/java/seedu/address/logic/commands/ListCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/ListCommandTest.java
@@ -28,12 +28,12 @@ public void setUp() {
@Test
public void execute_listIsNotFiltered_showsSameList() {
- assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel);
+ assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel, false);
}
@Test
public void execute_listIsFiltered_showsEverything() {
showPersonAtIndex(model, INDEX_FIRST_PERSON);
- assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel);
+ assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel, false);
}
}
diff --git a/src/test/java/seedu/address/logic/commands/ListGroupCommandTest.java b/src/test/java/seedu/address/logic/commands/ListGroupCommandTest.java
new file mode 100644
index 00000000000..a3d6a671c17
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/ListGroupCommandTest.java
@@ -0,0 +1,41 @@
+package seedu.address.logic.commands;
+
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.logic.commands.CommandTestUtil.showGroupAtIndex;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_GROUP;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.testutil.TypicalGroups;
+
+/**
+ * Contains integration tests (interaction with the Model) and unit tests for ListGroupCommand.
+ */
+public class ListGroupCommandTest {
+
+ private Model model;
+ private Model expectedModel;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager(TypicalGroups.getTypicalAddressBook(), new UserPrefs());
+ expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ }
+
+ @Test
+ public void execute_listIsNotFiltered_showsSameList() {
+ assertCommandSuccess(new ListGroupCommand(), model,
+ ListGroupCommand.MESSAGE_SUCCESS, expectedModel, true);
+ }
+
+ @Test
+ public void execute_listIsFiltered_showsEverything() {
+ showGroupAtIndex(model, INDEX_FIRST_GROUP);
+ assertCommandSuccess(new ListGroupCommand(), model,
+ ListGroupCommand.MESSAGE_SUCCESS, expectedModel, true);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/MarkCommandTest.java b/src/test/java/seedu/address/logic/commands/MarkCommandTest.java
new file mode 100644
index 00000000000..8e634de56cd
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/MarkCommandTest.java
@@ -0,0 +1,189 @@
+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.model.Model.PREDICATE_SHOW_ALL_GROUPS;
+import static seedu.address.testutil.TypicalGroups.getTypicalAddressBook;
+
+import java.util.Arrays;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+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.group.Group;
+import seedu.address.model.group.GroupContainsKeywordsPredicate;
+import seedu.address.model.group.tasks.TaskList;
+import seedu.address.model.tutorial.Tutorial;
+
+public class MarkCommandTest {
+
+ private Group group;
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager();
+ group = new Group(1, new Tutorial("01"));
+ // No need to use TaskInitializer as tasks are automatically initialised when group is created.
+ model.addGroup(group);
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ }
+
+ @Test
+ public void execute_validTaskIndex_success() throws CommandException {
+ int groupId = 1;
+ int taskIndex = 0; // When printing message, CommandResult adds 1 to taskIndex.
+ MarkCommand markCommand = new MarkCommand(1, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId))));
+
+ CommandResult result = markCommand.execute(model);
+
+ assertEquals("Marked task number 1 for group 1\n"
+ + "✅ T 1. CS2101 Upload video of OP1. \n"
+ + "❌ T 2. CS2101 Complete peer review for OP2. \n"
+ + "❌ D 3. CS2101 Submit slides for OP2. 29/10/2023 2359\n"
+ + "❌ D 4. CS2101 Complete peer review. 02/11/2023 2359\n"
+ + "❌ T 5. CS2101 Research on the SCQA framework. \n"
+ + "❌ D 6. CS2101 Plan for OP2. 24/10/2023 2359\n"
+ + "❌ D 7. CS2101 Submit UG. 11/11/2023 2359\n"
+ + "❌ T 8. CS2103T Complete mid semester review form. \n"
+ + "❌ D 9. CS2103T Add demo screenshots to project notes. 20/11/2023 2359\n"
+ + "❌ D 10. CS2103T Release v1.3.trial jar file. 27/10/2023 2359\n"
+ + "❌ D 11. CS2103T Wrap up milestone 1.3. 03/11/2023 2359\n"
+ + "❌ D 12. CS2103T Finalise TP. 17/11/2023 2359\n"
+ + "❌ T 13. CS2103T Update DG for each feature. \n",
+ result.getFeedbackToUser());
+ }
+
+ @Test
+ public void execute_taskAlreadyMarked_success() throws CommandException {
+ int groupId = 1;
+ int taskIndex = 0; // Task is already marked
+ MarkCommand markCommand = new MarkCommand(groupId, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId))));
+
+ CommandResult result = markCommand.execute(model);
+
+ assertEquals("Marked task number 1 for group 1\n"
+ + "✅ T 1. CS2101 Upload video of OP1. \n"
+ + "❌ T 2. CS2101 Complete peer review for OP2. \n"
+ + "❌ D 3. CS2101 Submit slides for OP2. 29/10/2023 2359\n"
+ + "❌ D 4. CS2101 Complete peer review. 02/11/2023 2359\n"
+ + "❌ T 5. CS2101 Research on the SCQA framework. \n"
+ + "❌ D 6. CS2101 Plan for OP2. 24/10/2023 2359\n"
+ + "❌ D 7. CS2101 Submit UG. 11/11/2023 2359\n"
+ + "❌ T 8. CS2103T Complete mid semester review form. \n"
+ + "❌ D 9. CS2103T Add demo screenshots to project notes. 20/11/2023 2359\n"
+ + "❌ D 10. CS2103T Release v1.3.trial jar file. 27/10/2023 2359\n"
+ + "❌ D 11. CS2103T Wrap up milestone 1.3. 03/11/2023 2359\n"
+ + "❌ D 12. CS2103T Finalise TP. 17/11/2023 2359\n"
+ + "❌ T 13. CS2103T Update DG for each feature. \n",
+ result.getFeedbackToUser());
+ }
+
+ @Test
+ public void execute_groupNotFound_throwsCommandException() {
+ int groupId = 2; // Invalid groupId
+ int taskIndex = 0;
+ MarkCommand markCommand = new MarkCommand(groupId, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId))));
+
+ assertThrows(CommandException.class, () -> markCommand.execute(model));
+ }
+
+ @Test
+ public void execute_invalidTaskIndex_throwsCommandException() {
+ int groupId = 1;
+ int taskIndex = 15; // Invalid task index
+ MarkCommand markCommand = new MarkCommand(groupId, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId))));
+
+ assertThrows(CommandException.class, () -> markCommand.execute(model));
+ }
+
+ @Test
+ public void execute_nullModel_throwsNullPointerException() {
+ int groupId = 1;
+ int taskIndex = 0;
+ MarkCommand markCommand = new MarkCommand(groupId, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId))));
+
+ assertThrows(NullPointerException.class, () -> markCommand.execute(null));
+ }
+
+ @Test
+ public void execute_negativeTaskIndex_throwsCommandException() {
+ int groupId = 1;
+ int taskIndex = -1; // Negative task index
+ MarkCommand markCommand = new MarkCommand(groupId, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId))));
+
+ assertThrows(CommandException.class, () -> markCommand.execute(model));
+ }
+
+ @Test
+ public void isValidTaskIndex_validTaskIndex_returnsTrue() {
+ int taskIndex = 1;
+ TaskList taskList = group.getTasks();
+ MarkCommand markCommand = new MarkCommand(1, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(1))));
+
+ boolean isValid = markCommand.isValidTaskIndex(taskIndex, taskList);
+
+ assertTrue(isValid);
+ }
+
+ @Test
+ public void isValidTaskIndex_invalidTaskIndex_returnsFalse() {
+ int taskIndex = 15; // Invalid task index
+ TaskList taskList = group.getTasks();
+ MarkCommand markCommand = new MarkCommand(1, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(1))));
+
+ boolean isValid = markCommand.isValidTaskIndex(taskIndex, taskList);
+
+ assertFalse(isValid);
+ }
+
+ @Test
+ public void equals_sameObject_returnsTrue() {
+ int groupId = 1;
+ int taskIndex = 0;
+ MarkCommand markCommand = new MarkCommand(groupId, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId))));
+
+ assertTrue(markCommand.equals(markCommand));
+ }
+
+ @Test
+ public void equals_differentObjectSameValues_returnsTrue() {
+ int groupId = 1;
+ int taskIndex = 0;
+ MarkCommand markCommand1 = new MarkCommand(groupId, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId))));
+ MarkCommand markCommand2 = new MarkCommand(groupId, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId))));
+
+ assertTrue(markCommand1.equals(markCommand2));
+ }
+
+ @Test
+ public void equals_differentObjectDifferentValues_returnsFalse() {
+ int groupId1 = 1;
+ int taskIndex1 = 0;
+ int groupId2 = 2;
+ int taskIndex2 = 1;
+ MarkCommand markCommand1 = new MarkCommand(groupId1, taskIndex1,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId1))));
+ MarkCommand markCommand2 = new MarkCommand(groupId2, taskIndex2,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId2))));
+
+ assertFalse(markCommand1.equals(markCommand2));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/TasksCommandTest.java b/src/test/java/seedu/address/logic/commands/TasksCommandTest.java
new file mode 100644
index 00000000000..2047079a9da
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/TasksCommandTest.java
@@ -0,0 +1,108 @@
+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.model.Model.PREDICATE_SHOW_ALL_GROUPS;
+
+import java.util.List;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.group.Group;
+import seedu.address.model.group.GroupContainsKeywordsPredicate;
+import seedu.address.model.tutorial.Tutorial;
+
+public class TasksCommandTest {
+
+ private Model model;
+ private Group group;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager();
+ group = new Group(1, new Tutorial("01"));
+ model.addGroup(group);
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ }
+
+ @Test
+ public void execute_validGroup_success() throws CommandException {
+ int groupId = 1;
+ TasksCommand tasksCommand = new TasksCommand(groupId,
+ new GroupContainsKeywordsPredicate(List.of(String.valueOf(groupId))));
+
+ CommandResult result = tasksCommand.execute(model);
+
+ assertEquals(String.format(TasksCommand.MESSAGE_SUCCESS, groupId)
+ + "\n❌ T 1. CS2101 Upload video of OP1. \n"
+ + "❌ T 2. CS2101 Complete peer review for OP2. \n"
+ + "❌ D 3. CS2101 Submit slides for OP2. 29/10/2023 2359\n"
+ + "❌ D 4. CS2101 Complete peer review. 02/11/2023 2359\n"
+ + "❌ T 5. CS2101 Research on the SCQA framework. \n"
+ + "❌ D 6. CS2101 Plan for OP2. 24/10/2023 2359\n"
+ + "❌ D 7. CS2101 Submit UG. 11/11/2023 2359\n"
+ + "❌ T 8. CS2103T Complete mid semester review form. \n"
+ + "❌ D 9. CS2103T Add demo screenshots to project notes. 20/11/2023 2359\n"
+ + "❌ D 10. CS2103T Release v1.3.trial jar file. 27/10/2023 2359\n"
+ + "❌ D 11. CS2103T Wrap up milestone 1.3. 03/11/2023 2359\n"
+ + "❌ D 12. CS2103T Finalise TP. 17/11/2023 2359\n"
+ + "❌ T 13. CS2103T Update DG for each feature. \n",
+ result.getFeedbackToUser());
+ }
+
+ @Test
+ public void execute_groupNotFound_throwsCommandException() {
+ int groupId = 999; // Non-existent group
+ TasksCommand tasksCommand = new TasksCommand(groupId,
+ new GroupContainsKeywordsPredicate(List.of(String.valueOf(groupId))));
+
+ assertThrows(CommandException.class, () -> tasksCommand.execute(model));
+ }
+
+ @Test
+ public void execute_nullModel_throwsNullPointerException() {
+ int groupId = 1;
+ TasksCommand tasksCommand = new TasksCommand(groupId,
+ new GroupContainsKeywordsPredicate(List.of(String.valueOf(groupId))));
+
+ assertThrows(NullPointerException.class, () -> tasksCommand.execute(null));
+ }
+
+ @Test
+ public void equals_sameObject_returnsTrue() {
+ int groupId = 1;
+ TasksCommand tasksCommand = new TasksCommand(groupId,
+ new GroupContainsKeywordsPredicate(List.of(String.valueOf(groupId))));
+
+ assertTrue(tasksCommand.equals(tasksCommand));
+ }
+
+ @Test
+ public void equals_differentObjectSameValues_returnsTrue() {
+ int groupId = 1;
+ TasksCommand tasksCommand1 = new TasksCommand(groupId,
+ new GroupContainsKeywordsPredicate(List.of(String.valueOf(groupId))));
+ TasksCommand tasksCommand2 = new TasksCommand(groupId,
+ new GroupContainsKeywordsPredicate(List.of(String.valueOf(groupId))));
+
+ assertTrue(tasksCommand1.equals(tasksCommand2));
+ }
+
+ @Test
+ public void equals_differentObjectDifferentValues_returnsFalse() {
+ int groupId1 = 1;
+ int groupId2 = 2;
+ TasksCommand tasksCommand1 = new TasksCommand(groupId1,
+ new GroupContainsKeywordsPredicate(List.of(String.valueOf(groupId1))));
+ TasksCommand tasksCommand2 = new TasksCommand(groupId2,
+ new GroupContainsKeywordsPredicate(List.of(String.valueOf(groupId2))));
+
+ assertFalse(tasksCommand1.equals(tasksCommand2));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/UnMarkCommandTest.java b/src/test/java/seedu/address/logic/commands/UnMarkCommandTest.java
new file mode 100644
index 00000000000..673f8d1b60f
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/UnMarkCommandTest.java
@@ -0,0 +1,189 @@
+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.model.Model.PREDICATE_SHOW_ALL_GROUPS;
+import static seedu.address.testutil.TypicalGroups.getTypicalAddressBook;
+
+import java.util.Arrays;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+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.group.Group;
+import seedu.address.model.group.GroupContainsKeywordsPredicate;
+import seedu.address.model.group.tasks.TaskList;
+import seedu.address.model.tutorial.Tutorial;
+
+public class UnMarkCommandTest {
+
+ private Group group;
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager();
+ group = new Group(1, new Tutorial("01"));
+ // No need to use TaskInitializer as tasks are automatically initialised when group is created.
+ model.addGroup(group);
+ model.updateFilteredGroupList(PREDICATE_SHOW_ALL_GROUPS);
+ }
+
+ @Test
+ public void execute_validTaskIndex_success() throws CommandException {
+ int groupId = 1;
+ int taskIndex = 0; // When printing message, CommandResult adds 1 to taskIndex.
+ UnMarkCommand unMarkCommand = new UnMarkCommand(1, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId))));
+
+ CommandResult result = unMarkCommand.execute(model);
+
+ assertEquals("Unmarked task number 1 for group 1\n"
+ + "❌ T 1. CS2101 Upload video of OP1. \n"
+ + "❌ T 2. CS2101 Complete peer review for OP2. \n"
+ + "❌ D 3. CS2101 Submit slides for OP2. 29/10/2023 2359\n"
+ + "❌ D 4. CS2101 Complete peer review. 02/11/2023 2359\n"
+ + "❌ T 5. CS2101 Research on the SCQA framework. \n"
+ + "❌ D 6. CS2101 Plan for OP2. 24/10/2023 2359\n"
+ + "❌ D 7. CS2101 Submit UG. 11/11/2023 2359\n"
+ + "❌ T 8. CS2103T Complete mid semester review form. \n"
+ + "❌ D 9. CS2103T Add demo screenshots to project notes. 20/11/2023 2359\n"
+ + "❌ D 10. CS2103T Release v1.3.trial jar file. 27/10/2023 2359\n"
+ + "❌ D 11. CS2103T Wrap up milestone 1.3. 03/11/2023 2359\n"
+ + "❌ D 12. CS2103T Finalise TP. 17/11/2023 2359\n"
+ + "❌ T 13. CS2103T Update DG for each feature. \n",
+ result.getFeedbackToUser());
+ }
+
+ @Test
+ public void execute_taskAlreadyUnmarked_success() throws CommandException {
+ int groupId = 1;
+ int taskIndex = 0; // Task is already unmarked
+ UnMarkCommand unMarkCommand = new UnMarkCommand(groupId, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId))));
+
+ CommandResult result = unMarkCommand.execute(model);
+
+ assertEquals("Unmarked task number 1 for group 1\n"
+ + "❌ T 1. CS2101 Upload video of OP1. \n"
+ + "❌ T 2. CS2101 Complete peer review for OP2. \n"
+ + "❌ D 3. CS2101 Submit slides for OP2. 29/10/2023 2359\n"
+ + "❌ D 4. CS2101 Complete peer review. 02/11/2023 2359\n"
+ + "❌ T 5. CS2101 Research on the SCQA framework. \n"
+ + "❌ D 6. CS2101 Plan for OP2. 24/10/2023 2359\n"
+ + "❌ D 7. CS2101 Submit UG. 11/11/2023 2359\n"
+ + "❌ T 8. CS2103T Complete mid semester review form. \n"
+ + "❌ D 9. CS2103T Add demo screenshots to project notes. 20/11/2023 2359\n"
+ + "❌ D 10. CS2103T Release v1.3.trial jar file. 27/10/2023 2359\n"
+ + "❌ D 11. CS2103T Wrap up milestone 1.3. 03/11/2023 2359\n"
+ + "❌ D 12. CS2103T Finalise TP. 17/11/2023 2359\n"
+ + "❌ T 13. CS2103T Update DG for each feature. \n",
+ result.getFeedbackToUser());
+ }
+
+ @Test
+ public void execute_groupNotFound_throwsCommandException() {
+ int groupId = 2; // Invalid groupId
+ int taskIndex = 0;
+ UnMarkCommand unMarkCommand = new UnMarkCommand(groupId, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId))));
+
+ assertThrows(CommandException.class, () -> unMarkCommand.execute(model));
+ }
+
+ @Test
+ public void execute_invalidTaskIndex_throwsCommandException() {
+ int groupId = 1;
+ int taskIndex = 15; // Invalid task index
+ UnMarkCommand unMarkCommand = new UnMarkCommand(groupId, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId))));
+
+ assertThrows(CommandException.class, () -> unMarkCommand.execute(model));
+ }
+
+ @Test
+ public void execute_negativeTaskIndex_throwsCommandException() {
+ int groupId = 1;
+ int taskIndex = -1; // Negative task index
+ UnMarkCommand unMarkCommand = new UnMarkCommand(groupId, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId))));
+
+ assertThrows(CommandException.class, () -> unMarkCommand.execute(model));
+ }
+
+ @Test
+ public void execute_nullModel_throwsNullPointerException() {
+ int groupId = 1;
+ int taskIndex = 0;
+ UnMarkCommand unMarkCommand = new UnMarkCommand(groupId, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId))));
+
+ assertThrows(NullPointerException.class, () -> unMarkCommand.execute(null));
+ }
+
+ @Test
+ public void isValidTaskIndex_validTaskIndex_returnsTrue() {
+ int taskIndex = 1;
+ TaskList taskList = group.getTasks();
+ UnMarkCommand unMarkCommand = new UnMarkCommand(1, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(1))));
+
+ boolean isValid = unMarkCommand.isValidTaskIndex(taskIndex, taskList);
+
+ assertTrue(isValid);
+ }
+
+ @Test
+ public void isValidTaskIndex_invalidTaskIndex_returnsFalse() {
+ int taskIndex = 15; // Invalid task index
+ TaskList taskList = group.getTasks();
+ UnMarkCommand unMarkCommand = new UnMarkCommand(1, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(1))));
+
+ boolean isValid = unMarkCommand.isValidTaskIndex(taskIndex, taskList);
+
+ assertFalse(isValid);
+ }
+
+ @Test
+ public void equals_sameObject_returnsTrue() {
+ int groupId = 1;
+ int taskIndex = 0;
+ UnMarkCommand unMarkCommand = new UnMarkCommand(groupId, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId))));
+
+ assertTrue(unMarkCommand.equals(unMarkCommand));
+ }
+
+ @Test
+ public void equals_differentObjectSameValues_returnsTrue() {
+ int groupId = 1;
+ int taskIndex = 0;
+ UnMarkCommand unMarkCommand1 = new UnMarkCommand(groupId, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId))));
+ UnMarkCommand unMarkCommand2 = new UnMarkCommand(groupId, taskIndex,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId))));
+
+ assertTrue(unMarkCommand1.equals(unMarkCommand2));
+ }
+
+ @Test
+ public void equals_differentObjectDifferentValues_returnsFalse() {
+ int groupId1 = 1;
+ int taskIndex1 = 0;
+ int groupId2 = 2;
+ int taskIndex2 = 1;
+ UnMarkCommand unMarkCommand1 = new UnMarkCommand(groupId1, taskIndex1,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId1))));
+ UnMarkCommand unMarkCommand2 = new UnMarkCommand(groupId2, taskIndex2,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupId2))));
+
+ assertFalse(unMarkCommand1.equals(unMarkCommand2));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java
index 5bc11d3cdaa..b5579804751 100644
--- a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java
@@ -1,33 +1,51 @@
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.DESCRIPTION_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.DESCRIPTION_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.GENDER_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.GENDER_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_DESCRIPTION_DESC;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_GENDER_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_MAJOR_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.INVALID_NATIONALITY_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_SM_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_TUTORIAL_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_YEAR_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.MAJOR_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.MAJOR_DESC_BOB;
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.NATIONALITY_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.NATIONALITY_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.SM_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.TUTORIAL_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_DESCRIPTION_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_GENDER_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_MAJOR_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.commands.CommandTestUtil.VALID_NATIONALITY_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_SM_GITHUB_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_SM_LINKEDIN_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TUT_FIRST_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TUT_SECOND_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_YEAR_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.YEAR_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.YEAR_DESC_BOB;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GENDER;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MAJOR;
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_NATIONALITY;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_YEAR;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
import static seedu.address.testutil.TypicalPersons.AMY;
@@ -37,12 +55,16 @@
import seedu.address.logic.Messages;
import seedu.address.logic.commands.AddCommand;
-import seedu.address.model.person.Address;
+import seedu.address.model.person.Description;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Gender;
+import seedu.address.model.person.Major;
import seedu.address.model.person.Name;
+import seedu.address.model.person.Nationality;
import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.person.Year;
+import seedu.address.model.socialmedialink.SocialMediaLink;
+import seedu.address.model.tutorial.Tutorial;
import seedu.address.testutil.PersonBuilder;
public class AddCommandParserTest {
@@ -50,47 +72,71 @@ public class AddCommandParserTest {
@Test
public void parse_allFieldsPresent_success() {
- Person expectedPerson = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND).build();
+ Person expectedPerson = new PersonBuilder(BOB)
+ .withSocialMediaLinks(VALID_SM_LINKEDIN_BOB, VALID_SM_GITHUB_BOB).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));
-
+ assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_BOB + MAJOR_DESC_BOB + YEAR_DESC_BOB
+ + EMAIL_DESC_BOB + DESCRIPTION_DESC_BOB + TUTORIAL_DESC_BOB + SM_DESC_BOB
+ + NATIONALITY_DESC_BOB + GENDER_DESC_BOB,
+ 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));
+ // multiple tutorials - all accepted
+ Person expectedPersonMultipleTutorials = new PersonBuilder(BOB)
+ .withTutorials(VALID_TUT_FIRST_BOB, VALID_TUT_SECOND_BOB).build();
+ assertParseSuccess(parser, NAME_DESC_BOB + MAJOR_DESC_BOB + YEAR_DESC_BOB + EMAIL_DESC_BOB
+ + DESCRIPTION_DESC_BOB + TUTORIAL_DESC_BOB + SM_DESC_BOB
+ + NATIONALITY_DESC_BOB + GENDER_DESC_BOB,
+ new AddCommand(expectedPersonMultipleTutorials));
+
+ // multiple social media links - all accepted
+ Person expectedPersonMultipleSocialMediaLinks = new PersonBuilder(BOB)
+ .withSocialMediaLinks(VALID_SM_LINKEDIN_BOB, VALID_SM_GITHUB_BOB).build();
+ assertParseSuccess(parser, NAME_DESC_BOB + MAJOR_DESC_BOB + YEAR_DESC_BOB + EMAIL_DESC_BOB
+ + DESCRIPTION_DESC_BOB + TUTORIAL_DESC_BOB + SM_DESC_BOB
+ + NATIONALITY_DESC_BOB + GENDER_DESC_BOB,
+ new AddCommand(expectedPersonMultipleSocialMediaLinks));
}
@Test
- public void parse_repeatedNonTagValue_failure() {
- String validExpectedPersonString = NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
- + ADDRESS_DESC_BOB + TAG_DESC_FRIEND;
-
+ public void parse_repeatedNonSocialMediaLinkValue_failure() {
+ String validExpectedPersonString = NAME_DESC_BOB + MAJOR_DESC_BOB + YEAR_DESC_BOB + EMAIL_DESC_BOB
+ + DESCRIPTION_DESC_BOB + TUTORIAL_DESC_BOB + SM_DESC_BOB + NATIONALITY_DESC_BOB + GENDER_DESC_BOB;
// 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 majors
+ assertParseFailure(parser, MAJOR_DESC_AMY + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_MAJOR));
+
+ // multiple years
+ assertParseFailure(parser, YEAR_DESC_AMY + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_YEAR));
// 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 descriptions
+ assertParseFailure(parser, DESCRIPTION_DESC_AMY + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_DESCRIPTION));
+
+ // multiple nationalities
+ assertParseFailure(parser, NATIONALITY_DESC_AMY + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NATIONALITY));
+
+ // multiple genders
+ assertParseFailure(parser, GENDER_DESC_AMY + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_GENDER));
// 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));
+ validExpectedPersonString + MAJOR_DESC_AMY + EMAIL_DESC_AMY + NAME_DESC_AMY
+ + YEAR_DESC_AMY + DESCRIPTION_DESC_AMY + NATIONALITY_DESC_BOB
+ + GENDER_DESC_BOB + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME, PREFIX_MAJOR, PREFIX_YEAR,
+ PREFIX_EMAIL, PREFIX_DESCRIPTION, PREFIX_NATIONALITY, PREFIX_GENDER));
// invalid value followed by valid value
@@ -98,17 +144,29 @@ public void parse_repeatedNonTagValue_failure() {
assertParseFailure(parser, INVALID_NAME_DESC + validExpectedPersonString,
Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
+ // invalid major
+ assertParseFailure(parser, INVALID_MAJOR_DESC + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_MAJOR));
+
+ // invalid year
+ assertParseFailure(parser, INVALID_YEAR_DESC + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_YEAR));
+
// 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 description
+ assertParseFailure(parser, INVALID_DESCRIPTION_DESC + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_DESCRIPTION));
- // invalid address
- assertParseFailure(parser, INVALID_ADDRESS_DESC + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
+ // invalid nationality
+ assertParseFailure(parser, INVALID_NATIONALITY_DESC + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NATIONALITY));
+
+ // invalid gender
+ assertParseFailure(parser, INVALID_GENDER_DESC + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_GENDER));
// valid value followed by invalid value
@@ -116,25 +174,38 @@ public void parse_repeatedNonTagValue_failure() {
assertParseFailure(parser, validExpectedPersonString + INVALID_NAME_DESC,
Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
+ // invalid major
+ assertParseFailure(parser, validExpectedPersonString + INVALID_MAJOR_DESC,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_MAJOR));
+
+ // invalid year
+ assertParseFailure(parser, validExpectedPersonString + INVALID_YEAR_DESC,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_YEAR));
+
// 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 description
+ assertParseFailure(parser, validExpectedPersonString + INVALID_DESCRIPTION_DESC,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_DESCRIPTION));
+
+ // invalid nationality
+ assertParseFailure(parser, validExpectedPersonString + INVALID_NATIONALITY_DESC,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NATIONALITY));
- // invalid address
- assertParseFailure(parser, validExpectedPersonString + INVALID_ADDRESS_DESC,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
+ // invalid gender
+ assertParseFailure(parser, validExpectedPersonString + INVALID_GENDER_DESC,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_GENDER));
}
@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));
+ // zero social media links
+ Person expectedPerson = new PersonBuilder(AMY).withTutorials().withSocialMediaLinks().build();
+ assertParseSuccess(parser, NAME_DESC_AMY + MAJOR_DESC_AMY + YEAR_DESC_AMY
+ + EMAIL_DESC_AMY + DESCRIPTION_DESC_AMY + NATIONALITY_DESC_AMY + GENDER_DESC_AMY,
+ new AddCommand(expectedPerson));
}
@Test
@@ -142,55 +213,110 @@ 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,
+ assertParseFailure(parser, VALID_NAME_BOB + MAJOR_DESC_BOB + YEAR_DESC_BOB + EMAIL_DESC_BOB
+ + DESCRIPTION_DESC_BOB + TUTORIAL_DESC_BOB + SM_DESC_BOB
+ + NATIONALITY_DESC_BOB + GENDER_DESC_BOB,
+ expectedMessage);
+
+ // missing major prefix
+ assertParseFailure(parser, NAME_DESC_BOB + VALID_MAJOR_BOB + YEAR_DESC_BOB + EMAIL_DESC_BOB
+ + DESCRIPTION_DESC_BOB + TUTORIAL_DESC_BOB + SM_DESC_BOB
+ + NATIONALITY_DESC_BOB + GENDER_DESC_BOB,
expectedMessage);
- // missing phone prefix
- assertParseFailure(parser, NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB,
+ // missing year prefix
+ assertParseFailure(parser, NAME_DESC_BOB + MAJOR_DESC_BOB + VALID_YEAR_BOB + EMAIL_DESC_BOB
+ + DESCRIPTION_DESC_BOB + TUTORIAL_DESC_BOB + SM_DESC_BOB
+ + NATIONALITY_DESC_BOB + GENDER_DESC_BOB,
expectedMessage);
// missing email prefix
- assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB + ADDRESS_DESC_BOB,
+ assertParseFailure(parser, NAME_DESC_BOB + MAJOR_DESC_BOB + YEAR_DESC_BOB + VALID_EMAIL_BOB
+ + DESCRIPTION_DESC_BOB + TUTORIAL_DESC_BOB + SM_DESC_BOB
+ + NATIONALITY_DESC_BOB + GENDER_DESC_BOB,
expectedMessage);
- // missing address prefix
- assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + VALID_ADDRESS_BOB,
+ // missing description prefix
+ assertParseFailure(parser, NAME_DESC_BOB + MAJOR_DESC_BOB + YEAR_DESC_BOB + EMAIL_DESC_BOB
+ + VALID_DESCRIPTION_BOB + TUTORIAL_DESC_BOB + SM_DESC_BOB
+ + NATIONALITY_DESC_BOB + GENDER_DESC_BOB,
expectedMessage);
+ // missing gender prefix
+ assertParseFailure(parser, NAME_DESC_BOB + MAJOR_DESC_BOB + YEAR_DESC_BOB + EMAIL_DESC_BOB
+ + DESCRIPTION_DESC_BOB + TUTORIAL_DESC_BOB + SM_DESC_BOB
+ + NATIONALITY_DESC_BOB + VALID_GENDER_BOB,
+ expectedMessage);
+
+ // missing nationality prefix
+ assertParseFailure(parser, NAME_DESC_BOB + MAJOR_DESC_BOB + YEAR_DESC_BOB + EMAIL_DESC_BOB
+ + DESCRIPTION_DESC_BOB + TUTORIAL_DESC_BOB + SM_DESC_BOB
+ + VALID_NATIONALITY_BOB + GENDER_DESC_BOB,
+ expectedMessage);
+
// all prefixes missing
- assertParseFailure(parser, VALID_NAME_BOB + VALID_PHONE_BOB + VALID_EMAIL_BOB + VALID_ADDRESS_BOB,
+ assertParseFailure(parser, VALID_NAME_BOB + VALID_MAJOR_BOB + VALID_YEAR_BOB + VALID_EMAIL_BOB
+ + VALID_DESCRIPTION_BOB + NATIONALITY_DESC_BOB + GENDER_DESC_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);
+ assertParseFailure(parser, INVALID_NAME_DESC + MAJOR_DESC_BOB + YEAR_DESC_BOB + EMAIL_DESC_BOB
+ + DESCRIPTION_DESC_BOB + TUTORIAL_DESC_BOB + SM_DESC_BOB
+ + NATIONALITY_DESC_BOB + GENDER_DESC_BOB, 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 major
+ assertParseFailure(parser, NAME_DESC_BOB + INVALID_MAJOR_DESC + YEAR_DESC_BOB + EMAIL_DESC_BOB
+ + DESCRIPTION_DESC_BOB + TUTORIAL_DESC_BOB + SM_DESC_BOB
+ + NATIONALITY_DESC_BOB + GENDER_DESC_BOB, Major.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 year
+ assertParseFailure(parser, NAME_DESC_BOB + MAJOR_DESC_BOB + INVALID_YEAR_DESC + EMAIL_DESC_BOB
+ + DESCRIPTION_DESC_BOB + TUTORIAL_DESC_BOB + SM_DESC_BOB
+ + NATIONALITY_DESC_BOB + GENDER_DESC_BOB, Year.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);
+ // invalid email
+ assertParseFailure(parser, NAME_DESC_BOB + MAJOR_DESC_BOB + YEAR_DESC_BOB + INVALID_EMAIL_DESC
+ + DESCRIPTION_DESC_BOB + TUTORIAL_DESC_BOB + SM_DESC_BOB
+ + NATIONALITY_DESC_BOB + GENDER_DESC_BOB, Email.MESSAGE_CONSTRAINTS);
+
+ // invalid description
+ assertParseFailure(parser, NAME_DESC_BOB + MAJOR_DESC_BOB + YEAR_DESC_BOB + EMAIL_DESC_BOB
+ + INVALID_DESCRIPTION_DESC + TUTORIAL_DESC_BOB + SM_DESC_BOB
+ + NATIONALITY_DESC_BOB + GENDER_DESC_BOB, Description.MESSAGE_CONSTRAINTS);
+
+ // invalid tutorial
+ assertParseFailure(parser, NAME_DESC_BOB + MAJOR_DESC_BOB + YEAR_DESC_BOB + EMAIL_DESC_BOB
+ + DESCRIPTION_DESC_BOB + INVALID_TUTORIAL_DESC + SM_DESC_BOB
+ + NATIONALITY_DESC_BOB + GENDER_DESC_BOB, Tutorial.MESSAGE_CONSTRAINTS);
+
+ // invalid social media
+ assertParseFailure(parser, NAME_DESC_BOB + MAJOR_DESC_BOB + YEAR_DESC_BOB + EMAIL_DESC_BOB
+ + DESCRIPTION_DESC_BOB + TUTORIAL_DESC_BOB + INVALID_SM_DESC
+ + NATIONALITY_DESC_BOB + GENDER_DESC_BOB, SocialMediaLink.MESSAGE_CONSTRAINTS);
+
+ // invalid gender
+ assertParseFailure(parser, NAME_DESC_BOB + MAJOR_DESC_BOB + YEAR_DESC_BOB + EMAIL_DESC_BOB
+ + DESCRIPTION_DESC_BOB + TUTORIAL_DESC_BOB + SM_DESC_BOB
+ + NATIONALITY_DESC_BOB + INVALID_GENDER_DESC, Gender.MESSAGE_CONSTRAINTS);
+
+ // invalid nationality
+ assertParseFailure(parser, NAME_DESC_BOB + MAJOR_DESC_BOB + YEAR_DESC_BOB + EMAIL_DESC_BOB
+ + DESCRIPTION_DESC_BOB + TUTORIAL_DESC_BOB + SM_DESC_BOB
+ + INVALID_NATIONALITY_DESC + GENDER_DESC_BOB, Nationality.MESSAGE_CONSTRAINTS);
// two invalid values, only first invalid value reported
- assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC,
+ assertParseFailure(parser, INVALID_NAME_DESC + MAJOR_DESC_BOB + YEAR_DESC_BOB + EMAIL_DESC_BOB
+ + INVALID_DESCRIPTION_DESC + TUTORIAL_DESC_BOB + SM_DESC_BOB
+ + NATIONALITY_DESC_BOB + GENDER_DESC_BOB,
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,
+ assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + MAJOR_DESC_BOB + YEAR_DESC_BOB
+ + EMAIL_DESC_BOB + DESCRIPTION_DESC_BOB + TUTORIAL_DESC_BOB + SM_DESC_BOB
+ + NATIONALITY_DESC_BOB + GENDER_DESC_BOB,
String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.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..aea2a887aab 100644
--- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
@@ -5,7 +5,7 @@
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
import static seedu.address.testutil.Assert.assertThrows;
-import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+import static seedu.address.testutil.TypicalEmails.EMAIL_FIRST_PERSON;
import java.util.Arrays;
import java.util.List;
@@ -19,12 +19,14 @@
import seedu.address.logic.commands.EditCommand;
import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
import seedu.address.logic.commands.ExitCommand;
+import seedu.address.logic.commands.FilterCommand;
import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.ListCommand;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.person.NameContainsKeywordsPredicate;
import seedu.address.model.person.Person;
+import seedu.address.model.tutorial.TutorialContainsSlotsPredicate;
import seedu.address.testutil.EditPersonDescriptorBuilder;
import seedu.address.testutil.PersonBuilder;
import seedu.address.testutil.PersonUtil;
@@ -49,19 +51,20 @@ public void parseCommand_clear() throws Exception {
@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);
+ DeleteCommand.COMMAND_WORD + " " + EMAIL_FIRST_PERSON);
+ assertEquals(new DeleteCommand(EMAIL_FIRST_PERSON), command);
}
@Test
public void parseCommand_edit() throws Exception {
- Person person = new PersonBuilder().build();
+ Person person = new PersonBuilder().withSocialMediaLinks("https://example.com").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);
+ + EMAIL_FIRST_PERSON + " " + PersonUtil.getEditPersonDescriptorDetails(descriptor));
+ assertEquals(new EditCommand(EMAIL_FIRST_PERSON, descriptor), command);
}
+
@Test
public void parseCommand_exit() throws Exception {
assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD) instanceof ExitCommand);
@@ -76,6 +79,14 @@ public void parseCommand_find() throws Exception {
assertEquals(new FindCommand(new NameContainsKeywordsPredicate(keywords)), command);
}
+ @Test
+ public void parseCommand_filter() throws Exception {
+ List slots = Arrays.asList("01", "02", "03");
+ FilterCommand command = (FilterCommand) parser.parseCommand(
+ FilterCommand.COMMAND_WORD + " " + slots.stream().collect(Collectors.joining(" ")));
+ assertEquals(new FilterCommand(new TutorialContainsSlotsPredicate(slots)), command);
+ }
+
@Test
public void parseCommand_help() throws Exception {
assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD) instanceof HelpCommand);
diff --git a/src/test/java/seedu/address/logic/parser/CheckCommandParserTest.java b/src/test/java/seedu/address/logic/parser/CheckCommandParserTest.java
new file mode 100644
index 00000000000..e1d43790363
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/CheckCommandParserTest.java
@@ -0,0 +1,32 @@
+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.CheckCommand;
+import seedu.address.model.group.GroupContainsKeywordsPredicate;
+
+public class CheckCommandParserTest {
+
+ private CheckCommandParser parser = new CheckCommandParser();
+
+ @Test
+ public void parse_emptyArg_throwsParseException() {
+ assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, CheckCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_validArgs_returnsCheckCommand() {
+ // no leading and trailing whitespaces
+ CheckCommand expectedCheckCommand = new CheckCommand(1, new GroupContainsKeywordsPredicate(Arrays.asList("1")));
+ assertParseSuccess(parser, "1", expectedCheckCommand);
+
+ // multiple whitespaces before and after group number
+ assertParseSuccess(parser, " \n 1 \n \t", expectedCheckCommand);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/CreateCommandParserTest.java b/src/test/java/seedu/address/logic/parser/CreateCommandParserTest.java
new file mode 100644
index 00000000000..a2e12304b72
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/CreateCommandParserTest.java
@@ -0,0 +1,46 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.testutil.Assert.assertThrows;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.CreateCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.tutorial.Tutorial;
+
+public class CreateCommandParserTest {
+
+ private CreateCommandParser parser = new CreateCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsCreateCommand() {
+ CommandParserTestUtil.assertParseSuccess(parser, " t/01", new CreateCommand(new Tutorial("01")));
+ }
+
+ @Test
+ public void parse_missingTutorialPrefix_throwsParseException() {
+ Assertions.assertThrows(ParseException.class, () -> parser.parse(" 01"));
+ }
+
+ @Test
+ public void parse_invalidTutorialNumber_throwsParseException() {
+ assertThrows(ParseException.class, () -> parser.parse(" t/40"));
+ }
+
+ @Test
+ public void parse_multipleTutorialNumber_throwsParseException() {
+ assertThrows(ParseException.class, () -> parser.parse(" t/01 t/02"));
+ }
+
+ @Test
+ public void parse_emptyArgument_throwsParseException() {
+ Assertions.assertThrows(ParseException.class, () -> parser.parse(""));
+ }
+
+ @Test
+ public void parse_nullArgument_throwsNullPointerException() {
+ Assertions.assertThrows(NullPointerException.class, () -> parser.parse(null));
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java
index 6a40e14a649..6d0821265ba 100644
--- a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java
@@ -3,11 +3,11 @@
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;
+import seedu.address.testutil.TypicalEmails;
/**
* As we are only doing white-box testing, our test cases do not cover path variations
@@ -22,7 +22,7 @@ public class DeleteCommandParserTest {
@Test
public void parse_validArgs_returnsDeleteCommand() {
- assertParseSuccess(parser, "1", new DeleteCommand(INDEX_FIRST_PERSON));
+ assertParseSuccess(parser, "first@u.nus.edu", new DeleteCommand(TypicalEmails.EMAIL_FIRST_PERSON));
}
@Test
diff --git a/src/test/java/seedu/address/logic/parser/DeleteGroupCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteGroupCommandParserTest.java
new file mode 100644
index 00000000000..32e55541a8e
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/DeleteGroupCommandParserTest.java
@@ -0,0 +1,48 @@
+package seedu.address.logic.parser;
+
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GROUP;
+import static seedu.address.testutil.Assert.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.DeleteGroupCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+public class DeleteGroupCommandParserTest {
+
+ private DeleteGroupCommandParser parser = new DeleteGroupCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsDeleteGroupCommand() {
+ // Test valid input
+ String userInput = " " + PREFIX_GROUP + "1";
+ int groupNumber = 1;
+ DeleteGroupCommand expectedCommand = new DeleteGroupCommand(groupNumber);
+
+ // Use assertDoesNotThrow to check if parsing is successful
+ assertDoesNotThrow(() -> {
+ DeleteGroupCommand actualCommand = parser.parse(userInput);
+
+ // Compare the groupNumber attribute directly
+ assertEquals(expectedCommand.getGroupNumber(), actualCommand.getGroupNumber());
+ });
+ }
+ @Test
+ public void parse_missingGroupPrefix_throwsParseException() {
+ // Test input without group prefix
+ String userInput = "1";
+ String expectedMessage = String.format("Invalid command format! \n" + DeleteGroupCommand.MESSAGE_USAGE);
+ CommandParserTestUtil.assertParseFailure(parser, userInput, expectedMessage);
+ }
+
+ @Test
+ public void parse_invalidGroupNumber_throwsParseException() {
+ // Test invalid group number
+ String userInput = " " + PREFIX_GROUP + "abc";
+ assertThrows(ParseException.class, () -> parser.parse(userInput));
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
index cc7175172d4..f36323ed77d 100644
--- a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
@@ -1,53 +1,76 @@
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.DESCRIPTION_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.DESCRIPTION_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.GENDER_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.GENDER_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_DESCRIPTION_DESC;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_GENDER_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_MAJOR_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.INVALID_NATIONALITY_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_SM_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_TUTORIAL_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_YEAR_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.MAJOR_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.MAJOR_DESC_BOB;
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.NATIONALITY_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.NATIONALITY_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.SM_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.SM_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.TUTORIAL_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.TUTORIAL_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_DESCRIPTION_AMY;
import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_GENDER_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_MAJOR_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.commands.CommandTestUtil.VALID_NATIONALITY_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_SM_GITHUB_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_SM_LINKEDIN_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TUT_FIRST_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TUT_SECOND_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_YEAR_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_YEAR_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.YEAR_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.YEAR_DESC_BOB;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
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.CliSyntax.PREFIX_GENDER;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MAJOR;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NATIONALITY;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SOCIAL_MEDIA_LINK;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_YEAR;
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 static seedu.address.testutil.TypicalEmails.EMAIL_FIRST_PERSON;
+import static seedu.address.testutil.TypicalEmails.EMAIL_SECOND_PERSON;
+import static seedu.address.testutil.TypicalEmails.EMAIL_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.Description;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Gender;
+import seedu.address.model.person.Major;
import seedu.address.model.person.Name;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.person.Nationality;
+import seedu.address.model.person.Year;
+import seedu.address.model.socialmedialink.SocialMediaLink;
+import seedu.address.model.tutorial.Tutorial;
import seedu.address.testutil.EditPersonDescriptorBuilder;
public class EditCommandParserTest {
- private static final String TAG_EMPTY = " " + PREFIX_TAG;
+ private static final String SOCIAL_MEDIA_LINK_EMPTY = " " + PREFIX_SOCIAL_MEDIA_LINK;
private static final String MESSAGE_INVALID_FORMAT =
String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE);
@@ -56,75 +79,88 @@ public class EditCommandParserTest {
@Test
public void parse_missingParts_failure() {
- // no index specified
+ // no email specified
assertParseFailure(parser, VALID_NAME_AMY, MESSAGE_INVALID_FORMAT);
// no field specified
- assertParseFailure(parser, "1", EditCommand.MESSAGE_NOT_EDITED);
+ assertParseFailure(parser, VALID_EMAIL_AMY, EditCommand.MESSAGE_NOT_EDITED);
- // no index and no field specified
+ // no email 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);
+ assertParseFailure(parser, "some random string", MESSAGE_INVALID_FORMAT);
// invalid prefix being parsed as preamble
- assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT);
+ assertParseFailure(parser, VALID_EMAIL_AMY + " 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);
+ assertParseFailure(parser, VALID_EMAIL_AMY + INVALID_NAME_DESC,
+ Name.MESSAGE_CONSTRAINTS); // invalid name
+ assertParseFailure(parser, VALID_EMAIL_AMY + INVALID_MAJOR_DESC,
+ Major.MESSAGE_CONSTRAINTS); // invalid major
+ assertParseFailure(parser, VALID_EMAIL_AMY + INVALID_YEAR_DESC,
+ Year.MESSAGE_CONSTRAINTS); // invalid year
+ assertParseFailure(parser, VALID_EMAIL_AMY + INVALID_EMAIL_DESC,
+ Email.MESSAGE_CONSTRAINTS); // invalid email
+ assertParseFailure(parser, VALID_EMAIL_AMY + INVALID_DESCRIPTION_DESC,
+ Description.MESSAGE_CONSTRAINTS); // invalid description
+ assertParseFailure(parser, VALID_EMAIL_AMY + INVALID_TUTORIAL_DESC,
+ Tutorial.MESSAGE_CONSTRAINTS); // invalid tutorial
+ assertParseFailure(parser, VALID_EMAIL_AMY + INVALID_SM_DESC,
+ SocialMediaLink.MESSAGE_CONSTRAINTS); // invalid social media link
+ assertParseFailure(parser, VALID_EMAIL_AMY + INVALID_GENDER_DESC,
+ Gender.MESSAGE_CONSTRAINTS); // invalid gender
+ assertParseFailure(parser, VALID_EMAIL_AMY + INVALID_NATIONALITY_DESC,
+ Nationality.MESSAGE_CONSTRAINTS); // invalid nationality
+
+
+
+ // invalid year followed by valid email
+ assertParseFailure(parser, VALID_EMAIL_AMY + INVALID_YEAR_DESC + EMAIL_DESC_AMY,
+ Year.MESSAGE_CONSTRAINTS);
+
+ // while parsing {@code PREFIX_SOCIAL_MEDIA_LINK} alone will reset the social media links of the {@code Person}
+ // being edited, parsing it together with a valid social media link results in error
+ assertParseFailure(parser, VALID_EMAIL_AMY + SM_DESC_AMY + SOCIAL_MEDIA_LINK_EMPTY,
+ SocialMediaLink.MESSAGE_CONSTRAINTS);
+ assertParseFailure(parser, VALID_EMAIL_AMY + SOCIAL_MEDIA_LINK_EMPTY + SM_DESC_AMY,
+ SocialMediaLink.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,
+ assertParseFailure(parser, VALID_EMAIL_AMY + INVALID_NAME_DESC + INVALID_EMAIL_DESC + VALID_MAJOR_AMY
+ + VALID_YEAR_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;
+ String userInput = EMAIL_SECOND_PERSON + YEAR_DESC_BOB + SM_DESC_AMY
+ + EMAIL_DESC_AMY + DESCRIPTION_DESC_AMY + NAME_DESC_AMY + MAJOR_DESC_AMY + TUTORIAL_DESC_AMY
+ + NATIONALITY_DESC_AMY + GENDER_DESC_AMY;
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);
+ .withMajor(VALID_MAJOR_AMY).withYear(VALID_YEAR_BOB).withEmail(VALID_EMAIL_AMY)
+ .withDescription(VALID_DESCRIPTION_AMY).withTutorials(VALID_TUT_FIRST_AMY, VALID_TUT_SECOND_AMY)
+ .withSocialMediaLinks(VALID_SM_GITHUB_AMY, VALID_SM_LINKEDIN_AMY)
+ .withNationality(VALID_NATIONALITY_AMY).withGender(VALID_GENDER_AMY).build();
+ EditCommand expectedCommand = new EditCommand(EMAIL_SECOND_PERSON, 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;
+ String userInput = EMAIL_FIRST_PERSON + YEAR_DESC_BOB + EMAIL_DESC_AMY;
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB)
+ EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withYear(VALID_YEAR_BOB)
.withEmail(VALID_EMAIL_AMY).build();
- EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
+ EditCommand expectedCommand = new EditCommand(EMAIL_FIRST_PERSON, descriptor);
assertParseSuccess(parser, userInput, expectedCommand);
}
@@ -132,34 +168,58 @@ public void parse_someFieldsSpecified_success() {
@Test
public void parse_oneFieldSpecified_success() {
// name
- Index targetIndex = INDEX_THIRD_PERSON;
- String userInput = targetIndex.getOneBased() + NAME_DESC_AMY;
+ String userInput = EMAIL_THIRD_PERSON + NAME_DESC_AMY;
EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY).build();
- EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
+ EditCommand expectedCommand = new EditCommand(EMAIL_THIRD_PERSON, 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);
+ // major
+ userInput = EMAIL_THIRD_PERSON + MAJOR_DESC_AMY;
+ descriptor = new EditPersonDescriptorBuilder().withMajor(VALID_MAJOR_AMY).build();
+ expectedCommand = new EditCommand(EMAIL_THIRD_PERSON, descriptor);
+ assertParseSuccess(parser, userInput, expectedCommand);
+
+ // year
+ userInput = EMAIL_THIRD_PERSON + YEAR_DESC_AMY;
+ descriptor = new EditPersonDescriptorBuilder().withYear(VALID_YEAR_AMY).build();
+ expectedCommand = new EditCommand(EMAIL_THIRD_PERSON, descriptor);
assertParseSuccess(parser, userInput, expectedCommand);
// email
- userInput = targetIndex.getOneBased() + EMAIL_DESC_AMY;
+ userInput = EMAIL_THIRD_PERSON + EMAIL_DESC_AMY;
descriptor = new EditPersonDescriptorBuilder().withEmail(VALID_EMAIL_AMY).build();
- expectedCommand = new EditCommand(targetIndex, descriptor);
+ expectedCommand = new EditCommand(EMAIL_THIRD_PERSON, 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);
+ // description
+ userInput = EMAIL_THIRD_PERSON + DESCRIPTION_DESC_AMY;
+ descriptor = new EditPersonDescriptorBuilder().withDescription(VALID_DESCRIPTION_AMY).build();
+ expectedCommand = new EditCommand(EMAIL_THIRD_PERSON, 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);
+ // tutorial
+ userInput = EMAIL_THIRD_PERSON + TUTORIAL_DESC_AMY;
+ descriptor = new EditPersonDescriptorBuilder().withTutorials(VALID_TUT_FIRST_AMY, VALID_TUT_SECOND_AMY).build();
+ expectedCommand = new EditCommand(EMAIL_THIRD_PERSON, descriptor);
+ assertParseSuccess(parser, userInput, expectedCommand);
+
+ // social media links
+ userInput = EMAIL_THIRD_PERSON + SM_DESC_AMY;
+ descriptor = new EditPersonDescriptorBuilder().withSocialMediaLinks(VALID_SM_LINKEDIN_AMY, VALID_SM_GITHUB_AMY)
+ .build();
+ expectedCommand = new EditCommand(EMAIL_THIRD_PERSON, descriptor);
+ assertParseSuccess(parser, userInput, expectedCommand);
+
+ // gender
+ userInput = EMAIL_THIRD_PERSON + GENDER_DESC_AMY;
+ descriptor = new EditPersonDescriptorBuilder().withGender(VALID_GENDER_AMY).build();
+ expectedCommand = new EditCommand(EMAIL_THIRD_PERSON, descriptor);
+ assertParseSuccess(parser, userInput, expectedCommand);
+
+ // nationality
+ userInput = EMAIL_THIRD_PERSON + NATIONALITY_DESC_AMY;
+ descriptor = new EditPersonDescriptorBuilder().withNationality(VALID_NATIONALITY_AMY).build();
+ expectedCommand = new EditCommand(EMAIL_THIRD_PERSON, descriptor);
assertParseSuccess(parser, userInput, expectedCommand);
}
@@ -169,40 +229,45 @@ public void parse_multipleRepeatedFields_failure() {
// AddCommandParserTest#parse_repeatedNonTagValue_failure()
// valid followed by invalid
- Index targetIndex = INDEX_FIRST_PERSON;
- String userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + PHONE_DESC_BOB;
+ String userInput = EMAIL_FIRST_PERSON + INVALID_YEAR_DESC + YEAR_DESC_BOB;
- assertParseFailure(parser, userInput, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
+ assertParseFailure(parser, userInput, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_YEAR));
// invalid followed by valid
- userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + INVALID_PHONE_DESC;
+ userInput = EMAIL_FIRST_PERSON + YEAR_DESC_BOB + INVALID_YEAR_DESC;
- assertParseFailure(parser, userInput, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
+ assertParseFailure(parser, userInput, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_YEAR));
- // 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;
+ // multiple valid fields repeated
+ userInput = EMAIL_FIRST_PERSON + YEAR_DESC_AMY + DESCRIPTION_DESC_AMY + EMAIL_DESC_AMY + MAJOR_DESC_AMY
+ + SM_DESC_AMY + YEAR_DESC_AMY + DESCRIPTION_DESC_AMY + EMAIL_DESC_AMY + SM_DESC_AMY + TUTORIAL_DESC_AMY
+ + GENDER_DESC_AMY + NATIONALITY_DESC_AMY
+ + YEAR_DESC_BOB + DESCRIPTION_DESC_BOB + EMAIL_DESC_BOB + SM_DESC_BOB + MAJOR_DESC_BOB
+ + TUTORIAL_DESC_BOB + GENDER_DESC_BOB + NATIONALITY_DESC_BOB;
assertParseFailure(parser, userInput,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS));
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_MAJOR, PREFIX_YEAR, PREFIX_EMAIL,
+ PREFIX_DESCRIPTION, PREFIX_NATIONALITY, PREFIX_GENDER));
// multiple invalid values
- userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + INVALID_ADDRESS_DESC + INVALID_EMAIL_DESC
- + INVALID_PHONE_DESC + INVALID_ADDRESS_DESC + INVALID_EMAIL_DESC;
+ userInput = EMAIL_FIRST_PERSON + INVALID_MAJOR_DESC + INVALID_YEAR_DESC + INVALID_DESCRIPTION_DESC
+ + INVALID_EMAIL_DESC + INVALID_MAJOR_DESC + INVALID_YEAR_DESC + INVALID_DESCRIPTION_DESC
+ + INVALID_TUTORIAL_DESC + INVALID_EMAIL_DESC + INVALID_TUTORIAL_DESC
+ + INVALID_GENDER_DESC + INVALID_NATIONALITY_DESC + INVALID_GENDER_DESC + INVALID_NATIONALITY_DESC;
assertParseFailure(parser, userInput,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS));
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_MAJOR, PREFIX_YEAR, PREFIX_EMAIL,
+ PREFIX_DESCRIPTION, PREFIX_NATIONALITY, PREFIX_GENDER));
}
@Test
- public void parse_resetTags_success() {
- Index targetIndex = INDEX_THIRD_PERSON;
- String userInput = targetIndex.getOneBased() + TAG_EMPTY;
+ public void parse_resetSocialMediaLinks_success() {
+ String userInput = EMAIL_THIRD_PERSON + SOCIAL_MEDIA_LINK_EMPTY;
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withTags().build();
- EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
+ EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withSocialMediaLinks().build();
+ EditCommand expectedCommand = new EditCommand(EMAIL_THIRD_PERSON, descriptor);
assertParseSuccess(parser, userInput, expectedCommand);
}
+
}
diff --git a/src/test/java/seedu/address/logic/parser/FilterCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FilterCommandParserTest.java
new file mode 100644
index 00000000000..46226f342f0
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/FilterCommandParserTest.java
@@ -0,0 +1,60 @@
+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.FilterCommand;
+import seedu.address.model.tutorial.Tutorial;
+import seedu.address.model.tutorial.TutorialContainsSlotsPredicate;
+
+public class FilterCommandParserTest {
+
+ private FilterCommandParser parser = new FilterCommandParser();
+
+ @Test
+ public void parse_emptyArg_throwsParseException() {
+ assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_invalidArg_throwsParseException() {
+ assertParseFailure(parser, "1", Tutorial.MESSAGE_CONSTRAINTS);
+ }
+
+ @Test
+ public void parse_invalidArgs_throwsParseException() {
+ assertParseFailure(parser, "1 2", Tutorial.MESSAGE_CONSTRAINTS);
+ }
+
+ @Test
+ public void parse_validAndInvalidArgs_throwsParseException() {
+ assertParseFailure(parser, "01 2", Tutorial.MESSAGE_CONSTRAINTS);
+ }
+
+ @Test
+ public void parse_validArg_returnsFilterCommand() {
+ // no leading and trailing whitespaces
+ FilterCommand expectedFilterCommand =
+ new FilterCommand(new TutorialContainsSlotsPredicate(Arrays.asList("01")));
+ assertParseSuccess(parser, "01", expectedFilterCommand);
+
+ // multiple whitespaces before and after slot
+ assertParseSuccess(parser, " \n 01 \n \t", expectedFilterCommand);
+ }
+
+ @Test
+ public void parse_validArgs_returnsFilterCommand() {
+ // no leading and trailing whitespaces
+ FilterCommand expectedFilterCommand =
+ new FilterCommand(new TutorialContainsSlotsPredicate(Arrays.asList("01", "02")));
+ assertParseSuccess(parser, "01 02", expectedFilterCommand);
+
+ // multiple whitespaces between slots
+ assertParseSuccess(parser, " \n 01 \n \t 02 \t", expectedFilterCommand);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/FilterGroupCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FilterGroupCommandParserTest.java
new file mode 100644
index 00000000000..7e7cf6a96fa
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/FilterGroupCommandParserTest.java
@@ -0,0 +1,37 @@
+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 org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.FilterGroupCommand;
+import seedu.address.model.group.GroupBelongsTutorialPredicate;
+import seedu.address.model.tutorial.Tutorial;
+
+public class FilterGroupCommandParserTest {
+
+ private FilterGroupCommandParser parser = new FilterGroupCommandParser();
+
+ @Test
+ public void parse_emptyArg_throwsParseException() {
+ assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ FilterGroupCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_invalidArg_throwsParseException() {
+ assertParseFailure(parser, "1", Tutorial.MESSAGE_CONSTRAINTS);
+ }
+
+ @Test
+ public void parse_validArg_returnsFilterGroupCommand() {
+ // no leading and trailing whitespaces
+ FilterGroupCommand expectedFilterGroupCommand = new FilterGroupCommand(new GroupBelongsTutorialPredicate("01"));
+ assertParseSuccess(parser, "01", expectedFilterGroupCommand);
+
+ // multiple whitespaces before and after slot
+ assertParseSuccess(parser, " \n 01 \n \t", expectedFilterGroupCommand);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/FindGroupCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FindGroupCommandParserTest.java
new file mode 100644
index 00000000000..e57b4655be1
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/FindGroupCommandParserTest.java
@@ -0,0 +1,60 @@
+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.FindGroupCommand;
+import seedu.address.model.group.GroupContainsKeywordsPredicate;
+
+public class FindGroupCommandParserTest {
+
+ private FindGroupCommandParser parser = new FindGroupCommandParser();
+
+ @Test
+ public void parse_emptyArg_throwsParseException() {
+ assertParseFailure(parser, " ",
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindGroupCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_invalidArg_throwsParseException() {
+ assertParseFailure(parser, "a", ParserUtil.MESSAGE_INVALID_GROUP_NUMBER);
+ }
+
+ @Test
+ public void parse_invalidArgs_throwsParseException() {
+ assertParseFailure(parser, "a b", ParserUtil.MESSAGE_INVALID_GROUP_NUMBER);
+ }
+
+ @Test
+ public void parse_validAndInvalidArgs_throwsParseException() {
+ assertParseFailure(parser, "1 b", ParserUtil.MESSAGE_INVALID_GROUP_NUMBER);
+ }
+
+ @Test
+ public void parse_validArg_returnsFindGroupCommand() {
+ // no leading and trailing whitespaces
+ FindGroupCommand expectedFindGroupCommand =
+ new FindGroupCommand(new GroupContainsKeywordsPredicate(Arrays.asList("1")));
+ assertParseSuccess(parser, "1", expectedFindGroupCommand);
+
+ // multiple whitespaces before and after keyword
+ assertParseSuccess(parser, " \n 1 \n \t", expectedFindGroupCommand);
+ }
+
+ @Test
+ public void parse_validArgs_returnsFindGroupCommand() {
+ // no leading and trailing whitespaces
+ FindGroupCommand expectedFindGroupCommand =
+ new FindGroupCommand(new GroupContainsKeywordsPredicate(Arrays.asList("1", "2")));
+ assertParseSuccess(parser, "1 2", expectedFindGroupCommand);
+
+ // multiple whitespaces between keywords
+ assertParseSuccess(parser, " \n 1 \n \t 2 \t", expectedFindGroupCommand);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/JoinCommandParserTest.java b/src/test/java/seedu/address/logic/parser/JoinCommandParserTest.java
new file mode 100644
index 00000000000..2e05603a564
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/JoinCommandParserTest.java
@@ -0,0 +1,66 @@
+package seedu.address.logic.parser;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GROUP;
+import static seedu.address.testutil.Assert.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.JoinCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.Email;
+
+public class JoinCommandParserTest {
+
+ private JoinCommandParser parser = new JoinCommandParser();
+
+ @Test
+ public void parse_validInput_success() throws ParseException {
+ String userInput = " " + PREFIX_EMAIL + "johnd@u.nus.edu " + PREFIX_GROUP + "1";
+ JoinCommand expectedCommand = new JoinCommand(new Email("johnd@u.nus.edu"), 1);
+ assertEquals(expectedCommand, parser.parse(userInput));
+ }
+
+ @Test
+ public void parse_missingEmailPrefix_throwsParseException() {
+ String userInput = " johnd@u.nus.edu " + PREFIX_GROUP + "1";
+ assertThrows(ParseException.class, () -> parser.parse(userInput));
+ }
+
+ @Test
+ public void parse_missingGroupPrefix_throwsParseException() {
+ String userInput = PREFIX_EMAIL + "johnd@u.nus.edu " + "1";
+ assertThrows(ParseException.class, () -> parser.parse(userInput));
+ }
+
+ @Test
+ public void parse_invalidEmail_throwsParseException() {
+ String userInput = PREFIX_EMAIL + "johnd" + " " + PREFIX_GROUP + "1";
+ assertThrows(ParseException.class, () -> parser.parse(userInput));
+ }
+
+ @Test
+ public void parse_invalidGroupNumber_throwsParseException() {
+ String userInput = PREFIX_EMAIL + "johnd@u.nus.edu " + PREFIX_GROUP + "one";
+ assertThrows(ParseException.class, () -> parser.parse(userInput));
+ }
+
+ @Test
+ public void parse_missingGroupNumber_throwsParseException() {
+ String userInput = PREFIX_EMAIL + "johnd@u.nus.edu " + PREFIX_GROUP;
+ assertThrows(ParseException.class, () -> parser.parse(userInput));
+ }
+
+ @Test
+ public void parse_missingEmail_throwsParseException() {
+ String userInput = " " + PREFIX_EMAIL + " " + PREFIX_GROUP + "1";
+ assertThrows(ParseException.class, () -> parser.parse(userInput));
+ }
+
+ @Test
+ public void parse_emptyArguments_throwsParseException() {
+ String userInput = "";
+ assertThrows(ParseException.class, () -> parser.parse(userInput));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/LeaveCommandParserTest.java b/src/test/java/seedu/address/logic/parser/LeaveCommandParserTest.java
new file mode 100644
index 00000000000..7868fc409d6
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/LeaveCommandParserTest.java
@@ -0,0 +1,67 @@
+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.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GROUP;
+import static seedu.address.testutil.Assert.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.LeaveCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.Email;
+
+
+
+public class LeaveCommandParserTest {
+
+ private LeaveCommandParser parser = new LeaveCommandParser();
+
+ @Test
+ public void parse_validInput_success() throws ParseException {
+ // Valid input with email and group number
+ String userInput = " " + PREFIX_EMAIL + "johnd@u.nus.edu " + PREFIX_GROUP + "1";
+ LeaveCommand expectedCommand = new LeaveCommand(new Email("johnd@u.nus.edu"), 1);
+ assertEquals(expectedCommand, parser.parse(userInput));
+ }
+
+ @Test
+ public void parse_missingEmailPrefix_failure() {
+ // Missing email prefix
+ String userInput = PREFIX_GROUP + "1";
+ assertThrows(ParseException.class, () -> parser.parse(userInput));
+ }
+
+ @Test
+ public void parse_missingGroupPrefix_failure() {
+ // Missing group prefix
+ String userInput = PREFIX_EMAIL + "johnd@u.nus.edu";
+ assertThrows(ParseException.class, () -> parser.parse(userInput));
+ }
+
+ @Test
+ public void parse_invalidEmail_failure() {
+ // Invalid email
+ String userInput = PREFIX_EMAIL + "johnd" + " " + PREFIX_GROUP + "1";
+ assertThrows(ParseException.class, () -> parser.parse(userInput));
+ }
+
+ @Test
+ public void parse_invalidGroupNumber_failure() {
+ // Invalid group number
+ String userInput = PREFIX_EMAIL + "johnd@u.nus.edu " + PREFIX_GROUP + "group";
+ assertThrows(ParseException.class, () -> parser.parse(userInput));
+ }
+
+ @Test
+ public void parse_invalidEmailFormat_throwsParseExceptionWithProperMessage() {
+ LeaveCommandParser parser = new LeaveCommandParser();
+ String userInput = PREFIX_EMAIL + "invalid_email_format " + PREFIX_GROUP + "1";
+
+ assertThrows(ParseException.class, String.format(
+ MESSAGE_INVALID_COMMAND_FORMAT, LeaveCommand.MESSAGE_USAGE), () -> {
+ parser.parse(userInput);
+ });
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/MarkCommandParserTest.java b/src/test/java/seedu/address/logic/parser/MarkCommandParserTest.java
new file mode 100644
index 00000000000..ede1eed5e1a
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/MarkCommandParserTest.java
@@ -0,0 +1,66 @@
+package seedu.address.logic.parser;
+
+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 org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.MarkCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+public class MarkCommandParserTest {
+ private MarkCommandParser parser;
+
+ @BeforeEach
+ public void setUp() {
+ parser = new MarkCommandParser();
+ }
+
+ @Test
+ public void parse_validInputWithDifferentGroupAndTaskIndices_success() throws ParseException {
+ assertEquals(parser.parse("gr/2 ti/3"), new MarkCommand(2, 2, null));
+ assertEquals(parser.parse("gr/3 ti/1"), new MarkCommand(3, 0, null));
+ assertEquals(parser.parse("gr/4 ti/4"), new MarkCommand(4, 3, null));
+ }
+
+ @Test
+ public void parse_invalidGroupNumber_throwsParseException() {
+ assertThrows(ParseException.class, () -> parser.parse("gr/0 ti/2"));
+ assertThrows(ParseException.class, () -> parser.parse("gr/-1 ti/2"));
+ assertThrows(ParseException.class, () -> parser.parse("gr/not_a_number ti/2"));
+ }
+
+ @Test
+ public void parse_invalidTaskIndex_throwsParseException() {
+ assertThrows(ParseException.class, () -> parser.parse("gr/1 ti/0"));
+ assertThrows(ParseException.class, () -> parser.parse("gr/1 ti/-1"));
+ assertThrows(ParseException.class, () -> parser.parse("gr/1 ti/not_a_number"));
+ }
+
+ @Test
+ public void parse_missingGroupOrTaskPrefix_throwsParseException() {
+ assertThrows(ParseException.class, () -> parser.parse("1 ti/2"));
+ assertThrows(ParseException.class, () -> parser.parse("gr/1 2"));
+ }
+
+ @Test
+ public void parse_nullArgs_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> parser.parse(null));
+ }
+
+ @Test
+ public void equals_sameObject_returnsTrue() {
+ assertTrue(parser.equals(parser));
+ }
+
+ @Test
+ public void equals_differentObject_returnsFalse() {
+ UnMarkCommandParser parser1 = new UnMarkCommandParser();
+ UnMarkCommandParser parser2 = new UnMarkCommandParser();
+
+ assertFalse(parser1.equals(parser2));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
index 4256788b1a7..ac1932abc51 100644
--- a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
+++ b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
@@ -2,58 +2,69 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
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.TypicalEmails.EMAIL_FIRST_PERSON;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.Test;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.Address;
+import seedu.address.model.person.Description;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Gender;
+import seedu.address.model.person.Major;
import seedu.address.model.person.Name;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.person.Nationality;
+import seedu.address.model.person.Year;
+import seedu.address.model.socialmedialink.SocialMediaLink;
+import seedu.address.model.tutorial.Tutorial;
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_MAJOR = "Computer Games";
+ private static final String INVALID_YEAR = "1.5";
+ private static final String INVALID_DESCRIPTION = " ";
private static final String INVALID_EMAIL = "example.com";
- private static final String INVALID_TAG = "#friend";
-
+ private static final String INVALID_TUTORIAL = "1";
+ private static final String INVALID_SM = "#www.invalid.com";
+ private static final String INVALID_GENDER = "a";
+ private static final String INVALID_NATIONALITY = "abc";
+ private static final String INVALID_GROUP_NUMBER = "a";
+ private static final String INVALID_TASK_INDEX_ZERO = "0";
+ private static final String INVALID_TASK_INDEX_NEGATIVE = "-1";
+ private static final String INVALID_TASK_INDEX_NON_NUMERIC = "abc";
private static final String VALID_NAME = "Rachel Walker";
- private static final String VALID_PHONE = "123456";
- 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_MAJOR = "Computer Science";
+ private static final String VALID_YEAR = "2";
+ private static final String VALID_DESCRIPTION = "Web Developer";
+ private static final String VALID_EMAIL = "rachel@u.nus.edu";
+ private static final String VALID_TUTORIAL_FIRST = "01";
+ private static final String VALID_TUTORIAL_SECOND = "02";
+ private static final String VALID_SM_LINKEDIN = "https://www.linkedin.com/in/rachel";
+ private static final String VALID_SM_GITHUB = "https://github.com/rachel";
+ private static final String VALID_GENDER = "f";
+ private static final String VALID_NATIONALITY = "local";
+ private static final String VALID_GROUP_NUMBER = "1";
+ private static final String VALID_TASK_INDEX = "5";
private static final String WHITESPACE = " \t\r\n";
@Test
- public void parseIndex_invalidInput_throwsParseException() {
- assertThrows(ParseException.class, () -> ParserUtil.parseIndex("10 a"));
- }
-
- @Test
- public void parseIndex_outOfRangeInput_throwsParseException() {
- assertThrows(ParseException.class, MESSAGE_INVALID_INDEX, ()
- -> ParserUtil.parseIndex(Long.toString(Integer.MAX_VALUE + 1)));
+ public void parseEmail_invalidInput_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseEmail(INVALID_EMAIL));
}
@Test
- public void parseIndex_validInput_success() throws Exception {
+ public void parseEmail_validInput_success() throws Exception {
// No whitespaces
- assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex("1"));
+ assertEquals(EMAIL_FIRST_PERSON, ParserUtil.parseEmail("first@u.nus.edu"));
// Leading and trailing whitespaces
- assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex(" 1 "));
+ assertEquals(EMAIL_FIRST_PERSON, ParserUtil.parseEmail(" first@u.nus.edu "));
}
@Test
@@ -80,49 +91,72 @@ public void parseName_validValueWithWhitespace_returnsTrimmedName() throws Excep
}
@Test
- public void parsePhone_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> ParserUtil.parsePhone((String) null));
+ public void parseMajor_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseMajor((String) null));
+ }
+
+ @Test
+ public void parseMajor_invalidValue_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseMajor(INVALID_MAJOR));
+ }
+
+ @Test
+ public void parseMajor_validValueWithoutWhitespace_returnsMajor() throws Exception {
+ Major expectedMajor = new Major(VALID_MAJOR);
+ assertEquals(expectedMajor, ParserUtil.parseMajor(VALID_MAJOR));
+ }
+
+ @Test
+ public void parseMajor_validValueWithWhitespace_returnsTrimmedMajor() throws Exception {
+ String majorWithWhitespace = WHITESPACE + VALID_MAJOR + WHITESPACE;
+ Major expectedMajor = new Major(VALID_MAJOR);
+ assertEquals(expectedMajor, ParserUtil.parseMajor(majorWithWhitespace));
+ }
+
+ @Test
+ public void parseYear_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseYear((String) null));
}
@Test
- public void parsePhone_invalidValue_throwsParseException() {
- assertThrows(ParseException.class, () -> ParserUtil.parsePhone(INVALID_PHONE));
+ public void parseYear_invalidValue_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseYear(INVALID_YEAR));
}
@Test
- public void parsePhone_validValueWithoutWhitespace_returnsPhone() throws Exception {
- Phone expectedPhone = new Phone(VALID_PHONE);
- assertEquals(expectedPhone, ParserUtil.parsePhone(VALID_PHONE));
+ public void parseYear_validValueWithoutWhitespace_returnsYear() throws Exception {
+ Year expectedYear = new Year(VALID_YEAR);
+ assertEquals(expectedYear, ParserUtil.parseYear(VALID_YEAR));
}
@Test
- public void parsePhone_validValueWithWhitespace_returnsTrimmedPhone() throws Exception {
- String phoneWithWhitespace = WHITESPACE + VALID_PHONE + WHITESPACE;
- Phone expectedPhone = new Phone(VALID_PHONE);
- assertEquals(expectedPhone, ParserUtil.parsePhone(phoneWithWhitespace));
+ public void parseYear_validValueWithWhitespace_returnsTrimmedYear() throws Exception {
+ String yearWithWhitespace = WHITESPACE + VALID_YEAR + WHITESPACE;
+ Year expectedYear = new Year(VALID_YEAR);
+ assertEquals(expectedYear, ParserUtil.parseYear(yearWithWhitespace));
}
@Test
- public void parseAddress_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> ParserUtil.parseAddress((String) null));
+ public void parseDescription_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseDescription((String) null));
}
@Test
- public void parseAddress_invalidValue_throwsParseException() {
- assertThrows(ParseException.class, () -> ParserUtil.parseAddress(INVALID_ADDRESS));
+ public void parseDescription_invalidValue_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseDescription(INVALID_DESCRIPTION));
}
@Test
- public void parseAddress_validValueWithoutWhitespace_returnsAddress() throws Exception {
- Address expectedAddress = new Address(VALID_ADDRESS);
- assertEquals(expectedAddress, ParserUtil.parseAddress(VALID_ADDRESS));
+ public void parseDescription_validValueWithoutWhitespace_returnsDescription() throws Exception {
+ Description expectedDescription = new Description(VALID_DESCRIPTION);
+ assertEquals(expectedDescription, ParserUtil.parseDescription(VALID_DESCRIPTION));
}
@Test
- public void parseAddress_validValueWithWhitespace_returnsTrimmedAddress() throws Exception {
- String addressWithWhitespace = WHITESPACE + VALID_ADDRESS + WHITESPACE;
- Address expectedAddress = new Address(VALID_ADDRESS);
- assertEquals(expectedAddress, ParserUtil.parseAddress(addressWithWhitespace));
+ public void parseDescription_validValueWithWhitespace_returnsTrimmedDescription() throws Exception {
+ String descriptionWithWhitespace = WHITESPACE + VALID_DESCRIPTION + WHITESPACE;
+ Description expectedDescription = new Description(VALID_DESCRIPTION);
+ assertEquals(expectedDescription, ParserUtil.parseDescription(descriptionWithWhitespace));
}
@Test
@@ -149,48 +183,228 @@ public void parseEmail_validValueWithWhitespace_returnsTrimmedEmail() throws Exc
}
@Test
- public void parseTag_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> ParserUtil.parseTag(null));
+ public void parseTutorial_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseTutorial(null));
+ }
+
+ @Test
+ public void parseTutorial_invalidValue_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseTutorial(INVALID_TUTORIAL));
}
@Test
- public void parseTag_invalidValue_throwsParseException() {
- assertThrows(ParseException.class, () -> ParserUtil.parseTag(INVALID_TAG));
+ public void parseTutorial_validValueWithoutWhitespace_returnsTutorial() throws Exception {
+ Tutorial expectedTutorial = new Tutorial(VALID_TUTORIAL_FIRST);
+ assertEquals(expectedTutorial, ParserUtil.parseTutorial(VALID_TUTORIAL_FIRST));
}
@Test
- public void parseTag_validValueWithoutWhitespace_returnsTag() throws Exception {
- Tag expectedTag = new Tag(VALID_TAG_1);
- assertEquals(expectedTag, ParserUtil.parseTag(VALID_TAG_1));
+ public void parseTutorial_validValueWithWhitespace_returnsTrimmedTutorial() throws Exception {
+ String tutorialWithWhitespace = WHITESPACE + VALID_TUTORIAL_FIRST + WHITESPACE;
+ Tutorial expectedTutorial = new Tutorial(VALID_TUTORIAL_FIRST);
+ assertEquals(expectedTutorial, ParserUtil.parseTutorial(tutorialWithWhitespace));
}
@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 parseTutorials_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseTutorials(null));
}
@Test
- public void parseTags_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> ParserUtil.parseTags(null));
+ public void parseTutorials_collectionWithInvalidTutorials_throwsParseException() {
+ assertThrows(ParseException.class, () ->
+ ParserUtil.parseTutorials(Arrays.asList(VALID_TUTORIAL_FIRST, INVALID_TUTORIAL)));
}
@Test
- public void parseTags_collectionWithInvalidTags_throwsParseException() {
- assertThrows(ParseException.class, () -> ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, INVALID_TAG)));
+ public void parseTutorials_emptyCollection_returnsEmptySet() throws Exception {
+ assertTrue(ParserUtil.parseTutorials(Collections.emptyList()).isEmpty());
}
@Test
- public void parseTags_emptyCollection_returnsEmptySet() throws Exception {
- assertTrue(ParserUtil.parseTags(Collections.emptyList()).isEmpty());
+ public void parseTutorials_collectionWithValidTutorials_returnsTutorialSet() throws Exception {
+ Set actualTutorialSet =
+ ParserUtil.parseTutorials(Arrays.asList(VALID_TUTORIAL_FIRST, VALID_TUTORIAL_SECOND));
+ Set expectedTutorialSet =
+ new HashSet(Arrays.asList(new Tutorial(VALID_TUTORIAL_FIRST),
+ new Tutorial(VALID_TUTORIAL_SECOND)));
+
+ assertEquals(expectedTutorialSet, actualTutorialSet);
}
@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 parseSocialMediaLink_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseSocialMediaLink(null));
+ }
+
+ @Test
+ public void parseSocialMediaLink_invalidValue_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseSocialMediaLink(INVALID_SM));
+ }
+
+ @Test
+ public void parseSocialMediaLink_validValueWithoutWhitespace_returnsSocialMediaLink() throws Exception {
+ SocialMediaLink expectedSocialMediaLink = new SocialMediaLink(VALID_SM_LINKEDIN);
+ assertEquals(expectedSocialMediaLink, ParserUtil.parseSocialMediaLink(VALID_SM_LINKEDIN));
+ }
+
+ @Test
+ public void parseSocialMediaLink_validValueWithWhitespace_returnsTrimmedSocialMediaLink() throws Exception {
+ String socialMediaLinkWithWhitespace = WHITESPACE + VALID_SM_LINKEDIN + WHITESPACE;
+ SocialMediaLink expectedSocialMediaLink = new SocialMediaLink(VALID_SM_LINKEDIN);
+ assertEquals(expectedSocialMediaLink, ParserUtil.parseSocialMediaLink(socialMediaLinkWithWhitespace));
+ }
+
+ @Test
+ public void parseSocialMediaLinks_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseSocialMediaLinks(null));
+ }
+
+ @Test
+ public void parseSocialMediaLinks_collectionWithInvalidSocialMediaLinks_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseSocialMediaLinks(
+ Arrays.asList(VALID_SM_LINKEDIN, INVALID_SM)));
+ }
+
+ @Test
+ public void parseSocialMediaLinks_emptyCollection_returnsEmptySet() throws Exception {
+ assertTrue(ParserUtil.parseSocialMediaLinks(Collections.emptyList()).isEmpty());
+ }
+
+ @Test
+ public void parseSocialMediaLinks_collectionWithValidSocialMediaLinks_returnsSocialMediaLinkSet() throws Exception {
+ Set actualSocialMediaLinkSet = ParserUtil.parseSocialMediaLinks(
+ Arrays.asList(VALID_SM_LINKEDIN, VALID_SM_GITHUB));
+ Set expectedSocialMediaLinkSet = new HashSet(
+ Arrays.asList(new SocialMediaLink(VALID_SM_LINKEDIN), new SocialMediaLink(VALID_SM_GITHUB)));
+
+ assertEquals(expectedSocialMediaLinkSet, actualSocialMediaLinkSet);
+ }
+
+ @Test
+ public void parseTutorials_validInput_success() throws Exception {
+ Set validTutorials = new HashSet<>(Arrays.asList("02", "05"));
+ Set expectedTutorialSet = new HashSet<>(Arrays.asList(new Tutorial("02"), new Tutorial("05")));
+
+ Set actualTutorialSet = ParserUtil.parseTutorials(validTutorials);
+
+ assertEquals(expectedTutorialSet, actualTutorialSet);
+ }
+
+ @Test
+ public void parseTutorials_invalidInput_throwsParseException() {
+ List invalidTutorials = Arrays.asList("25", "T02");
+ for (String invalidTutorial : invalidTutorials) {
+ assertThrows(ParseException.class, () ->
+ ParserUtil.parseTutorials(Collections.singletonList(invalidTutorial)));
+ }
+ }
+
+ @Test
+ public void parseTutorials_duplicateInput_singleInstanceStored() throws Exception {
+ List duplicateTutorials = Arrays.asList("01", "01", "02", "02");
+ Set expectedTutorials = new HashSet<>(Arrays.asList(new Tutorial("01"), new Tutorial("02")));
+
+ Set actualTutorials = ParserUtil.parseTutorials(duplicateTutorials);
- assertEquals(expectedTagSet, actualTagSet);
+ assertEquals(expectedTutorials, actualTutorials);
+ }
+
+ @Test
+ public void parseTaskIndex_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseTaskIndex((String) null));
+ }
+
+ @Test
+ public void parseTaskIndex_invalidValueZero_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseTaskIndex(INVALID_TASK_INDEX_ZERO));
+ }
+
+ @Test
+ public void parseTaskIndex_invalidValueNegative_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseTaskIndex(INVALID_TASK_INDEX_NEGATIVE));
+ }
+
+ @Test
+ public void parseTaskIndex_invalidValueNonNumeric_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseTaskIndex(INVALID_TASK_INDEX_NON_NUMERIC));
+ }
+
+ @Test
+ public void parseTaskIndex_validValueWithoutWhitespace_returnsTaskIndex() throws Exception {
+ assertEquals(5, ParserUtil.parseTaskIndex(VALID_TASK_INDEX));
+ }
+
+ @Test
+ public void parseTaskIndex_validValueWithWhitespace_returnsTrimmedTaskIndex() throws Exception {
+ String taskIndexWithWhitespace = WHITESPACE + VALID_TASK_INDEX + WHITESPACE;
+ assertEquals(5, ParserUtil.parseTaskIndex(taskIndexWithWhitespace));
+ }
+
+ @Test
+ public void parseNationality_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseNationality((String) null));
+ }
+
+ @Test
+ public void parseNationality_invalidValue_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseNationality(INVALID_NATIONALITY));
+ }
+
+ @Test
+ public void parseNationality_validValueWithoutWhitespace_returnsNationality() throws Exception {
+ Nationality expectedNationality = new Nationality(VALID_NATIONALITY);
+ assertEquals(expectedNationality, ParserUtil.parseNationality(VALID_NATIONALITY));
+ }
+
+ @Test
+ public void parseNationality_validValueWithWhitespace_returnsTrimmedNationality() throws Exception {
+ String nationalityWithWhitespace = WHITESPACE + VALID_NATIONALITY + WHITESPACE;
+ Nationality expectedNationality = new Nationality(VALID_NATIONALITY);
+ assertEquals(expectedNationality, ParserUtil.parseNationality(nationalityWithWhitespace));
+ }
+
+ @Test
+ public void parseGender_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseGender((String) null));
+ }
+
+ @Test
+ public void parseGender_invalidValue_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseGender(INVALID_GENDER));
+ }
+
+ @Test
+ public void parseGender_validValueWithoutWhitespace_returnsGender() throws Exception {
+ Gender expectedGender = new Gender(VALID_GENDER);
+ assertEquals(expectedGender, ParserUtil.parseGender(VALID_GENDER));
+ }
+
+ @Test
+ public void parseGender_validValueWithWhitespace_returnsTrimmedGender() throws Exception {
+ String genderWithWhitespace = WHITESPACE + VALID_GENDER + WHITESPACE;
+ Gender expectedGender = new Gender(VALID_GENDER);
+ assertEquals(expectedGender, ParserUtil.parseGender(genderWithWhitespace));
+ }
+
+ @Test
+ public void parseGroupNumber_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseGroupNumber((String) null));
+ }
+
+ @Test
+ public void parseGroupNumber_invalidValue_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseGroupNumber(INVALID_GROUP_NUMBER));
+ }
+
+ @Test
+ public void parseGroupNumber_validValueWithoutWhitespace_returnsGroupNumber() throws Exception {
+ assertEquals(1, ParserUtil.parseGroupNumber(VALID_GROUP_NUMBER));
+ }
+
+ @Test
+ public void parseGroupNumber_validValueWithWhitespace_returnsTrimmedGroupNumber() throws Exception {
+ String groupNumberWithWhitespace = WHITESPACE + VALID_GROUP_NUMBER + WHITESPACE;
+ assertEquals(1, ParserUtil.parseGroupNumber(groupNumberWithWhitespace));
}
}
diff --git a/src/test/java/seedu/address/logic/parser/TasksCommandParserTest.java b/src/test/java/seedu/address/logic/parser/TasksCommandParserTest.java
new file mode 100644
index 00000000000..eba24657f64
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/TasksCommandParserTest.java
@@ -0,0 +1,55 @@
+package seedu.address.logic.parser;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.util.Arrays;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.TasksCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.group.GroupContainsKeywordsPredicate;
+
+public class TasksCommandParserTest {
+
+ private final TasksCommandParser parser = new TasksCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsTasksCommand() {
+ int groupNumber = 1;
+ TasksCommand expectedTasksCommand = new TasksCommand(
+ groupNumber,
+ new GroupContainsKeywordsPredicate(Arrays.asList(String.valueOf(groupNumber)))
+ );
+
+ try {
+ TasksCommand command = parser.parse("1");
+ assertEquals(expectedTasksCommand, command);
+ } catch (ParseException pe) {
+ throw new AssertionError("Valid input should not result in ParseException.", pe);
+ }
+ }
+
+ @Test
+ public void parse_invalidArgs_throwsParseException() {
+ // Test non-numeric input
+ assertThrows(ParseException.class, () -> parser.parse("a"));
+
+ // Test zero as input
+ assertThrows(ParseException.class, () -> parser.parse("0"));
+
+ // Test negative input
+ assertThrows(ParseException.class, () -> parser.parse("-1"));
+ }
+
+ @Test
+ public void parse_emptyArgs_throwsParseException() {
+ assertThrows(ParseException.class, () -> parser.parse(""));
+ }
+
+ @Test
+ public void parse_spacesArgs_throwsParseException() {
+ assertThrows(ParseException.class, () -> parser.parse(" "));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/UnMarkCommandParserTest.java b/src/test/java/seedu/address/logic/parser/UnMarkCommandParserTest.java
new file mode 100644
index 00000000000..479184bf4f0
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/UnMarkCommandParserTest.java
@@ -0,0 +1,66 @@
+package seedu.address.logic.parser;
+
+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 org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.UnMarkCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+public class UnMarkCommandParserTest {
+ private UnMarkCommandParser parser;
+
+ @BeforeEach
+ public void setUp() {
+ parser = new UnMarkCommandParser();
+ }
+
+ @Test
+ public void parse_validInputWithDifferentGroupAndTaskIndices_success() throws ParseException {
+ assertEquals(parser.parse("gr/2 ti/3"), new UnMarkCommand(2, 2, null));
+ assertEquals(parser.parse("gr/3 ti/1"), new UnMarkCommand(3, 0, null));
+ assertEquals(parser.parse("gr/4 ti/4"), new UnMarkCommand(4, 3, null));
+ }
+
+ @Test
+ public void parse_invalidGroupNumber_throwsParseException() {
+ assertThrows(ParseException.class, () -> parser.parse("gr/0 ti/2"));
+ assertThrows(ParseException.class, () -> parser.parse("gr/-1 ti/2"));
+ assertThrows(ParseException.class, () -> parser.parse("gr/not_a_number ti/2"));
+ }
+
+ @Test
+ public void parse_invalidTaskIndex_throwsParseException() {
+ assertThrows(ParseException.class, () -> parser.parse("gr/1 ti/0"));
+ assertThrows(ParseException.class, () -> parser.parse("gr/1 ti/-1"));
+ assertThrows(ParseException.class, () -> parser.parse("gr/1 ti/not_a_number"));
+ }
+
+ @Test
+ public void parse_missingGroupOrTaskPrefix_throwsParseException() {
+ assertThrows(ParseException.class, () -> parser.parse("1 ti/2"));
+ assertThrows(ParseException.class, () -> parser.parse("gr/1 2"));
+ }
+
+ @Test
+ public void parse_nullArgs_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> parser.parse(null));
+ }
+
+ @Test
+ public void equals_sameObject_returnsTrue() {
+ assertTrue(parser.equals(parser));
+ }
+
+ @Test
+ public void equals_differentObject_returnsFalse() {
+ UnMarkCommandParser parser1 = new UnMarkCommandParser();
+ UnMarkCommandParser parser2 = new UnMarkCommandParser();
+
+ assertFalse(parser1.equals(parser2));
+ }
+}
diff --git a/src/test/java/seedu/address/model/AddressBookTest.java b/src/test/java/seedu/address/model/AddressBookTest.java
index 68c8c5ba4d5..65a92cbc2e3 100644
--- a/src/test/java/seedu/address/model/AddressBookTest.java
+++ b/src/test/java/seedu/address/model/AddressBookTest.java
@@ -3,9 +3,9 @@
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.logic.commands.CommandTestUtil.VALID_NAME_BOB;
import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalGroups.GROUP1;
import static seedu.address.testutil.TypicalPersons.ALICE;
import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
@@ -18,6 +18,7 @@
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
+import seedu.address.model.group.Group;
import seedu.address.model.person.Person;
import seedu.address.model.person.exceptions.DuplicatePersonException;
import seedu.address.testutil.PersonBuilder;
@@ -46,10 +47,10 @@ 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)
- .build();
+ Person editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build();
List newPersons = Arrays.asList(ALICE, editedAlice);
- AddressBookStub newData = new AddressBookStub(newPersons);
+ List groups = Arrays.asList(GROUP1);
+ AddressBookStub newData = new AddressBookStub(newPersons, groups);
assertThrows(DuplicatePersonException.class, () -> addressBook.resetData(newData));
}
@@ -73,8 +74,7 @@ public void hasPerson_personInAddressBook_returnsTrue() {
@Test
public void hasPerson_personWithSameIdentityFieldsInAddressBook_returnsTrue() {
addressBook.addPerson(ALICE);
- Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND)
- .build();
+ Person editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build();
assertTrue(addressBook.hasPerson(editedAlice));
}
@@ -94,15 +94,25 @@ public void toStringMethod() {
*/
private static class AddressBookStub implements ReadOnlyAddressBook {
private final ObservableList persons = FXCollections.observableArrayList();
+ private final ObservableList groups = FXCollections.observableArrayList();
- AddressBookStub(Collection persons) {
+ AddressBookStub(Collection persons, Collection groups) {
this.persons.setAll(persons);
+ this.groups.setAll(groups);
}
@Override
public ObservableList getPersonList() {
return persons;
}
+
+ @Override
+ public ObservableList getGroupList() {
+ return groups;
+ }
+
+ @Override
+ public void sortGroups() {}
}
}
diff --git a/src/test/java/seedu/address/model/group/GroupBelongsTutorialPredicateTest.java b/src/test/java/seedu/address/model/group/GroupBelongsTutorialPredicateTest.java
new file mode 100644
index 00000000000..34751c39c4d
--- /dev/null
+++ b/src/test/java/seedu/address/model/group/GroupBelongsTutorialPredicateTest.java
@@ -0,0 +1,60 @@
+package seedu.address.model.group;
+
+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.testutil.GroupBuilder;
+
+public class GroupBelongsTutorialPredicateTest {
+
+ @Test
+ public void equals() {
+ String firstPredicateKeyword = "01";
+ String secondPredicateKeyword = "02";
+
+ GroupBelongsTutorialPredicate firstPredicate = new GroupBelongsTutorialPredicate(firstPredicateKeyword);
+ GroupBelongsTutorialPredicate secondPredicate = new GroupBelongsTutorialPredicate(secondPredicateKeyword);
+
+ // same object -> returns true
+ assertTrue(firstPredicate.equals(firstPredicate));
+
+ // same values -> returns true
+ GroupBelongsTutorialPredicate firstPredicateCopy = new GroupBelongsTutorialPredicate(firstPredicateKeyword);
+ assertTrue(firstPredicate.equals(firstPredicateCopy));
+
+ // different types -> returns false
+ assertFalse(firstPredicate.equals(1));
+
+ // null -> returns false
+ assertFalse(firstPredicate.equals(null));
+
+ // different group -> returns false
+ assertFalse(firstPredicate.equals(secondPredicate));
+ }
+
+ @Test
+ public void test_groupBelongsTutorial_returnsTrue() {
+ // One keyword
+ GroupBelongsTutorialPredicate predicate = new GroupBelongsTutorialPredicate("01");
+ assertTrue(predicate.test(new GroupBuilder().withTutorial("01").build()));
+ }
+
+ @Test
+ public void test_groupDoesNotBelongTutorial_returnsFalse() {
+ // Non-matching tutorial
+ GroupBelongsTutorialPredicate predicate = new GroupBelongsTutorialPredicate("02");
+ assertFalse(predicate.test(new GroupBuilder().withTutorial("01").build()));
+ }
+
+ @Test
+ public void toStringMethod() {
+ String tutorial = "01";
+ GroupBelongsTutorialPredicate predicate = new GroupBelongsTutorialPredicate(tutorial);
+
+ String expected = GroupBelongsTutorialPredicate.class.getCanonicalName() + "{tutorial=" + tutorial + "}";
+ assertEquals(expected, predicate.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/model/group/GroupContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/group/GroupContainsKeywordsPredicateTest.java
new file mode 100644
index 00000000000..e120aed9f84
--- /dev/null
+++ b/src/test/java/seedu/address/model/group/GroupContainsKeywordsPredicateTest.java
@@ -0,0 +1,73 @@
+package seedu.address.model.group;
+
+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.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.testutil.GroupBuilder;
+
+public class GroupContainsKeywordsPredicateTest {
+
+ @Test
+ public void equals() {
+ List firstPredicateKeywordList = Collections.singletonList("1");
+ List secondPredicateKeywordList = Arrays.asList("1", "2");
+
+ GroupContainsKeywordsPredicate firstPredicate = new GroupContainsKeywordsPredicate(firstPredicateKeywordList);
+ GroupContainsKeywordsPredicate secondPredicate = new GroupContainsKeywordsPredicate(secondPredicateKeywordList);
+
+ // same object -> returns true
+ assertTrue(firstPredicate.equals(firstPredicate));
+
+ // same values -> returns true
+ GroupContainsKeywordsPredicate firstPredicateCopy =
+ new GroupContainsKeywordsPredicate(firstPredicateKeywordList);
+ assertTrue(firstPredicate.equals(firstPredicateCopy));
+
+ // different types -> returns false
+ assertFalse(firstPredicate.equals(1));
+
+ // null -> returns false
+ assertFalse(firstPredicate.equals(null));
+
+ // different group -> returns false
+ assertFalse(firstPredicate.equals(secondPredicate));
+ }
+
+ @Test
+ public void test_groupContainsKeywords_returnsTrue() {
+ // one keyword
+ GroupContainsKeywordsPredicate predicate = new GroupContainsKeywordsPredicate(Collections.singletonList("1"));
+ assertTrue(predicate.test(new GroupBuilder().withNumber("1").build()));
+
+ // Only one matching keyword
+ predicate = new GroupContainsKeywordsPredicate(Arrays.asList("1", "2"));
+ assertTrue(predicate.test(new GroupBuilder().withNumber("1").build()));
+ }
+
+ @Test
+ public void test_groupDoesNotContainKeywords_returnsFalse() {
+ // Zero keywords
+ GroupContainsKeywordsPredicate predicate = new GroupContainsKeywordsPredicate(Collections.emptyList());
+ assertFalse(predicate.test(new GroupBuilder().withNumber("1").build()));
+
+ // Non-matching keyword
+ predicate = new GroupContainsKeywordsPredicate(Arrays.asList("2"));
+ assertFalse(predicate.test(new GroupBuilder().withNumber("1").build()));
+ }
+
+ @Test
+ public void toStringMethod() {
+ List keywords = List.of("1", "2");
+ GroupContainsKeywordsPredicate predicate = new GroupContainsKeywordsPredicate(keywords);
+
+ String expected = GroupContainsKeywordsPredicate.class.getCanonicalName() + "{keywords=" + keywords + "}";
+ assertEquals(expected, predicate.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/model/group/GroupTest.java b/src/test/java/seedu/address/model/group/GroupTest.java
new file mode 100644
index 00000000000..4b08af61284
--- /dev/null
+++ b/src/test/java/seedu/address/model/group/GroupTest.java
@@ -0,0 +1,151 @@
+package seedu.address.model.group;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_MEMBER1_GROUP1;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_MEMBER1_GROUP2;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_MEMBER2_GROUP1;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_MEMBER2_GROUP2;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_MEMBER3_GROUP1;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_MEMBER3_GROUP2;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_NUMBER_GROUP1;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_NUMBER_GROUP2;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TASK1;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TASK3;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TUTORIAL_GROUP1;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TUTORIAL_GROUP2;
+import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalGroups.GROUP1;
+import static seedu.address.testutil.TypicalGroups.GROUP2;
+
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.group.tasks.TaskList;
+import seedu.address.testutil.GroupBuilder;
+
+public class GroupTest {
+
+ @Test
+ public void asObservableList_modifyList_throwsUnsupportedOperationException() {
+ Group group = new GroupBuilder().build();
+ assertThrows(UnsupportedOperationException.class, () -> group.getMembers().remove(0));
+ }
+
+ @Test
+ public void isSameGroup() {
+ // same object -> returns true
+ assertTrue(GROUP1.isSameGroup(GROUP1));
+
+ // null -> returns false
+ assertFalse(GROUP1.isSameGroup(null));
+
+ // same number, all other attributes different -> returns true
+ Group editedGroup1 = new GroupBuilder(GROUP1).withNumber(VALID_NUMBER_GROUP1)
+ .withTutorial(VALID_TUTORIAL_GROUP1)
+ .withMembers(VALID_MEMBER1_GROUP2, VALID_MEMBER2_GROUP2).withTasks(VALID_TASK1).build();
+ assertTrue(GROUP1.isSameGroup(editedGroup1));
+
+ // different number, all other attributes same -> returns false
+ editedGroup1 = new GroupBuilder(GROUP1).withNumber(VALID_NUMBER_GROUP2).build();
+ assertFalse(GROUP1.isSameGroup(editedGroup1));
+ }
+
+ @Test
+ public void equals() {
+ // same values -> returns true
+ Group group1Copy = new GroupBuilder(GROUP1).build();
+ assertTrue(GROUP1.equals(group1Copy));
+
+ // same object -> returns true
+ assertTrue(GROUP1.equals(GROUP1));
+
+ // null -> returns false
+ assertFalse(GROUP1.equals(null));
+
+ // different type -> returns false
+ assertFalse(GROUP1.equals(5));
+
+ // different group -> returns false
+ assertFalse(GROUP1.equals(GROUP2));
+
+ // different number -> returns false
+ Group editedGroup1 = new GroupBuilder(GROUP1).withNumber(VALID_NUMBER_GROUP2).build();
+ assertFalse(GROUP1.equals(editedGroup1));
+
+ // different tutorial -> returns false
+ editedGroup1 = new GroupBuilder(GROUP1).withTutorial(VALID_TUTORIAL_GROUP2).build();
+ assertFalse(GROUP1.equals(editedGroup1));
+
+ // different members -> returns false
+ editedGroup1 = new GroupBuilder(GROUP1)
+ .withMembers(VALID_MEMBER1_GROUP2, VALID_MEMBER2_GROUP2, VALID_MEMBER3_GROUP2).build();
+ assertFalse(GROUP1.equals(editedGroup1));
+ }
+
+ @Test
+ public void toStringMethod() {
+ String expected = Group.class.getCanonicalName() + "{group number="
+ + GROUP1.getNumber() + ", tutorial=" + GROUP1.getTutorial()
+ + ", members=" + GROUP1.getMembers() + ", tasks=" + GROUP1.getTasks() + "}";
+ assertEquals(expected, GROUP1.toString());
+ }
+
+ @Test
+ public void addMemberTest() {
+ Group editedGroup1 = new GroupBuilder(GROUP1)
+ .withMembers(VALID_MEMBER1_GROUP1, VALID_MEMBER2_GROUP1).build();
+ editedGroup1.addMember(VALID_MEMBER3_GROUP1);
+ assertEquals(editedGroup1, GROUP1);
+ }
+
+ @Test
+ public void removeMemberTest() {
+ Group editedGroup1 = new GroupBuilder(GROUP1).build();
+ editedGroup1.removeMember(VALID_MEMBER1_GROUP1);
+ assertNotEquals(editedGroup1, GROUP1);
+ }
+
+ @Test
+ public void hasMemberTest() {
+ assertTrue(GROUP1.hasMember(VALID_MEMBER1_GROUP1));
+ }
+
+ @Test
+ public void isFullTest() {
+ // not full -> returns false
+ assertFalse(GROUP1.isFull());
+
+ // full -> returns true
+ Group editedGroup1 = new GroupBuilder(GROUP1).build();
+ editedGroup1.addMember(VALID_MEMBER1_GROUP2);
+ editedGroup1.addMember(VALID_MEMBER2_GROUP2);
+ assertTrue(editedGroup1.isFull());
+ }
+
+ @Test
+ public void isValidGroupNumberTest() {
+ // non-numerical returns false
+ assertFalse(Group.isValidGroupNumber("One"));
+
+ // negative number returns false
+ assertFalse(Group.isValidGroupNumber("-1"));
+
+ // floating point number returns false
+ assertFalse(Group.isValidGroupNumber("1.0"));
+
+ // positive integer returns true
+ assertTrue(Group.isValidGroupNumber("1"));
+ }
+
+ @Test
+ public void addTasksTest() {
+ Group editedGroup1 = new GroupBuilder(GROUP1).build();
+ editedGroup1.addTasks(new TaskList(List.of(VALID_TASK3)));
+ assertTrue(editedGroup1.getTasks()
+ .isTaskInAllTasks(VALID_TASK3.getTaskType(), VALID_TASK3.getTask()));
+ }
+}
diff --git a/src/test/java/seedu/address/model/group/UniqueGroupListTest.java b/src/test/java/seedu/address/model/group/UniqueGroupListTest.java
new file mode 100644
index 00000000000..30bce31f134
--- /dev/null
+++ b/src/test/java/seedu/address/model/group/UniqueGroupListTest.java
@@ -0,0 +1,155 @@
+package seedu.address.model.group;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalGroups.GROUP1;
+import static seedu.address.testutil.TypicalGroups.GROUP2;
+import static seedu.address.testutil.TypicalGroups.GROUP3;
+import static seedu.address.testutil.TypicalPersons.GEORGE;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.group.exceptions.DuplicateGroupException;
+import seedu.address.model.group.exceptions.GroupNotFoundException;
+import seedu.address.model.person.Person;
+
+public class UniqueGroupListTest {
+
+ private final UniqueGroupList uniqueGroupList = new UniqueGroupList();
+
+ @Test
+ public void contains_nullGroup_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueGroupList.contains(null));
+ }
+
+ @Test
+ public void contains_groupNotInList_returnsFalse() {
+ assertFalse(uniqueGroupList.contains(GROUP1));
+ }
+
+ @Test
+ public void contains_groupInList_returnsTrue() {
+ uniqueGroupList.add(GROUP1);
+ assertTrue(uniqueGroupList.contains(GROUP1));
+ }
+
+ @Test
+ public void add_nullGroup_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueGroupList.add(null));
+ }
+
+ @Test
+ public void add_duplicateGroup_throwsDuplicateGroupException() {
+ uniqueGroupList.add(GROUP1);
+ assertThrows(DuplicateGroupException.class, () -> uniqueGroupList.add(GROUP1));
+ }
+
+ @Test
+ public void remove_nullGroup_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueGroupList.remove((Group) null));
+ }
+
+ @Test
+ public void remove_groupDoesNotExist_throwsGroupNotFoundException() {
+ assertThrows(GroupNotFoundException.class, () -> uniqueGroupList.remove(GROUP1));
+ }
+
+ @Test
+ public void remove_existingGroup_removesGroup() {
+ uniqueGroupList.add(GROUP1);
+ uniqueGroupList.remove(GROUP1);
+ UniqueGroupList expecteduniqueGroupList = new UniqueGroupList();
+ assertEquals(expecteduniqueGroupList, uniqueGroupList);
+ }
+
+ @Test
+ public void remove_nullPerson_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueGroupList.remove((Person) null));
+ }
+
+ @Test
+ public void remove_validPerson_success() {
+ uniqueGroupList.add(GROUP3);
+ uniqueGroupList.remove(GEORGE);
+ assertFalse(GROUP3.hasMember(GEORGE));
+ }
+
+ @Test
+ public void setGroups_nullUniqueGroupList_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueGroupList.setGroups((UniqueGroupList) null));
+ }
+
+ @Test
+ public void setGroups_uniqueGroupList_replacesOwnListWithProvidedUniqueGroupList() {
+ uniqueGroupList.add(GROUP1);
+ UniqueGroupList expectedUniqueGroupList = new UniqueGroupList();
+ expectedUniqueGroupList.add(GROUP2);
+ uniqueGroupList.setGroups(expectedUniqueGroupList);
+ assertEquals(expectedUniqueGroupList, uniqueGroupList);
+ }
+
+ @Test
+ public void setGroups_nullList_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueGroupList.setGroups((List) null));
+ }
+
+ @Test
+ public void setGroups_list_replacesOwnListWithProvidedList() {
+ uniqueGroupList.add(GROUP1);
+ List groupList = Collections.singletonList(GROUP2);
+ uniqueGroupList.setGroups(groupList);
+ UniqueGroupList expectedUniqueGroupList = new UniqueGroupList();
+ expectedUniqueGroupList.add(GROUP2);
+ assertEquals(expectedUniqueGroupList, uniqueGroupList);
+ }
+
+ @Test
+ public void setGroups_listWithDuplicateGroups_throwsDuplicateGroupException() {
+ List listWithDuplicateGroups = Arrays.asList(GROUP1, GROUP1);
+ assertThrows(DuplicateGroupException.class, () -> uniqueGroupList.setGroups(listWithDuplicateGroups));
+ }
+
+ @Test
+ public void toStringMethod() {
+ assertEquals(uniqueGroupList.asUnmodifiableObservableList().toString(), uniqueGroupList.toString());
+ }
+
+ @Test
+ public void equals_sameList_returnsTrue() {
+ UniqueGroupList firstList = new UniqueGroupList();
+ UniqueGroupList secondList = new UniqueGroupList();
+ assertTrue(firstList.equals(secondList));
+ }
+
+ @Test
+ public void equals_differentLists_returnsFalse() {
+ UniqueGroupList firstList = new UniqueGroupList();
+ UniqueGroupList secondList = new UniqueGroupList();
+ firstList.add(GROUP1);
+ secondList.add(GROUP2);
+ assertFalse(firstList.equals(secondList));
+ }
+
+ @Test
+ public void hashCode_sameList_returnsSameHashCode() {
+ UniqueGroupList firstList = new UniqueGroupList();
+ UniqueGroupList secondList = new UniqueGroupList();
+ assertEquals(firstList.hashCode(), secondList.hashCode());
+ }
+
+ @Test
+ public void hashCode_differentLists_returnsDifferentHashCode() {
+ UniqueGroupList firstList = new UniqueGroupList();
+ UniqueGroupList secondList = new UniqueGroupList();
+ firstList.add(GROUP1);
+ secondList.add(GROUP2);
+ assertNotEquals(firstList.hashCode(), secondList.hashCode());
+ }
+}
diff --git a/src/test/java/seedu/address/model/group/tasks/DeadlineTest.java b/src/test/java/seedu/address/model/group/tasks/DeadlineTest.java
new file mode 100644
index 00000000000..e04296c2fa9
--- /dev/null
+++ b/src/test/java/seedu/address/model/group/tasks/DeadlineTest.java
@@ -0,0 +1,42 @@
+package seedu.address.model.group.tasks;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.group.exceptions.TaskException;
+
+public class DeadlineTest {
+
+ @Test
+ void constructor_validDeadline_createsDeadline() throws TaskException {
+ String taskDescription = "Submit report";
+ TaskStatus status = TaskStatus.NOT_DONE;
+ TaskModule module = TaskModule.CS2103T;
+ String by = "24/12/2023 2359";
+
+ Deadline deadline = new Deadline(taskDescription, status, module, by);
+
+ assertEquals(taskDescription, deadline.getTask());
+ assertEquals(status, deadline.getStatus());
+ assertEquals(module, deadline.getModule());
+ assertEquals("D", deadline.getType());
+ assertEquals(by, deadline.getBy());
+ assertEquals("(by " + by + ")", deadline.getDeadline());
+ }
+
+ @Test
+ void testToString_withValidDeadline() throws TaskException {
+ Deadline deadline = new Deadline("Finish assignment", TaskStatus.NOT_DONE, TaskModule.CS2101,
+ "23/09/2023 1300");
+ String expectedToString = "D NOT_DONE CS2101 Finish assignment 23/09/2023 1300";
+ assertEquals(expectedToString, deadline.toString());
+ }
+
+ @Test
+ void testToString_withDoneStatus() throws TaskException {
+ Deadline deadline = new Deadline("Finish assignment", TaskStatus.DONE, TaskModule.CS2103T, "23/09/2023 1300");
+ String expectedGenerateStr = "D DONE CS2103T Finish assignment 23/09/2023 1300";
+ assertEquals(expectedGenerateStr, deadline.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/model/group/tasks/TaskInitializerTest.java b/src/test/java/seedu/address/model/group/tasks/TaskInitializerTest.java
new file mode 100644
index 00000000000..a696fd04912
--- /dev/null
+++ b/src/test/java/seedu/address/model/group/tasks/TaskInitializerTest.java
@@ -0,0 +1,46 @@
+package seedu.address.model.group.tasks;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.group.exceptions.TaskException;
+
+public class TaskInitializerTest {
+
+ @Test
+ void initializeTasks_correctlyInitializesTaskList() throws TaskException {
+ // Call the method to test
+ TaskList taskList = TaskInitializer.initializeTasks();
+
+ // The task list should not be null
+ assertNotNull(taskList);
+
+ // Verify the number of tasks created
+ assertEquals(13, taskList.getTaskList().size());
+
+ // Verify the correct tasks have been added to the list
+ verifyTask(taskList.getTask(0), Todo.class,
+ "Upload video of OP1.", TaskStatus.NOT_DONE, TaskModule.CS2101);
+ verifyTask(taskList.getTask(1), Todo.class,
+ "Complete peer review for OP2.", TaskStatus.NOT_DONE, TaskModule.CS2101);
+
+ // Verify the tasks of type Deadline have correct deadlines
+ verifyDeadline(taskList.getTask(2), "(by 29/10/2023 2359)");
+ }
+
+ // Helper method to verify the task details
+ private void verifyTask(Task task, Class> expectedClass, String expectedDescription,
+ TaskStatus expectedStatus, TaskModule expectedModule) {
+ assertEquals(expectedClass, task.getClass());
+ assertEquals(expectedDescription, task.getTask());
+ assertEquals(expectedStatus, task.getStatus());
+ assertEquals(expectedModule, task.getModule());
+ }
+
+ // Helper method to verify the deadline details
+ private void verifyDeadline(Task task, String expectedDeadline) {
+ assertEquals(expectedDeadline, ((Deadline) task).getDeadline());
+ }
+}
diff --git a/src/test/java/seedu/address/model/group/tasks/TaskListTest.java b/src/test/java/seedu/address/model/group/tasks/TaskListTest.java
new file mode 100644
index 00000000000..bab383f2d8e
--- /dev/null
+++ b/src/test/java/seedu/address/model/group/tasks/TaskListTest.java
@@ -0,0 +1,89 @@
+package seedu.address.model.group.tasks;
+
+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.util.Arrays;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.group.exceptions.TaskException;
+
+public class TaskListTest {
+ private TaskList taskList;
+
+ @BeforeEach
+ void setUp() {
+ taskList = new TaskList();
+ }
+
+ @Test
+ void construct_emptyTaskList() {
+ assertTrue(taskList.isEmpty());
+ }
+
+ @Test
+ void addTask_singleTask_taskAdded() {
+ Task task = new Todo("Read book", TaskStatus.NOT_DONE, TaskModule.CS2101);
+ taskList.addTask(task);
+ assertEquals(1, taskList.getTasks().size());
+ }
+
+ @Test
+ void deleteTask_taskPresent_taskDeleted() {
+ Task task = new Todo("Read book", TaskStatus.NOT_DONE, TaskModule.CS2101);
+ taskList.addTask(task);
+ taskList.deleteTask(0);
+ assertTrue(taskList.isEmpty());
+ }
+
+ @Test
+ void getTask_validIndex_taskReturned() {
+ Task task = new Todo("Read book", TaskStatus.NOT_DONE, TaskModule.CS2101);
+ taskList.addTask(task);
+ assertEquals(task, taskList.getTask(0));
+ }
+
+ @Test
+ void isTaskInAllTasks_taskExists_trueReturned() {
+ Task task = new Todo("Read book", TaskStatus.NOT_DONE, TaskModule.CS2101);
+ taskList.addTask(task);
+ assertTrue(taskList.isTaskInAllTasks("T", "Read book"));
+ }
+
+ @Test
+ void addTasks_multipleTasks_tasksAdded() {
+ Task task1 = new Todo("Read book", TaskStatus.NOT_DONE, TaskModule.CS2101);
+ Task task2 = null;
+ try {
+ task2 = new Deadline("Submit assignment", TaskStatus.NOT_DONE, TaskModule.CS2103T, "23/09/2023 2359");
+ } catch (TaskException e) {
+ throw new RuntimeException(e);
+ }
+ taskList.addTasks(Arrays.asList(task1, task2));
+ assertEquals(2, taskList.getTasks().size());
+ }
+
+ @Test
+ void isEmpty_taskListEmpty_trueReturned() {
+ assertTrue(taskList.isEmpty());
+ }
+
+ @Test
+ void getTaskList_tasksPresent_copyReturned() {
+ Task task = new Todo("Read book", TaskStatus.NOT_DONE, TaskModule.CS2101);
+ taskList.addTask(task);
+ assertEquals(taskList.getTasks(), taskList.getTaskList());
+ assertFalse(taskList.getTasks() == taskList.getTaskList()); // Checking for actual copy, not same reference
+ }
+
+ @Test
+ void toString_tasksInList_correctStringRepresentation() {
+ Task task = new Todo("Read book", TaskStatus.NOT_DONE, TaskModule.CS2101);
+ taskList.addTask(task);
+ String expected = "❌ T 1. CS2101 Read book \n";
+ assertEquals(expected, taskList.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/model/group/tasks/TaskTest.java b/src/test/java/seedu/address/model/group/tasks/TaskTest.java
new file mode 100644
index 00000000000..7c3ca8dc2e6
--- /dev/null
+++ b/src/test/java/seedu/address/model/group/tasks/TaskTest.java
@@ -0,0 +1,125 @@
+package seedu.address.model.group.tasks;
+
+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.testutil.TypicalTasks.CS2101_OP1_UPLOAD;
+import static seedu.address.testutil.TypicalTasks.CS2101_PLAN_OP2;
+
+import java.time.LocalDateTime;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.group.exceptions.TaskException;
+import seedu.address.testutil.TaskBuilder;
+
+public class TaskTest {
+
+ @Test
+ void constructor_withValidArguments_createsCorrectTaskObject() {
+ Task task = new Task("Read book", TaskStatus.NOT_DONE, TaskModule.CS2101, "TODO", "");
+ assertEquals("Read book", task.getTask());
+ assertEquals(TaskStatus.NOT_DONE, task.getStatus());
+ assertEquals(TaskModule.CS2101, task.getModule());
+ assertEquals("TODO", task.getType());
+ assertEquals("", task.getBy());
+ }
+
+ @Test
+ void toString_validTask_correctStringRepresentation() {
+ Task task = new Task("Read book", TaskStatus.NOT_DONE, TaskModule.CS2101, "TODO", "");
+ String expected = "TODO NOT_DONE CS2101 Read book ";
+ assertEquals(expected, task.toString());
+ }
+
+ @Test
+ void setStatus_setsNewStatus_statusIsUpdated() {
+ Task task = new Task("Read book", TaskStatus.NOT_DONE, TaskModule.CS2101, "TODO", "");
+ task.setStatus(TaskStatus.DONE);
+ assertEquals(TaskStatus.DONE, task.getStatus());
+ }
+
+ @Test
+ void parseDateTime_validDateTimeString_parsesCorrectly() {
+ Task task = new Task();
+ LocalDateTime expectedDateTime = LocalDateTime.of(2023, 9, 23, 23, 59);
+ assertEquals(expectedDateTime, task.parseDateTime("23/09/2023 2359"));
+ }
+
+ @Test
+ void parseDateTime_invalidDateTimeString_throwsException() {
+ Task task = new Task();
+ assertThrows(IllegalArgumentException.class, () -> {
+ task.parseDateTime("invalid-date-time");
+ });
+ }
+
+ @Test
+ void mark_asDone_taskIsMarkedDone() {
+ Task task = new Task("Read book", TaskStatus.NOT_DONE, TaskModule.CS2101, "TODO", "");
+ task.mark();
+ assertEquals(TaskStatus.DONE, task.getStatus());
+ }
+
+ @Test
+ void unMark_asNotDone_taskIsMarkedNotDone() {
+ Task task = new Task("Read book", TaskStatus.DONE, TaskModule.CS2101, "TODO", "");
+ task.unMark();
+ assertEquals(TaskStatus.NOT_DONE, task.getStatus());
+ }
+
+ @Test
+ void taskException_withMessage_containsCorrectMessage() {
+ String errorMessage = "Test error message";
+ TaskException exception = new TaskException(errorMessage);
+ assertEquals(errorMessage, exception.getMessage());
+ }
+
+ @Test
+ void constructor_withInvalidDateFormat_throwsTaskException() {
+ assertThrows(TaskException.class, () -> {
+ new Deadline("Read book", TaskStatus.NOT_DONE, TaskModule.CS2101, "invalid-date-format");
+ });
+ }
+
+ @Test
+ public void equals() {
+ // same values -> returns true
+ Task taskCopy = new TaskBuilder(CS2101_OP1_UPLOAD).build();
+ assertTrue(CS2101_OP1_UPLOAD.equals(taskCopy));
+
+ // same object -> returns true
+ assertTrue(CS2101_OP1_UPLOAD.equals(CS2101_OP1_UPLOAD));
+
+ // null -> returns false
+ assertFalse(CS2101_OP1_UPLOAD.equals(null));
+
+ // different type -> returns false
+ assertFalse(CS2101_OP1_UPLOAD.equals("CS2101_OP1_UPLOAD"));
+
+ // different task -> returns false
+ assertFalse(CS2101_OP1_UPLOAD.equals(CS2101_PLAN_OP2));
+
+ // different description -> returns false
+ Task editedDescription = new TaskBuilder(CS2101_OP1_UPLOAD).withDescription("Different Task").build();
+ assertFalse(CS2101_OP1_UPLOAD.equals(editedDescription));
+
+ // different status -> returns false
+ Task editedStatus = new TaskBuilder(CS2101_OP1_UPLOAD).withStatus(TaskStatus.NOT_DONE).build();
+ assertFalse(CS2101_OP1_UPLOAD.equals(editedStatus));
+
+ // different module -> returns false
+ Task editedModule = new TaskBuilder(CS2101_OP1_UPLOAD).withModule(TaskModule.CS2103T).build();
+ assertFalse(CS2101_OP1_UPLOAD.equals(editedModule));
+
+ // different type -> returns false
+ Task editedType = new TaskBuilder(CS2101_OP1_UPLOAD).withType("DEADLINE").build();
+ assertFalse(CS2101_OP1_UPLOAD.equals(editedType));
+
+ // different deadline -> returns false
+ Task editedDeadline = new TaskBuilder(CS2101_OP1_UPLOAD).withBy("01/01/2024 0000").build();
+ assertFalse(CS2101_OP1_UPLOAD.equals(editedDeadline));
+
+ }
+}
diff --git a/src/test/java/seedu/address/model/group/tasks/TodoTest.java b/src/test/java/seedu/address/model/group/tasks/TodoTest.java
new file mode 100644
index 00000000000..236d5ac61c1
--- /dev/null
+++ b/src/test/java/seedu/address/model/group/tasks/TodoTest.java
@@ -0,0 +1,39 @@
+package seedu.address.model.group.tasks;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.group.exceptions.TaskException;
+
+public class TodoTest {
+
+ @Test
+ void constructor_validTodo_createsTodo() throws TaskException {
+ String taskDescription = "Submit report";
+ TaskStatus status = TaskStatus.NOT_DONE;
+ TaskModule module = TaskModule.CS2103T;
+
+ Todo deadline = new Todo(taskDescription, status, module);
+
+ assertEquals(taskDescription, deadline.getTask());
+ assertEquals(status, deadline.getStatus());
+ assertEquals(module, deadline.getModule());
+ assertEquals("T", deadline.getType());
+ assertEquals("", deadline.getDeadline());
+ }
+
+ @Test
+ void testToString_withValidTodo() throws TaskException {
+ Todo todo = new Todo("Finish assignment", TaskStatus.NOT_DONE, TaskModule.CS2101);
+ String expectedToString = "T NOT_DONE CS2101 Finish assignment ";
+ assertEquals(expectedToString, todo.toString());
+ }
+
+ @Test
+ void testToString_withDoneStatus() throws TaskException {
+ Todo todo = new Todo("Finish assignment", TaskStatus.DONE, TaskModule.CS2103T);
+ String expectedGenerateStr = "T DONE CS2103T Finish assignment ";
+ assertEquals(expectedGenerateStr, todo.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/AddressTest.java b/src/test/java/seedu/address/model/person/AddressTest.java
deleted file mode 100644
index 314885eca26..00000000000
--- a/src/test/java/seedu/address/model/person/AddressTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package seedu.address.model.person;
-
-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;
-
-public class AddressTest {
-
- @Test
- public void constructor_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> new Address(null));
- }
-
- @Test
- public void constructor_invalidAddress_throwsIllegalArgumentException() {
- String invalidAddress = "";
- assertThrows(IllegalArgumentException.class, () -> new Address(invalidAddress));
- }
-
- @Test
- public void isValidAddress() {
- // null address
- assertThrows(NullPointerException.class, () -> Address.isValidAddress(null));
-
- // invalid addresses
- assertFalse(Address.isValidAddress("")); // empty string
- assertFalse(Address.isValidAddress(" ")); // spaces only
-
- // valid addresses
- assertTrue(Address.isValidAddress("Blk 456, Den Road, #01-355"));
- assertTrue(Address.isValidAddress("-")); // one character
- assertTrue(Address.isValidAddress("Leng Inc; 1234 Market St; San Francisco CA 2349879; USA")); // long address
- }
-
- @Test
- public void equals() {
- Address address = new Address("Valid Address");
-
- // same values -> returns true
- assertTrue(address.equals(new Address("Valid Address")));
-
- // same object -> returns true
- assertTrue(address.equals(address));
-
- // null -> returns false
- assertFalse(address.equals(null));
-
- // different types -> returns false
- assertFalse(address.equals(5.0f));
-
- // different values -> returns false
- assertFalse(address.equals(new Address("Other Valid Address")));
- }
-}
diff --git a/src/test/java/seedu/address/model/person/DescriptionTest.java b/src/test/java/seedu/address/model/person/DescriptionTest.java
new file mode 100644
index 00000000000..0bb00c6d394
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/DescriptionTest.java
@@ -0,0 +1,65 @@
+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.testutil.Assert.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+public class DescriptionTest {
+
+ @Test
+ public void constructor_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new Description(null));
+ }
+
+ @Test
+ public void constructor_invalidDescription_throwsIllegalArgumentException() {
+ String invalidDescription = "";
+ assertThrows(IllegalArgumentException.class, () -> new Description(invalidDescription));
+ }
+
+ @Test
+ public void isValidDescription() {
+ // null description
+ assertThrows(NullPointerException.class, () -> Description.isValidDescription(null));
+
+ // invalid description
+ assertFalse(Description.isValidDescription("")); // empty string
+ assertFalse(Description.isValidDescription("3dUxHctmS8CrcahkimCRb5o33qMXhpMAoyheGmi9BLF8BJnTouR2KsH34as"
+ + "RqUKDppYnEBHmRq54p5LkBjRsGyGcjWFU6m6pRU2SGQDiAOQMD4ZGqsMwTO3SAxVDWaCwfiQdqBlopNXqyu9cIcg"
+ + "Djdh\n")); // more than 150 characters
+
+ // valid description
+ assertTrue(Description.isValidDescription("test description"));
+ }
+
+ @Test
+ public void equals() {
+ Description description = new Description("test");
+
+ // same values -> returns true
+ assertTrue(description.equals(new Description("test")));
+
+ // same object -> returns true
+ assertTrue(description.equals(description));
+
+ // null -> returns false
+ assertFalse(description.equals(null));
+
+ // different types -> returns false
+ assertFalse(description.equals(5.0f));
+
+ // different values -> returns false
+ assertFalse(description.equals("diff"));
+ }
+
+ @Test
+ public void hashCode_returnsExpectedHashCode() {
+ Description description = new Description("test");
+ int expectedHashCode = "test".hashCode();
+ int actualHashCode = description.hashCode();
+ assertEquals(expectedHashCode, actualHashCode);
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/EmailTest.java b/src/test/java/seedu/address/model/person/EmailTest.java
index f08cdff0a64..722ad4763b0 100644
--- a/src/test/java/seedu/address/model/person/EmailTest.java
+++ b/src/test/java/seedu/address/model/person/EmailTest.java
@@ -1,5 +1,6 @@
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.testutil.Assert.assertThrows;
@@ -26,7 +27,7 @@ public void isValidEmail() {
// blank email
assertFalse(Email.isValidEmail("")); // empty string
- assertFalse(Email.isValidEmail(" ")); // spaces only
+ assertFalse(Email.isValidEmail(" ")); // empty string
// missing parts
assertFalse(Email.isValidEmail("@example.com")); // missing local part
@@ -53,25 +54,21 @@ public void isValidEmail() {
assertFalse(Email.isValidEmail("peterjack@example.c")); // top level domain has less than two chars
// valid email
- assertTrue(Email.isValidEmail("PeterJack_1190@example.com")); // underscore in local part
- assertTrue(Email.isValidEmail("PeterJack.1190@example.com")); // period in local part
- assertTrue(Email.isValidEmail("PeterJack+1190@example.com")); // '+' symbol in local part
- assertTrue(Email.isValidEmail("PeterJack-1190@example.com")); // hyphen in local part
- assertTrue(Email.isValidEmail("a@bc")); // minimal
- assertTrue(Email.isValidEmail("test@localhost")); // alphabets only
- assertTrue(Email.isValidEmail("123@145")); // numeric local part and domain name
- assertTrue(Email.isValidEmail("a1+be.d@example1.com")); // mixture of alphanumeric and special characters
- assertTrue(Email.isValidEmail("peter_jack@very-very-very-long-example.com")); // long domain name
- assertTrue(Email.isValidEmail("if.you.dream.it_you.can.do.it@example.com")); // long local part
+ assertTrue(Email.isValidEmail("PeterJack_1190@u.nus.edu")); // underscore in local part
+ assertTrue(Email.isValidEmail("PeterJack.1190@u.nus.edu")); // period in local part
+ assertTrue(Email.isValidEmail("PeterJack+1190@u.nus.edu")); // '+' symbol in local part
+ assertTrue(Email.isValidEmail("PeterJack-1190@u.nus.edu")); // hyphen in local part
+ assertTrue(Email.isValidEmail("a1+be.d@u.nus.edu")); // mixture of alphanumeric and special characters
+ assertTrue(Email.isValidEmail("if.you.dream.it_you.can.do.it@u.nus.edu")); // long local part
assertTrue(Email.isValidEmail("e1234567@u.nus.edu")); // more than one period in domain
}
@Test
public void equals() {
- Email email = new Email("valid@email");
+ Email email = new Email("valid@u.nus.edu");
// same values -> returns true
- assertTrue(email.equals(new Email("valid@email")));
+ assertTrue(email.equals(new Email("valid@u.nus.edu")));
// same object -> returns true
assertTrue(email.equals(email));
@@ -83,6 +80,14 @@ public void equals() {
assertFalse(email.equals(5.0f));
// different values -> returns false
- assertFalse(email.equals(new Email("other.valid@email")));
+ assertFalse(email.equals(new Email("other.valid@u.nus.edu")));
+ }
+
+ @Test
+ public void hashCode_returnsExpectedHashCode() {
+ Email email = new Email("valid@u.nus.edu");
+ int expectedHashCode = "valid@u.nus.edu".hashCode();
+ int actualHashCode = email.hashCode();
+ assertEquals(expectedHashCode, actualHashCode);
}
}
diff --git a/src/test/java/seedu/address/model/person/GenderTest.java b/src/test/java/seedu/address/model/person/GenderTest.java
new file mode 100644
index 00000000000..d5edce4eec4
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/GenderTest.java
@@ -0,0 +1,69 @@
+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.testutil.Assert.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+public class GenderTest {
+
+ @Test
+ public void constructor_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new Gender(null));
+ }
+
+ @Test
+ public void constructor_invalidGender_throwsIllegalArgumentException() {
+ String invalidGender = "Male";
+ assertThrows(IllegalArgumentException.class, () -> new Gender(invalidGender));
+ }
+
+ @Test
+ public void isValidGender() {
+ // null gender
+ assertThrows(NullPointerException.class, () -> Gender.isValidGender(null));
+
+ // invalid gender
+ assertFalse(Gender.isValidGender("Male"));
+ assertFalse(Gender.isValidGender("Female"));
+ assertFalse(Gender.isValidGender("1234"));
+ assertFalse(Gender.isValidGender("M F"));
+
+ // valid gender
+ assertTrue(Gender.isValidGender("M"));
+ assertTrue(Gender.isValidGender("F"));
+ assertTrue(Gender.isValidGender("m"));
+ assertTrue(Gender.isValidGender("f"));
+ }
+
+ @Test
+ public void equals() {
+ Gender gender = new Gender("M");
+
+ // same values -> returns true
+ assertTrue(gender.equals(new Gender("M")));
+ assertTrue(gender.equals(new Gender("m")));
+
+ // same object -> returns true
+ assertTrue(gender.equals(gender));
+
+ // null -> returns false
+ assertFalse(gender.equals(null));
+
+ // different types -> returns false
+ assertFalse(gender.equals(5.0f));
+
+ // different values -> returns false
+ assertFalse(gender.equals(new Gender("F")));
+ }
+
+ @Test
+ public void hashCode_returnsExpectedHashCode() {
+ Gender gender = new Gender("M");
+ int expectedHashCode = "M".hashCode();
+ int actualHashCode = gender.hashCode();
+ assertEquals(expectedHashCode, actualHashCode);
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/MajorTest.java b/src/test/java/seedu/address/model/person/MajorTest.java
new file mode 100644
index 00000000000..58273fe69b8
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/MajorTest.java
@@ -0,0 +1,72 @@
+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.testutil.Assert.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+public class MajorTest {
+
+ @Test
+ public void constructor_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new Major(null));
+ }
+
+ @Test
+ public void constructor_invalidMajor_throwsIllegalArgumentException() {
+ String invalidMajor = "Computer Games";
+ assertThrows(IllegalArgumentException.class, () -> new Major(invalidMajor));
+ }
+
+ @Test
+ public void isValidEmail() {
+ // null email
+ assertThrows(NullPointerException.class, () -> Major.isValidMajor(null));
+
+ // blank major
+ assertFalse(Major.isValidMajor("")); // empty string
+ assertFalse(Major.isValidMajor(" ")); // spaces only
+
+ // invalid major
+ assertFalse(Major.isValidMajor("Computer Games"));
+ assertFalse(Major.isValidMajor("Finance"));
+ assertFalse(Major.isValidMajor("ComputerScience")); // no space
+ assertFalse(Major.isValidMajor(" Computer Science")); // leading space
+ assertFalse(Major.isValidMajor("Computer Science ")); // trailing space
+
+ // valid major
+ assertTrue(Major.isValidMajor("computer science")); // lowercase
+ assertTrue(Major.isValidMajor("Computer Science"));
+ assertTrue(Major.isValidMajor("Information Systems"));
+ }
+
+ @Test
+ public void equals() {
+ Major major = new Major("Computer Science");
+
+ // same values -> returns true
+ assertTrue(major.equals(new Major("Computer Science")));
+
+ // same object -> returns true
+ assertTrue(major.equals(major));
+
+ // null -> returns false
+ assertFalse(major.equals(null));
+
+ // different types -> returns false
+ assertFalse(major.equals(5.0f));
+
+ // different values -> returns false
+ assertFalse(major.equals(new Name("Information Systems")));
+ }
+
+ @Test
+ public void hashCode_returnsExpectedHashCode() {
+ Major major = new Major("Computer Science");
+ int expectedHashCode = "Computer Science".hashCode();
+ int actualHashCode = major.hashCode();
+ assertEquals(expectedHashCode, actualHashCode);
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java
index 6b3fd90ade7..165939aac7e 100644
--- a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java
+++ b/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java
@@ -56,6 +56,14 @@ public void test_nameContainsKeywords_returnsTrue() {
// Mixed-case keywords
predicate = new NameContainsKeywordsPredicate(Arrays.asList("aLIce", "bOB"));
assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build()));
+
+ // One partial keyword
+ predicate = new NameContainsKeywordsPredicate(Arrays.asList("Bob"));
+ assertTrue(predicate.test(new PersonBuilder().withName("Alice Bobby").build()));
+
+ // Multiple partial keywords
+ predicate = new NameContainsKeywordsPredicate(Arrays.asList("Bob", "Carol"));
+ assertTrue(predicate.test(new PersonBuilder().withName("Alice Bobby Caroline").build()));
}
@Test
@@ -68,10 +76,11 @@ public void test_nameDoesNotContainKeywords_returnsFalse() {
predicate = new NameContainsKeywordsPredicate(Arrays.asList("Carol"));
assertFalse(predicate.test(new PersonBuilder().withName("Alice Bob").build()));
- // Keywords match phone, email and address, but does not match name
- predicate = new NameContainsKeywordsPredicate(Arrays.asList("12345", "alice@email.com", "Main", "Street"));
- assertFalse(predicate.test(new PersonBuilder().withName("Alice").withPhone("12345")
- .withEmail("alice@email.com").withAddress("Main Street").build()));
+ // Keywords match major, year, email, and description, but does not match name
+ predicate = new NameContainsKeywordsPredicate(
+ Arrays.asList("ComputerScience", "2", "alice@u.nus.edu", "webdev"));
+ assertFalse(predicate.test(new PersonBuilder().withName("Alice").withMajor("Computer Science").withYear("2")
+ .withEmail("alice@u.nus.edu").withDescription("web dev").build()));
}
@Test
diff --git a/src/test/java/seedu/address/model/person/NameTest.java b/src/test/java/seedu/address/model/person/NameTest.java
index 94e3dd726bd..f6c2eb2b729 100644
--- a/src/test/java/seedu/address/model/person/NameTest.java
+++ b/src/test/java/seedu/address/model/person/NameTest.java
@@ -1,5 +1,6 @@
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.testutil.Assert.assertThrows;
@@ -29,13 +30,13 @@ public void isValidName() {
assertFalse(Name.isValidName(" ")); // spaces only
assertFalse(Name.isValidName("^")); // only non-alphanumeric characters
assertFalse(Name.isValidName("peter*")); // contains non-alphanumeric characters
+ assertFalse(Name.isValidName("12345")); // numbers only
+ assertFalse(Name.isValidName("peter the 2nd")); // alphanumeric character
// valid name
assertTrue(Name.isValidName("peter jack")); // alphabets only
- assertTrue(Name.isValidName("12345")); // numbers only
- assertTrue(Name.isValidName("peter the 2nd")); // alphanumeric characters
assertTrue(Name.isValidName("Capital Tan")); // with capital letters
- assertTrue(Name.isValidName("David Roger Jackson Ray Jr 2nd")); // long names
+ assertTrue(Name.isValidName("David Roger Jackson Ray Jr")); // long names
}
@Test
@@ -57,4 +58,12 @@ public void equals() {
// different values -> returns false
assertFalse(name.equals(new Name("Other Valid Name")));
}
+
+ @Test
+ public void hashCode_returnsExpectedHashCode() {
+ Name name = new Name("Valid Name");
+ int expectedHashCode = "Valid Name".hashCode();
+ int actualHashCode = name.hashCode();
+ assertEquals(expectedHashCode, actualHashCode);
+ }
}
diff --git a/src/test/java/seedu/address/model/person/NationalityTest.java b/src/test/java/seedu/address/model/person/NationalityTest.java
new file mode 100644
index 00000000000..07bb6c2cdf3
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/NationalityTest.java
@@ -0,0 +1,74 @@
+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.testutil.Assert.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+public class NationalityTest {
+
+ @Test
+ public void constructor_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new Nationality(null));
+ }
+
+ @Test
+ public void constructor_invalidNationality_throwsIllegalArgumentException() {
+ String invalidNationality = "invalid";
+ assertThrows(IllegalArgumentException.class, () -> new Nationality(invalidNationality));
+ }
+
+ @Test
+ public void isValidNationality() {
+ // null nationality
+ assertThrows(NullPointerException.class, () -> Nationality.isValidNationality(null));
+
+ // invalid nationality
+ assertFalse(Nationality.isValidNationality("")); // empty string
+ assertFalse(Nationality.isValidNationality(" ")); // empty string
+ assertFalse(Nationality.isValidNationality("random")); // random string
+
+ // valid nationalities
+ assertTrue(Nationality.isValidNationality("local"));
+ assertTrue(Nationality.isValidNationality("Local"));
+ assertTrue(Nationality.isValidNationality("foreigner"));
+ assertTrue(Nationality.isValidNationality("Foreigner"));
+ }
+
+ @Test
+ public void equals() {
+ Nationality nationality = new Nationality("local");
+
+ // same values -> returns true
+ assertTrue(nationality.equals(new Nationality("local")));
+ assertTrue(nationality.equals(new Nationality("Local")));
+
+ // same object -> returns true
+ assertTrue(nationality.equals(nationality));
+
+ // null -> returns false
+ assertFalse(nationality.equals(null));
+
+ // different types -> returns false
+ assertFalse(nationality.equals(5.0f));
+
+ // different values -> returns false
+ assertFalse(nationality.equals(new Nationality("foreigner")));
+ }
+
+ @Test
+ public void hashCode_returnsExpectedHashCode() {
+ Nationality nationality = new Nationality("local");
+ int expectedHashCode = "local".hashCode();
+ int actualHashCode = nationality.hashCode();
+ assertEquals(expectedHashCode, actualHashCode);
+ }
+
+ @Test
+ public void toString_returnsValue() {
+ Nationality nationality = new Nationality("local");
+ assertEquals("local", nationality.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/PersonTest.java b/src/test/java/seedu/address/model/person/PersonTest.java
index 31a10d156c9..9f39193181c 100644
--- a/src/test/java/seedu/address/model/person/PersonTest.java
+++ b/src/test/java/seedu/address/model/person/PersonTest.java
@@ -3,11 +3,14 @@
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_DESCRIPTION_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY;
import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_MAJOR_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.VALID_SM_GITHUB_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_SM_LINKEDIN_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_YEAR_BOB;
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalPersons.ALICE;
import static seedu.address.testutil.TypicalPersons.BOB;
@@ -21,7 +24,7 @@ public class PersonTest {
@Test
public void asObservableList_modifyList_throwsUnsupportedOperationException() {
Person person = new PersonBuilder().build();
- assertThrows(UnsupportedOperationException.class, () -> person.getTags().remove(0));
+ assertThrows(UnsupportedOperationException.class, () -> person.getSocialMediaLinks().remove(0));
}
@Test
@@ -32,22 +35,19 @@ public void isSamePerson() {
// 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();
+ // same email, all other attributes different -> returns true
+ Person editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB)
+ .withMajor(VALID_MAJOR_BOB).withYear(VALID_YEAR_BOB)
+ .withDescription(VALID_DESCRIPTION_BOB)
+ .withSocialMediaLinks(VALID_SM_LINKEDIN_BOB, VALID_SM_GITHUB_BOB).build();
assertTrue(ALICE.isSamePerson(editedAlice));
- // different name, all other attributes same -> returns false
- editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build();
+ // different email, all other attributes same -> returns false
+ editedAlice = new PersonBuilder(ALICE).withEmail(VALID_EMAIL_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();
+ // email differs in case, all other attributes same -> returns false
+ Person editedBob = new PersonBuilder(BOB).withEmail(VALID_EMAIL_AMY).build();
assertFalse(BOB.isSamePerson(editedBob));
}
@@ -73,27 +73,57 @@ public void equals() {
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();
+ // different major -> returns false
+ editedAlice = new PersonBuilder(ALICE).withMajor(VALID_MAJOR_BOB).build();
+ assertFalse(ALICE.equals(editedAlice));
+
+ // different year -> returns false
+ editedAlice = new PersonBuilder(ALICE).withYear(VALID_YEAR_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();
+ // different description -> returns false
+ editedAlice = new PersonBuilder(ALICE).withDescription(VALID_DESCRIPTION_BOB).build();
assertFalse(ALICE.equals(editedAlice));
- // different tags -> returns false
- editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND).build();
+ // different social media links -> returns false
+ editedAlice = new PersonBuilder(ALICE).withSocialMediaLinks(VALID_SM_LINKEDIN_BOB, VALID_SM_GITHUB_BOB).build();
assertFalse(ALICE.equals(editedAlice));
}
+ @Test
+ public void hashCode_returnsExpectedHashCode() {
+ Person aliceCopy = new PersonBuilder(ALICE).build();
+ int expectedHashCode = ALICE.hashCode();
+ int actualHashCode = aliceCopy.hashCode();
+ assertEquals(expectedHashCode, actualHashCode);
+ }
+
@Test
public void toStringMethod() {
- String expected = Person.class.getCanonicalName() + "{name=" + ALICE.getName() + ", phone=" + ALICE.getPhone()
- + ", email=" + ALICE.getEmail() + ", address=" + ALICE.getAddress() + ", tags=" + ALICE.getTags() + "}";
+ String expected = Person.class.getCanonicalName() + "{name=" + ALICE.getName() + ", major=" + ALICE.getMajor()
+ + ", year=" + ALICE.getYear() + ", email=" + ALICE.getEmail() + ", description="
+ + ALICE.getDescription() + ", tutorials=" + ALICE.getTutorials()
+ + ", socialMediaLinks=" + ALICE.getSocialMediaLinks()
+ + ", nationality=" + ALICE.getNationality() + ", gender=" + ALICE.getGender() + "}";
assertEquals(expected, ALICE.toString());
}
+
+ @Test
+ public void equals_sameTutorials_returnsTrue() {
+ // Create two persons with the same tutorials
+ Person aliceWithTutorials = new PersonBuilder(ALICE)
+ .withTutorials("01", "02")
+ .build();
+ Person aliceCopyWithTutorials = new PersonBuilder(ALICE)
+ .withTutorials("01", "02")
+ .build();
+
+ // They should be considered equal even if other fields are different
+ assertTrue(aliceWithTutorials.equals(aliceCopyWithTutorials));
+ }
+
}
diff --git a/src/test/java/seedu/address/model/person/PhoneTest.java b/src/test/java/seedu/address/model/person/PhoneTest.java
deleted file mode 100644
index deaaa5ba190..00000000000
--- a/src/test/java/seedu/address/model/person/PhoneTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package seedu.address.model.person;
-
-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;
-
-public class PhoneTest {
-
- @Test
- public void constructor_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> new Phone(null));
- }
-
- @Test
- public void constructor_invalidPhone_throwsIllegalArgumentException() {
- String invalidPhone = "";
- assertThrows(IllegalArgumentException.class, () -> new Phone(invalidPhone));
- }
-
- @Test
- public void isValidPhone() {
- // null phone number
- assertThrows(NullPointerException.class, () -> Phone.isValidPhone(null));
-
- // invalid phone numbers
- assertFalse(Phone.isValidPhone("")); // empty string
- assertFalse(Phone.isValidPhone(" ")); // spaces only
- assertFalse(Phone.isValidPhone("91")); // less than 3 numbers
- assertFalse(Phone.isValidPhone("phone")); // non-numeric
- assertFalse(Phone.isValidPhone("9011p041")); // alphabets within digits
- assertFalse(Phone.isValidPhone("9312 1534")); // spaces within digits
-
- // valid phone numbers
- assertTrue(Phone.isValidPhone("911")); // exactly 3 numbers
- assertTrue(Phone.isValidPhone("93121534"));
- assertTrue(Phone.isValidPhone("124293842033123")); // long phone numbers
- }
-
- @Test
- public void equals() {
- Phone phone = new Phone("999");
-
- // same values -> returns true
- assertTrue(phone.equals(new Phone("999")));
-
- // same object -> returns true
- assertTrue(phone.equals(phone));
-
- // null -> returns false
- assertFalse(phone.equals(null));
-
- // different types -> returns false
- assertFalse(phone.equals(5.0f));
-
- // different values -> returns false
- assertFalse(phone.equals(new Phone("995")));
- }
-}
diff --git a/src/test/java/seedu/address/model/person/UniquePersonListTest.java b/src/test/java/seedu/address/model/person/UniquePersonListTest.java
index 17ae501df08..7f6308249e5 100644
--- a/src/test/java/seedu/address/model/person/UniquePersonListTest.java
+++ b/src/test/java/seedu/address/model/person/UniquePersonListTest.java
@@ -2,9 +2,9 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
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_EMAIL_BOB;
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalPersons.ALICE;
import static seedu.address.testutil.TypicalPersons.BOB;
@@ -40,11 +40,10 @@ public void contains_personInList_returnsTrue() {
}
@Test
- public void contains_personWithSameIdentityFieldsInList_returnsTrue() {
+ public void contains_personWithSameIdentityFieldsInList_returnsFalse() {
uniquePersonList.add(ALICE);
- Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND)
- .build();
- assertTrue(uniquePersonList.contains(editedAlice));
+ Person editedAlice = new PersonBuilder(ALICE).withEmail(VALID_EMAIL_BOB).build();
+ assertFalse(uniquePersonList.contains(editedAlice));
}
@Test
@@ -85,8 +84,7 @@ public void setPerson_editedPersonIsSamePerson_success() {
@Test
public void setPerson_editedPersonHasSameIdentity_success() {
uniquePersonList.add(ALICE);
- Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND)
- .build();
+ Person editedAlice = new PersonBuilder(ALICE).withEmail(VALID_EMAIL_BOB).build();
uniquePersonList.setPerson(ALICE, editedAlice);
UniquePersonList expectedUniquePersonList = new UniquePersonList();
expectedUniquePersonList.add(editedAlice);
@@ -172,4 +170,36 @@ public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationEx
public void toStringMethod() {
assertEquals(uniquePersonList.asUnmodifiableObservableList().toString(), uniquePersonList.toString());
}
+
+ @Test
+ public void equals_sameList_returnsTrue() {
+ UniquePersonList firstList = new UniquePersonList();
+ UniquePersonList secondList = new UniquePersonList();
+ assertTrue(firstList.equals(secondList));
+ }
+
+ @Test
+ public void equals_differentLists_returnsFalse() {
+ UniquePersonList firstList = new UniquePersonList();
+ UniquePersonList secondList = new UniquePersonList();
+ firstList.add(ALICE);
+ secondList.add(BOB);
+ assertFalse(firstList.equals(secondList));
+ }
+
+ @Test
+ public void hashCode_sameList_returnsSameHashCode() {
+ UniquePersonList firstList = new UniquePersonList();
+ UniquePersonList secondList = new UniquePersonList();
+ assertEquals(firstList.hashCode(), secondList.hashCode());
+ }
+
+ @Test
+ public void hashCode_differentLists_returnsDifferentHashCode() {
+ UniquePersonList firstList = new UniquePersonList();
+ UniquePersonList secondList = new UniquePersonList();
+ firstList.add(ALICE);
+ secondList.add(BOB);
+ assertNotEquals(firstList.hashCode(), secondList.hashCode());
+ }
}
diff --git a/src/test/java/seedu/address/model/person/YearTest.java b/src/test/java/seedu/address/model/person/YearTest.java
new file mode 100644
index 00000000000..07b7327baf2
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/YearTest.java
@@ -0,0 +1,73 @@
+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.testutil.Assert.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+public class YearTest {
+
+ @Test
+ public void constructor_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new Year(null));
+ }
+
+ @Test
+ public void constructor_invalidYear_throwsIllegalArgumentException() {
+ String invalidYear = "10";
+ assertThrows(IllegalArgumentException.class, () -> new Year(invalidYear));
+ }
+
+ @Test
+ public void isValidYear() {
+ // null year
+ assertThrows(NullPointerException.class, () -> Year.isValidYear(null));
+
+ // blank year
+ assertFalse(Year.isValidYear("")); // empty string
+ assertFalse(Year.isValidYear(" ")); // spaces only
+
+ // invalid year
+ assertFalse(Year.isValidYear("one")); // string
+ assertFalse(Year.isValidYear("-1")); // negative number
+ assertFalse(Year.isValidYear("1.5")); // decimal
+ assertFalse(Year.isValidYear("10")); // more than one digit long
+ assertFalse(Year.isValidYear(" 1")); // leading space
+ assertFalse(Year.isValidYear("1 ")); // trailing space
+ assertFalse(Year.isValidYear("^")); // only non-alphanumeric characters
+
+ // valid year
+ assertTrue(Year.isValidYear("1"));
+ assertTrue(Year.isValidYear("2"));
+ }
+
+ @Test
+ public void equals() {
+ Year year = new Year("2");
+
+ // same values -> returns true
+ assertTrue(year.equals(new Year("2")));
+
+ // same object -> returns true
+ assertTrue(year.equals(year));
+
+ // null -> returns false
+ assertFalse(year.equals(null));
+
+ // different types -> returns false
+ assertFalse(year.equals(5.0f));
+
+ // different values -> returns false
+ assertFalse(year.equals(new Year("3")));
+ }
+
+ @Test
+ public void hashCode_returnsExpectedHashCode() {
+ Year year = new Year("2");
+ int expectedHashCode = "2".hashCode();
+ int actualHashCode = year.hashCode();
+ assertEquals(expectedHashCode, actualHashCode);
+ }
+}
diff --git a/src/test/java/seedu/address/model/socialmedialink/SocialMediaLinkTest.java b/src/test/java/seedu/address/model/socialmedialink/SocialMediaLinkTest.java
new file mode 100644
index 00000000000..c2803f5a40b
--- /dev/null
+++ b/src/test/java/seedu/address/model/socialmedialink/SocialMediaLinkTest.java
@@ -0,0 +1,25 @@
+package seedu.address.model.socialmedialink;
+
+import static seedu.address.testutil.Assert.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+public class SocialMediaLinkTest {
+
+ @Test
+ public void constructor_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new SocialMediaLink(null));
+ }
+
+ @Test
+ public void constructor_invalidSocialMediaLink_throwsIllegalArgumentException() {
+ String invalidSocialMediaLink = "";
+ assertThrows(IllegalArgumentException.class, () -> new SocialMediaLink(invalidSocialMediaLink));
+ }
+
+ @Test
+ public void isValidSocialMediaLink() {
+ // null social media link
+ assertThrows(NullPointerException.class, () -> SocialMediaLink.isValidSocialMediaLink(null));
+ }
+}
diff --git a/src/test/java/seedu/address/model/tag/TagTest.java b/src/test/java/seedu/address/model/tag/TagTest.java
deleted file mode 100644
index 64d07d79ee2..00000000000
--- a/src/test/java/seedu/address/model/tag/TagTest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package seedu.address.model.tag;
-
-import static seedu.address.testutil.Assert.assertThrows;
-
-import org.junit.jupiter.api.Test;
-
-public class TagTest {
-
- @Test
- public void constructor_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> new Tag(null));
- }
-
- @Test
- public void constructor_invalidTagName_throwsIllegalArgumentException() {
- String invalidTagName = "";
- assertThrows(IllegalArgumentException.class, () -> new Tag(invalidTagName));
- }
-
- @Test
- public void isValidTagName() {
- // null tag name
- assertThrows(NullPointerException.class, () -> Tag.isValidTagName(null));
- }
-
-}
diff --git a/src/test/java/seedu/address/model/tutorial/TutorialContainsSlotsPredicateTest.java b/src/test/java/seedu/address/model/tutorial/TutorialContainsSlotsPredicateTest.java
new file mode 100644
index 00000000000..dd677fd21a2
--- /dev/null
+++ b/src/test/java/seedu/address/model/tutorial/TutorialContainsSlotsPredicateTest.java
@@ -0,0 +1,72 @@
+package seedu.address.model.tutorial;
+
+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.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.testutil.PersonBuilder;
+
+public class TutorialContainsSlotsPredicateTest {
+
+ @Test
+ public void equals() {
+ List firstPredicateSlotList = Collections.singletonList("01");
+ List secondPredicateSlotList = Arrays.asList("01", "02");
+
+ TutorialContainsSlotsPredicate firstPredicate = new TutorialContainsSlotsPredicate(firstPredicateSlotList);
+ TutorialContainsSlotsPredicate secondPredicate = new TutorialContainsSlotsPredicate(secondPredicateSlotList);
+
+ // same object -> returns true
+ assertTrue(firstPredicate.equals(firstPredicate));
+
+ // same values -> returns true
+ TutorialContainsSlotsPredicate firstPredicateCopy = new TutorialContainsSlotsPredicate(firstPredicateSlotList);
+ assertTrue(firstPredicate.equals(firstPredicateCopy));
+
+ // different types -> returns false
+ assertFalse(firstPredicate.equals(1));
+
+ // null -> returns false
+ assertFalse(firstPredicate.equals(null));
+
+ // different person -> returns false
+ assertFalse(firstPredicate.equals(secondPredicate));
+ }
+
+ @Test
+ public void test_tutorialContainsSlots_returnsTrue() {
+ // One slot
+ TutorialContainsSlotsPredicate predicate = new TutorialContainsSlotsPredicate(Collections.singletonList("01"));
+ assertTrue(predicate.test(new PersonBuilder().withTutorials("01", "02").build()));
+
+ // Multiple slots
+ predicate = new TutorialContainsSlotsPredicate(Arrays.asList("01", "02"));
+ assertTrue(predicate.test(new PersonBuilder().withTutorials("01", "02").build()));
+
+ // Only one matching slot
+ predicate = new TutorialContainsSlotsPredicate(Arrays.asList("02", "03"));
+ assertTrue(predicate.test(new PersonBuilder().withTutorials("01", "03").build()));
+ }
+
+ @Test
+ public void test_tutorialDoesNotContainSlots_returnsFalse() {
+ // Non-matching slot
+ TutorialContainsSlotsPredicate predicate = new TutorialContainsSlotsPredicate(Arrays.asList("03"));
+ assertFalse(predicate.test(new PersonBuilder().withTutorials("01", "02").build()));
+ }
+
+ @Test
+ public void toStringMethod() {
+ List slots = List.of("01", "02");
+ TutorialContainsSlotsPredicate predicate = new TutorialContainsSlotsPredicate(slots);
+
+ String expected = TutorialContainsSlotsPredicate.class.getCanonicalName() + "{slots=" + slots + "}";
+ assertEquals(expected, predicate.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/model/tutorial/TutorialTest.java b/src/test/java/seedu/address/model/tutorial/TutorialTest.java
new file mode 100644
index 00000000000..b3b4b23306a
--- /dev/null
+++ b/src/test/java/seedu/address/model/tutorial/TutorialTest.java
@@ -0,0 +1,82 @@
+package seedu.address.model.tutorial;
+
+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;
+
+public class TutorialTest {
+
+ @Test
+ public void constructor_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new Tutorial(null));
+ }
+
+ @Test
+ public void constructor_invalidTutorial_throwsIllegalArgumentException() {
+ String invalidTutorial = "00";
+ assertThrows(IllegalArgumentException.class, () -> new Tutorial(invalidTutorial));
+ }
+
+ @Test
+ public void constructor_validTutorial_success() {
+ String validTutorial = "05";
+ Tutorial tutorial = new Tutorial(validTutorial);
+ assertTrue(tutorial.isValidTutorial(validTutorial));
+ }
+
+ @Test
+ public void isValidTutorial() {
+ // null tutorial
+ assertThrows(NullPointerException.class, () -> Tutorial.isValidTutorial(null));
+
+ // invalid tutorial numbers
+ assertFalse(Tutorial.isValidTutorial("00"));
+ assertFalse(Tutorial.isValidTutorial("23"));
+ assertFalse(Tutorial.isValidTutorial("invalid"));
+ assertFalse(Tutorial.isValidTutorial("1A"));
+
+ // valid tutorial numbers
+ assertTrue(Tutorial.isValidTutorial("01"));
+ assertTrue(Tutorial.isValidTutorial("12"));
+ assertTrue(Tutorial.isValidTutorial("22"));
+ }
+
+ @Test
+ public void getValue() {
+ Tutorial tutorial = new Tutorial("05");
+ assertEquals("05", tutorial.getValue());
+ }
+
+ @Test
+ public void equals() {
+ Tutorial tutorial1 = new Tutorial("05");
+ Tutorial tutorial2 = new Tutorial("05");
+ Tutorial tutorial3 = new Tutorial("01");
+
+ // same object -> returns true
+ assertTrue(tutorial1.equals(tutorial1));
+
+ // same values -> returns true
+ assertTrue(tutorial1.equals(tutorial2));
+
+ // different types -> returns false
+ assertFalse(tutorial1.equals(5));
+
+ // null -> returns false
+ assertFalse(tutorial1.equals(null));
+
+ // different values -> returns false
+ assertFalse(tutorial1.equals(tutorial3));
+ }
+
+ @Test
+ public void hashCode_returnsExpectedHashCode() {
+ Tutorial tutorial = new Tutorial("05");
+ int expectedHashCode = "05".hashCode();
+ int actualHashCode = tutorial.hashCode();
+ assertEquals(expectedHashCode, actualHashCode);
+ }
+}
diff --git a/src/test/java/seedu/address/storage/JsonAdaptedGroupTest.java b/src/test/java/seedu/address/storage/JsonAdaptedGroupTest.java
new file mode 100644
index 00000000000..c06bd9bcaa4
--- /dev/null
+++ b/src/test/java/seedu/address/storage/JsonAdaptedGroupTest.java
@@ -0,0 +1,59 @@
+package seedu.address.storage;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static seedu.address.storage.JsonAdaptedGroup.INVALID_NUMBER_MESSAGE;
+import static seedu.address.storage.JsonAdaptedPerson.MISSING_FIELD_MESSAGE_FORMAT;
+import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalGroups.GROUP1;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.tutorial.Tutorial;
+
+public class JsonAdaptedGroupTest {
+
+ private static final int INVALID_NUMBER = -1;
+ private static final JsonAdaptedTutorial INVALID_TUTORIAL = new JsonAdaptedTutorial("1");
+
+ private static final int VALID_NUMBER = GROUP1.getNumber();
+ private static final JsonAdaptedTutorial VALID_TUTORIAL = new JsonAdaptedTutorial(GROUP1.getTutorial());
+ private static final List VALID_MEMBERS = GROUP1.getMembers().stream()
+ .map(JsonAdaptedPerson::new)
+ .collect(Collectors.toList());
+ private static final List VALID_TASKS = GROUP1.getTasks().getTasks().stream()
+ .map(JsonAdaptedTask::new)
+ .collect(Collectors.toList());
+
+ @Test
+ public void toModelType_validGroupDetails_returnsGroup() throws Exception {
+ JsonAdaptedGroup group = new JsonAdaptedGroup(GROUP1);
+ assertEquals(GROUP1, group.toModelType());
+ }
+
+ @Test
+ public void toModelType_invalidNumber_throwsIllegalValueException() {
+ JsonAdaptedGroup group = new JsonAdaptedGroup(INVALID_NUMBER,
+ VALID_TUTORIAL, VALID_MEMBERS, VALID_TASKS);
+ String expectedMessage = INVALID_NUMBER_MESSAGE;
+ assertThrows(IllegalValueException.class, expectedMessage, group::toModelType);
+ }
+
+ @Test
+ public void toModelType_nullTutorial_throwsIllegalValueException() {
+ JsonAdaptedGroup group = new JsonAdaptedGroup(VALID_NUMBER, null, VALID_MEMBERS, VALID_TASKS);
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Tutorial.class.getSimpleName());
+ assertThrows(IllegalValueException.class, expectedMessage, group::toModelType);
+ }
+
+ @Test
+ public void toModelType_invalidTutorial_throwsIllegalValueException() {
+ JsonAdaptedGroup group = new JsonAdaptedGroup(VALID_NUMBER, INVALID_TUTORIAL, VALID_MEMBERS, VALID_TASKS);
+ String expectedMessage = Tutorial.MESSAGE_CONSTRAINTS;
+ assertThrows(IllegalValueException.class, expectedMessage, group::toModelType);
+ }
+
+}
diff --git a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java b/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java
index 83b11331cdb..2aa0011155d 100644
--- a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java
+++ b/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java
@@ -12,24 +12,35 @@
import org.junit.jupiter.api.Test;
import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.person.Address;
+import seedu.address.model.person.Description;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Major;
import seedu.address.model.person.Name;
-import seedu.address.model.person.Phone;
+import seedu.address.model.person.Year;
public class JsonAdaptedPersonTest {
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_MAJOR = "Computer Games";
+ private static final String INVALID_YEAR = "1.5";
+ private static final String INVALID_DESCRIPTION = "";
private static final String INVALID_EMAIL = "example.com";
- private static final String INVALID_TAG = "#friend";
+ private static final String INVALID_TUTORIAL = "1";
+ private static final String INVALID_SM = "#www.invalid.com";
+ private static final String INVALID_NATIONALITY = "singaporean";
+ private static final String INVALID_GENDER = "male";
private static final String VALID_NAME = BENSON.getName().toString();
- private static final String VALID_PHONE = BENSON.getPhone().toString();
+ private static final String VALID_MAJOR = BENSON.getMajor().toString();
+ private static final String VALID_YEAR = BENSON.getYear().toString();
private static final String VALID_EMAIL = BENSON.getEmail().toString();
- private static final String VALID_ADDRESS = BENSON.getAddress().toString();
- private static final List VALID_TAGS = BENSON.getTags().stream()
- .map(JsonAdaptedTag::new)
+ private static final String VALID_DESCRIPTION = BENSON.getDescription().toString();
+ private static final String VALID_NATIONALITY = BENSON.getNationality().toString();
+ private static final String VALID_GENDER = BENSON.getGender().toString();
+ private static final List VALID_TUTORIALS = BENSON.getTutorials().stream()
+ .map(JsonAdaptedTutorial::new)
+ .collect(Collectors.toList());
+ private static final List VALID_SM = BENSON.getSocialMediaLinks().stream()
+ .map(JsonAdaptedSocialMedia::new)
.collect(Collectors.toList());
@Test
@@ -41,69 +52,112 @@ public void toModelType_validPersonDetails_returnsPerson() throws Exception {
@Test
public void toModelType_invalidName_throwsIllegalValueException() {
JsonAdaptedPerson person =
- new JsonAdaptedPerson(INVALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
+ new JsonAdaptedPerson(INVALID_NAME, VALID_MAJOR, VALID_YEAR, VALID_EMAIL, VALID_DESCRIPTION,
+ VALID_TUTORIALS, VALID_SM, VALID_NATIONALITY, VALID_GENDER);
+
String expectedMessage = Name.MESSAGE_CONSTRAINTS;
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
public void toModelType_nullName_throwsIllegalValueException() {
- JsonAdaptedPerson person = new JsonAdaptedPerson(null, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
+ JsonAdaptedPerson person =
+ new JsonAdaptedPerson(null, VALID_MAJOR, VALID_YEAR, VALID_EMAIL, VALID_DESCRIPTION,
+ VALID_TUTORIALS, VALID_SM, VALID_NATIONALITY, VALID_GENDER);
String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName());
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
- public void toModelType_invalidPhone_throwsIllegalValueException() {
+ public void toModelType_invalidMajor_throwsIllegalValueException() {
+ JsonAdaptedPerson person =
+ new JsonAdaptedPerson(VALID_NAME, INVALID_MAJOR, VALID_YEAR, VALID_EMAIL, VALID_DESCRIPTION,
+ VALID_TUTORIALS, VALID_SM, VALID_NATIONALITY, VALID_GENDER);
+ String expectedMessage = Major.MESSAGE_CONSTRAINTS;
+ assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
+ }
+
+ @Test
+ public void toModelType_nullMajor_throwsIllegalValueException() {
+ JsonAdaptedPerson person =
+ new JsonAdaptedPerson(VALID_NAME, null, VALID_YEAR, VALID_EMAIL, VALID_DESCRIPTION, VALID_TUTORIALS,
+ VALID_SM, VALID_NATIONALITY, VALID_GENDER);
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Major.class.getSimpleName());
+ assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
+ }
+
+ @Test
+ public void toModelType_invalidYear_throwsIllegalValueException() {
JsonAdaptedPerson person =
- new JsonAdaptedPerson(VALID_NAME, INVALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
- String expectedMessage = Phone.MESSAGE_CONSTRAINTS;
+ new JsonAdaptedPerson(VALID_NAME, VALID_MAJOR, INVALID_YEAR, VALID_EMAIL, VALID_DESCRIPTION,
+ VALID_TUTORIALS, VALID_SM, VALID_NATIONALITY, VALID_GENDER);
+ String expectedMessage = Year.MESSAGE_CONSTRAINTS;
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
- public void toModelType_nullPhone_throwsIllegalValueException() {
- JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, null, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
- String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName());
+ public void toModelType_nullYear_throwsIllegalValueException() {
+ JsonAdaptedPerson person =
+ new JsonAdaptedPerson(VALID_NAME, VALID_MAJOR, null, VALID_EMAIL, VALID_DESCRIPTION,
+ VALID_TUTORIALS, VALID_SM, VALID_NATIONALITY, VALID_GENDER);
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Year.class.getSimpleName());
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
public void toModelType_invalidEmail_throwsIllegalValueException() {
JsonAdaptedPerson person =
- new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, INVALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
+ new JsonAdaptedPerson(VALID_NAME, VALID_MAJOR, VALID_YEAR, INVALID_EMAIL, VALID_DESCRIPTION,
+ VALID_TUTORIALS, VALID_SM, VALID_NATIONALITY, VALID_GENDER);
String expectedMessage = Email.MESSAGE_CONSTRAINTS;
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
public void toModelType_nullEmail_throwsIllegalValueException() {
- JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, null, VALID_ADDRESS, VALID_TAGS);
+ JsonAdaptedPerson person =
+ new JsonAdaptedPerson(VALID_NAME, VALID_MAJOR, VALID_YEAR, null, VALID_DESCRIPTION,
+ VALID_TUTORIALS, VALID_SM, VALID_NATIONALITY, VALID_GENDER);
String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName());
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
- public void toModelType_invalidAddress_throwsIllegalValueException() {
+ public void toModelType_invalidDescription_throwsIllegalValueException() {
JsonAdaptedPerson person =
- new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, INVALID_ADDRESS, VALID_TAGS);
- String expectedMessage = Address.MESSAGE_CONSTRAINTS;
+ new JsonAdaptedPerson(VALID_NAME, VALID_MAJOR, VALID_YEAR, VALID_EMAIL, INVALID_DESCRIPTION,
+ VALID_TUTORIALS, VALID_SM, VALID_NATIONALITY, VALID_GENDER);
+
+ String expectedMessage = Description.MESSAGE_CONSTRAINTS;
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
- public void toModelType_nullAddress_throwsIllegalValueException() {
- JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, null, VALID_TAGS);
- String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName());
+ public void toModelType_nullDescription_throwsIllegalValueException() {
+ JsonAdaptedPerson person =
+ new JsonAdaptedPerson(VALID_NAME, VALID_MAJOR, VALID_YEAR, VALID_EMAIL, null,
+ VALID_TUTORIALS, VALID_SM, VALID_NATIONALITY, VALID_GENDER);
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Description.class.getSimpleName());
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
- public void toModelType_invalidTags_throwsIllegalValueException() {
- List invalidTags = new ArrayList<>(VALID_TAGS);
- invalidTags.add(new JsonAdaptedTag(INVALID_TAG));
+ public void toModelType_invalidTutorials_throwsIllegalValueException() {
+ List invalidTutorials = new ArrayList<>(VALID_TUTORIALS);
+ invalidTutorials.add(new JsonAdaptedTutorial(INVALID_TUTORIAL));
+ JsonAdaptedPerson person =
+ new JsonAdaptedPerson(VALID_NAME, VALID_MAJOR, VALID_YEAR, VALID_EMAIL, VALID_DESCRIPTION,
+ invalidTutorials, VALID_SM, VALID_NATIONALITY, VALID_GENDER);
+ assertThrows(IllegalValueException.class, person::toModelType);
+ }
+
+ @Test
+ public void toModelType_invalidSocialMediaLinks_throwsIllegalValueException() {
+ List invalidSocialMediaLinks = new ArrayList<>(VALID_SM);
+ invalidSocialMediaLinks.add(new JsonAdaptedSocialMedia(INVALID_SM));
JsonAdaptedPerson person =
- new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, invalidTags);
+ new JsonAdaptedPerson(VALID_NAME, VALID_MAJOR, VALID_YEAR, VALID_EMAIL, VALID_DESCRIPTION,
+ VALID_TUTORIALS, invalidSocialMediaLinks, VALID_NATIONALITY, VALID_GENDER);
assertThrows(IllegalValueException.class, person::toModelType);
}
diff --git a/src/test/java/seedu/address/storage/JsonAdaptedTaskTest.java b/src/test/java/seedu/address/storage/JsonAdaptedTaskTest.java
new file mode 100644
index 00000000000..0ac7008d1d6
--- /dev/null
+++ b/src/test/java/seedu/address/storage/JsonAdaptedTaskTest.java
@@ -0,0 +1,42 @@
+package seedu.address.storage;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static seedu.address.testutil.Assert.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.group.tasks.Task;
+import seedu.address.testutil.TypicalTasks;
+
+public class JsonAdaptedTaskTest {
+
+ @Test
+ public void toModelType_validTaskDetails_returnsTask() throws Exception {
+ Task standardTask = TypicalTasks.CS2101_OP1_UPLOAD;
+ JsonAdaptedTask jsonAdaptedTask = new JsonAdaptedTask(standardTask);
+ assertEquals(standardTask, jsonAdaptedTask.toModelType());
+ }
+
+ @Test
+ public void toModelType_nullTaskDescription_throwsIllegalValueException() {
+ JsonAdaptedTask adaptedTask = new JsonAdaptedTask(null, "NOT_DONE", "CS2101", "TODO", "24/12/2023");
+ String expectedMessage = "Task's description is missing!";
+ assertThrows(IllegalValueException.class, expectedMessage, adaptedTask::toModelType);
+ }
+
+ // Similar tests for other null fields
+
+ @Test
+ public void fromModelType_validTaskDetails_returnsJsonAdaptedTask() throws Exception {
+ Task standardTask = TypicalTasks.CS2101_OP1_UPLOAD;
+ JsonAdaptedTask jsonAdaptedTask = new JsonAdaptedTask(standardTask);
+ Task modelTask = jsonAdaptedTask.toModelType();
+
+ assertEquals(standardTask.getTask(), modelTask.getTask());
+ assertEquals(standardTask.getStatus(), modelTask.getStatus());
+ assertEquals(standardTask.getModule(), modelTask.getModule());
+ assertEquals(standardTask.getType(), modelTask.getType());
+ assertEquals(standardTask.getBy(), modelTask.getBy());
+ }
+}
diff --git a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java b/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java
index 4584bd5044e..7b412911434 100644
--- a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java
+++ b/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java
@@ -5,12 +5,16 @@
import java.util.stream.Stream;
import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
-import seedu.address.model.person.Address;
+import seedu.address.model.person.Description;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Gender;
+import seedu.address.model.person.Major;
import seedu.address.model.person.Name;
+import seedu.address.model.person.Nationality;
import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.person.Year;
+import seedu.address.model.socialmedialink.SocialMediaLink;
+import seedu.address.model.tutorial.Tutorial;
/**
* A utility class to help with building EditPersonDescriptor objects.
@@ -33,10 +37,13 @@ public EditPersonDescriptorBuilder(EditPersonDescriptor descriptor) {
public EditPersonDescriptorBuilder(Person person) {
descriptor = new EditPersonDescriptor();
descriptor.setName(person.getName());
- descriptor.setPhone(person.getPhone());
+ descriptor.setMajor(person.getMajor());
+ descriptor.setYear(person.getYear());
descriptor.setEmail(person.getEmail());
- descriptor.setAddress(person.getAddress());
- descriptor.setTags(person.getTags());
+ descriptor.setDescription(person.getDescription());
+ descriptor.setTutorials(person.getTutorials());
+ descriptor.setSocialMediaLinks(person.getSocialMediaLinks());
+ descriptor.setNationality(person.getNationality());
}
/**
@@ -48,10 +55,18 @@ public EditPersonDescriptorBuilder withName(String name) {
}
/**
- * Sets the {@code Phone} of the {@code EditPersonDescriptor} that we are building.
+ * Sets the {@code Major} of the {@code EditPersonDescriptor} that we are building.
*/
- public EditPersonDescriptorBuilder withPhone(String phone) {
- descriptor.setPhone(new Phone(phone));
+ public EditPersonDescriptorBuilder withMajor(String major) {
+ descriptor.setMajor(new Major(major));
+ return this;
+ }
+
+ /**
+ * Sets the {@code Year} of the {@code EditPersonDescriptor} that we are building.
+ */
+ public EditPersonDescriptorBuilder withYear(String year) {
+ descriptor.setYear(new Year(year));
return this;
}
@@ -64,20 +79,47 @@ public EditPersonDescriptorBuilder withEmail(String email) {
}
/**
- * Sets the {@code Address} of the {@code EditPersonDescriptor} that we are building.
+ * Sets the {@code Description} of the {@code EditPersonDescriptor} that we are building.
+ */
+ public EditPersonDescriptorBuilder withDescription(String description) {
+ descriptor.setDescription(new Description(description));
+ return this;
+ }
+
+ /**
+ * Parses the {@code socialMediaLinks} into a {@code Set} and set it to the
+ * {@code EditPersonDescriptor} that we are building.
*/
- public EditPersonDescriptorBuilder withAddress(String address) {
- descriptor.setAddress(new Address(address));
+ public EditPersonDescriptorBuilder withSocialMediaLinks(String... socialMediaLinks) {
+ Set socialmediaSet = Stream.of(socialMediaLinks).map(SocialMediaLink::new)
+ .collect(Collectors.toSet());
+ descriptor.setSocialMediaLinks(socialmediaSet);
return this;
}
/**
- * Parses the {@code tags} into a {@code Set} and set it to the {@code EditPersonDescriptor}
+ * Parses the {@code tutorials} into a {@code Set} and sets it to the {@code EditPersonDescriptor}
* that we are building.
*/
- public EditPersonDescriptorBuilder withTags(String... tags) {
- Set tagSet = Stream.of(tags).map(Tag::new).collect(Collectors.toSet());
- descriptor.setTags(tagSet);
+ public EditPersonDescriptorBuilder withTutorials(String... tutorials) {
+ Set tutorialSet = Stream.of(tutorials).map(Tutorial::new).collect(Collectors.toSet());
+ descriptor.setTutorials(tutorialSet);
+ return this;
+ }
+
+ /**
+ * Sets the {@code Nationality} of the {@code EditPersonDescriptor} that we are building.
+ */
+ public EditPersonDescriptorBuilder withNationality(String nationality) {
+ descriptor.setNationality(new Nationality(nationality));
+ return this;
+ }
+
+ /**
+ * Sets the {@code Gender} of the {@code EditPersonDescriptor} that we are building.
+ */
+ public EditPersonDescriptorBuilder withGender(String gender) {
+ descriptor.setGender(new Gender(gender));
return this;
}
diff --git a/src/test/java/seedu/address/testutil/GroupBuilder.java b/src/test/java/seedu/address/testutil/GroupBuilder.java
new file mode 100644
index 00000000000..92c84caab86
--- /dev/null
+++ b/src/test/java/seedu/address/testutil/GroupBuilder.java
@@ -0,0 +1,91 @@
+package seedu.address.testutil;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import seedu.address.model.group.Group;
+import seedu.address.model.group.exceptions.TaskException;
+import seedu.address.model.group.tasks.Task;
+import seedu.address.model.group.tasks.TaskInitializer;
+import seedu.address.model.group.tasks.TaskList;
+import seedu.address.model.person.Person;
+import seedu.address.model.tutorial.Tutorial;
+
+/**
+ * A utility class to help with building Group objects.
+ */
+public class GroupBuilder {
+
+ public static final String DEFAULT_NUMBER = "1";
+ public static final String DEFAULT_TUTORIAL = "01";
+
+ private int number;
+ private Tutorial tutorial;
+ private Set members;
+ private TaskList tasks;
+
+ /**
+ * Creates a {@code GroupBuilder} with the default details.
+ */
+ public GroupBuilder() {
+ number = Integer.parseInt(DEFAULT_NUMBER);
+ tutorial = new Tutorial(DEFAULT_TUTORIAL);
+ members = new HashSet<>();
+ try {
+ tasks = TaskInitializer.initializeTasks();
+ } catch (TaskException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Initializes the GroupBuilder with the data of {@code groupToCopy}.
+ */
+ public GroupBuilder(Group groupToCopy) {
+ number = groupToCopy.getNumber();
+ tutorial = groupToCopy.getTutorial();
+ members = new HashSet<>(groupToCopy.getMembers());
+ tasks = new TaskList(groupToCopy.getTasks().getTasks());
+ }
+
+ /**
+ * Sets the {@code Number} of the {@code Group} that we are building.
+ */
+ public GroupBuilder withNumber(String number) {
+ this.number = Integer.parseInt(number);
+ return this;
+ }
+
+ /**
+ * Sets the {@code Tutorial} of the {@code Group} that we are building.
+ */
+ public GroupBuilder withTutorial(String tutorial) {
+ this.tutorial = new Tutorial(tutorial);
+ return this;
+ }
+
+ /**
+ * Parses the {@code members} into a {@code Set} and set it to the {@code Group} that
+ * we are building.
+ */
+ public GroupBuilder withMembers(Person... members) {
+ this.members = new HashSet<>();
+ this.members.addAll(Arrays.stream(members).collect(Collectors.toSet()));
+ return this;
+ }
+
+ /**
+ * Parses the {@code tasks} into a {@code List} and set it to the {@code Group} that
+ * we are building.
+ */
+ public GroupBuilder withTasks(Task... tasks) {
+ this.tasks = new TaskList(Arrays.stream(tasks).collect(Collectors.toList()));
+ return this;
+ }
+
+ public Group build() {
+ return new Group(number, tutorial, members, tasks);
+ }
+}
diff --git a/src/test/java/seedu/address/testutil/PersonBuilder.java b/src/test/java/seedu/address/testutil/PersonBuilder.java
index 6be381d39ba..e312deed695 100644
--- a/src/test/java/seedu/address/testutil/PersonBuilder.java
+++ b/src/test/java/seedu/address/testutil/PersonBuilder.java
@@ -3,12 +3,16 @@
import java.util.HashSet;
import java.util.Set;
-import seedu.address.model.person.Address;
+import seedu.address.model.person.Description;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Gender;
+import seedu.address.model.person.Major;
import seedu.address.model.person.Name;
+import seedu.address.model.person.Nationality;
import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.person.Year;
+import seedu.address.model.socialmedialink.SocialMediaLink;
+import seedu.address.model.tutorial.Tutorial;
import seedu.address.model.util.SampleDataUtil;
/**
@@ -17,25 +21,36 @@
public class PersonBuilder {
public static final String DEFAULT_NAME = "Amy Bee";
- public static final String DEFAULT_PHONE = "85355255";
- public static final String DEFAULT_EMAIL = "amy@gmail.com";
- public static final String DEFAULT_ADDRESS = "123, Jurong West Ave 6, #08-111";
+ public static final String DEFAULT_MAJOR = "Computer Science";
+ public static final String DEFAULT_YEAR = "2";
+ public static final String DEFAULT_EMAIL = "amy@u.nus.edu";
+ public static final String DEFAULT_DESCRIPTION = "CS nerd";
+ public static final String DEFAULT_NATIONALITY = "local";
+ public static final String DEFAULT_GENDER = "F";
private Name name;
- private Phone phone;
+ private Major major;
+ private Year year;
private Email email;
- private Address address;
- private Set