diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 675075ac2f3..b1c5dda81c4 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -19,6 +19,9 @@ Refer to the guide [_Setting up and getting started_](SettingUp.md). -------------------------------------------------------------------------------------------------------------------- + +
+ ## **Design**
@@ -64,6 +67,7 @@ Each of the other four main components (also shown in the diagram above), * implements its functionality using a concrete `{Component Name}Manager` class which follows the corresponding API `interface` mentioned in the previous point. For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. + Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below. @@ -82,7 +86,7 @@ All these, including the `MainWindow`, inherit from the abstract `UiComponent` c The `MainWindow` includes a `DisplayPanel`, which has three different states it can toggle between 1. The `SplashPanel` for the opening splash window -2. The `CombinedPanel` that displays the student list and a course list sidebar +2. The `CombinedPanel` that displays the student list and a course list sidebar 3. The `CoursePanel` that displays the course list (this is otherwise known as the `home` screen) The `UI` component uses the JavaFx UI framework. @@ -114,7 +118,7 @@ After each command (assuming the application is not closed), the UI will check w This action flow will loop until the user decides to exit the application. During the application's runtime, the user may also exit the application through the MainWindow's top panel buttons (File -> Exit) -or the red '**x**' button on the top right of the application screen. +or the red '**x**' button on the top right of the application screen. ### Logic component @@ -124,16 +128,16 @@ Here's a (partial) class diagram of the `Logic` component: +The sequence diagram bellow ilustrates the interactions within the `Logic` componenet, using `execute("delete 1")` API call as an example. + +![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png) + How the `Logic` component works: -1. When `Logic` is called upon to execute a command, it uses the `CodeSphereParser` class to parse the user command. +1. When `Logic` is called upon to execute a command, it uses the `CodeSphereParser` object to parse the user command. This in turns creates a parser that matches the command (e.g., `DeleteCourseCommandParser`) which will parse the relevant arguments in the user input. 2. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddCommand`) which is executed by the `LogicManager`. 3. The command can communicate with the `Model` when it is executed (e.g. to add an item). 4. The result of the command execution is encapsulated as a `CommandResult` object which is returned from `Logic`. -The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("delete 1")` API call. - -![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png) -
:information_source: **Note:** The lifeline for `DeleteCourseCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
@@ -153,8 +157,9 @@ How the parsing works: The `Model` component, -* stores the app data i.e., all `Course` objects (which are contained in a `UniqueCourseList` object). +* stores the app data i.e., all `Course` and `Student` objects (which are contained in a `UniqueCourseList` and `UniqueStudentList` objects respectively). * stores the currently 'selected' `Course` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. +* stores the currently 'selected course's' `Student` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. * stores a `UserPref` object that represents the user’s preferences. This is exposed to the outside as a `ReadOnlyUserPref` objects. * does not depend on any of the other three components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components). @@ -169,8 +174,8 @@ The `Storage` component, * inherits from both `CourseListStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed). * depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`) -The Storage component builds upon the AB-3 Storage component by adding an InputStorage, which encapsulates the concept of -storing user inputs. +The Storage component builds upon the AB-3 Storage component by adding an InputStorage, which encapsulates the concept of +storing user inputs. These user inputs are stored in chronological order and are accessed through the StorageManager. All user inputs will be stored in the InputStorage, and will also contain the data whether the input was accepted as a valid command or not. Handling of this input validity will be done by the UI component. @@ -227,6 +232,83 @@ Given below is an example usage scenario and how the adding pending question mec * Step 5. A new `Student` object is created by passing in the `PendingQuestion` instance to the `Student` constructor. * Step 6. Update the newly created `Student` instance to replace the old `Student` instance. +### Finding a student from a selected course + +#### About the find feature + +The `find` command allows users to search for relevant students based on details of the specified criteria in their input. For example, a user could use `find n/KEYWORDS` to find a students names which contain or match that with the keywords. +They can use keywords in `NAME`, `EMAIL`, `TAG`, `REMARK` to find students with words or phrases containing all of the words mentioned in keywords. +Do note that only 1 criteria can be used at one time. + +#### Implementation Details + +The partial class diagram of the `find` command can be seen. What is important to note here is the use of a `Predicate` class and the `updateFilteredStudentList` method which utilises the predicate class. + +![FindCommandPartial](images/FindCommandClassDiagram.png) + +Their are multiple different predicate classes created to accurately filter through the student list. Below, is the example usage scenario and how the `find` mechanism behaves at each step. + +#### Parsing user input + +1. The user inputs the `find` command and provide the input with the attribute of the student contact and its respective prefix (eg. `n/NAME` or `r/REMARK`) in which the user wants to find the student. + +2. The `CodeSphereParser` then does preliminary processing to the user input and creates a new `FindCommandParser`. + +3. The `FindCommandParser` parses the user inputs and checks for whether the input is the correct format. The input needs to contain only 1 prefix and the keywords cannot be empty. + +If the conditions are not met a `ParseException` is thrown. + +4. If the format is valid, a predicate class matching the prefix is created. For example, for `NAME`, the predicate object created is `NameContainsKeywordsPredicate`. This class takes in the values of the keywords and the object is passed into the newly created `FindCommand` object. + +#### Command execution + +1. The `LogicManager` executes the `FindCommand`. + +2. The `FindCommand` calls the `Model#updateFilteredPersonList()` to update the filtered person list based on predicate class. + +3. The `FindCommand` then calls the `Model#getFilteredPersonList()#size()` to get the size of the person list. The size will correspond to the number of persons listed. + +#### Displaying of result + +1. The `FindCommand` will create a `CommandResult` with a success message and return it to the `LogicManager` to complete the command execution. The GUI will also be updated accordingly as it calls the `filteredStudentList` which was updated during the execution of the command. + +The following sequence diagram shows how the `find` mechanism works: + +![](images/FindCommandSequenceDiagram.png) + +#### Design Considerations + +**Aspect:** How should multiple keywords be considered + +- *Currently:* Whether all keywords are contained in any order in the specified criteria +- *Alternative 1:* At least 1 keyword is contained + +**Pros and Cons:** +- *Currently:* + - **Pros:** Provides a more specific and refined search. + - **Cons:** May result in fewer matches if all keywords are not present. + +- *Alternative 1:* + - **Pros:** Increases the likelihood of finding matches. + - **Cons:** Might lead to less precise results if only one keyword is present. + +**Aspect:** How should the filtered list be filtered based on the keywords + +- *Currently:* As long as keywords are contained inside +- *Alternative 1:* Instead of contains, do a complete word match + +**Pros and Cons:** +- *Currently:* + - **Pros:** More flexible and tolerant to variations in keyword placement. + - **Cons:** May include irrelevant matches if keywords are part of larger words. + +- *Alternative 1:* + - **Pros:** Provides more precise matching by requiring complete word matches. + - **Cons:** May exclude relevant matches if keywords are not complete words. + +These considerations and alternatives should be weighed based on the specific requirements and expected user experience in your application. +You can copy and paste this code into a Markdown editor or file to render the formatted text. + -------------------------------------------------------------------------------------------------------------------- ## **Documentation, logging, testing, configurations, dev-ops** * [Documentation guide](Documentation.md) @@ -402,15 +484,15 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli This is a list of prefixes used for manual testing. -| Prefix | Representation | +| Prefix | Representation | |--------|--------------------| | `c/` | `COURSE_NAME` | -| `n/` | `NAME` | -| `e/` | `EMAIL` | -| `t/` | `TAG` | -| `r/` | `REMARK` | -| `pq/` | `PENDING_QUESTION` | -| `s/` | `SORT` | +| `n/` | `NAME` | +| `e/` | `EMAIL` | +| `t/` | `TAG` | +| `r/` | `REMARK` | +| `pq/` | `PENDING_QUESTION` | +| `s/` | `SORT` | Given below are instructions to test the app manually. diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml index 91686b564cc..b1e22d3cf39 100644 --- a/docs/diagrams/ArchitectureSequenceDiagram.puml +++ b/docs/diagrams/ArchitectureSequenceDiagram.puml @@ -15,13 +15,13 @@ activate ui UI_COLOR ui -[UI_COLOR]> logic : execute("delete 1") activate logic LOGIC_COLOR -logic -[LOGIC_COLOR]> stage : getCurrent() +logic -[LOGIC_COLOR]> stage : getInstance() activate stage STAGE_COLOR stage -[STAGE_COLOR]->logic deactivate stage -logic -[LOGIC_COLOR]> stage : current.getCurrentCourse() +logic -[LOGIC_COLOR]> stage : instance.getStage() activate stage STAGE_COLOR stage -[STAGE_COLOR]->logic diff --git a/docs/diagrams/FindCommandClassDiagram.puml b/docs/diagrams/FindCommandClassDiagram.puml new file mode 100644 index 00000000000..23b40e7acba --- /dev/null +++ b/docs/diagrams/FindCommandClassDiagram.puml @@ -0,0 +1,21 @@ +@startuml +!define COMMAND_COLOR #A9A9A9 +!define ATTRIBUTE_COLOR #FFFFFF +!define METHOD_COLOR #FFFFFF + +class FindCommand{ + - predicate: Predicate + + FindCommand(predicate: Predicate) + + execute(model: Model): CommandResult + + equals(other: Object): boolean + + toString(): String +} + +class Course{ + + updateFilteredStudentList(predicate: Predicate): void + + getFilteredStudentList(): List +} + +Command <|-- FindCommand +FindCommand ..> Course : calls +@enduml diff --git a/docs/diagrams/FindCommandSequenceDiagram.puml b/docs/diagrams/FindCommandSequenceDiagram.puml new file mode 100644 index 00000000000..948117d6587 --- /dev/null +++ b/docs/diagrams/FindCommandSequenceDiagram.puml @@ -0,0 +1,87 @@ +@startuml +!include style.puml +skinparam ArrowFontStyle plain + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":CodeSphereParser" as CodeSphereParser LOGIC_COLOR +participant ":FindCommandParser" as FindCommandParser LOGIC_COLOR +participant "d:FindCommand" as FindCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +participant "d:NameContainsKeywordsPredicate" as NameContainsKeywordsPredicate MODEL_COLOR +end box + +[-> LogicManager : execute("find n/john") +activate LogicManager + +LogicManager -> CodeSphereParser : parseCommand("find n/john") +activate CodeSphereParser + +create FindCommandParser +CodeSphereParser -> FindCommandParser +activate FindCommandParser + +FindCommandParser --> CodeSphereParser +deactivate FindCommandParser + +CodeSphereParser -> FindCommandParser : parse("n/john") +activate FindCommandParser + +create NameContainsKeywordsPredicate +FindCommandParser -> NameContainsKeywordsPredicate +activate NameContainsKeywordsPredicate + +NameContainsKeywordsPredicate --> FindCommandParser +deactivate NameContainsKeywordsPredicate + +create FindCommand +FindCommandParser -> FindCommand +activate FindCommand + +FindCommand --> FindCommandParser : f +deactivate FindCommand + +FindCommandParser --> CodeSphereParser : f +deactivate FindCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +FindCommandParser -[hidden]-> CodeSphereParser +destroy FindCommandParser + +CodeSphereParser --> LogicManager : f +deactivate CodeSphereParser + +LogicManager -> FindCommand : execute() +activate FindCommand + +FindCommand -> Model : updateFilteredStudentList(NameContainsKeywordsPredicate("john")) +activate Model + +Model --> FindCommand +deactivate Model + +FindCommand -> Model : getFilteredStudentList().size() +activate Model + +Model --> FindCommand +deactivate Model + +create CommandResult +FindCommand -> CommandResult +activate CommandResult + +CommandResult --> FindCommand +deactivate CommandResult + +FindCommand --> CodeSphereParser : result +deactivate FindCommand + +CodeSphereParser --> LogicManager : result +deactivate CodeSphereParser + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 75ed8a6aac6..00564894b95 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -8,24 +8,31 @@ Package Model as ModelPackage <>{ Class "<>\nReadOnlyCourseList" as ReadOnlyCourseList Class "<>\nReadOnlyUserPrefs" as ReadOnlyUserPrefs Class "<>\nModel" as Model -Class CourseList Class ModelManager Class UserPrefs +Class I #FFFFFF + + +Package Course <>{ +Class CourseList Class UniqueCourseList +Class Course +Class CourseName +} + +Package Student <>{ Class UniqueStudentList Class FilteredList Class SortedList -Class Course -Class CourseName Class Student Class Name Class Email Class Remark Class Tag Class PendingQuestion +} -Class I #FFFFFF } Class HiddenOutside #FFFFFF @@ -59,5 +66,4 @@ Student *-down-> Email Student *-down-> Remark Student *-down-> Tag Student *-down-> PendingQuestion - @enduml diff --git a/docs/images/FindCommandClassDiagram.png b/docs/images/FindCommandClassDiagram.png new file mode 100644 index 00000000000..b575fbb885e Binary files /dev/null and b/docs/images/FindCommandClassDiagram.png differ diff --git a/docs/images/FindCommandSequenceDiagram.png b/docs/images/FindCommandSequenceDiagram.png new file mode 100644 index 00000000000..d449a4d0269 Binary files /dev/null and b/docs/images/FindCommandSequenceDiagram.png differ