diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index a4694729915..cdec39ddcc2 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -14,9 +14,25 @@ ## **Acknowledgements** +**Code:** + * Trie implementation is reused from [eugenp's tutorials](https://github.com/eugenp/tutorials) with minor modifications. -_{ list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well }_ +**Documentation:** + +* Icons in our User Guide and Developer Guide are taken from [Font Awesome](https://fontawesome.com/). +* Our documentation is generated using [MarkBind](https://markbind.org/index.html), with [Nunjucks](https://mozilla.github.io/nunjucks/) variables and macros. +* Our UML diagrams are generated using [PlantUML](https://plantuml.com/). + +**Dev-dependencies:** + +* The dependency [MarkBind](https://markbind.org/index.html), was used for having a live preview locally, through installing the [`markbind-cli`](https://www.npmjs.com/package/markbind-cli) package with [NPM](https://www.npmjs.com/), that runs over the [Node.js](https://nodejs.org/) runtime. +* The dependency [`captain-githook`](https://github.com/swellaby/captain-githook), was used for configuring git hooks, for pre-push and pre-commit checks. + +**Ideas:** + +* The idea of autocomplete and command history retrieval was adapted from common command terminals, like the [Windows Terminal](https://github.com/microsoft/terminal) and [Bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)), which had these features. +* The idea of syntax highlighting was adapted from common file editors, like [Visual Studio Code](https://code.visualstudio.com/) and Integrated Development Environments like [IntelliJ IDEA](https://www.jetbrains.com/idea/), which had syntax highlighting. {{ newPage }} @@ -58,6 +74,7 @@ The bulk of TAPro's work is done by the following four components: [**`Commons`**](#common-classes) represents a collection of classes used by multiple other components.
+{{ newPageBetween }} #### How the architecture components interact with each other @@ -125,13 +142,15 @@ call as an example. -**Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline continues till the end of diagram. +**Note:** The lifeline for `DeletePersonCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline continues till the end of diagram. +{{ newPageBetween }} + **How command execution works in `Logic` component:** -1. When `Logic` is called upon to execute a command, it is passed to an `AddressBookParser` object which in turn creates a parser that matches the command (e.g., `DeleteCommandParser`) and uses it to parse the command. -1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `DeleteCommand`) which is executed by the `LogicManager`. +1. When `Logic` is called upon to execute a command, it is passed to an `AddressBookParser` object which in turn creates a parser that matches the command (e.g., `DeletePersonCommandParser`) and uses it to parse the command. +1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `DeletePersonCommand`) which is executed by the `LogicManager`. 1. The command can communicate with the `Model` when it is executed (e.g. to delete a person).
Note that although this is shown as a single step in the diagram above (for simplicity), in the code it can take several interactions (between the command object and the `Model`) to achieve. 1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. @@ -141,16 +160,21 @@ Here are the other classes in `Logic` (omitted from the class diagram above) tha

