From f67b60657863408b193538b58d1de1c52e6eb1d1 Mon Sep 17 00:00:00 2001 From: GabrielWLM Date: Tue, 26 Oct 2021 04:12:02 +0800 Subject: [PATCH 1/7] add basic find group command --- .../seedu/address/commons/core/Messages.java | 3 +- .../logic/commands/FindGroupCommand.java | 51 +++++++++++++++++++ .../ClassCodeContainsKeywordsPredicate.java | 2 +- ...utorialGroupContainsKeywordsPredicate.java | 33 ++++++++++++ 4 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 src/main/java/seedu/address/logic/commands/FindGroupCommand.java create mode 100644 src/main/java/seedu/address/model/tutorialgroup/TutorialGroupContainsKeywordsPredicate.java diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java index 94c5f7a9dca..7193f209bfa 100644 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ b/src/main/java/seedu/address/commons/core/Messages.java @@ -11,6 +11,7 @@ public class Messages { public static final String MESSAGE_INVALID_CLASS_DISPLAYED_INDEX = "The class index provided is invalid"; public static final String MESSAGE_INVALID_GROUP_DISPLAYED_INDEX = "The group index provided is invalid"; public static final String MESSAGE_STUDENTS_LISTED_OVERVIEW = "%1$d students listed!"; - public static final String MESSAGE_TUTORIAL_CLASSES_LISTED_OVERVIEW = "%1$d classes listed!"; + public static final String MESSAGE_TUTORIAL_CLASSES_LISTED_OVERVIEW = "%1$d tutorial classes listed!"; + public static final String MESSAGE_TUTORIAL_GROUPS_LISTED_OVERVIEW = "%1$d tutorial groups listed!"; } 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..4a51ed924a3 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/FindGroupCommand.java @@ -0,0 +1,51 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_CLASSCODE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TYPE; + +import seedu.address.commons.core.Messages; +import seedu.address.model.Model; +import seedu.address.model.tutorialgroup.TutorialGroupContainsKeywordsPredicate; + +/** + * Finds and lists all tutorial groups in ClassMATE whose class code and tutorial group contains any of the argument + * keywords. + * Keyword matching is case-insensitive. + */ +public class FindGroupCommand extends Command { + + public static final String COMMAND_WORD = "findc"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all tutorial groups whose class code and" + + "tutorial group contain any of the specified keywords (case-insensitive) and displays them as a list" + + "with index numbers.\n" + + "Parameters: " + + PREFIX_CLASSCODE + "KEYWORD" + + "[" + PREFIX_TYPE + "KEYWORD\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_CLASSCODE + "G06 " + + PREFIX_TYPE + "OP1 "; + + private final TutorialGroupContainsKeywordsPredicate predicate; + + public FindGroupCommand(TutorialGroupContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredTutorialGroupList(predicate); + return new CommandResult( + String.format(Messages.MESSAGE_TUTORIAL_GROUPS_LISTED_OVERVIEW, + model.getFilteredTutorialGroupList().size())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FindGroupCommand // instanceof handles nulls + && predicate.equals(((FindGroupCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/address/model/tutorialclass/ClassCodeContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/tutorialclass/ClassCodeContainsKeywordsPredicate.java index d9fa367ffb8..e0cbf40c15b 100644 --- a/src/main/java/seedu/address/model/tutorialclass/ClassCodeContainsKeywordsPredicate.java +++ b/src/main/java/seedu/address/model/tutorialclass/ClassCodeContainsKeywordsPredicate.java @@ -6,7 +6,7 @@ import seedu.address.commons.util.StringUtil; /** - * Tests that a {@code Student}'s {@code Name} matches any of the keywords given. + * Tests that a {@code TutorialClass}'s {@code ClassCode} matches any of the keywords given. */ public class ClassCodeContainsKeywordsPredicate implements Predicate { private final List keywords; diff --git a/src/main/java/seedu/address/model/tutorialgroup/TutorialGroupContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/tutorialgroup/TutorialGroupContainsKeywordsPredicate.java new file mode 100644 index 00000000000..8fa1e2a0797 --- /dev/null +++ b/src/main/java/seedu/address/model/tutorialgroup/TutorialGroupContainsKeywordsPredicate.java @@ -0,0 +1,33 @@ +package seedu.address.model.tutorialgroup; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.util.StringUtil; + +/** + * Tests that a {@code TutorialGroup}'s {@code GroupName} matches any of the keywords given. + */ +public class TutorialGroupContainsKeywordsPredicate implements Predicate { + private final List keywords; + + public TutorialGroupContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(TutorialGroup tutorialGroup) { + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase( + tutorialGroup.getClassCode().toString(), keyword)); + //TODO Add GroupType as a keyword + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TutorialGroupContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((TutorialGroupContainsKeywordsPredicate) other).keywords)); // state check + } + +} From 5ab998027e57e5312996f36bc29405c344084bde Mon Sep 17 00:00:00 2001 From: rushilramesh Date: Thu, 28 Oct 2021 16:50:24 +0800 Subject: [PATCH 2/7] Update UG --- docs/UserGuide.md | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index a0dedc6315f..c73be9d0fac 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -47,21 +47,18 @@ We hope you find this User Guide helpful in using ClassMATE! 1. Double-click the file to start the app. The GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
![Ui](images/Ui-annotated.png) 1. Type the command in the **Command-Line Input** and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
- Some example commands you can try: - * **`liststu`** : Lists all students. - * **`addstu n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01 c/G06`**: Adds a student named `John Doe` to ClassMATE. - - * **`deletestu 3`**: Deletes the 3rd student shown in the current list. - - * **`clear`** : Deletes all students. - - * **`exit`** : Exits the app. -1. Refer to the [Features](#features) below for details of each command. +1. Refer to the [Tutorial](#CLI-Tutorial) below for details of each command. -------------------------------------------------------------------------------------------------------------------- -## Features +## CLI Tutorial + +In this section, you will familiarize yourself with the use of CLI to facilitate your experience when using ClassMATE. +All commands would be typed in the **Command-Line Input** as shown in the image below. + + +### Command Features
**:information_source: Notes about the command format:**
From 415ceae98f75f6a0ea66a1b76de73b12b40017da Mon Sep 17 00:00:00 2001 From: rushilramesh Date: Thu, 28 Oct 2021 22:38:57 +0800 Subject: [PATCH 3/7] Update UG --- docs/UserGuide.md | 74 +++++++++++++++++++++--- docs/images/CommandLineInput.png | Bin 0 -> 6586 bytes docs/images/DeleteCommandScreenshot.png | Bin 0 -> 36897 bytes docs/img.png | Bin 0 -> 6586 bytes 4 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 docs/images/CommandLineInput.png create mode 100644 docs/images/DeleteCommandScreenshot.png create mode 100644 docs/img.png diff --git a/docs/UserGuide.md b/docs/UserGuide.md index c73be9d0fac..bff4dc87a3d 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -44,21 +44,43 @@ We hope you find this User Guide helpful in using ClassMATE! 1. Ensure you have Java `11` or above installed in your Computer. (Go to [this website](https://codejava.net/java-se/download-and-install-java-11-openjdk-and-oracle-jdk) and follow the instructions to download and install Oracle JDK 11, which is _basically_ Java 11.) 1. Download the latest `classmate.jar` from [here](https://github.com/AY2122S1-CS2103T-W15-1/tp/releases). 1. Copy the file to the folder you want to use as the _home folder_ for your ClassMATE. -1. Double-click the file to start the app. The GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
+1. Double-click the file to start the app. The GUI similar to the image below should appear in a few seconds. Note how the app contains some sample data.
![Ui](images/Ui-annotated.png) 1. Type the command in the **Command-Line Input** and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
-1. Refer to the [Tutorial](#CLI-Tutorial) below for details of each command. +1. Refer to the [Features](#Features) below for details of each command. -------------------------------------------------------------------------------------------------------------------- ## CLI Tutorial In this section, you will familiarize yourself with the use of CLI to facilitate your experience when using ClassMATE. -All commands would be typed in the **Command-Line Input** as shown in the image below. +All commands would be typed in the **Command-Line Input** located at the top of the user interface as shown in the image below. +![Command Line Input](images/CommandLineInput.png) -### Command Features +Once you have familiarised yourself with the layout of the application, try out some example commands! + +Some example commands you can try: +* **`liststu`** : Lists all students. All students currently stored in ClassMATE will be displayed in the **Student Panel**. + +* **`addc c/G99 s/Tuesday 2 to 4pm, Friday 2 to 4pm`**: Adds a tutorial class with the code `G99`. The **Tutorial Class Panel** +should reflect the updated list of tutorial classes including your new class, `G99`. + +* **`addstu n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01 c/G99`**: Adds a student named `John Doe` to ClassMATE. +The **Student Panel** Should reflect the updated list of students including your new student, `John Doe`. + +* **`deletestu 3`**: Deletes the 3rd student shown in the current list. + +* **`clear`** : Deletes all students. + +* **`exit`** : Exits the app. + +Once you have attempted these commands, you're ready to go! + +### Command format + +This section will help you understand the syntax used to describe the command features in this guide better.
**:information_source: Notes about the command format:**
@@ -83,16 +105,27 @@ All commands would be typed in the **Command-Line Input** as shown in the image
-### Viewing help : `help` +## Features -Shows a message explaning how to access the help page. +Here, you can find instructions on how to use ClassMATE's various features. The features are divided into three main subsections, +each covering different subsections, namely the Student features, Tutorial Class Features and Tutorial Group Features. Each +subsection will provide you with an overview of the section, followed by the individual commands' formats, instructions on how to use them, +examples of use and the expected outcome of executing these commands. -![help message](images/helpMessage.png) -Format: `help` ## Student Commands +This part of the guide covers all the features you can use to manage student information.These features include: +1. Add a new Student +1. Edit and existing Student +1. View a student's details +1. Delete a student +1. List all students +1. Find students by name +1. Add Class participation marks +1. Delete all students + ### Adding a student: `addstu` Adds a student to ClassMATE. @@ -248,11 +281,14 @@ Examples: Deletes a class from ClassMATE +![Deletec](images/DeleteCommandScreenshot.png) Format: `deletec INDEX` * Deletes the class at the specified INDEX. * The index refers to the index number shown in the displayed list of classes. The index **must be a positive integer** 1, 2, 3, …​ +* Students assinged to that class will have thier clascode replaced by `No Class`. They can be edited +to be added to another class. Examples: * `listc` followed by `deletec 2` deletes the 2nd class in the list of classes. @@ -260,6 +296,13 @@ Examples: ## Tutorial Group Commands +This part of the guide covers all the features you can use to manage Groups within a tutorial class. These features include: +1. Add a group +1. View all groups in a class +1. Delete a group +1. Add a student to a group +1. Remove a student from a group + ### Adding a group: `addcg` Adds a group to a particular tutorial class @@ -304,7 +347,7 @@ Format: `deletecg INDEX` Examples: * `listg n/G06` followed by `deleteg 2` deletes the 2nd group in the list of group in class G06 -## Adding Student to a group: `addsg` [Coming Soon] +### Adding Student to a group: `addsg` [Coming Soon] Adds student to a group. @@ -317,6 +360,15 @@ Example: * `liststu c/G06`shows that Betsy is a student in class G06. `addsg n/Betsy tp/OP1 g/A` then adds a student called Betsy to OP1 Group A in class G06 +## Additional Commands + +### Viewing help : `help` + +Shows a message explaning how to access the help page. + +![help message](images/helpMessage.png) + +Format: `help` ### Exiting the program : `exit` @@ -372,3 +424,7 @@ Action | Format, Examples **Java 11** | Java is a programming language, more on it [here](https://en.wikipedia.org/wiki/Java_(programming_language)). **JSON** | a JSON file is an open standard file format, more on it [here](https://en.wikipedia.org/wiki/JSON). +**CLI** | Command Line Interface (CLI) enables users to interact with a program by typing in text commands following +visual prompts from the program. +**GUI** | Graphical User Interface (GUI) is a system of interactive visual components that allows users to interact with +a program through graphical icons. \ No newline at end of file diff --git a/docs/images/CommandLineInput.png b/docs/images/CommandLineInput.png new file mode 100644 index 0000000000000000000000000000000000000000..a15b173ad14651e7124dc114d7e35879bfb6e4bb GIT binary patch literal 6586 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 E0(5fQ(EtDd literal 0 HcmV?d00001 diff --git a/docs/img.png b/docs/img.png new file mode 100644 index 0000000000000000000000000000000000000000..a15b173ad14651e7124dc114d7e35879bfb6e4bb GIT binary patch literal 6586 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 Date: Thu, 28 Oct 2021 22:47:23 +0800 Subject: [PATCH 4/7] Fix whitespace --- docs/UserGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index bff4dc87a3d..879355c977e 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -427,4 +427,4 @@ Action | Format, Examples **CLI** | Command Line Interface (CLI) enables users to interact with a program by typing in text commands following visual prompts from the program. **GUI** | Graphical User Interface (GUI) is a system of interactive visual components that allows users to interact with -a program through graphical icons. \ No newline at end of file +a program through graphical icons. From 25dcbcea634eb6f409cc41b6acc1277f43a08beb Mon Sep 17 00:00:00 2001 From: rushilramesh Date: Thu, 28 Oct 2021 22:53:24 +0800 Subject: [PATCH 5/7] Fix Checstyle --- docs/DeveloperGuide.md | 12 ++++++------ docs/UserGuide.md | 25 ++++++++++++------------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 8f70c6c48e0..dbbaf21d67c 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -303,11 +303,11 @@ Execution of the `AddClassCommand` * Alternative 1 (current choice): Find Tutorial Classes by selecting all classes with classcodes matching the search keyword * Pros: Shorter keyword to type, therefore increasing user typing speed. User is also able to find multiple classes * Cons: Lower Accuracy in searching for a specific class, having to search through multiple classes - + * Alternative 2: Find tutorial class by exact class code * Pros: Higher Accuracy in search * Cons: Takes longer for user to type commands, less user-friendly - + #### Aspect: Student and Tutorial Class lists * Alternative 1 (current choice): Use two separate lists to store students and tutorial classes * Pros: Faster, simpler command executions for student and tutorial class commands. @@ -439,7 +439,7 @@ The class `Classmate` facilitates all operations related to tutorial groups. It tutorial group list to be displayed to the user. TutorialGroups are identical only if all its attributes, Group name, Class code and Group type are the same. The `Classmate` contains a summary of all the logic of the tutorial group commands which can be split into two parts, adding tutorial groups to tutorial classes (e.g. `AddGroupCommand`) executed on the `UniqueTutorialGroupList`, and adding students to tutorial groups. -Displaying of groups in the UI has not been implemented yet. +Displaying of groups in the UI has not been implemented yet. The following operations are implemented: * `Classmate#hasTutorialGroup(TutorialGroup tutorialGroup)` - Checks if tutorial group is in ClassMATE @@ -476,7 +476,7 @@ it checks whether the specified tutorial class exists before invoking the `model * Pros: Simpler to implement, without the use of multiple lists to store tutorial groups of different types ("OP1" or "OP2"). Storing tutorial groups as arrays in JSON is less complicated. * Cons: Searching or filtering the list of tutorial groups by group types may take a longer time. - + * Alternative 2: Use multiple lists to store groups of different categories (by class or type) * Pros: Faster when performing find functions and groups are better organised. * Cons: Splitting groups based on a category makes it harder to extend to support filtering groups with a different category from what is implemented. Deleting of groups may also become more complicated. @@ -519,9 +519,9 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli | `* * *` | user | add a new student | | | `* * *` | user | view a student's details | easily check the details and progress of the students | | `* * *` | user | add a new class | | -| `* * ` | user | add a class schedule | plan my week in advance +| `* * ` | user | add a class schedule | plan my week in advance | | `* * *` | user | view a class' details | easily check the details of a particular class | -| `* * *` | user | delete a student | remove entries that I no longer need +| `* * *` | user | delete a student | remove entries that I no longer need | | `* * *` | user | delete a class | remove classes that I no longer need | | `* * *` | user | find a student by name | locate details of students without having to go through the entire list | | `* * *` | user | find a class by code | locate details of a class without having to go through the entire list | diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 879355c977e..ce62dda1276 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -4,11 +4,11 @@ title: User Guide --- ClassMATE is a **desktop app designed for CS2101 tutors to manage student contacts and organize them into their tutorial classes and groups, as well as -other administrative matters including recording of students' class participation. It is optimized for use via a Command Line Interface** (CLI) while +other administrative matters including recording of students' class participation. It is optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, ClassMATE can get your contact management tasks done faster than traditional GUI apps. -This guide will get you started on how to create students, classes and groups, -assigning students to their respective classes and groups and covers other functionalities for users +This guide will get you started on how to create students, classes and groups, +assigning students to their respective classes and groups and covers other functionalities for users such as filtering and searching students. Finally, it will also guide you through on how you can add class participation marks for your students. @@ -47,7 +47,6 @@ We hope you find this User Guide helpful in using ClassMATE! 1. Double-click the file to start the app. The GUI similar to the image below should appear in a few seconds. Note how the app contains some sample data.
![Ui](images/Ui-annotated.png) 1. Type the command in the **Command-Line Input** and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
- 1. Refer to the [Features](#Features) below for details of each command. -------------------------------------------------------------------------------------------------------------------- @@ -64,23 +63,23 @@ Once you have familiarised yourself with the layout of the application, try out Some example commands you can try: * **`liststu`** : Lists all students. All students currently stored in ClassMATE will be displayed in the **Student Panel**. -* **`addc c/G99 s/Tuesday 2 to 4pm, Friday 2 to 4pm`**: Adds a tutorial class with the code `G99`. The **Tutorial Class Panel** +* **`addc c/G99 s/Tuesday 2 to 4pm, Friday 2 to 4pm`**: Adds a tutorial class with the code `G99`. The **Tutorial Class Panel** should reflect the updated list of tutorial classes including your new class, `G99`. * **`addstu n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01 c/G99`**: Adds a student named `John Doe` to ClassMATE. The **Student Panel** Should reflect the updated list of students including your new student, `John Doe`. -* **`deletestu 3`**: Deletes the 3rd student shown in the current list. +* **`deletestu 3`**: Deletes the 3rd student shown in the current list. * **`clear`** : Deletes all students. * **`exit`** : Exits the app. -Once you have attempted these commands, you're ready to go! +Once you have attempted these commands, you're ready to go! ### Command format -This section will help you understand the syntax used to describe the command features in this guide better. +This section will help you understand the syntax used to describe the command features in this guide better.
**:information_source: Notes about the command format:**
@@ -105,10 +104,10 @@ This section will help you understand the syntax used to describe the command fe
-## Features +## Features Here, you can find instructions on how to use ClassMATE's various features. The features are divided into three main subsections, -each covering different subsections, namely the Student features, Tutorial Class Features and Tutorial Group Features. Each +each covering different subsections, namely the Student features, Tutorial Class Features and Tutorial Group Features. Each subsection will provide you with an overview of the section, followed by the individual commands' formats, instructions on how to use them, examples of use and the expected outcome of executing these commands. @@ -298,7 +297,7 @@ Examples: This part of the guide covers all the features you can use to manage Groups within a tutorial class. These features include: 1. Add a group -1. View all groups in a class +1. View all groups in a class 1. Delete a group 1. Add a student to a group 1. Remove a student from a group @@ -414,7 +413,7 @@ Action | Format, Examples **List all classes** | `listc` **Find class** | `findc KEYWORD [MORE_KEYWORDS]`
e.g., `findc A02` **Add Tutorial Group** | `addcg gn/GROUP_NAME c/CLASS_CODE type/TYPE`
e.g.,`addsg n/Betsy tp/OP1 g/A` -**Delete Tutorial Group** | `deletecg INDEX`
e.g., `deletecg 2` +**Delete Tutorial Group** | `deletecg INDEX`
e.g., `deletecg 2` **List Tutorial Group** | `listg` **Add Student to Group** | `addsg n/NAME tp/TYPE g/GROUP_NAME [t/TAG]`
e.g., `addcg gn/Group 1 c/G01 type/OP1` **Clear all students** | `clear` @@ -424,7 +423,7 @@ Action | Format, Examples **Java 11** | Java is a programming language, more on it [here](https://en.wikipedia.org/wiki/Java_(programming_language)). **JSON** | a JSON file is an open standard file format, more on it [here](https://en.wikipedia.org/wiki/JSON). -**CLI** | Command Line Interface (CLI) enables users to interact with a program by typing in text commands following +**CLI** | Command Line Interface (CLI) enables users to interact with a program by typing in text commands following visual prompts from the program. **GUI** | Graphical User Interface (GUI) is a system of interactive visual components that allows users to interact with a program through graphical icons. From 1940280b089f73fd7ef6da1eb836dae1cd1f301e Mon Sep 17 00:00:00 2001 From: GabrielWLM Date: Fri, 29 Oct 2021 07:55:53 +0800 Subject: [PATCH 6/7] commit before merging --- .../seedu/address/commons/core/Messages.java | 5 +-- .../logic/commands/AddGroupCommand.java | 9 ++--- ...roupCommand.java => ViewGroupCommand.java} | 34 +++++++++++-------- .../seedu/address/logic/parser/CliSyntax.java | 2 +- .../logic/parser/FindClassCommandParser.java | 6 ++-- .../parser/FindStudentCommandParser.java | 6 ++-- .../logic/parser/ViewGroupCommandParser.java | 33 ++++++++++++++++++ .../model/student/GroupNamePredicate.java | 30 ++++++++++++++++ .../model/student/GroupTypePredicate.java | 32 +++++++++++++++++ .../seedu/address/model/student/Student.java | 5 ++- 10 files changed, 134 insertions(+), 28 deletions(-) rename src/main/java/seedu/address/logic/commands/{FindGroupCommand.java => ViewGroupCommand.java} (50%) create mode 100644 src/main/java/seedu/address/logic/parser/ViewGroupCommandParser.java create mode 100644 src/main/java/seedu/address/model/student/GroupNamePredicate.java create mode 100644 src/main/java/seedu/address/model/student/GroupTypePredicate.java diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java index 7193f209bfa..62016154176 100644 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ b/src/main/java/seedu/address/commons/core/Messages.java @@ -12,6 +12,7 @@ public class Messages { public static final String MESSAGE_INVALID_GROUP_DISPLAYED_INDEX = "The group index provided is invalid"; public static final String MESSAGE_STUDENTS_LISTED_OVERVIEW = "%1$d students listed!"; public static final String MESSAGE_TUTORIAL_CLASSES_LISTED_OVERVIEW = "%1$d tutorial classes listed!"; - public static final String MESSAGE_TUTORIAL_GROUPS_LISTED_OVERVIEW = "%1$d tutorial groups listed!"; - + public static final String MESSAGE_TUTORIAL_GROUP_LISTED_OVERVIEW = "%1$d students from tutorial group listed!"; + public static final String MESSAGE_CLASS_DOES_NOT_EXIST = "This class does not exist in Classmate"; + public static final String MESSAGE_GROUP_DOES_NOT_EXIST = "This group does not exist in Classmate"; } diff --git a/src/main/java/seedu/address/logic/commands/AddGroupCommand.java b/src/main/java/seedu/address/logic/commands/AddGroupCommand.java index 7776feab5b5..f0b344e24e3 100644 --- a/src/main/java/seedu/address/logic/commands/AddGroupCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddGroupCommand.java @@ -19,8 +19,8 @@ public class AddGroupCommand extends Command { public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a group to a class to Classmate. " + "Parameters: " - + PREFIX_GROUPNAME + "GROUPNAME " + PREFIX_CLASSCODE + "CLASSCODE " + + PREFIX_GROUPNAME + "GROUPNAME " + PREFIX_TYPE + "TYPE " + "Example: " + COMMAND_WORD + " " + PREFIX_GROUPNAME + "1 " @@ -29,7 +29,7 @@ public class AddGroupCommand extends Command { public static final String MESSAGE_SUCCESS = "New group added: %1$s"; public static final String MESSAGE_DUPLICATE_GROUP = "This group already exists in Classmate"; - public static final String MESSAGE_GROUP_NOT_EXIST = "The class does not exist in Classmate"; + public static final String MESSAGE_CLASS_DOES_NOT_EXIST = "The class does not exist in Classmate"; private final TutorialGroup toAdd; private final TutorialClass toAddTutorialClass; @@ -41,7 +41,8 @@ public AddGroupCommand(TutorialGroup tutorialGroup) { requireNonNull(tutorialGroup); toAdd = tutorialGroup; // new class with the same class code created to check whether it already exists in ClassMATE - toAddTutorialClass = new TutorialClass(tutorialGroup.getClassCode(), new Schedule("dummy"), new HashSet()); + toAddTutorialClass = new TutorialClass(tutorialGroup.getClassCode(), new Schedule("dummy"), + new HashSet()); } @Override @@ -50,7 +51,7 @@ public CommandResult execute(Model model) throws CommandException { // check if tutorial class already exists in ClassMATE if (!model.hasTutorialClass(toAddTutorialClass)) { - throw new CommandException(MESSAGE_GROUP_NOT_EXIST); + throw new CommandException(MESSAGE_CLASS_DOES_NOT_EXIST); } if (model.hasTutorialGroup(toAdd)) { diff --git a/src/main/java/seedu/address/logic/commands/FindGroupCommand.java b/src/main/java/seedu/address/logic/commands/ViewGroupCommand.java similarity index 50% rename from src/main/java/seedu/address/logic/commands/FindGroupCommand.java rename to src/main/java/seedu/address/logic/commands/ViewGroupCommand.java index 4a51ed924a3..8b0e9093b7e 100644 --- a/src/main/java/seedu/address/logic/commands/FindGroupCommand.java +++ b/src/main/java/seedu/address/logic/commands/ViewGroupCommand.java @@ -2,34 +2,37 @@ import static java.util.Objects.requireNonNull; import static seedu.address.logic.parser.CliSyntax.PREFIX_CLASSCODE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_GROUPNAME; import static seedu.address.logic.parser.CliSyntax.PREFIX_TYPE; import seedu.address.commons.core.Messages; import seedu.address.model.Model; +import seedu.address.model.student.ClassMemberPredicate; import seedu.address.model.tutorialgroup.TutorialGroupContainsKeywordsPredicate; /** - * Finds and lists all tutorial groups in ClassMATE whose class code and tutorial group contains any of the argument - * keywords. + * Lists all students in a tutorial group in ClassMATE given a class code, tutorial group type and tutorial group name * Keyword matching is case-insensitive. */ -public class FindGroupCommand extends Command { +public class ViewGroupCommand extends Command { - public static final String COMMAND_WORD = "findc"; + public static final String COMMAND_WORD = "viewg"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all tutorial groups whose class code and" - + "tutorial group contain any of the specified keywords (case-insensitive) and displays them as a list" + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all students in a tutorial group given a class " + + "code, tutorial group type and tutorial group name (case-insensitive) and displays them as a list" + "with index numbers.\n" + "Parameters: " - + PREFIX_CLASSCODE + "KEYWORD" - + "[" + PREFIX_TYPE + "KEYWORD\n" + + PREFIX_CLASSCODE + "CLASSCODE " + + PREFIX_GROUPNAME + "GROUPNAME " + + PREFIX_TYPE + "TYPE " + "Example: " + COMMAND_WORD + " " + + PREFIX_GROUPNAME + "3 " + PREFIX_CLASSCODE + "G06 " - + PREFIX_TYPE + "OP1 "; + + PREFIX_TYPE + "OP2 "; private final TutorialGroupContainsKeywordsPredicate predicate; - public FindGroupCommand(TutorialGroupContainsKeywordsPredicate predicate) { + public ViewGroupCommand(TutorialGroupContainsKeywordsPredicate predicate) { this.predicate = predicate; } @@ -37,15 +40,18 @@ public FindGroupCommand(TutorialGroupContainsKeywordsPredicate predicate) { public CommandResult execute(Model model) { requireNonNull(model); model.updateFilteredTutorialGroupList(predicate); + + + model.updateFilteredStudentList(new ClassMemberPredicate(targetClassCode)); return new CommandResult( - String.format(Messages.MESSAGE_TUTORIAL_GROUPS_LISTED_OVERVIEW, - model.getFilteredTutorialGroupList().size())); + String.format(Messages.MESSAGE_TUTORIAL_GROUP_LISTED_OVERVIEW, + model.getFilteredStudentList().size())); } @Override public boolean equals(Object other) { return other == this // short circuit if same object - || (other instanceof FindGroupCommand // instanceof handles nulls - && predicate.equals(((FindGroupCommand) other).predicate)); // state check + || (other instanceof ViewGroupCommand // instanceof handles nulls + && predicate.equals(((ViewGroupCommand) other).predicate)); // state check } } diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java index 65d32ced65b..5005df0e10f 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java @@ -5,7 +5,7 @@ */ public class CliSyntax { - /* Student Prefix definitions */ + // Student Prefix definitions public static final Prefix PREFIX_NAME = new Prefix("n/"); public static final Prefix PREFIX_PHONE = new Prefix("p/"); public static final Prefix PREFIX_EMAIL = new Prefix("e/"); diff --git a/src/main/java/seedu/address/logic/parser/FindClassCommandParser.java b/src/main/java/seedu/address/logic/parser/FindClassCommandParser.java index 9a1e8daaedd..3fff11c09bf 100644 --- a/src/main/java/seedu/address/logic/parser/FindClassCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/FindClassCommandParser.java @@ -9,13 +9,13 @@ import seedu.address.model.tutorialclass.ClassCodeContainsKeywordsPredicate; /** - * Parses input arguments and creates a new FindCommand object + * Parses input arguments and creates a new FindClassCommand object */ public class FindClassCommandParser implements Parser { /** - * Parses the given {@code String} of arguments in the context of the FindCommand - * and returns a FindCommand object for execution. + * Parses the given {@code String} of arguments in the context of the FindClassCommand + * and returns a FindClassCommand object for execution. * @throws ParseException if the user input does not conform the expected format */ public FindClassCommand parse(String args) throws ParseException { diff --git a/src/main/java/seedu/address/logic/parser/FindStudentCommandParser.java b/src/main/java/seedu/address/logic/parser/FindStudentCommandParser.java index b0b2b595f31..8d64b519545 100644 --- a/src/main/java/seedu/address/logic/parser/FindStudentCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/FindStudentCommandParser.java @@ -9,13 +9,13 @@ import seedu.address.model.student.NameContainsKeywordsPredicate; /** - * Parses input arguments and creates a new FindCommand object + * Parses input arguments and creates a new FindStudentCommand object */ public class FindStudentCommandParser implements Parser { /** - * Parses the given {@code String} of arguments in the context of the FindCommand - * and returns a FindCommand object for execution. + * Parses the given {@code String} of arguments in the context of the FindStudentCommand + * and returns a FindStudentCommand object for execution. * @throws ParseException if the user input does not conform the expected format */ public FindStudentCommand parse(String args) throws ParseException { diff --git a/src/main/java/seedu/address/logic/parser/ViewGroupCommandParser.java b/src/main/java/seedu/address/logic/parser/ViewGroupCommandParser.java new file mode 100644 index 00000000000..cedf492c061 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ViewGroupCommandParser.java @@ -0,0 +1,33 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.Arrays; + +import seedu.address.logic.commands.ViewGroupCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.tutorialgroup.TutorialGroupContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new ViewGroupCommand object + */ +public class ViewGroupCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ViewGroupCommand + * and returns a ViewGroupCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ViewGroupCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewGroupCommand.MESSAGE_USAGE)); + } + + String[] nameKeywords = trimmedArgs.split("\\s+"); + + return new ViewGroupCommand(new TutorialGroupContainsKeywordsPredicate(Arrays.asList(nameKeywords))); + } + +} diff --git a/src/main/java/seedu/address/model/student/GroupNamePredicate.java b/src/main/java/seedu/address/model/student/GroupNamePredicate.java new file mode 100644 index 00000000000..cdb72639d63 --- /dev/null +++ b/src/main/java/seedu/address/model/student/GroupNamePredicate.java @@ -0,0 +1,30 @@ +package seedu.address.model.student; + +import java.util.function.Predicate; + +/** + * Tests that a {@code Student}'s {@code GroupName} matches the given group name. + */ +public class GroupNamePredicate implements Predicate { + private final ClassCode classCode; + + public GroupNamePredicate(ClassCode classCode) { + this.classCode = classCode; + } + + public ClassCode getClassCode() { + return classCode; + } + + @Override + public boolean test(Student student) { + return student.getClassCode().equals(classCode); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same instance + || other instanceof GroupNamePredicate // instanceof handles null + && classCode.equals(((GroupNamePredicate) other).getClassCode()); // state check + } +} diff --git a/src/main/java/seedu/address/model/student/GroupTypePredicate.java b/src/main/java/seedu/address/model/student/GroupTypePredicate.java new file mode 100644 index 00000000000..1ed5466d7e0 --- /dev/null +++ b/src/main/java/seedu/address/model/student/GroupTypePredicate.java @@ -0,0 +1,32 @@ +package seedu.address.model.student; + +import java.util.function.Predicate; + +import seedu.address.model.tutorialgroup.GroupType; + +/** + * Tests that a {@code Student}'s {@code GroupType} matches the given group type. + */ +public class GroupTypePredicate implements Predicate { + private final GroupType groupType; + + public GroupTypePredicate(GroupType groupType) { + this.groupType = groupType; + } + + public GroupType getGroupType() { + return groupType; + } + + @Override + public boolean test(Student student) { + return student.getGroupType().equals(groupType); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same instance + || other instanceof GroupTypePredicate // instanceof handles null + && groupType.equals(((GroupTypePredicate) other).getGroupType()); // state check + } +} diff --git a/src/main/java/seedu/address/model/student/Student.java b/src/main/java/seedu/address/model/student/Student.java index 92411585f0a..a015e2727de 100644 --- a/src/main/java/seedu/address/model/student/Student.java +++ b/src/main/java/seedu/address/model/student/Student.java @@ -8,6 +8,8 @@ import java.util.Set; import seedu.address.model.tag.Tag; +import seedu.address.model.tutorialgroup.GroupType; +import seedu.address.model.tutorialgroup.TutorialGroup; /** * Represents a Student in the ClassMATE. @@ -23,8 +25,9 @@ public class Student { // Data fields private final Address address; private final ClassCode classCode; + // private final List marks; private final Set tags = new HashSet<>(); - //private final List marks = new ArrayList<>(); + private final Set tutorialGroups = new HashSet<>(); /** * Every field must be present and not null. From 9edc41957ccef30702eeee27c08d301a4a97cd41 Mon Sep 17 00:00:00 2001 From: GabrielWLM <77065071+GabrielWLM@users.noreply.github.com> Date: Fri, 29 Oct 2021 08:57:40 +0800 Subject: [PATCH 7/7] add find group command --- .../seedu/address/commons/core/Messages.java | 1 - .../logic/commands/AddGroupCommand.java | 4 +- .../logic/commands/ViewGroupCommand.java | 38 +++++++++++++------ .../address/logic/parser/ClassmateParser.java | 4 ++ .../logic/parser/ViewGroupCommandParser.java | 32 ++++++++++++---- .../java/seedu/address/model/Classmate.java | 11 ++++-- .../model/student/GroupMemberPredicate.java | 32 ++++++++++++++++ .../model/student/GroupNamePredicate.java | 30 --------------- .../model/student/GroupTypePredicate.java | 32 ---------------- 9 files changed, 95 insertions(+), 89 deletions(-) create mode 100644 src/main/java/seedu/address/model/student/GroupMemberPredicate.java delete mode 100644 src/main/java/seedu/address/model/student/GroupNamePredicate.java delete mode 100644 src/main/java/seedu/address/model/student/GroupTypePredicate.java diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java index 62016154176..adb2cf16db3 100644 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ b/src/main/java/seedu/address/commons/core/Messages.java @@ -9,7 +9,6 @@ public class Messages { public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; public static final String MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX = "The student index provided is invalid"; public static final String MESSAGE_INVALID_CLASS_DISPLAYED_INDEX = "The class index provided is invalid"; - public static final String MESSAGE_INVALID_GROUP_DISPLAYED_INDEX = "The group index provided is invalid"; public static final String MESSAGE_STUDENTS_LISTED_OVERVIEW = "%1$d students listed!"; public static final String MESSAGE_TUTORIAL_CLASSES_LISTED_OVERVIEW = "%1$d tutorial classes listed!"; public static final String MESSAGE_TUTORIAL_GROUP_LISTED_OVERVIEW = "%1$d students from tutorial group listed!"; diff --git a/src/main/java/seedu/address/logic/commands/AddGroupCommand.java b/src/main/java/seedu/address/logic/commands/AddGroupCommand.java index 9d1fac49cf2..a940d79a7b7 100644 --- a/src/main/java/seedu/address/logic/commands/AddGroupCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddGroupCommand.java @@ -1,6 +1,7 @@ package seedu.address.logic.commands; import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_CLASS_DOES_NOT_EXIST; import static seedu.address.logic.parser.CliSyntax.PREFIX_CLASSCODE; import static seedu.address.logic.parser.CliSyntax.PREFIX_GROUPNUMBER; import static seedu.address.logic.parser.CliSyntax.PREFIX_TYPE; @@ -16,8 +17,8 @@ public class AddGroupCommand extends Command { public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a group to a class to Classmate. " + "Parameters: " + PREFIX_CLASSCODE + "CLASSCODE " - + PREFIX_GROUPNAME + "GROUPNAME " + PREFIX_TYPE + "TYPE " + + PREFIX_GROUPNUMBER + "GROUPNUMBER " + "Example: " + COMMAND_WORD + " " + PREFIX_GROUPNUMBER + "1 " + PREFIX_CLASSCODE + "G06 " @@ -25,7 +26,6 @@ public class AddGroupCommand extends Command { public static final String MESSAGE_SUCCESS = "New group added: %1$s"; public static final String MESSAGE_DUPLICATE_GROUP = "This group already exists in Classmate"; - public static final String MESSAGE_CLASS_DOES_NOT_EXIST = "The class does not exist in Classmate"; private final TutorialGroup toAdd; private final TutorialClass toAddTutorialClass; diff --git a/src/main/java/seedu/address/logic/commands/ViewGroupCommand.java b/src/main/java/seedu/address/logic/commands/ViewGroupCommand.java index 8b0e9093b7e..feec367db12 100644 --- a/src/main/java/seedu/address/logic/commands/ViewGroupCommand.java +++ b/src/main/java/seedu/address/logic/commands/ViewGroupCommand.java @@ -2,13 +2,15 @@ import static java.util.Objects.requireNonNull; import static seedu.address.logic.parser.CliSyntax.PREFIX_CLASSCODE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_GROUPNAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_GROUPNUMBER; import static seedu.address.logic.parser.CliSyntax.PREFIX_TYPE; import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.student.ClassMemberPredicate; -import seedu.address.model.tutorialgroup.TutorialGroupContainsKeywordsPredicate; +import seedu.address.model.student.GroupMemberPredicate; +import seedu.address.model.tutorialclass.TutorialClass; +import seedu.address.model.tutorialgroup.TutorialGroup; /** * Lists all students in a tutorial group in ClassMATE given a class code, tutorial group type and tutorial group name @@ -23,26 +25,38 @@ public class ViewGroupCommand extends Command { + "with index numbers.\n" + "Parameters: " + PREFIX_CLASSCODE + "CLASSCODE " - + PREFIX_GROUPNAME + "GROUPNAME " + PREFIX_TYPE + "TYPE " + + PREFIX_GROUPNUMBER + "GROUPNUMBER " + "Example: " + COMMAND_WORD + " " - + PREFIX_GROUPNAME + "3 " + + PREFIX_GROUPNUMBER + "3 " + PREFIX_CLASSCODE + "G06 " + PREFIX_TYPE + "OP2 "; - private final TutorialGroupContainsKeywordsPredicate predicate; + private final TutorialGroup toView; + private final TutorialClass toViewTutorialClass; - public ViewGroupCommand(TutorialGroupContainsKeywordsPredicate predicate) { - this.predicate = predicate; + /** + * Creates a ViewGroupCommand to show the students in the specified {@code TutorialGroup} + */ + public ViewGroupCommand(TutorialGroup tutorialGroup) { + requireNonNull(tutorialGroup); + toView = tutorialGroup; + // new class with the same class code created to check whether it exists in ClassMATE + toViewTutorialClass = TutorialClass.createTestTutorialClass(toView.getClassCode()); } @Override - public CommandResult execute(Model model) { + public CommandResult execute(Model model) throws CommandException { requireNonNull(model); - model.updateFilteredTutorialGroupList(predicate); + // check if tutorial class exists in ClassMATE + if (!model.hasTutorialClass(toViewTutorialClass)) { + throw new CommandException(Messages.MESSAGE_CLASS_DOES_NOT_EXIST); + } - model.updateFilteredStudentList(new ClassMemberPredicate(targetClassCode)); + + + model.updateFilteredStudentList(new GroupMemberPredicate(toView)); return new CommandResult( String.format(Messages.MESSAGE_TUTORIAL_GROUP_LISTED_OVERVIEW, model.getFilteredStudentList().size())); @@ -52,6 +66,6 @@ public CommandResult execute(Model model) { public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof ViewGroupCommand // instanceof handles nulls - && predicate.equals(((ViewGroupCommand) other).predicate)); // state check + && toView.equals(((ViewGroupCommand) other).toView)); // state check } } diff --git a/src/main/java/seedu/address/logic/parser/ClassmateParser.java b/src/main/java/seedu/address/logic/parser/ClassmateParser.java index 879ff4ddbc4..303963d2616 100644 --- a/src/main/java/seedu/address/logic/parser/ClassmateParser.java +++ b/src/main/java/seedu/address/logic/parser/ClassmateParser.java @@ -27,6 +27,7 @@ import seedu.address.logic.commands.ListClassCommand; import seedu.address.logic.commands.ListStudentCommand; import seedu.address.logic.commands.ViewClassCommand; +import seedu.address.logic.commands.ViewGroupCommand; import seedu.address.logic.commands.ViewStudentCommand; import seedu.address.logic.parser.exceptions.ParseException; @@ -97,6 +98,9 @@ public Command parseCommand(String userInput) throws ParseException { case ViewClassCommand.COMMAND_WORD: return new ViewClassCommandParser().parse(arguments); + case ViewGroupCommand.COMMAND_WORD: + return new ViewGroupCommandParser().parse(arguments); + case ViewStudentCommand.COMMAND_WORD: return new ViewStudentCommandParser().parse(arguments); diff --git a/src/main/java/seedu/address/logic/parser/ViewGroupCommandParser.java b/src/main/java/seedu/address/logic/parser/ViewGroupCommandParser.java index cedf492c061..261e22f8f76 100644 --- a/src/main/java/seedu/address/logic/parser/ViewGroupCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/ViewGroupCommandParser.java @@ -1,12 +1,19 @@ package seedu.address.logic.parser; import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_CLASSCODE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_GROUPNUMBER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TYPE; -import java.util.Arrays; +import java.util.stream.Stream; +import seedu.address.logic.commands.AddGroupCommand; import seedu.address.logic.commands.ViewGroupCommand; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.tutorialgroup.TutorialGroupContainsKeywordsPredicate; +import seedu.address.model.student.ClassCode; +import seedu.address.model.tutorialgroup.GroupNumber; +import seedu.address.model.tutorialgroup.GroupType; +import seedu.address.model.tutorialgroup.TutorialGroup; /** * Parses input arguments and creates a new ViewGroupCommand object @@ -19,15 +26,24 @@ public class ViewGroupCommandParser implements Parser { * @throws ParseException if the user input does not conform the expected format */ public ViewGroupCommand parse(String args) throws ParseException { - String trimmedArgs = args.trim(); - if (trimmedArgs.isEmpty()) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewGroupCommand.MESSAGE_USAGE)); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_CLASSCODE, PREFIX_TYPE, PREFIX_GROUPNUMBER); + + if (!arePrefixesPresent(argMultimap, PREFIX_CLASSCODE, PREFIX_TYPE, PREFIX_GROUPNUMBER) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddGroupCommand.MESSAGE_USAGE)); } - String[] nameKeywords = trimmedArgs.split("\\s+"); + GroupNumber groupNumber = ParserUtil.parseGroupName(argMultimap.getValue(PREFIX_GROUPNUMBER).get()); + ClassCode classCode = ParserUtil.parseClassCode(argMultimap.getValue(PREFIX_CLASSCODE).get()); + GroupType groupType = ParserUtil.parseGroupType(argMultimap.getValue(PREFIX_TYPE).get()); + + TutorialGroup tutorialGroup = new TutorialGroup(groupNumber, classCode, groupType); - return new ViewGroupCommand(new TutorialGroupContainsKeywordsPredicate(Arrays.asList(nameKeywords))); + return new ViewGroupCommand(tutorialGroup); } + 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/model/Classmate.java b/src/main/java/seedu/address/model/Classmate.java index a21a3e19dc8..da007a101b3 100644 --- a/src/main/java/seedu/address/model/Classmate.java +++ b/src/main/java/seedu/address/model/Classmate.java @@ -11,6 +11,7 @@ import seedu.address.model.tutorialclass.TutorialClass; import seedu.address.model.tutorialclass.UniqueTutorialClassList; import seedu.address.model.tutorialgroup.TutorialGroup; +import seedu.address.model.tutorialgroup.UniqueTutorialGroupList; /** * Wraps all data at the address-book level @@ -22,6 +23,7 @@ public class Classmate implements ReadOnlyClassmate { private final UniqueStudentList students; private final UniqueTutorialClassList tutorialClasses; + private final UniqueTutorialGroupList tutorialGroups; /* * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication @@ -33,6 +35,7 @@ public class Classmate implements ReadOnlyClassmate { { students = new UniqueStudentList(); tutorialClasses = new UniqueTutorialClassList(); + tutorialGroups = new UniqueTutorialGroupList(); } public Classmate() {} @@ -155,7 +158,7 @@ public void removeTutorialClass(TutorialClass key) { * {@code key} must exist in the ClassMATE. */ public void removeTutorialGroup(TutorialGroup key) { - tutorialClasses.remove(key); + tutorialGroups.remove(key); } /** @@ -163,7 +166,7 @@ public void removeTutorialGroup(TutorialGroup key) { */ public boolean hasTutorialGroup(TutorialGroup tutorialGroup) { requireNonNull(tutorialGroup); - return tutorialClasses.contains(tutorialGroup); + return tutorialGroups.contains(tutorialGroup); } /** @@ -171,14 +174,14 @@ public boolean hasTutorialGroup(TutorialGroup tutorialGroup) { * The tutorial group must not already exist in the ClassMATE. */ public void addTutorialGroup(TutorialGroup tutorialGroup) { - tutorialClasses.add(tutorialGroup); + tutorialGroups.add(tutorialGroup); } /** * Sorts the tutorial groups in ClassMATE. */ public void sortTutorialGroups() { - tutorialClasses.sort(); + tutorialGroups.sort(); } diff --git a/src/main/java/seedu/address/model/student/GroupMemberPredicate.java b/src/main/java/seedu/address/model/student/GroupMemberPredicate.java new file mode 100644 index 00000000000..a802086cee2 --- /dev/null +++ b/src/main/java/seedu/address/model/student/GroupMemberPredicate.java @@ -0,0 +1,32 @@ +package seedu.address.model.student; + +import java.util.function.Predicate; + +import seedu.address.model.tutorialgroup.TutorialGroup; + +/** + * Tests that a {@code Student}'s {@code TutorialGroup} matches the given TutorialGroup. + */ +public class GroupMemberPredicate implements Predicate { + private final TutorialGroup tutorialGroup; + + public GroupMemberPredicate(TutorialGroup tutorialGroup) { + this.tutorialGroup = tutorialGroup; + } + + public TutorialGroup getTutorialGroup() { + return tutorialGroup; + } + + @Override + public boolean test(Student student) { + return student.getTutorialGroups().contains(tutorialGroup); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same instance + || other instanceof GroupMemberPredicate // instanceof handles null + && tutorialGroup.equals(((GroupMemberPredicate) other).getTutorialGroup()); // state check + } +} diff --git a/src/main/java/seedu/address/model/student/GroupNamePredicate.java b/src/main/java/seedu/address/model/student/GroupNamePredicate.java deleted file mode 100644 index cdb72639d63..00000000000 --- a/src/main/java/seedu/address/model/student/GroupNamePredicate.java +++ /dev/null @@ -1,30 +0,0 @@ -package seedu.address.model.student; - -import java.util.function.Predicate; - -/** - * Tests that a {@code Student}'s {@code GroupName} matches the given group name. - */ -public class GroupNamePredicate implements Predicate { - private final ClassCode classCode; - - public GroupNamePredicate(ClassCode classCode) { - this.classCode = classCode; - } - - public ClassCode getClassCode() { - return classCode; - } - - @Override - public boolean test(Student student) { - return student.getClassCode().equals(classCode); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same instance - || other instanceof GroupNamePredicate // instanceof handles null - && classCode.equals(((GroupNamePredicate) other).getClassCode()); // state check - } -} diff --git a/src/main/java/seedu/address/model/student/GroupTypePredicate.java b/src/main/java/seedu/address/model/student/GroupTypePredicate.java deleted file mode 100644 index 1ed5466d7e0..00000000000 --- a/src/main/java/seedu/address/model/student/GroupTypePredicate.java +++ /dev/null @@ -1,32 +0,0 @@ -package seedu.address.model.student; - -import java.util.function.Predicate; - -import seedu.address.model.tutorialgroup.GroupType; - -/** - * Tests that a {@code Student}'s {@code GroupType} matches the given group type. - */ -public class GroupTypePredicate implements Predicate { - private final GroupType groupType; - - public GroupTypePredicate(GroupType groupType) { - this.groupType = groupType; - } - - public GroupType getGroupType() { - return groupType; - } - - @Override - public boolean test(Student student) { - return student.getGroupType().equals(groupType); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same instance - || other instanceof GroupTypePredicate // instanceof handles null - && groupType.equals(((GroupTypePredicate) other).getGroupType()); // state check - } -}