**How the parsing works:** -* When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `AddressBookParser` returns back as a `Command` object. - * All `XYZCommandParser` classes (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing. -* When called upon to parse an autocomplete input, the `AddressBookParser` class checks whether the input contains arguments. If it does not contain arguments, it creates an `AutoCompleteCommand` object which autocompletes Commands. Otherwise, it checks for the last argument in the user input and creates the matching `AutoComplete` object if it exists (e.g. `arbitrary_command arg_a/arbitrary_arg` lead to the `AutoCompleteArgA` object, if it exists). Otherwise, a default `AutoComplete` object that always return an empty string is returned. +* When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddPersonCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddPersonCommand`) which the `AddressBookParser` returns back as a `Command` object. + * All `XYZCommandParser` classes (e.g., `AddPersonCommandParser`, `DeletePersonCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing. +* When called upon to parse an autocomplete input, the `AddressBookParser` class checks whether the input contains arguments. If it does not contain arguments, it creates an `AutoCompleteCommand` object which autocompletes Commands. Otherwise, it checks for the last argument in the user input and creates the matching `AutoComplete` object if it exists. Otherwise, a default `AutoComplete` object that always return an empty string is returned. + + + +While a command is named as `XYZPersonCommandParser` internally (where `XYZ` is a placeholder for a specific command), it acts on a student externally in TAPro. + {{ newPage }} ### Model component **API** : [`Model.java`](https://github.com/AY2324S2-CS2103T-F13-1/tp/tree/master/src/main/java/seedu/address/model/Model.java) -

+ **The `Model` component,** * stores the contact book data i.e., all `Person` objects (which are contained in a `UniquePersonList` object). @@ -165,23 +189,31 @@ Here are the other classes in `Logic` (omitted from the class diagram above) tha An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Person` references. This allows `AddressBook` to only require one `Tag` object per unique tag, instead of each `Person` needing their own `Tag` objects.
- + -{{ newPage }} + + +While `Person` objects are used internally, it represents a student externally in TAPro. + ### Storage component **API** : [`Storage.java`](https://github.com/AY2324S2-CS2103T-F13-1/tp/tree/master/src/main/java/seedu/address/storage/Storage.java) -

+

**The `Storage` component,** * can save both contact book data and user preference data and course name data in JSON format, and read them back into corresponding objects. * inherits from both `AddressBookStorage` and `UserPrefStorage` and `CourseStorageName`, 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`) + + +While `AddressBookXYZ` objects are used internally (where `XYZ` is a placeholder), it represents the student contact book externally in TAPro. + + {{ newPage }} ### Common classes @@ -239,16 +271,16 @@ There are two variants of autocomplete feature. One variant is the autocompletio **How autocompletion of dynamic data works using `AutoCompletePrefixResolver` in the `Logic` component:** -In the autocompletion of dynamic data there are 5 main processes: +In the autocompletion of dynamic data there are 5 key steps: 1. Notifying that the data is outdated 2. Using the autocomplete hotkey to return the autocompletion 3. Parsing the input to call the corresponding kind of autocompletion -4. Updating the new attribute data into the `AttributeTrie` -5. Generating the autocomplete result +4. Generating the autocomplete result +5. Updating the new attribute data into the `AttributeTrie` {{ newPageBetween }} -**How the `AttributeTrie` is notified of new data:** +**Step 1. How the `AttributeTrie` is notified of new data:** 1. When a command is executed, a check is performed in `LogicManager` to determine if the command could potentially modify the data. 1. If the data could be modified, then we would get the latest `addressBook` using `getAddressBook()`. @@ -260,7 +292,7 @@ In the autocompletion of dynamic data there are 5 main processes: {{ newPageBetween }} -**How the autocomplete hotkey works:** +**Step 2. How the autocomplete hotkey works:** 1. After the autocomplete hotkey {{ macros.keyFormat('Tab') }} is pressed, the method `handleTabKeyPressEvent(...)` is called. 1. Whenever `lastModifiedText` has changed, it means that the text in the `CommandBox`'s command input box has changed (e.g. the user types into the command input box), so the `autoCompleteResultCache` is set to `null` to indicate that the `AutoCompleteResult` is outdated. @@ -280,7 +312,7 @@ The reference frame below, `create empty autocomplete`, is used in the next few {{ newPageBetween }} -**How `LogicManager` parses the input to generate a new `AutoCompleteResult`:** +**Step 3. How `LogicManager` parses the input to generate a new `AutoCompleteResult`:** 1. When `LogicManager#autoComplete(commandText)` is called with the text `commandText` to autocomplete, it calls the `AddressBookParser#parseAutoComplete(commandText)` method. 1. Depending on the `commandText` passed into `parseAutoComplete`, there are 3 possible paths: @@ -290,11 +322,11 @@ The reference frame below, `create empty autocomplete`, is used in the next few 1. In all cases, a `AutoComplete` is returned, which `AutoCompleteCommand` and `AutoCompletePrefixResolver` are subclasses of. 1. The `AutoComplete` produces a `AutoCompleteResult`, which is passed back to the `MainWindow`. -

+

{{ newPageBetween }} -**How `AutoCompletePrefixResolver` is generates an `AutoCompleteResult`:** +**Step 4. How `AutoCompletePrefixResolver` is generates an `AutoCompleteResult`:** 1. When `AutoCompletePrefixResolver#getAutoComplete(input)` is called, if the `input` is blank, an empty `AutoCompleteResult` is returned. 1. Otherwise, it calls `findTriePrefixContinuation(input)` which would find the text, that continues from a given input, in the `AttributeTrie`. This may update the `AttributeTrie` with new attribute data if necessary, due to the lazy evaluation. @@ -306,7 +338,7 @@ The reference frame below, `create empty autocomplete`, is used in the next few {{ newPageBetween }} -**How `AttributeTrie` is updated with new attribute data and returns the matches:** +**Step 5. How `AttributeTrie` is updated with new attribute data and returns the matches:** 1. When `AutoCompletePrefixResolver#findTriePrefixContinuation(input)` is called, `findLastPrefixIndex(input)` is called which returns `indexOfLastPrefix`. 1. Then, with the `indexOfLastPrefix`, it calls `findLastPrefix` to find the last prefix in the `input`. @@ -322,7 +354,7 @@ The reference frame below, `create empty autocomplete`, is used in the next few #### Design considerations -**Separation of concerns:** +**1. Separation of concerns:** As the autocomplete feature involves many classes all over the codebase, it is important to handle the separation of concerns carefully, to lead to higher cohesion and lower coupling. @@ -332,7 +364,7 @@ This was done through the following: * Separating the dynamic autocompletion from the static autocompletion, as their internal implementations were different. * Having each of the subcomponents, Attribute Classes, Parser Classes and AutoComplete Classes, as packages, which encapsulates them, limiting functional overlaps. -**Caching of intermediate results for improved performance:** +**2. Caching of intermediate results for improved performance:** There are two layers in the current implementation used for caching, which are: * When the current `lastModifiedText` doesn't change, caching the `AutoCompleteResult` in `autoCompleteResultCache`. @@ -340,7 +372,7 @@ There are two layers in the current implementation used for caching, which are: By having caching of intermediate results, it reduces the need to recompute certain results, thus improving performance of TAPro on users' systems. -**Lazy evaluation to reduce redundant computations:** +**3. Lazy evaluation to reduce redundant computations:** Lazy evaluation is carried out in `AttributeTrie`, where the new `Trie` was lazily evaluated. It means that the `Trie` was only generated when the autocompletion of a parameter value doesn't have an `AttributeTrie` already present. @@ -355,10 +387,10 @@ Lazy evaluation is carried out in `AttributeTrie`, where the new `Trie` was lazi ### Command History Retrieval Let's consider the scenario where the user wants to retrieve the last command executed. The user can do this by -pressing the UP key on +pressing the {{ macros.keyFormat('Up', '') }} key on the keyboard. -The UP key press event is captured by the `CommandBox` class, which then +The {{ macros.keyFormat('Up', '') }} key press event is captured by the `CommandBox` class, which then retrieves the last command from the `CommandHistory` Singleton object. @@ -371,7 +403,7 @@ retrieves the last command from the `CommandHistory` Singleton object. Below is the activity diagram that shows how the process of a user interacting with the input field to retrieve the last command executed. - + {{ newPage }} @@ -404,7 +436,7 @@ be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted

-**Step 3.** The user executes `add n/David …​` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified contact book state to be saved into the `addressBookStateList`. +**Step 3.** The user executes `addstu n/David …​` to add a new person. The `addstu` command also calls `Model#commitAddressBook()`, causing another modified contact book state to be saved into the `addressBookStateList`. @@ -448,19 +480,19 @@ The `redo` command does the opposite — it calls `Model#redoAddressBook()`, **Note:** If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest contact book state, then there are no undone `AddressBook` states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo. -

+
**Step 6.** The user then decides to execute the command `list`. Commands that do not modify the contact book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged.

-**Step 7.** The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all contact book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …​` command. This is the behavior that most modern desktop applications follow. +**Step 7.** The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all contact book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `addstu n/David …​` command. This is the behavior that most modern desktop applications follow. The following activity diagram summarizes what happens when a user executes a new command: -

+

{{ newPageBetween }} @@ -477,14 +509,6 @@ The following activity diagram summarizes what happens when a user executes a ne * Pros: Will use less memory (e.g. for `delstu`, just save the person being deleted). * Cons: We must ensure that the implementation of each individual command are correct. -_{more aspects and alternatives to be added}_ - -{{ newPage }} - -### \[Proposed\] Data archiving - -_{Explain here how the data archiving feature will be implemented}_ - {{ newPage }} @@ -505,14 +529,14 @@ _{Explain here how the data archiving feature will be implemented}_ ### Product scope **Target user profile**: -* Teaching Assistant for a Computer Science module in NUS +* Teaching Assistant for one Computer Science module in NUS * Tech savvy * Prefer desktop apps over other types * Can type fast * Prefers typing to mouse interactions * Is reasonably comfortable using CLI apps -**Value proposition**: All in one contact book managing student’s progress in the course, by means of participation, grades, and other course specific attributes of an NUS CS class. Can quickly find information, filter and sort with keyboard shortcuts. +**Value proposition**: All in one contact book managing student’s progress in the course, by means of attendance of the NUS CS class. Can quickly find information and perform operations with keyboard shortcuts. {{ newPage }} @@ -530,26 +554,25 @@ _{Explain here how the data archiving feature will be implemented}_ [//]: # (whitespace is added to force the header row into one line) {% set whitespace = '        ' %} -| Priority | As a … | I want to … | So that I can… | -|------------------|---------------------|--------------------------------------------------------------------|------------------------------------------------------| -| {{ threeStars }} | TA {{ whitespace }} | name/rename the CS course that I am tutoring this semester | keep track of the module I am teaching | -| {{ threeStars }} | TA | add a student to the my class that I am tutoring this semester | keep track of him or her | -| {{ threeStars }} | TA | view all students from my class | view details about all of them | -| {{ threeStars }} | TA | mark attendance for a student in my class for a particular week | keep track of who is present | -| {{ threeStars }} | TA | unmark attendance for a student in my class for a particular week | keep track of who is absent | -| {{ threeStars }} | TA | delete a student | remove a student if he or she leaves the class | -| {{ threeStars }} | TA | know all the commands of TAPro via the help window | use it effectively | -| {{ threeStars }} | TA | see all students in the contact book | have an overview of all students | -| {{ threeStars }} | TA | edit a student's details | have the latest data | -| {{ threeStars }} | TA | find a student by name | get a student's data easily | -| {{ threeStars }} | TA | delete all students from a previous semester from the contact book | clear my contacts quickly at the start of a semester | -| {{ twoStars }} | TA | retrieve command history | avoid retyping a command | -| {{ twoStars }} | TA | to autocomplete my input | to save time | -| {{ twoStars }} | TA | exit the program smoothly | to save time | - -**TODO: Add more user stories that are applicable** - -*{More to be added}* +| Priority | As a … | I want to … | So that I can… | +|------------------|----------------------------|--------------------------------------------------------------------|------------------------------------------------------| +| {{ threeStars }} | TA | name/rename the CS course that I am tutoring this semester | keep track of the module I am teaching | +| {{ threeStars }} | TA | add a student to the my class that I am tutoring this semester | keep track of him or her | +| {{ threeStars }} | TA | view all students from my class | view details about all of them | +| {{ threeStars }} | TA | delete a student | remove a student if he or she leaves the class | +| {{ threeStars }} | TA | see all students in the contact book | have an overview of all students | +| {{ threeStars }} | TA | edit a student's details | have the latest data | +| {{ threeStars }} | TA | find a student by name | get a student's data easily | +| {{ threeStars }} | attendance tracking TA | mark attendance for a student in my class for a particular week | keep track of who is present | +| {{ threeStars }} | attendance tracking TA | unmark attendance for a student in my class for a particular week | keep track of who is absent | +| {{ twoStars }} | TA over multiple semesters | delete all students from a previous semester from the contact book | clear my contacts quickly at the start of a semester | +| {{ twoStars }} | new user | know all the commands of TAPro via the help window | use it effectively | +| {{ twoStars }} | CLI user | retrieve command history | avoid retyping a command | +| {{ twoStars }} | CLI user | autocomplete my input | save time | +| {{ twoStars }} | fast typist | use keyboard inputs to interact with TAPro | save time | +| {{ twoStars }} | user | exit the program smoothly | save time | +| {{ oneStar }} | user | easily read the result message of a command | save time | + {{ newPage }} @@ -597,6 +620,8 @@ For all use cases below, the **System** is TAPro and the **Actor** is the user, +{{ newPageBetween }} + **Use case: Editing a Student** @@ -659,6 +684,8 @@ For all use cases below, the **System** is TAPro and the **Actor** is the user, +{{ newPageBetween }} + **Use case: Name or Rename CS Course** @@ -717,6 +744,8 @@ For all use cases below, the **System** is TAPro and the **Actor** is the user, +{{ newPageBetween }} + **Use case: Mark Attendance** @@ -856,10 +885,6 @@ For all use cases below, the **System** is TAPro and the **Actor** is the user, -**TODO: Add more use cases** - -*{More to be added}* - {{ newPage }} ### Non-Functional Requirements @@ -871,8 +896,6 @@ For all use cases below, the **System** is TAPro and the **Actor** is the user, 5. Input validation to prevent errors due to incorrect data entry. 6. Application to handle common errors gracefully, such as incorrect data entry, without crashing or losing data. -*{More to be added}* - {{ newPage }} ### Glossary @@ -948,6 +971,7 @@ Expected: The most recent window size and location is retained.
+{{ newPageBetween }} ### Adding a student If TAPro does not have any student contacts, the following commands can be used to add some @@ -1012,9 +1036,8 @@ shown in the status message. This command differs from most other commands that use the `NUSNET` to identify a student. This command uses the index number shown in the displayed person list to identify the student to be edited. -
- -
+

+{{ newPageBetween }} ### Deleting a student @@ -1066,8 +1089,6 @@ Expected: Student with name 'John Doe' is displayed on the Student Contact Cards * `find`: Missing keyword. -2. _{ more test cases …​ }_ -
### Marking a student's attendance @@ -1097,6 +1118,7 @@ Details of the marked student is shown in the result message panel.
+{{ newPageBetween }} ### Unmarking a student's attendance @@ -1154,7 +1176,34 @@ Expected: TAPro's main window's title contains the course code `CS2103` provided ### Autocompleting fields -**TODO** +1. **Autocompleting the `addstu` command name** + + + +1. Prerequisites: No prerequisites. + + + + +2. Test case: Enter `a` and press {{ macros.keyFormat('Tab') }} + +Expected: The command box input text changes to `addstu`. + + + +2. **Autocompleting the parameter `MAJOR`** + + + +1. Prerequisites: There is at least one student, and all students have the major `Computer Science`. + + + + +2. Test case: Enter `edit 1 m/` and press {{ macros.keyFormat('Tab') }} + +Expected: The command box input text changes to `edit 1 m/Computer Science` +
@@ -1168,8 +1217,8 @@ Expected: TAPro's main window's title contains the course code `CS2103` provided -2. Test case: Retrieving a previous command with UP key +2. Test case: Retrieving a previous command with +{{ macros.keyFormat('Up', '') }} key * After the entering a previous command, the command input box is empty. * Pressing {{ macros.keyFormat('Up', '') }} will @@ -1219,17 +1268,6 @@ Expected: The help window automatically pops up, giving further information abou Expected: TAPro's Contact Book resets, clearing all existing students (if any). -
- -### Saving data - -1. **Dealing with missing/corrupted data files** - - 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ - -1. _{ more test cases …​ }_ - - {{ newPage }} ## **Appendix: Design Decisions** diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 6744c9ea0bd..f4b84b63540 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -43,13 +43,13 @@ UserPrefs .up.|> ReadOnlyUserPrefs AddressBook *--> "1" UniquePersonList UniquePersonList --> "~* all" Person -Person *--> Name -Person *--> NusNetId -Person *--> Phone -Person *--> Email -Person *--> Major -Person *--> "*" Tag -Person *--> "*" WeekNumber +Person *---> Name +Person *---> NusNetId +Person *---> Phone +Person *---> Email +Person *---> Major +Person *---> "*" Tag +Person *---> "*" WeekNumber Person -[hidden]up--> I UniquePersonList -[hidden]right-> I diff --git a/docs/diagrams/ParserClasses.puml b/docs/diagrams/ParserClasses.puml index ce4c5ce8c8d..adf1e492903 100644 --- a/docs/diagrams/ParserClasses.puml +++ b/docs/diagrams/ParserClasses.puml @@ -21,10 +21,10 @@ Class Prefix Class HiddenOutside #FFFFFF HiddenOutside ..> AddressBookParser -AddressBookParser .down.> XYZCommandParser: <> +AddressBookParser .down.> XYZCommandParser: creates > -XYZCommandParser ..> XYZCommand : <> -AddressBookParser ..> Command : <> +XYZCommandParser ..> XYZCommand : creates > +AddressBookParser ..> Command : uses > XYZCommandParser .up.|> Parser XYZCommandParser ..> ArgumentMultimap XYZCommandParser ..> ArgumentTokenizer