diff --git a/README.md b/README.md index 13f5c77403f..705cda8b7e8 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) +[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/AY2223S1-CS2103T-T17-1/tp/actions) ![Ui](docs/images/Ui.png) -* This is **a sample project for Software Engineering (SE) students**.
+* Teacher's Assistance Book (TAB) is **a desktop app made for professors or teaching assistants for managing contacts**.
Example usages: - * as a starting point of a course project (as opposed to writing everything from scratch) - * as a case study -* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details. + * as a contact book, storing contacts + * as a storage to store other relevant details of contacts (e.g a student's exam results) +* The project simulates an ongoing software project for a desktop application (called _TAB_) used for managing contact details. * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big. * It comes with a **reasonable level of user and developer documentation**. -* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...). -* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**. +* It is named `Teacher's Assistance Book` (`TAB` for short) because it was created to facilitate teaching assistants' and professors' when managing their students, and even managing teaching assistants (for professors) too. +* For the detailed documentation of this project, see **[TAB's Website](https://github.com/AY2223S1-CS2103T-T17-1/tp)**. * This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info. diff --git a/build.gradle b/build.gradle index 108397716bd..7046cc48400 100644 --- a/build.gradle +++ b/build.gradle @@ -66,7 +66,11 @@ dependencies { } shadowJar { - archiveFileName = 'addressbook.jar' + archiveFileName = 'TAB.jar' +} + +run { + enableAssertions = true } defaultTasks 'clean', 'test' diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..46e0ee6b765 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -5,55 +5,61 @@ title: About Us We are a team based in the [School of Computing, National University of Singapore](http://www.comp.nus.edu.sg). -You can reach us at the email `seer[at]comp.nus.edu.sg` +You can reach us at the email `seer@comp.nus.edu.sg` -## Project team +## Project Team -### John Doe +### Sun Ruoxin - + -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/vantemoon)] +[[portfolio](team/vantemoon.md)] -* Role: Project Advisor -### Jane Doe +* Role: Developer +* Responsibilities: Scheduling and Tracking + +### Khor Jun Wei - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/kjunwei)] +[[portfolio](team/kjunwei.md)] -* Role: Team Lead -* Responsibilities: UI -### Johnny Doe +* Role: Developer +* Responsibilities: Testing + +### Chong Xi Yuen, Terence - + + +[[github](http://github.com/cxyterence)] +[[portfolio](team/cxyterence.md)] -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] * Role: Developer -* Responsibilities: Data +* Responsibilities: Deliverables and Deadlines + +### Guo Wei -### Jean Doe + - +[[github](http://github.com/guowei42)] +[[portfolio](team/guowei42.md)] -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] * Role: Developer -* Responsibilities: Dev Ops + Threading +* Responsibilities: Integration + +### Wee Xin Yang, Markus -### James Doe + - +[[github](http://github.com/yellow-294)] +[[portfolio](team/yellow-294.md)] -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] * Role: Developer -* Responsibilities: UI +* Responsibilities: Code Quality diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 46eae8ee565..609cdac8b5e 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -2,14 +2,20 @@ layout: page title: Developer Guide --- + +## Table of contents + * Table of Contents {:toc} -------------------------------------------------------------------------------------------------------------------- +
+ ## **Acknowledgements** -* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +* This project is built upon the [AddressBook Level-3](https://github.com/se-edu/addressbook-level3) project created as part of the [SE-EDU Initiative](https://se-education.org). +* Libraries used: [JavaFX](https://openjfx.io), [Jackson](https://github.com/FasterXML/jackson), [JUnit 5](https://github.com/junit-team/junit5) -------------------------------------------------------------------------------------------------------------------- @@ -154,48 +160,251 @@ Classes used by multiple components are in the `seedu.addressbook.commons` packa This section describes some noteworthy details on how certain features are implemented. -### \[Proposed\] Undo/redo feature +### Adding a person -#### Proposed Implementation +In TAB, the addition of a new `Person`(contact) is facilitated by the `AddCommand` command. `AddCommand` extends the abstract `Command` class, +where it overrides the `Command#execute` method to add in new contacts whenever called. -The proposed undo/redo mechanism is facilitated by `VersionedAddressBook`. It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. Additionally, it implements the following operations: +#### Implementation -* `VersionedAddressBook#commit()` — Saves the current address book state in its history. -* `VersionedAddressBook#undo()` — Restores the previous address book state from its history. -* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history. +1. The user input to add a `Person` to TAB, is passed to and executed by `LogicManager`, which calls `AddressBookParser#parseCommand` to instantiate a `AddCommandParser`. +2. The `AddCommandParser#parse` will return a `AddCommand`, provided the user input is valid. +3. `LogicManager` will then call the `Command#execute` method of `AddCommand`, +creating a new `Person` through `Model#addPerson` method. +4. Upon successful execution of the command, a message will be displayed to the user, by returning a `CommandResult` +to `LogicManager`. -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. +The sequence diagram below illustrates how the command to add a `Person` works: -Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. +![AddCommandSequence](images/AddCommandSequence.png) -Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state. +### Editing a contact -![UndoRedoState0](images/UndoRedoState0.png) +The editing of a contact's details in TAB is facilitated by the `EditCommand` command. `EditCommand` extends the abstract `Command` class, +where it overrides the `Command#execute` method to edit details of contacts whenever called. -Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state. +#### Implementation -![UndoRedoState1](images/UndoRedoState1.png) +1. The user input to edit a contact on TAB, is passed to and executed by `LogicManager`, which calls `AddressBookParser#parseCommand` to instantiate a `EditCommandParser`. +2. The `EditCommandParser#parse` will return a `EditCommand`, provided the user input is valid. +3. `LogicManager` will then call the `Command#execute` method of `EditCommand`, + replacing the old `Person` with a modified `Person` through `Model#setPerson` method. +4. Upon successful execution of the command, a message will be displayed to the user, by returning a `CommandResult ` + to `LogicManager`. -Step 3. The user executes `add n/David …​` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`. +The sequence diagram below illustrates how the command to edit a `Person` works: -![UndoRedoState2](images/UndoRedoState2.png) +![EditCommandSequence](images/EditCommandSequence.png) -
:information_source: **Note:** If a command fails its execution, it will not call `Model#commitAddressBook()`, so the address book state will not be saved into the `addressBookStateList`. +### Adding the attendance of a student -
+The addition of attendance of a `Student` in TAB is facilitated by `AttendanceCommand` and `AttendanceCommandParser`. + +#### Implementation + +1. The user input to add the attendance of a student in TAB, is passed to and executed by `LogicManager`, +which calls `AddressBookParser#parseCommand` to instantiate a `AttendanceCommandParser`. +2. The `AttendanceCommandParser#parse` will return a `AttendanceCommand`, provided the user input is valid. +3. `LogicManager` will then call the `Command#execute` method of `AttendanceCommand`. +4. The internals of `AttendanceCommand` creates a new `Student` with the updated `attendance`. +5. A `Person` object is created with the `Student` object as `position`, and replaces the `Person` to be edited in the `AddressBook`. +through the `Model#setPerson` method. +5. Upon successful execution of the command, a message will be displayed to the user, by returning a `CommandResult ` + to `LogicManager`. + +The sequence diagram below illustrates how the command to add attendance works: + +![AddAttendanceSequenceDiagram](images/AddAttendanceSequenceDiagram.png) + +### Adding assignments to all students + +The addition of assignments in TAB is facilitated by `AddAssignmentsCommand` and `AddAssignmentsCommandParser`. + +This feature allows assignments with weightages to be added to each `Student` in TAB. Weightages are separated from the assignment name with the prefix: `w/` + +#### Implementation + +The `AddAssignmentsCommandParser` parses the user input to check the validity of the Assignment. Then, every `Student` currently listed in TAB will be assigned these Assignments. This is done with the help of the following methods: + +* `Student#addAssignment` Adds a single Assignment into the `ArrayList` +* `Student#setAssignments` Adds every Assignment in the user input into `ArrayList` + +Listed below are the possible scenarios as well as the behavior of the feature in each scenario. + +Scenario 1: User inputs assignments with weightage that does not add up to 100% + +e.g. `assignments assignments/ Assignment 1 w/50, Finals w/40` + +It will be detected that the weightage of the assignments does not add up to 100 and a `CommandException` is thrown + +Scenario 2: User inputs assignments with negative weightage + +e.g. `assignments assignments/ Assignment 1 w/-50, Midterms w/50, Finals w/100` + +It will be detected that a particular assignment has a negative weightage and a `CommandException` is thrown + +Given below is an example usage scenario and how the Add Assignments mechanism behaves at each step. + +Step 1. The user launches the application. `TAB` will initially display all Persons + +![AddAssignmentsDiagram1](images/AddAssignmentsDiagram1.png) + +Step 2. The user executes `assignments assignments/ Assignment 1 w/15, Assignment 2 w/15, Midterms w/20, Finals w/50`. The `assignments` keyword +causes `AddressBookParser#parseCommand()` to call `AddAssignmentsCommandParser#parse()`. This returns a `AddAssignmentsCommand` + +Step 3. The internals of `AddAssignmentCommand` loops through all the people in the list, checking if they have the position of student + +![AddAssignmentsDiagram2](images/AddAssignmentsDiagram2.png) + +Step 4. `Assignment` objects will be created according to the user input and added to the `assignmentsList` field in `Student` + +The following sequence diagram shows how the AddAssignments operation works: + +![AddAssignmentsDiagram3](images/AddAssignmentsDiagram3.png) + +The following activity diagram summarizes what happens in AddressBookParser when a user executes a AddAssignment command: + +![AddAssignmentsDiagram4](images/AddAssignmentsDiagram4.png) + +Design considerations: + +Aspect: How AddAssignments executes: +* Alternative 1: Only adds assignments to indexed student + * Pros: Each student can have different assignments + * Cons: Will be tedious when there is a large number of students in `TAB` +* Alternative 2: Save Assignments in a json file to be read so every student added after will be automatically instanciated with those assignments + * Pros: Eliminates the need to run AddAssignments command for new students + * Cons: Difficulty in implementation + +### Adding the grade of a student + +#### Implementation + +The proposed add grade feature is facilitated by `GradeCommand` which extends `Command` with an index of the student, an index of the assignment, and a grade to be stored. +It overwrites the following operations: +* `GradeCommand#execute()` - Executes the command, storing the given grade of an assignment of a specified student. +* `GradeCommand#equals(Object o)` - Checks if two objects are equal. + +A `GradeCommandParser` facilitates the parsing of the user input. It implements `Parser.` + +After the command is parsed, the given grade is stored inside the `Assignment` of the specified `Student`. This is done with the help of the following methods: +* `Student#updateOverallGrade(Index indexOfAssignment, Sting Grade)` - Calls `setAssignmentGrade()` with the given index and grade, and update the overall grade of the `Student`. +* `Student#setAssignmentGrade(Index indexOfAssignment, String grade)` - Checks whether the index of the assignment is valid. If so, calls `setGrade()` of the corresponding `Assignment` with the given grade. +* `Assignment#setGrade(String grade)` - Stores the given grade inside the `Assignment`. + +Given below is an example usage scenario and how the add grade feature behaves at each step. + +Step 1. The user launches the application. The `AddressBook` will initially display all Persons with their `Positions`. + +![AddGradeDiagram0](images/AddGradeDiagram0.png) + +Step 2. The user executes `grade 1 assignment/1 grade/86/100`. The `grade` keyword causes `AddressBookParser#parseCommand()` to call `GradeCommandParser#parse()`. This returns a `GradeCommand`. + +![AddGradeDiagram1](images/AddGradeDiagram1.png) + +Step 3. The grade of the specified `Assignment` is added. + +![AddGradeDiagram2](images/AddGradeDiagram2.png) + +Step 4. The internals of `GradeCommand` creates a new `Student` with the updated `overallGrade` and `assignmentList`. + +Step 5. A `Person` object is created with the `Student` object as `position`, and replaces the `Person` to be edited in the `AddressBook`. + +Step 6. The `AddressBook` displays the updated list of `Person`. + +The following sequence diagram shows how the add grade operation works: + +![AddGradeSequenceDiagram](images/AddGradeSequenceDiagram.png) + +#### Design considerations: + +* **Alternative 1 (current choice):** `Student#updateOverallGrade()` calls `Student#setAssignmentGrade()` and returns the updated overall grade of the student + * Pros: The updated `overallGrade` can be easily used to create the new `Student` object. + * Cons: Can be confusing as in whether the `assignmentsList` of the `Student` is updated as well. +* **Alternative 2:** `Student#updateOverallGrade()` does not return a value and only handle the calculation of the overall grade with the updated `assignmentsList` provided + * Pros: Separates the operations done on the `overallGrade` and the `assignmentsList`. + * Cons: The updated `overallGrade` and `assignmentsList` are not available for creating new `Student` object. + +### Editing the availability of a TA + +The addition of availability of a `TeachingAssistant` in TAB is facilitated by `AvailabilityCommand` and `AvailabilityCommandParser`. + +#### Implementation + +1. The user input to add the availability of a TA in TAB, is passed to and executed by `LogicManager`, + which calls `AddressBookParser#parseCommand` to instantiate a `AvailabilityCommandParser`. +2. The `AvailabilityCommandParser#parse` will return a `AvailabilityCommand`, provided the user input is valid. +3. `LogicManager` will then call the `Command#execute` method of `AvailabilityCommand`. +4. The internals of `AvailabilityCommand` creates a new `TeachingAssistant` with the updated `availability`. +5. A `Person` object is created with the `Student` object as `position`, and replaces the `Person` to be edited in the `AddressBook`. + through the `Model#setPerson` method. +5. Upon successful execution of the command, a message will be displayed to the user, by returning a `CommandResult ` + to `LogicManager`. + +The sequence diagram below illustrates how the command to add availability works: + +![AddAvailabilitySequenceDiagram](images/AddAvailabilitySequenceDiagram.png) + +### Editing the roles of a Professor + +The addition of roles of a `Professor` in TAB is facilitated by `RolesCommand` and `RolesCommandParser`. + +#### Implementation + +1. The user input to add the availability of a TA in TAB, is passed to and executed by `LogicManager`, + which calls `AddressBookParser#parseCommand` to instantiate a `RolesCommandParser`. +2. The `RolesCommandParser#parse` will return a `RolesCommand`, provided the user input is valid. +3. `LogicManager` will then call the `Command#execute` method of `RolesCommand`. +4. The internals of `RolesCommand` creates a new `Professor` with the updated `role`. +5. A `Person` object is created with the `Professor` object as `position`, and replaces the `Person` to be edited in the `AddressBook`. + through the `Model#setPerson` method. +5. Upon successful execution of the command, a message will be displayed to the user, by returning a `CommandResult ` + to `LogicManager`. + +The sequence diagram below illustrates how the command to add roles works: + +![AddRolesSequenceDiagram](images/AddRolesSequenceDiagram.png) + +### Filter by tutorial group + +#### Implementation + +The filtering feature is facilitated by `FilterCommand`. It extends `Command` with a checking predicate, stored internally as a `TagContainsKeywordPredicate`. It overwrites the following operations: + +* `FilterCommand#execute()` — Executes the command, filtering the list of people according to whether they have a matching tag. +* `FilterCommand#equals(Object o)` — Checks if two objects are equal. + +Given below is a Class Diagram of this feature. + +![FilterDiagram1](images/FilterDiagram1.png) -Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state. +Given below is an example usage scenario and how the filtering mechanism behaves at each step. -![UndoRedoState3](images/UndoRedoState3.png) +Step 1. The user launches the application. The `AddressBook` will initially display all Persons. -
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather -than attempting to perform the undo. +![FilterDiagram2](images/FilterDiagram2.png) + +Step 2. The user executes `filter CS2103-T17` command to filter people that have the `CS2103-T17 tag`. The `filter` keyword causes `AddressBookParser#parseCommand()` to call `FilterCommandParser#parse()`. This creates a `TagContainsKeywordPredicate` with the keyword `CS2103-T17`, and returns a `FilterCommand` containing this predicate. + +![FilterDiagram3](images/FilterDiagram3.png) + +Step 3. `Model` then loops through all people in the list, checking if they have a `CS2103-T17` tag. + +![FilterDiagram4](images/FilterDiagram4.png) + +
:information_source: **Note:** If the filter command fails its execution, this step will not be reached. A Message will be displayed, letting the reader know how the filter command should be used.
-The following sequence diagram shows how the undo operation works: +Step 4. The filtered list will then be displayed to the user. + +![FilterDiagram5](images/FilterDiagram5.png) + + + +The following sequence diagram shows how the filter operation works: -![UndoSequenceDiagram](images/UndoSequenceDiagram.png) +![FilterDiagram6](images/FilterDiagram6.png)
:information_source: **Note:** The lifeline for `UndoCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. @@ -207,37 +416,130 @@ The `redo` command does the opposite — it calls `Model#redoAddressBook()`,
-Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged. +Step 5. The user then decides to execute the command `list`. `AddressBook` removes the filter, showing all persons. -![UndoRedoState4](images/UndoRedoState4.png) +![FilterDiagram2](images/FilterDiagram2.png) -Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …​` command. This is the behavior that most modern desktop applications follow. +The following activity diagram summarizes what happens in AddressBookParser when a user executes a filter command: -![UndoRedoState5](images/UndoRedoState5.png) + -The following activity diagram summarizes what happens when a user executes a new command: +#### Design considerations: - +**Aspect: How filter executes:** -#### Design considerations: +* **Alternative 1 (current choice):** Updates the displayed list by hiding information. + * Pros: Uses less memory + * Cons: May have performance issues in terms of memory usage. + +* **Alternative 2:** Creates a new Address Book. + * Pros: Does not modify the master address book. + * Cons: May have performance issues in terms of memory usage. + +### Display details of contacts in secondary panel + +#### Implementation + +This feature is facilitated by `ShowCommand`, which extends `Command` with an index, stored internally as `index`. +It overwrites the following operations: +* `ShowCommand#execute()` — Executes the command, displaying the details of the contact at the specified index. +* `ShowCommand#equals(Object o)` — Checks if two objects are equal. + +Given below is an example usage scenario and how the displaying mechanism behaves at each step. + +Step 1. The user launches the application. The `AddressBook` will initially display all Persons with their `Positions`. + +![ShowDiagram1](images/ShowDiagram1.png) + +Step 2. The user executes `show 1` command to display the details of the first contact shown. The `show` keyowrd causes +`AddressBookParser#parseCommand()` to call `ShowCommandParser#parse()`. This returns a `ShowCommand` containing the +`index 1`. + +![ShowDiagram2](images/ShowDiagram2.png) + +Step 3. `Model` retrieves the `Person` object located at `index 1`. This returns a `CommandResult` containing the `Person` +object. + +![ShowDiagram3](images/ShowDiagram3.png) + +Step 4. `MainWindow` from `UI` component will process the `CommandResult` and display the details found inside the +included `Person` object. + +The following show diagram shows how the show operation works: + +![ShowSequenceDiagram](images/ShowSequenceDiagram.png) + +#### Design Considerations +* **Alternative 1 (current choice):** Passing `Person` object to `MainWindow` + * Pros: Reduced coupling throughout the program + * Cons: Changing of many method signatures, unintuitive to implement at first. + +* **Alternative 2:** Passing `index` to `MainWindow` to retrieve details from `Model` + * Pros: More intuitive to implement in the sense that only `MainWindow` would be primarily modified. + * Cons: Increased coupling, violation of Law of Demeter. + +### Multiple Teacher’s Address Books (TABs) +**Feature Summary** +- Users are able to create multiple TABs (currently limited to 5). + - Through `new` command +- Users are able to swap between the TABs + - Through `swap` command + +**Implementation** + +Current implementation consists of two parts. First part is creating new books, second part is swapping between books. + +**Creating new Books** + +This feature builds on `MainWindow` class to facilitate communication between the UI and backend functions. It +communicates with `Logic` to create new TABs and uses `Model` class to alter the current `UserPref` instance. + +**Swapping between Books** + +This feature, similar to creating new TABs builds on `MainWindow` class and communicates with `Logic` class to swap +between TABs. It uses `Model` class to fetch the next TAB from `UserPref`. The TABs' information are stored as an +array under `preferences.json` as well as the current TAB's index. `UserPref` loops around the array when it reaches +the end. `Logic` then make use of the path information to call `storage` to update the current TAB. + +**Example** +Given below is an example usage scenario and how the creation of new TAB mechanism behaves at each step. + +Step 1: User enters the `new` command into the Command-Line Interface (CLI) or user navigates via `Files -> New Book` to +create a new TAB. -**Aspect: How undo & redo executes:** +The following sequence diagram shows how creation of new book work: +![Creating New Book](images/CreateNewBookSequenceDiagram.png) -* **Alternative 1 (current choice):** Saves the entire address book. - * Pros: Easy to implement. - * Cons: May have performance issues in terms of memory usage. +Step 1.1: User adds X amount of TABs (up to 5): -* **Alternative 2:** Individual command knows how to undo/redo by - itself. - * Pros: Will use less memory (e.g. for `delete`, just save the person being deleted). - * Cons: We must ensure that the implementation of each individual command are correct. +![Swapping Books](images/CreatingNewBookState0.png) -_{more aspects and alternatives to be added}_ +![Swapping Books](images/CreatingNewBookState1.png) -### \[Proposed\] Data archiving +Step 2: User enters the `swap` command or navigates via `Files -> New Book` to toggle between TABs: -_{Explain here how the data archiving feature will be implemented}_ +![Swapping Books](images/SwapState0.png) +The following sequence diagram shows how swapping works: +NOTE: Sequence Diagram Only includes added portions +![Swapping Books](images/SwapBooksSequenceDiagram.png) + +**Design Considerations** + +- Alternative 1 (current choice): Toggle between books + - Pros: + - Swapping between TAB occurs within 1 window + - Open for future features + - Cons: + - Might be more prone to bugs due to complex implementations + - Might be slower to load between books +- Alternative 2: Multiple copies of .java applications + - Pros + - Easier to implement + - Can have multiple books open at once + - Cons: + - User need to create multiple books + - Less open to future feature extensions (communicate between books) -------------------------------------------------------------------------------------------------------------------- @@ -257,42 +559,44 @@ _{Explain here how the data archiving feature will be implemented}_ **Target user profile**: -* has a need to manage a significant number of contacts -* prefer desktop apps over other types -* can type fast -* prefers typing to mouse interactions -* is reasonably comfortable using CLI apps +* Teaching Staff, either a Teaching Assistant or a Professor +* Teaches multiple classes simultaneously +* Has a need to manage the contacts of their students +* Requires to filter their students/contacts by their classes +* Prefers to filter and profile their students/contacts through other information -**Value proposition**: manage contacts faster than a typical mouse/GUI driven app +**Value proposition**: Customizing their focus to each student’s needs ### User stories Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` -| Priority | As a …​ | I want to …​ | So that I can…​ | -| -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- | -| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App | -| `* * *` | user | add a new person | | -| `* * *` | user | delete a person | remove entries that I no longer need | -| `* * *` | user | find a person by name | locate details of persons without having to go through the entire list | -| `* *` | user | hide private contact details | minimize chance of someone else seeing them by accident | -| `*` | user with many persons in the address book | sort persons by name | locate a person easily | +| Priority | As a …​ | I want to …​ | So that I can…​ | +|---------|--------------------|--------------------------------------------------------------------------|---------------------------------------------------------------------------------------| +| `* * *` | Teaching Assistant | store all my student’s contacts in one place | easily contact whoever needs to be contacted. | +| `* * *` | Teaching Assistant | remove student contacts who dropped the module | have an updated address book for the current semester | +| `* * *` | Teaching Assistant | view the performance of students | know who is doing well and who needs more help | +| `* * *` | Professor | store all my TAs’ contacts in one place | know who to contact should I need help | +| `* * *` | Professor | view the availability of the TAs | I know who to reach out to should one be unavailable to teach due to medical reasons. | +| `* * *` | Professor | remove contacts of TAs from my address book when they are no longer a TA | my address book is updated for the current semester | +| `* * *` | New User | view the summary of all commands | know the features of TAB | +| `* * *` | New User | go through a tutorial to get started. | understand how to use TAB | *{More to be added}* ### Use cases -(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise) +(For all use cases below, the **System** is the `Teachers Address Book (TAB)` and the **Actor** is the `user`, unless specified otherwise) -**Use case: Delete a person** +**Use case: UC01 - Delete a person** **MSS** 1. User requests to list persons -2. AddressBook shows a list of persons +2. TAB shows a list of persons 3. User requests to delete a specific person in the list -4. AddressBook deletes the person +4. TAB deletes the person Use case ends. @@ -304,24 +608,246 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli * 3a. The given index is invalid. - * 3a1. AddressBook shows an error message. + * 3a1. TAB shows an error message. Use case resumes at step 2. -*{More to be added}* +**Use case: UC02 - Add a person** -### Non-Functional Requirements +**MSS** + +1. User enters the command to add a particular person. +2. TAB shows that the command is successful and displays the added person. + + Use case ends. + +**Extensions** + +* 1a. The given student details are invalid. + * 1a1. TAB shows an error message. + + Use case resumes at step 1. + +**Use case: UC03 - Edit a person’s details** + +**MSS** + +1. User enters the command to edit a particular person’s details. +2. TAB shows that the command is successful and displays the edited person. + + Use case ends. + +**Extensions** + +* 1a. The new student details are invalid. + * 1a1. TAB shows an error message. + + Use case resumes at step 1. + +* 1b. The given index is invalid. + * 1b1. TAB shows an error message. + + Use case resumes at step 1. + +**Use case: UC04 - Show details of a person** + +**MSS** + +1. User requests to view details of a person. +2. TAB shows the details of the specified person. + + Use case ends. + +**Extensions** + +* 1a. The given index is invalid. + + * 1a1. TAB shows an error message. + + Use case resumes at step 1. -1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed. -2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage. -3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. +**Use case: UC05 - Add remarks of a person** + +**MSS** + +1. User enters the command to input a remark on a specific person. +2. TAB shows that the command is successful and displays the edited person. + + Use case ends. + +**Extensions** + +* 1a. The given index is invalid. + + * 1a1. TAB shows an error message. + + Use case resumes at step 1. + +**Use case: UC06 - Edit attendance of a student** + +**MSS** + +1. User enters the command to edit attendance of a student. +2. TAB shows that the command is successful and displays the edited student. + + Use case ends. + +**Extensions** + +* 1a. The given index is invalid. + + * 1a1. TAB shows an error message. + + Use case resumes at step 1. + +* 1b. The person at the given index is not a student. + + * 1b1. TAB shows an error message. + + Use case resumes at step 1. + +* 1c. The new attendance details are invalid. + + * 1c1. TAB shows an error message. + + Use case resumes at step 1. + +**Use case: UC07 - Add assignments to all students** + +**MSS** + +1. User enters the command to add assignments to all students. +2. TAB shows that the command is successful and displays assignments in the students' details. + + Use case ends. + +**Extensions** + +* 1a. The new assignment details are invalid. + + * 1a1. TAB shows an error message. + + Use case resumes at step 1. + +**Use case: UC08 - Edit the grade of a student** + +**MSS** + +1. User enters the command to edit the grade of an student's assignment. +2. TAB shows that the command is successful and displays the updated student details. + + Use case ends. + +**Extensions** + +* 1a. The given index of the person and/or assignment is invalid. + + * 1a1. TAB shows an error message. + + Use case resumes at step 1. + +* 1b. The person at the given index is not a student. + + * 1b1. TAB shows an error message. + + Use case resumes at step 1. + +* 1c. The new grade details are invalid. + + * 1c1. TAB shows an error message. + + Use case resumes at step 1. + +**Use case: UC09 - Edit the availability of a TA** + +**MSS** + +1. User enters the command to edit the availability of a TA. +2. TAB shows that the command is successful and displays the updated TA's details. + + Use case ends. + +**Extensions** + +* 1a. The given index is invalid. + + * 1a1. TAB shows an error message. + + Use case resumes at step 1. + +* 1b. The person at the given index is not a TA. + + * 1b1. TAB shows an error message. + + Use case resumes at step 1. + +* 1c. The new availability details are invalid. + + * 1c1. TAB shows an error message. + + Use case resumes at step 1. + +**Use case: UC10 - Edit the roles of a Professor** + +**MSS** + +1. User enters the command to edit the roles of a Professor. +2. TAB shows that the command is successful and displays the updated Professor's details. + + Use case ends. + +**Extensions** + +* 1a. The given index is invalid. + + * 1a1. TAB shows an error message. + + Use case resumes at step 1. + +* 1b. The person at the given index is not a Professor. + + * 1b1. TAB shows an error message. + + Use case resumes at step 1. + +* 1c. The new role details are invalid. + + * 1c1. TAB shows an error message. + + Use case resumes at step 1. + +**Use case: UC11 - Find persons by name** + +**MSS** + +1. User enters the command to find specific persons by name. +2. TAB shows that the command is successful and displays the matching persons. + + Use case ends. + +**Extensions** + +* 1a. The given name is invalid. + + * 1a1. TAB shows an error message. + + Use case resumes at step 1. *{More to be added}* +### Non-Functional Requirements + +1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed. +2. Should be able to hold up to 1000 students or TAs without a noticeable sluggishness in performance for typical usage. +3. All user operations should complete within 2 seconds. +4. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. +5. Unauthorised users can only read data without modifying anything. + ### Glossary * **Mainstream OS**: Windows, Linux, Unix, OS-X * **Private contact detail**: A contact detail that is not meant to be shared with others +* **Authorised Users**: Professors and Teaching Assistants handling their respective modules -------------------------------------------------------------------------------------------------------------------- @@ -349,7 +875,21 @@ testers are expected to do more *exploratory* testing. 1. Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained. -1. _{ more test cases …​ }_ +1. Saving which TAB the user exited + + 1. Create a couple of TABs + + 1. Swap to the desired TAB
+ + 1. Exit and relaunch the application + Expected: The most recent TAB is opened. + +1. Exiting + + 1. Launch TAB + + 1. Enter into the command box `exit` or click the cross button at the top right corner + Expected: Application closes. ### Deleting a person @@ -366,12 +906,95 @@ testers are expected to do more *exploratory* testing. 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
Expected: Similar to previous. -1. _{ more test cases …​ }_ +### Creating a new TAB +1. Creating a new TAB while there are < 5 TABs + + 1. Prerequisites: There are a total of < 5 TABs + + 1. Test case: `new`
+ Expected: A new blank TAB is created. This is reflected at the bottom left corner of the application as a new name. + + 1. Test case: Use the keyboard shortcut `Ctrl+Shift+N`
+ Expected: Similar to previous. + + 1. Other correct new commands to try: `new x`(where x is any input)
+ Expected: Similar to previous. + +1. Creating a new TAB after having a total of 5 TABs + + 1. Prerequisites: There are 5 total TABs present + + 1. Test case: `new`
+ Expected: Error details shown in the status message. + + 1. Test case: Use the keyboard shortcut `Ctrl+Shift+N`
+ Expected: Similar to previous. + +### Swapping TABs +1. Swapping TABs while there are > 1 TABs + + 1. Prerequisites: > 1 TABs present + + 1. Test case: `swap`
+ Expected: Swaps between TABs created. This is reflected at the bottom left corner of the application as another name and the contents present. + + 1. Test case: Use the keyboard shortcut `Shift+Tab`
+ Expected: Similar to previous. + + 1. Other correct swap commands to try: `swap x`(where x is any input)
+ Expected: Similar to previous. + +1. Swapping TABS while there is only 1 TAB + + 1. Prerequisites: There 1 TAB in total + + 1. Test case: `swap`
+ Expected: Nothing happens. + + 1. Test case: Use the keyboard shortcut `Shift+Tab`
+ Expected: Similar to previous. + + 1. Other correct swap commands to try: `swap x`(where x is any input)
+ Expected: Similar to previous. + +### Renaming TABs +1. Renaming with alphanumeric characters and `-` and `_` + + 1. Test case: `rename CS2103T`
+ Expected: Renames the current TAB with `cs2103t`. This is reflected at the bottom left corner of the application as a lower cased name and keeps the name after re-launch. + + 1. Other correct rename commands to try: `rename x`(where x is any valid input)
+ Expected: Similar to previous. + +1. Renaming to a file that is already present + + 1. Prerequisites: There is a TAB named `x` (where x is any valid TAB name). There are 2 TABs. +In this case, assume {TAB1} stands for the TAB named `x` and {TAB2} is the **current active** TAB. + + 1. Test case: `rename x`
+ Expected: Error details shown in the status message. + + 1. Test case: Test case: `rename X`(where `X` is any variation of uppercase/lowercase characters)
+ Expected: Similar to previous. + + 1. Test case: Test case: `rename`
+ Expected: Error details shown in the status message. + +1. Renaming with invalid inputs + 1. Test case: Test case: `rename`
+ Expected: Error details shown in the status message. + + 1. Test case: Test case: `rename x`(where `x` contains any other non-alphanumeric characters than `-` and `_`)
+ Expected: Similar to previous. + +
+ +## **Appendix: Effort** -### Saving data +Our team discussed extensively the features we want to include in this tP. Having former TAs in our group, we seeded out problems and issues TAs might face so we would produce a product they would need and want. -1. Dealing with missing/corrupted data files +One of the biggest problems we faced was how to neatly list the persons in TAB to only show what needs to be seen. We solved this by implementing one of our biggest features: the New feature, which allows users to create new addressbooks so that each addressbook can be used to store students from each module. Swapping and editing each addressbook is also fast and easy - 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ +Another one of our big features is our Assignments feature. This feature enabled users to add Assignments to each student, grade each of them individually, and view these statistics on graphs. This entire feature took weeks and a lot of group effort to complete and is one of our proudest features. -1. _{ more test cases …​ }_ +In all, our team has placed a large amount of effort to craft this specific product made for teachers and through the weeks we have learnt a lot about teamwork and software engineering practises. diff --git a/docs/UserGuide.md b/docs/UserGuide.md old mode 100644 new mode 100755 index 3716f3ca8a4..cd82222e8cd --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -3,30 +3,32 @@ layout: page title: User Guide --- -AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps. +Teacher’s Address Book (TAB) is a **desktop app made for teachers, teaching assistants(TA) and professors for managing contacts of each other, as well as their students, 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, TAB can get your contact management tasks done faster than traditional GUI apps. +TAs and Professors are now taking on more roles in different modules. As such, TAB allows the creation of multiple addressbooks which can be used to store the contact information of each specific module, which leads to neater organisation of contacts. * Table of Contents {:toc} -------------------------------------------------------------------------------------------------------------------- +
## Quick start 1. Ensure you have Java `11` or above installed in your Computer. -1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases). +2. Download the latest `TAB.jar` from [here](https://github.com/AY2223S1-CS2103T-T17-1/tp/releases). -1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook. +3. Copy the file to the folder you want to use as the _home folder_ for your TAB. -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.
+4. 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.png) -1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
+5. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
Some example commands you can try: * **`list`** : Lists all contacts. - * **`add`**`n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` : Adds a contact named `John Doe` to the Address Book. + * **`add`**`n/John Doe p/98765432 e/johnd@example.com pos/Student a/John street, block 123, #01-01 t/CS2103T-T17` : Adds a contact named `John Doe` to TAB. * **`delete`**`3` : Deletes the 3rd contact shown in the current list. @@ -34,7 +36,11 @@ AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized fo * **`exit`** : Exits the app. -1. Refer to the [Features](#features) below for details of each command. +6. Refer to the [Features](#features) below for details of each command. + +
:bulb: **Tip:** +For teachers who are teaching more than one module, please use one TAB for each module. Refer to relevant commands to see how you can create new TAB and swap between existing TABs. +
-------------------------------------------------------------------------------------------------------------------- @@ -44,19 +50,18 @@ AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized fo **:information_source: Notes about the command format:**
+* All commands are **case sensitive** +* All tokenizers are **case sensitive** * Words in `UPPER_CASE` are the parameters to be supplied by the user.
e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`. * Items in square brackets are optional.
- e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`. - -* Items with `…`​ after them can be used multiple times including zero times.
- e.g. `[t/TAG]…​` can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc. + e.g. `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL]` can be used as `edit 1 n/John Doe a/Jurong` or as `n/John Doe`. * Parameters can be in any order.
e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable. -* If a parameter is expected only once in the command but you specified it multiple times, only the last occurrence of the parameter will be taken.
+* If a parameter is expected only once in the command, but you specified it multiple times, only the last occurrence of the parameter will be taken.
e.g. if you specify `p/12341234 p/56785678`, only `p/56785678` will be taken. * Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
@@ -66,49 +71,192 @@ AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized fo ### Viewing help : `help` -Shows a message explaning how to access the help page. +Launches the user guide in a browser window. -![help message](images/helpMessage.png) +![User Guide](images/userGuideWindow.png) Format: `help` +
:bulb: **Tip:** +This can also be done by clicking Help or pressing F1 on the keyboard. +
### Adding a person: `add` -Adds a person to the address book. - -Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` - -
:bulb: **Tip:** -A person can have any number of tags (including 0) +Adds a person to TAB. + +Format: `add n/NAME p/PHONE_NUMBER e/EMAIL pos/POSITION a/ADDRESS t/MODULE-GROUP` + +
:bulb: **Tip:** +A teaching assistant or professor can have more than one tag. A student can only have one tag. +Format of parameters: +* `NAME`: Alphanumerical +* `PHONE_NUMBER`: Numerical +* `Email`: local-part@domain + * local-part: Alphanumerical and the following special characters, excluding the parentheses, (+_.-) + * domain: The domain name must: + * end with a domain label at least 2 characters long + * have each domain label start and end with alphanumeric characters + * have each domain label consist of alphanumeric characters, separated only by hyphens, if any. +* `Position`: `Student`, `TA`, or `Professor` (case-insensitive) +* `Address`: Any value, but cannot be blank +* `Tags`: {Alphanumeric Module}-{Alphanumeric Tutorial group}
+Adds a new contact with the provided details. Required fields include name, phone number, address, email, position, and tag. Tags represent the module and tutorial group a person is associated with and must be in the format of module-tutorial group. + Examples: -* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` -* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal` +* `add n/Alex Yeoh p/87438807 e/alexyeoh@example.com pos/Student a/Blk 30 Geylang Street 29, #06-40 t/CS2103T-T17` +* `add n/Betsy Crowe p/92498754 e/betsycrowe@example.com pos/TA a/Blk 30 Lorong 3 Serangoon Gardens, #07-18 t/CS2103T-T17` ### Listing all persons : `list` -Shows a list of all persons in the address book. +Shows a list of all persons in TAB. Format: `list` +### Displaying the details of a person : `show` + +Shows the detailed information of a person. + +General displayed information are: +* Name +* Position +* Phone +* Email +* Tags +* Address +* Remarks + +There are also additional information unique to each role + +Professors: +* Roles: `Coordinator`, `Tutor`, `Lecturer`, `Advisor` or `Unassigned` + +TA: +* Availability: `Available` or `Unavailable` + +Student +* Attendance: represented as a fraction _e.g. 9/10_ +* Grade: represented as a fraction. This grade is the overall grade calculated from the list of already graded Assignments. +* Graph containing each Assignment and their scores(only if their grade has been updated) +Format: `show INDEX` + +* Shows the detailed information of the person at the specified `INDEX`. +* The index refers to the index number shown in the displayed person list. +* The index **must be a positive integer** 1, 2, 3, …​ + +
:bulb: **Tip:** +If you cannot see all the added assignments in the graph, simply enlarge your tab! +
+ +Examples: +* `show 3` shows the detailed information of the 3rd person in TAB. + +
:bulb: **Tip:** +This can also be done by clicking on the person. +
+ ### Editing a person : `edit` -Edits an existing person in the address book. +Edits an existing person in TAB. + +Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL]` -Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` -* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …​ +Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …​ * At least one of the optional fields must be provided. * Existing values will be updated to the input values. -* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative. -* You can remove all the person’s tags by typing `t/` without - specifying any tags after it. +* Editing of Tags and Position are not allowed. + +Examples: +* `edit 1 p/91234567 e/johndoe@example.com` edits the phone number and email address of the 1st person to be `91234567` and `johndoe@example.com` respectively. +* `edit 2 n/Betsy Crower` edits the name of the 2nd person to be `Betsy Crower` + +### Adding a remark : `remark` + +Adds or edits a remark of an existing person in TAB. + +Format: `remark INDEX r/REMARK` + +Modifies a remark of a person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …​ +* A person can have at most 1 remark. +* When editing remark, the existing remark of the person will be removed i.e. adding of remarks is not cumulative. +* You can remove the person's remark field by typing `r/` without + specifying any message after it. + +Examples: +* `remark 1 r/Interested to be a TA` edits the remark field of the 1st person to be `Interested to be a TA`. +* `remark 2 r/remark_one r/remark_two` edits the remark field of the 2nd person to be `remark_two` and not `remark_one`. +* `remark 3 r/` clears the remark field of the 3rd person. + +### Editing the attendance of a student : `attendance` + +Edits the attendance of an existing student in TAB. + +Format: `attendance INDEX attendance/ATTENDANCE` + +Edits the attendance of the person (whose position must be student) at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …​ + +Examples: +* `attendance 1 attendance/10/13` edits the attendance of the 1st person to be `10/13`. + +### Adding assignments to all students : `assignments` + +Adds assignments to all students in the TAB. + +Format: `assignments assignments/ ASSIGNMENT_1 w/ASSIGNMENT_1_WEIGHTAGE, ASSIGNMENT_2 w/ASSIGNMENT_2_WEIGHTAGE…​` + +* The assignments are added to all students in the TAB. Students that are added to the TAB afterwards will automatically be instantiated with the assignments. +* There must at least be one student inside TAB when running this command. The command will fail otherwise. +* The weightage of the assignments must be integers that add up to 100%. The command will fail otherwise. +* At most 10 assignments can be added. + +Examples: +* `assignments assignments/ Assignment 1 w/15, Assignment 2 w/15, Midterms w/30, Finals w/40` adds Assignment 1: 15% weightage, Assignment 2: 15% weightage, Midterms: 30% weightage, and Finals: 40% weightage to all students in the TAB. +* `assignments assignments/ Assignment 1 w/20 Finals w/15` will fail as the weightage does not add up to 100%. + +### Editing the grade of a student : `grade` + +Edits a student's grade of an assignment of in TAB. + +Format: `grade INDEX assignment/INDEX grade/GRADE` + +Edits the grade of an assignment of the person (whose position must be student) at the specified `INDEX`. The first index refers to the index number shown in the displayed person list. The second index refers to the index of the assignment to be edited as shown in the assignment list of the specified student. Grade must follow the format [integer]/[integer] + +The indices **must be positive integers** 1, 2, 3, …​ +* The grade shown on the detail page of a student represent the overall grade that the student has achieved, calculated based on the grade and weightage of each assignment. + +Examples: +* `grade 2 assignment/1 grade/68/80` edits the grade of the 1st assignment of the 2nd person (whose position is student) to be `68/80`. +* `grade 1 assignment/1 grade/68/80` will fail if the position of the 1st person in the displayed person list is not a student. + +### Editing the availability of a TA : `avail` + +Edits the availability of an existing TA in TAB. +Availabilities are case-insensitive. + +Format: `avail INDEX avail/AVAILABILITY` + +Edits the availability of the person (whose position must be TA) at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …​ Examples: -* `edit 1 p/91234567 e/johndoe@example.com` Edits the phone number and email address of the 1st person to be `91234567` and `johndoe@example.com` respectively. -* `edit 2 n/Betsy Crower t/` Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags. +* `avail 1 avail/Available` edits the availability of the 1st person to be `Available`. + +### Editing the roles of a Professor : `roles` + +Edits the roles of an existing professor in TAB. + +Format: `roles INDEX roles/ROLE` + +Edits the roles of the person (whose position must be professor) at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …​ +Roles are case-insensitive +* Each professor may only have 1 role + + +Examples: +* `roles 1 roles/Coordinator` edits the roles of the 1st person(must be a professor) to be `Coordinator`. + ### Locating persons by name: `find` @@ -116,7 +264,7 @@ Finds persons whose names contain any of the given keywords. Format: `find KEYWORD [MORE_KEYWORDS]` -* The search is case-insensitive. e.g `hans` will match `Hans` +* The search is case-insensitive. e.g. `hans` will match `Hans` * The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans` * Only the name is searched. * Only full words will be matched e.g. `Han` will not match `Hans` @@ -128,9 +276,22 @@ Examples: * `find alex david` returns `Alex Yeoh`, `David Li`
![result for 'find alex david'](images/findAlexDavidResult.png) +### Filtering persons by tag: `filter` + +Filters persons with the specified tag. + +Format: `filter TAG` + +* The search is case-insensitive. e.g. `cs2103t-t17` will match `CS2103T-T17` +* Only full word will be matched. e.g. `CS2103T` will not match `CS2103T-T17` + +Examples: +* `filter CS2103T-T17` returns persons with the tag `CS2103T-T17` +* `filter CS2103T` returns no result. + ### Deleting a person : `delete` -Deletes the specified person from the address book. +Deletes the specified person from the TAB. Format: `delete INDEX` @@ -139,43 +300,141 @@ Format: `delete INDEX` * The index **must be a positive integer** 1, 2, 3, …​ Examples: -* `list` followed by `delete 2` deletes the 2nd person in the address book. +* `list` followed by `delete 2` deletes the 2nd person in the TAB. * `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command. ### Clearing all entries : `clear` -Clears all entries from the address book. +Clears all entries from the TAB. Format: `clear` -### Exiting the program : `exit` +### Creating new TAB : `new` -Exits the program. +Creates a new TAB and automatically swaps to the newly created data file.
+There can be a maximum of 5 books. +Format: `new` -Format: `exit` +
:bulb: **Tip:** +This can also be done by clicking `File` - `New Book` +
+ +### Swapping between TABs : `swap` + +Swaps between existing TABs. + +Format: `swap` + +
:bulb: **Tip:** +This can also be done by clicking `File` - `Swap Book` +
+ +### Renaming the data file : `rename` + +Renames the current data file. + +Format: `rename {NEW_NAME}` + +Renames the current data file as the `NEW_NAME` in lowercase +* `NEW_NAME` only accepts alphanumeric, '-' and '_' characters. + +Example: +* `rename cs2103t` renames the current data file as `cs2103t.json` ### Saving the data -AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. +TAB data is saved in the hard disk automatically after any command that changes the data. There is no need to save manually. ### Editing the data file -AddressBook data are saved as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file. +TAB data is saved as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file.
:exclamation: **Caution:** -If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run. +If your changes to the data file makes its format invalid, TAB will discard all data and start with an empty data file at the next run.
-### Archiving data files `[coming in v2.0]` +### Exiting the program : `exit` + +Exits the program. + +Format: `exit` -_Details coming soon ..._ +
:bulb: **Tip:** +This can also be done by clicking File - Exit. +
-------------------------------------------------------------------------------------------------------------------- ## FAQ **Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder. +**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous TAB home folder. + +**Q**: Why is editing of Position restricted?
+**A**: TAB depicts the addressbook of a certain module at a certain period of time. Positions within TAB should be consistent in that time frame. For example, it would not make sense for a Student to become a Professor. + +**Q**: Why is editing of Tags restricted?
+**A**: AB depicts the addressbook of a certain module at a certain period of time. Tags represent the module and tutorial group of a students. If a student were to drop the module halfway, he should be removed from TAB instead using the delete feature. + +**Q**: Why should there be only Persons from one module in each addressbook? This is a big limitation!
+**A**: Not really. By only allowing Persons from one module in each addressbook, it ensures that your TAB is neat and streamlined. If you want to keep contacts across modules, please create a new TAB by using our `new` and `swap` features. + +**Q**: I just created a new book and nothing changed?
+**A**: The new TAB's name appears at the bottom left corner. + +![FAQ_Identify_New_Book](images/FAQ_Identify_New_Book.png) + +**Q**: How do I properly delete a TAB once I created it?
+**A**: To delete a created TAB here are the steps to follow:
+
:exclamation: **Caution:** +If there is only one TAB, do not follow the steps below! Use the `clear` command instead! +
+ +- Step 1: + - From the `data` directory, delete the `.json` file you like to remove +- Step 2: + - From the `preferences.json`, delete the **same** `.json` file from the `allAddressBookFilePath` parameter. + +**Case By Case Basis:** +- Step 3: (**Only if** `"addressBookFilePath"` has the same name as the TAB you want to delete) + - If the `.json` file is also `addressBookFilePath`, swap it for the first TAB under `allAddressBookFilePath` + and set `addressBookIndex` to 0 + +
:information_source: **Example:**
+ +Here I would like to delete a TAB named `addressbook`
+ +* Step 1:
+Go into `data` folder and delete `addressbook.json`
+  Before:
+ ![delete_step_1a.png](images/delete_step_1a.png)

+ After:
+ ![delete_step_1b.png](images/delete_step_1b.png)

+ +Step 2:
+* Open `preferences.json`
+* Remove `, "data\\addressbook.json"` from `"allAddressBookFilePath"`
+  Before: +  ![delete_step_2a.png](images/delete_step_2a.png)

+  After: +  ![delete_step_2b.png](images/delete_step_2b.png)
+ **Save the file and you are done!**

+ +**Case By Case Basis:**
+Step 3: (if `"addressBookFilePath"` has the same name as the TAB you want to delete):
+* Remove both `addressbook.json`
+  Before: +  ![delete_step_3a.png](images/delete_step_3a.png)

+  After: +  ![delete_step_3b.png](images/delete_step_3b.png)

+* Copy the first TAB name from `"allAddressBookFilePath"`, paste into `"addressBookFilePath"` and change `"addressBookIndex"` to 0.
+  It should look something like this. (Note the changes in green) +  ![delete_step_3c.png](images/delete_step_3c.png)

+ **Save the file and you are done!**
+ +
+ + -------------------------------------------------------------------------------------------------------------------- @@ -183,10 +442,24 @@ _Details coming soon ..._ Action | Format, Examples --------|------------------ -**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​`
e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague` -**Clear** | `clear` -**Delete** | `delete INDEX`
e.g., `delete 3` -**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…​`
e.g.,`edit 2 n/James Lee e/jameslee@example.com` -**Find** | `find KEYWORD [MORE_KEYWORDS]`
e.g., `find James Jake` -**List** | `list` **Help** | `help` +**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS pos/ POSITION [t/TAG]…​`
e.g., `add n/Alex Yeoh p/87438807 e/alexyeoh@example.com pos/Student a/Blk 30 Geylang Street 29, #06-40 t/CS2103T-T17` +**List** | `list` +**Show** | `show INDEX`
e.g., `show 2` +**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS]…​`
e.g.,`edit 2 n/James Lee e/jameslee@example.com` +**Remark** | `remark INDEX r/REMARK`
e.g., `remark 1 r/Interested to be a TA` +**Attendance** | `attendance INDEX attendance/ATTENDANCE`
e.g., `attendance 1 attendance/10/13` +**Assignments** | `assignments assignments/ASSIGNMENT_1 w/ASSIGNMENT_1_WEIGHTAGE, ASSIGNMENT_2 w/ASSIGNMENT_2_WEIGHTAGE…​`
e.g., `assignments assignments/Assignment 1 w/15, Assignment 2 w/15, Midterms w/30, Finals w/40` +**Grade** | `grade INDEX assignment/INDEX grade/GRADE`
e.g., `grade 2 assignment/1 grade/68/80` +**Availability** | `avail INDEX avail/AVAILABILITY`
e.g., `avail 1 avail/Available` +**Roles** | `roles INDEX roles/ROLE`
e.g., `roles 1 roles/Coordinator` +**Find** | `find KEYWORD [MORE_KEYWORDS]`
e.g., `find James Jake` +**Filter** | `filter TAG`
e.g., `filter CS2103T-T17` +**Delete** | `delete INDEX`
e.g., `delete 3` +**Clear** | `clear` +**New** | `new` +**Swap** | `swap` +**Rename** | `rename {NEW_NAME}`
e.g., `rename CS2103T` +**Exit** | `exit` + +
diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..093e66fdbda 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: "AB-3" +title: "TAB" theme: minima header_pages: @@ -8,7 +8,7 @@ header_pages: markdown: kramdown -repository: "se-edu/addressbook-level3" +repository: "AY2223S1-CS2103T-T17-1/tp" github_icon: "images/github-icon.png" plugins: diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss index 0d3f6e80ced..3bcf61a51ce 100644 --- a/docs/_sass/minima/_base.scss +++ b/docs/_sass/minima/_base.scss @@ -288,7 +288,7 @@ table { text-align: center; } .site-header:before { - content: "AB-3"; + content: "TAB"; font-size: 32px; } } diff --git a/docs/assets/css/style.scss b/docs/assets/css/style.scss index b5ec6976efa..58836b73244 100644 --- a/docs/assets/css/style.scss +++ b/docs/assets/css/style.scss @@ -10,3 +10,76 @@ height: 21px; width: 21px } + +/* Adapted from : https://www.w3schools.com/csSref/pr_gen_counter-reset.php */ +/* Adapted from: https://github.com/shd101wyy/markdown-preview-enhanced/issues/1034 */ +/* Recommended by: AY2223S1-CS2103T-T17-2 */ + +body { + counter-reset: h2; +} + +.index h2 { + counter-reset: h3; +} + +.index h3 { + counter-reset: h4; +} + +.index h4 { + counter-reset: h5; +} + +.index h5 { + counter-reset: h6; +} + +.index h2:before { + counter-increment: h2; + content: counter(h2) ". "; +} + +.index h3:before { + counter-increment: h3; + content: counter(h2) "." counter(h3) ". "; +} + +.index h4:before { + counter-increment: h4; + content: counter(h2) "." counter(h3) "." counter(h4) ". "; +} + +.index h5:before { + counter-increment: h5; + content: counter(h2) "." counter(h3) "." counter(h4) "." counter(h5) ". "; +} + +.index h6:before { + counter-increment: h6; + content: counter(h2) "." counter(h3) "." counter(h4) "." counter(h5) "." counter(h6) ". "; +} + +/* Adapted from: https://dev.to/adamlombard/css-automatically-number-nested-lists-59ei */ + +#markdown-toc, #markdown-toc ul { + list-style-type: none; + counter-reset: item; +} + +#markdown-toc li { + display: block +} + +#markdown-toc li:before { + counter-increment: item; + content: counters(item, ".") ". "; + color: #2a7ae2;; + font-weight: bold; +} + +// hide the first h2 from toc + +#markdown-toc > li:first-child { + display: none; +} diff --git a/docs/diagrams/AddAssignmentsDiagram1.puml b/docs/diagrams/AddAssignmentsDiagram1.puml new file mode 100644 index 00000000000..4013595ca7e --- /dev/null +++ b/docs/diagrams/AddAssignmentsDiagram1.puml @@ -0,0 +1,24 @@ +@startuml +object "__alpha:Person__" as alpha { +role = Student +} + +object "__bravo:Person__" as bravo { +role = Student +} + +object "__charlie:Person__" as charlie { +role = Professor +} + +object "__delta:Person__" as delta { +role = TA +} + +map "__:TAB__" as Tab { + :Person1 *-> alpha + :Person2 *--> bravo + :Person3 *---> charlie + :Person4 *----> delta +} +@end diff --git a/docs/diagrams/AddAssignmentsDiagram2.puml b/docs/diagrams/AddAssignmentsDiagram2.puml new file mode 100644 index 00000000000..fc9d50592c9 --- /dev/null +++ b/docs/diagrams/AddAssignmentsDiagram2.puml @@ -0,0 +1,33 @@ +@startuml +object "__alpha:Person__" as alpha { +role = Student +} + +object "__bravo:Person__" as bravo { +role = Student +} + +object "__charlie:Person__" as charlie { +role = Professor +} + +object "__delta:Person__" as delta { +role = TA +} + +map "__:TAB__" as Tab { + :Person1 *-> alpha + :Person2 *--> bravo + :Person3 *---> charlie + :Person4 *----> delta +} + +map ":AddAssignmentsCommand" as AddAssignmentsCommand { + +} + +AddAssignmentsCommand --> alpha : check +AddAssignmentsCommand ---> bravo : check +AddAssignmentsCommand ----> charlie : check +AddAssignmentsCommand -----> delta : check +@end diff --git a/docs/diagrams/AddAssignmentsDiagram3.puml b/docs/diagrams/AddAssignmentsDiagram3.puml new file mode 100644 index 00000000000..6857b9e37c3 --- /dev/null +++ b/docs/diagrams/AddAssignmentsDiagram3.puml @@ -0,0 +1,56 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":AddAssignmentsCommandParser" as AddAssignmentsCommandParser LOGIC_COLOR +participant ":AddAssignmentsCommand" as AddAssignmentsCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + + + +[-> AddressBookParser : parseCommand(assignments) +activate AddressBookParser + +create AddAssignmentsCommandParser +AddressBookParser -> AddAssignmentsCommandParser: +activate AddAssignmentsCommandParser + +AddAssignmentsCommandParser --> AddressBookParser +deactivate AddAssignmentsCommandParser + +AddressBookParser -> AddAssignmentsCommandParser: parse() +activate AddAssignmentsCommandParser + +create AddAssignmentsCommand +AddAssignmentsCommandParser -> AddAssignmentsCommand: parse() +activate AddAssignmentsCommand + +create Model +AddAssignmentsCommand -> Model: setPerson() +activate Model + +Model --> AddAssignmentsCommand +deactivate Model + +create CommandResult +AddAssignmentsCommand -> CommandResult: execute() +activate CommandResult + +CommandResult --> AddAssignmentsCommand +deactivate CommandResult + +AddAssignmentsCommand --> AddAssignmentsCommandParser +deactivate AddAssignmentsCommand + +AddAssignmentsCommandParser --> AddressBookParser +deactivate AddAssignmentsCommandParser + +[<-- AddressBookParser +deactivate AddressBookParser +@enduml diff --git a/docs/diagrams/AddAssignmentsDiagram4.puml b/docs/diagrams/AddAssignmentsDiagram4.puml new file mode 100644 index 00000000000..82084081ac3 --- /dev/null +++ b/docs/diagrams/AddAssignmentsDiagram4.puml @@ -0,0 +1,17 @@ +@startuml +start +:User executes command; + +'Since the beta syntax does not support placing the condition outside the +'diamond we place it as the true branch instead. + +if () then ([command is valid]) + :Parse inputs; + :Iterates TAB for Students; + :Creates Assignment objects and add them to an + ArrayList encapsulated in Student; +else ([else]) + :Display error; +endif +stop +@enduml diff --git a/docs/diagrams/AddAttendanceSequenceDiagram.puml b/docs/diagrams/AddAttendanceSequenceDiagram.puml new file mode 100644 index 00000000000..69f862af22d --- /dev/null +++ b/docs/diagrams/AddAttendanceSequenceDiagram.puml @@ -0,0 +1,89 @@ +@startuml +!include style.puml +!$userInput = ("attendance 1 attendance/...") +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":AttendanceCommandParser" as AttendanceCommandParser LOGIC_COLOR +participant "a:AttendanceCommand" as AttendanceCommand LOGIC_COLOR +participant "CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +participant "editedPerson:Person" as editedPerson MODEL_COLOR +end box + +[-> LogicManager : execute("$userInput") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("$userInput") +activate AddressBookParser + +create AttendanceCommandParser +AddressBookParser -> AttendanceCommandParser +activate AttendanceCommandParser + +AttendanceCommandParser --> AddressBookParser +deactivate AttendanceCommandParser + +AddressBookParser -> AttendanceCommandParser : parse("1 avail/...") +activate AttendanceCommandParser + +create AttendanceCommand +AttendanceCommandParser -> AttendanceCommand +activate AttendanceCommand + +AttendanceCommand --> AttendanceCommandParser : a +deactivate AttendanceCommand + +AttendanceCommandParser --> AddressBookParser : a +deactivate AttendanceCommandParser +AttendanceCommandParser -[hidden]-> AddressBookParser +destroy AttendanceCommandParser + +AddressBookParser --> LogicManager : a +deactivate AddressBookParser + +LogicManager -> AttendanceCommand : execute() +activate AttendanceCommand + +create editedPerson +AttendanceCommand --> editedPerson +activate editedPerson + +editedPerson -> AttendanceCommand +deactivate editedPerson + +AttendanceCommand -> Model : hasPerson(editedPerson) +activate Model + +Model -> AttendanceCommand +deactivate Model + +AttendanceCommand -> Model : setPerson(personToEdit, editedPerson) +activate Model + +Model --> AttendanceCommand +deactivate Model + +AttendanceCommand -> Model : updateFilteredPersonList(...) +activate Model + +Model --> AttendanceCommand +deactivate Model + +create CommandResult +AttendanceCommand -> CommandResult +activate CommandResult + +CommandResult --> AttendanceCommand +deactivate CommandResult + +AttendanceCommand --> LogicManager : result +deactivate AttendanceCommand + +[<-- LogicManager +deactivate LogicManager + +@enduml diff --git a/docs/diagrams/AddAvailabilitySequenceDiagram.puml b/docs/diagrams/AddAvailabilitySequenceDiagram.puml new file mode 100644 index 00000000000..e135eeba81c --- /dev/null +++ b/docs/diagrams/AddAvailabilitySequenceDiagram.puml @@ -0,0 +1,89 @@ +@startuml +!include style.puml +!$userInput = ("avail 1 avail/...") +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":AvailabilityCommandParser" as AvailabilityCommandParser LOGIC_COLOR +participant "a:AvailabilityCommand" as AvailabilityCommand LOGIC_COLOR +participant "CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +participant "editedPerson:Person" as editedPerson MODEL_COLOR +end box + +[-> LogicManager : execute("$userInput") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("$userInput") +activate AddressBookParser + +create AvailabilityCommandParser +AddressBookParser -> AvailabilityCommandParser +activate AvailabilityCommandParser + +AvailabilityCommandParser --> AddressBookParser +deactivate AvailabilityCommandParser + +AddressBookParser -> AvailabilityCommandParser : parse("1 avail/...") +activate AvailabilityCommandParser + +create AvailabilityCommand +AvailabilityCommandParser -> AvailabilityCommand +activate AvailabilityCommand + +AvailabilityCommand --> AvailabilityCommandParser : a +deactivate AvailabilityCommand + +AvailabilityCommandParser --> AddressBookParser : a +deactivate AvailabilityCommandParser +AvailabilityCommandParser -[hidden]-> AddressBookParser +destroy AvailabilityCommandParser + +AddressBookParser --> LogicManager : a +deactivate AddressBookParser + +LogicManager -> AvailabilityCommand : execute() +activate AvailabilityCommand + +create editedPerson +AvailabilityCommand --> editedPerson +activate editedPerson + +editedPerson -> AvailabilityCommand +deactivate editedPerson + +AvailabilityCommand -> Model : hasPerson(editedPerson) +activate Model + +Model -> AvailabilityCommand +deactivate Model + +AvailabilityCommand -> Model : setPerson(personToEdit, editedPerson) +activate Model + +Model --> AvailabilityCommand +deactivate Model + +AvailabilityCommand -> Model : updateFilteredPersonList(...) +activate Model + +Model --> AvailabilityCommand +deactivate Model + +create CommandResult +AvailabilityCommand -> CommandResult +activate CommandResult + +CommandResult --> AvailabilityCommand +deactivate CommandResult + +AvailabilityCommand --> LogicManager : result +deactivate AvailabilityCommand + +[<-- LogicManager +deactivate LogicManager + +@enduml diff --git a/docs/diagrams/AddCommandSequence.puml b/docs/diagrams/AddCommandSequence.puml new file mode 100644 index 00000000000..9808072390b --- /dev/null +++ b/docs/diagrams/AddCommandSequence.puml @@ -0,0 +1,75 @@ +@startuml +!include style.puml +!$userInput = ("add n/...") +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":AddCommandParser" as AddCommandParser LOGIC_COLOR +participant "a:AddCommand" as AddCommand LOGIC_COLOR +participant "CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("$userInput") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("$userInput") +activate AddressBookParser + +create AddCommandParser +AddressBookParser -> AddCommandParser +activate AddCommandParser + +AddCommandParser --> AddressBookParser +deactivate AddCommandParser + +AddressBookParser -> AddCommandParser : parse("n/...") +activate AddCommandParser + +create AddCommand +AddCommandParser -> AddCommand +activate AddCommand + +AddCommand --> AddCommandParser : a +deactivate AddCommand + +AddCommandParser --> AddressBookParser : a +deactivate AddCommandParser +AddCommandParser -[hidden]-> AddressBookParser +destroy AddCommandParser + +AddressBookParser --> LogicManager : a +deactivate AddressBookParser + +LogicManager -> AddCommand : execute() +activate AddCommand + +AddCommand -> Model : hasPerson(toAdd) +activate Model + +Model --> AddCommand +deactivate Model + +AddCommand -> Model : addPerson(toAdd) +activate Model + +Model --> AddCommand +deactivate Model + +create CommandResult +AddCommand -> CommandResult +activate CommandResult + +CommandResult --> AddCommand +deactivate CommandResult + +AddCommand --> LogicManager : result +deactivate AddCommand + +[<-- LogicManager +deactivate LogicManager + +@enduml diff --git a/docs/diagrams/AddGradeDiagram0.puml b/docs/diagrams/AddGradeDiagram0.puml new file mode 100644 index 00000000000..cba58635c92 --- /dev/null +++ b/docs/diagrams/AddGradeDiagram0.puml @@ -0,0 +1,24 @@ +@startuml +object "__alpha:Person__" as alpha { +role = Student +} + +object "__bravo:Person__" as bravo { +role = Student +} + +object "__charlie:Person__" as charlie { +role = Professor +} + +object "__delta:Person__" as delta { +role = TA +} + +map "__:TAB__" as Tab { + :Person1 *-> alpha + :Person2 *--> bravo + :Person3 *---> charlie + :Person4 *----> delta +} +@enduml diff --git a/docs/diagrams/AddGradeDiagram1.puml b/docs/diagrams/AddGradeDiagram1.puml new file mode 100644 index 00000000000..cdb92c2b8e0 --- /dev/null +++ b/docs/diagrams/AddGradeDiagram1.puml @@ -0,0 +1,16 @@ +@startuml + +object indexOfStudent +object indexOfAssignment + + +map GradeCommand { + indexOfStudent *-> indexOfStudent + indexOfAssignment *-> indexOfAssignment + grade => 86/100 +} + +indexOfAssignment : zeroBasedIndex = 0 +indexOfStudent : zeroBasedIndex = 0 + +@enduml diff --git a/docs/diagrams/AddGradeDiagram2.puml b/docs/diagrams/AddGradeDiagram2.puml new file mode 100644 index 00000000000..16ab05c1f79 --- /dev/null +++ b/docs/diagrams/AddGradeDiagram2.puml @@ -0,0 +1,30 @@ +@startuml + +object Assignment1 + +Assignment1 : grade = 86/100 +Assignment1 : weightage = 15 + +object Assignment2 + +Assignment2 : grade = 0/0 +Assignment2 : weightage = 15 + +object Assignment3 + +Assignment3 : grade = 0/0 +Assignment3 : weightage = 30 + +object Assignment4 + +Assignment4 : grade = 0/0 +Assignment4 : weightage = 40 + +map assignmentList { +Assignment1 *--> Assignment1 +Assignment2 *--> Assignment2 +Assignment3 *--> Assignment3 +Assignment4 *--> Assignment4 +} + +@enduml diff --git a/docs/diagrams/AddGradeSequenceDiagram.puml b/docs/diagrams/AddGradeSequenceDiagram.puml new file mode 100644 index 00000000000..18d75161fff --- /dev/null +++ b/docs/diagrams/AddGradeSequenceDiagram.puml @@ -0,0 +1,69 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":GradeCommandParser" as GradeCommandParser LOGIC_COLOR +participant "d:GradeCommand" as GradeCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute() +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand() +activate AddressBookParser + +create GradeCommandParser +AddressBookParser -> GradeCommandParser +activate GradeCommandParser + +GradeCommandParser --> AddressBookParser +deactivate GradeCommandParser + +AddressBookParser -> GradeCommandParser : parse() +activate GradeCommandParser + +create GradeCommand +GradeCommandParser -> GradeCommand +activate GradeCommand + +GradeCommand --> GradeCommandParser : g +deactivate GradeCommand + +GradeCommandParser --> AddressBookParser : g +deactivate GradeCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +GradeCommandParser -[hidden]-> AddressBookParser +destroy GradeCommandParser + +AddressBookParser --> LogicManager : g +deactivate AddressBookParser + +LogicManager -> GradeCommand : execute() +activate GradeCommand + +GradeCommand -> Model : setPerson(personToEdit, editedPerson) +activate Model + +Model --> GradeCommand +deactivate Model + +create CommandResult +GradeCommand -> CommandResult +activate CommandResult + +CommandResult --> GradeCommand +deactivate CommandResult + +GradeCommand --> LogicManager : result +deactivate GradeCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/AddRolesSequenceDiagram.puml b/docs/diagrams/AddRolesSequenceDiagram.puml new file mode 100644 index 00000000000..c1e1d29b878 --- /dev/null +++ b/docs/diagrams/AddRolesSequenceDiagram.puml @@ -0,0 +1,89 @@ +@startuml +!include style.puml +!$userInput = ("roles 1 roles/...") +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":RolesCommandParser" as RolesCommandParser LOGIC_COLOR +participant "a:RolesCommand" as RolesCommand LOGIC_COLOR +participant "CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +participant "editedPerson:Person" as editedPerson MODEL_COLOR +end box + +[-> LogicManager : execute("$userInput") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("$userInput") +activate AddressBookParser + +create RolesCommandParser +AddressBookParser -> RolesCommandParser +activate RolesCommandParser + +RolesCommandParser --> AddressBookParser +deactivate RolesCommandParser + +AddressBookParser -> RolesCommandParser : parse("1 roles/...") +activate RolesCommandParser + +create RolesCommand +RolesCommandParser -> RolesCommand +activate RolesCommand + +RolesCommand --> RolesCommandParser : a +deactivate RolesCommand + +RolesCommandParser --> AddressBookParser : a +deactivate RolesCommandParser +RolesCommandParser -[hidden]-> AddressBookParser +destroy RolesCommandParser + +AddressBookParser --> LogicManager : a +deactivate AddressBookParser + +LogicManager -> RolesCommand : execute() +activate RolesCommand + +create editedPerson +RolesCommand --> editedPerson +activate editedPerson + +editedPerson -> RolesCommand +deactivate editedPerson + +RolesCommand -> Model : hasPerson(editedPerson) +activate Model + +Model -> RolesCommand +deactivate Model + +RolesCommand -> Model : setPerson(personToEdit, editedPerson) +activate Model + +Model --> RolesCommand +deactivate Model + +RolesCommand -> Model : updateFilteredPersonList(...) +activate Model + +Model --> RolesCommand +deactivate Model + +create CommandResult +RolesCommand -> CommandResult +activate CommandResult + +CommandResult --> RolesCommand +deactivate CommandResult + +RolesCommand --> LogicManager : result +deactivate RolesCommand + +[<-- LogicManager +deactivate LogicManager + +@enduml diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml index 5731f9cbaa1..fd5e7c81316 100644 --- a/docs/diagrams/BetterModelClassDiagram.puml +++ b/docs/diagrams/BetterModelClassDiagram.puml @@ -18,4 +18,9 @@ Person *--> Name Person *--> Phone Person *--> Email Person *--> Address +Person *--> Position + +Student .up.|> Position +TeachingAssistant .up.|> Position +Professor .up.|> Position @enduml diff --git a/docs/diagrams/CreateNewBookSequenceDiagram.puml b/docs/diagrams/CreateNewBookSequenceDiagram.puml new file mode 100644 index 00000000000..cd7b4138832 --- /dev/null +++ b/docs/diagrams/CreateNewBookSequenceDiagram.puml @@ -0,0 +1,54 @@ +@startuml +'https://plantuml.com/sequence-diagram +!include style.puml +box UiPart UI_COLOR_T1 +participant ":MainWindow" as MainWindow UI_COLOR +end box + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":ModelManager" as ModelManager MODEL_COLOR +end box + +box ReadOnlyUserPrefs PREF_COLOR +participant ":UserPrefs" as UserPrefs PREF_COLOR_T1 +end box +[-> MainWindow : handleNewBook() +activate MainWindow + +MainWindow -> LogicManager : addAddressBook() +activate LogicManager + +LogicManager -> ModelManager : addAddressBook() +activate ModelManager + +ModelManager -> UserPrefs : addAddressBook() +activate UserPrefs + +UserPrefs --> ModelManager +deactivate UserPrefs + +ModelManager --> LogicManager +deactivate ModelManager + +LogicManager -> ModelManager : getAllAddressBookFilePath() +activate ModelManager + +ModelManager -> UserPrefs : getAllAddressBookFilePath() +activate UserPrefs + +UserPrefs --> ModelManager +deactivate UserPrefs + +ModelManager --> LogicManager +deactivate ModelManager + +LogicManager --> MainWindow : result +deactivate LogicManager + +[<--MainWindow +deactivate LogicManager +@enduml diff --git a/docs/diagrams/CreatingNewBookState0.puml b/docs/diagrams/CreatingNewBookState0.puml new file mode 100644 index 00000000000..2493be3c1e2 --- /dev/null +++ b/docs/diagrams/CreatingNewBookState0.puml @@ -0,0 +1,21 @@ +@startuml +!include style.puml +skinparam ClassFontColor #000000 +skinparam ClassBorderColor #000000 + +title Initial state + +package States <> { + class State1 as "addressBook.json" + class State2 as "addressBook1.json" + class State3 as "addressBook2.json" +} + +State1 -[hidden]right-> State2 +State2 -[hidden]right-> State3 +hide State2 +hide State3 + +class Pointer as "Currently Loaded Book" #FFFFFF +Pointer -up-> State1 +@end diff --git a/docs/diagrams/CreatingNewBookState1.puml b/docs/diagrams/CreatingNewBookState1.puml new file mode 100644 index 00000000000..acc44321d75 --- /dev/null +++ b/docs/diagrams/CreatingNewBookState1.puml @@ -0,0 +1,22 @@ +@startuml +!include style.puml +skinparam ClassFontColor #000000 +skinparam ClassBorderColor #000000 + +title After command "new" or clicking "New Book" button + +package States <> { + class State1 as "addressBook.json" + class State2 as "addressBook1.json" + class State3 as "addressBook2.json" +} + +State1 -[hidden]right-> State2 +State2 -[hidden]right-> State3 + +hide State3 + +class Pointer as "Currently Loaded Book" #FFFFFF + +Pointer -up-> State1 +@end diff --git a/docs/diagrams/EditCommandSequence.puml b/docs/diagrams/EditCommandSequence.puml new file mode 100644 index 00000000000..e0a03af074d --- /dev/null +++ b/docs/diagrams/EditCommandSequence.puml @@ -0,0 +1,81 @@ +@startuml +!include style.puml +!$userInput = ("edit 1 n/...") +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":EditCommandParser" as EditCommandParser LOGIC_COLOR +participant "e:EditCommand" as EditCommand LOGIC_COLOR +participant "CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("$userInput") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("$userInput") +activate AddressBookParser + +create EditCommandParser +AddressBookParser -> EditCommandParser +activate EditCommandParser + +EditCommandParser --> AddressBookParser +deactivate EditCommandParser + +AddressBookParser -> EditCommandParser : parse("1 n/...") +activate EditCommandParser + +create EditCommand +EditCommandParser -> EditCommand +activate EditCommand + +EditCommand --> EditCommandParser : e +deactivate EditCommand + +EditCommandParser --> AddressBookParser : e +deactivate EditCommandParser +EditCommandParser -[hidden]-> AddressBookParser +destroy EditCommandParser + +AddressBookParser --> LogicManager : e +deactivate AddressBookParser + +LogicManager -> EditCommand : execute() +activate EditCommand + +EditCommand -> Model : hasPerson(editedPerson) +activate Model + +Model -> EditCommand +deactivate Model + +EditCommand -> Model : setPerson(personToEdit, editedPerson) +activate Model + +Model --> EditCommand +deactivate Model + +EditCommand -> Model : updateFilteredPersonList(...) +activate Model + +Model --> EditCommand +deactivate Model + +create CommandResult +EditCommand -> CommandResult +activate CommandResult + +CommandResult --> EditCommand +deactivate CommandResult + +EditCommand --> LogicManager : result +deactivate EditCommand + +[<-- LogicManager +deactivate LogicManager + +@enduml diff --git a/docs/diagrams/FilterDiagram1.puml b/docs/diagrams/FilterDiagram1.puml new file mode 100644 index 00000000000..f462a09d0a5 --- /dev/null +++ b/docs/diagrams/FilterDiagram1.puml @@ -0,0 +1,11 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor STORAGE_COLOR +skinparam classBackgroundColor STORAGE_COLOR + +Class "FilterCommand" as FilterCommand +Class "TagContainsKeywordPredicate" as TagContainsKeywordPredicate +FilterCommand -> "1" TagContainsKeywordPredicate + +@enduml diff --git a/docs/diagrams/FilterDiagram2.puml b/docs/diagrams/FilterDiagram2.puml new file mode 100644 index 00000000000..44d9a9cd68f --- /dev/null +++ b/docs/diagrams/FilterDiagram2.puml @@ -0,0 +1,23 @@ +@startuml +'https://plantuml.com/object-diagram + +object " alpha:Person" as Alpha { + tag = "CS2103-T17" +} +object " bravo:Person" as Bravo { + tag = "CS2103-T16" +} +object " charlie:Person" as Charlie { + tag = "CS2103-T17" +} +object " delta:Person" as Delta { + tag = "CS2103-T18" +} + +map " :AddressBook" as AddressBook { + :Person1 *-> Alpha + :Person2 *--> Bravo + :Person3 *--> Charlie + :Person4 *--> Delta +} +@enduml diff --git a/docs/diagrams/FilterDiagram3.puml b/docs/diagrams/FilterDiagram3.puml new file mode 100644 index 00000000000..db7bf8286ef --- /dev/null +++ b/docs/diagrams/FilterDiagram3.puml @@ -0,0 +1,15 @@ +@startuml +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor STORAGE_COLOR +skinparam classBackgroundColor STORAGE_COLOR + +Class "Command" as Command + +Class "FilterCommand" as FilterCommand +Class "TagContainsKeywordPredicate" as TagContainsKeywordPredicate +FilterCommand -|> Command +FilterCommand -> "1" TagContainsKeywordPredicate + +@enduml diff --git a/docs/diagrams/FilterDiagram4.puml b/docs/diagrams/FilterDiagram4.puml new file mode 100644 index 00000000000..662a3aace1c --- /dev/null +++ b/docs/diagrams/FilterDiagram4.puml @@ -0,0 +1,29 @@ +@startuml +'https://plantuml.com/object-diagram + +object " alpha:Person" as Alpha { + tag = "CS2103-T17" +} +object " bravo:Person" as Bravo { + tag = "CS2103-T16" +} +object " charlie:Person" as Charlie { + tag = "CS2103-T17" +} +object " delta:Person" as Delta { + tag = "CS2103-T18" +} + +map " :AddressBook" as AddressBook { + :Person1 *-> Alpha + :Person2 *--> Bravo + :Person3 *--> Charlie + :Person4 *--> Delta +} + +object " :TagContainsKeywordPredicate" as check +check -> Alpha +check --> Bravo +check ---> Charlie +check ----> Delta +@enduml diff --git a/docs/diagrams/FilterDiagram5.puml b/docs/diagrams/FilterDiagram5.puml new file mode 100644 index 00000000000..4a63d905563 --- /dev/null +++ b/docs/diagrams/FilterDiagram5.puml @@ -0,0 +1,16 @@ +@startuml +'https://plantuml.com/object-diagram +object " alpha:Person" as Alpha { + tag = "CS2103-T17" +} +object " bravo:Person" as Charlie { + tag = "CS2103-T17" +} + + +map "ab:AddressBook" as __AddressBook__ { + :Person1 *-> Alpha + :Person2 *--> Charlie +} + +@enduml diff --git a/docs/diagrams/FilterDiagram6.puml b/docs/diagrams/FilterDiagram6.puml new file mode 100644 index 00000000000..0904ceff61a --- /dev/null +++ b/docs/diagrams/FilterDiagram6.puml @@ -0,0 +1,24 @@ +@startuml +'https://plantuml.com/sequence-diagram +!include style.puml +'autonumber +box Logic LOGIC_COLOR_T1 +participant ":addressBookParser" as AddressBookParser LOGIC_COLOR +participant ":filterCommandParser" as FilterCommandParser LOGIC_COLOR +participant ":tagContainsKeywordPredicate" as TagContainsKeywordPredicate LOGIC_COLOR +participant ":filterCommand" as FilterCommand LOGIC_COLOR +-> AddressBookParser ++: parseCommand() +AddressBookParser -> FilterCommandParser **: new FilterCommandParser() +activate FilterCommandParser +FilterCommandParser -> FilterCommandParser ++: parse(arguments) +FilterCommandParser -> TagContainsKeywordPredicate **: new TagContainsKeywordPredicate(trimmedArgs) +activate TagContainsKeywordPredicate +FilterCommandParser -> FilterCommand **: new FilterCommand(TagContainsKeywordPredicate) +activate FilterCommand +return +return +return +return +return +end box +@enduml diff --git a/docs/diagrams/FilterDiagram7.puml b/docs/diagrams/FilterDiagram7.puml new file mode 100644 index 00000000000..30ec2a68312 --- /dev/null +++ b/docs/diagrams/FilterDiagram7.puml @@ -0,0 +1,15 @@ +@startuml +start +:User executes command; + +'Since the beta syntax does not support placing the condition outside the +'diamond we place it as the true branch instead. + +if () then ([Command is valid]) + :Filter address book; + :Display filtered address book; +else ([else]) + :Display error; +endif +stop +@enduml diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 4439108973a..6faef3ca64a 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -19,7 +19,11 @@ Class Email Class Name Class Phone Class Tag - +Class Remark +Class "{abstract}\nPosition" as Position +Class Student +Class TeachingAssistant +Class Professor } Class HiddenOutside #FFFFFF @@ -34,17 +38,23 @@ ModelManager -left-> "1" AddressBook ModelManager -right-> "1" UserPrefs UserPrefs .up.|> ReadOnlyUserPrefs +Student .up.|> Position +TeachingAssistant .up.|> Position +Professor .up.|> Position + AddressBook *--> "1" UniquePersonList UniquePersonList --> "~* all" Person Person *--> Name Person *--> Phone Person *--> Email Person *--> Address +Person *--> Remark Person *--> "*" Tag +Person *--> Position Name -[hidden]right-> Phone Phone -[hidden]right-> Address Address -[hidden]right-> Email -ModelManager -->"~* filtered" Person +ModelManager ----->"~* filtered" Person @enduml diff --git a/docs/diagrams/ShowDiagram1.puml b/docs/diagrams/ShowDiagram1.puml new file mode 100644 index 00000000000..7e9d4ad1bfc --- /dev/null +++ b/docs/diagrams/ShowDiagram1.puml @@ -0,0 +1,19 @@ +@startuml +'https://plantuml.com/object-diagram + +object " Walter:Person" as Walter { + position = "Professor" + phone = "91234567" +} + +object " Jesse:Person" as Jesse { + position = "Student" + phone = "98765432" +} + +map " :AddressBook" as AddressBook { + :Person1 *-> Walter + :Person2 *--> Jesse +} + +@enduml diff --git a/docs/diagrams/ShowDiagram2.puml b/docs/diagrams/ShowDiagram2.puml new file mode 100644 index 00000000000..f735f9f8935 --- /dev/null +++ b/docs/diagrams/ShowDiagram2.puml @@ -0,0 +1,17 @@ +@startuml +'https://plantuml.com/class-diagram +class ShowCommand +class Index {} + +ShowCommand *-- Index + +class ShowCommand { +-COMMAND_WORD +-index +equals() +execute() +} +class Index { +-zeroBasedIndex = 1 +} +@enduml diff --git a/docs/diagrams/ShowDiagram3.puml b/docs/diagrams/ShowDiagram3.puml new file mode 100644 index 00000000000..5823850e1a8 --- /dev/null +++ b/docs/diagrams/ShowDiagram3.puml @@ -0,0 +1,20 @@ +@startuml +'https://plantuml.com/object-diagram + +object " Walter:Person" as Walter { + position = "Professor" + phone = "91234567" +} + +object " Jesse:Person" as Jesse { + position = "Student" + phone = "98765432" +} + +object " :List" as lastShownList +lastShownList --> Walter +lastShownList --> Jesse + +object " :CommandResult" as CommandResult +CommandResult --> Walter +@enduml diff --git a/docs/diagrams/ShowSequenceDiagram.puml b/docs/diagrams/ShowSequenceDiagram.puml new file mode 100644 index 00000000000..3b4864026de --- /dev/null +++ b/docs/diagrams/ShowSequenceDiagram.puml @@ -0,0 +1,50 @@ +@startuml +!include style.puml +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant "s:ShowCommand" as ShowCommand LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +participant ":List" as ListPerson MODEL_COLOR +end box +[-> LogicManager : execute(show) +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand(show) +activate AddressBookParser + +create ShowCommand +AddressBookParser -> ShowCommand +activate ShowCommand + +ShowCommand --> AddressBookParser : s +deactivate ShowCommand + +AddressBookParser --> LogicManager : s +deactivate AddressBookParser + +LogicManager -> ShowCommand : execute() +activate ShowCommand + +ShowCommand -> Model : getFilteredPersonList() +activate Model + +Model -> ListPerson +activate ListPerson + +ListPerson --> Model : personToShow +deactivate ListPerson + +Model --> ShowCommand : personToShow +deactivate Model + +ShowCommand --> LogicManager +deactivate ShowCommand + +[<-- LogicManager +deactivate LogicManager + +@enduml diff --git a/docs/diagrams/SwapBooksSequenceDiagram.puml b/docs/diagrams/SwapBooksSequenceDiagram.puml new file mode 100644 index 00000000000..09a9ba4cd76 --- /dev/null +++ b/docs/diagrams/SwapBooksSequenceDiagram.puml @@ -0,0 +1,84 @@ +@startuml +'https://plantuml.com/sequence-diagram +!include style.puml +box UiPart UI_COLOR_T1 +participant ":MainWindow" as MainWindow UI_COLOR +end box + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +end box + + + +box Model MODEL_COLOR_T1 +participant ":ModelManager" as ModelManager MODEL_COLOR +end box + +box Storage STORAGE_COLOR_T1 +participant ":Storage" as Storage STORAGE_COLOR +end box + +box ReadOnlyUserPrefs PREF_COLOR +participant ":UserPrefs" as UserPrefs PREF_COLOR_T1 +end box + +[-> MainWindow : handleSwap() +activate MainWindow + +MainWindow -> LogicManager : swapAddressBook() +activate LogicManager + + + +LogicManager -> ModelManager : getNextAddressBookPath() +activate ModelManager + +ModelManager -> UserPrefs : getNextAddressBookPath() +activate UserPrefs + +UserPrefs -> UserPrefs : incrementIndex() +activate UserPrefs + +UserPrefs --> UserPrefs +deactivate UserPrefs + +UserPrefs -> UserPrefs : setAddressBookFilePath(Path) +activate UserPrefs + +UserPrefs --> UserPrefs +deactivate UserPrefs + +UserPrefs --> ModelManager : result +deactivate UserPrefs + +ModelManager --> LogicManager +deactivate ModelManager + + + +LogicManager -> Storage : readAddressBook(Path) +activate Storage + +Storage --> LogicManager +deactivate Storage + +LogicManager -> Storage : setAddressBook(AddressBookStorage) +activate Storage + +Storage --> LogicManager +deactivate Storage + + +LogicManager -> ModelManager : setAddressBook(ReadOnlyAddressBook) +activate ModelManager + +ModelManager --> LogicManager +deactivate ModelManager + +LogicManager --> MainWindow +deactivate LogicManager + +[<--MainWindow +deactivate LogicManager +@enduml diff --git a/docs/diagrams/SwapState0.puml b/docs/diagrams/SwapState0.puml new file mode 100644 index 00000000000..1b0961bfb28 --- /dev/null +++ b/docs/diagrams/SwapState0.puml @@ -0,0 +1,22 @@ +@startuml +!include style.puml +skinparam ClassFontColor #000000 +skinparam ClassBorderColor #000000 + +title After command "swap" or clicking "Swap Book" button + +package States <> { + class State1 as "addressBook.json" + class State2 as "addressBook1.json" + class State3 as "addressBook2.json" +} + +State1 -[hidden]right-> State2 +State2 -[hidden]right-> State3 + +hide State3 + +class Pointer as "Currently Loaded Book" #FFFFFF + +Pointer -up-> State2 +@end diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml index 95473d5aa19..c1b2147f00a 100644 --- a/docs/diagrams/UiClassDiagram.puml +++ b/docs/diagrams/UiClassDiagram.puml @@ -9,8 +9,9 @@ Class "<>\nUi" as Ui Class "{abstract}\nUiPart" as UiPart Class UiManager Class MainWindow -Class HelpWindow Class ResultDisplay +Class InfoDisplay +Class UrlLauncher Class PersonListPanel Class PersonCard Class StatusBarFooter @@ -32,29 +33,31 @@ UiManager .left.|> Ui UiManager -down-> "1" MainWindow MainWindow *-down-> "1" CommandBox MainWindow *-down-> "1" ResultDisplay +MainWindow *-down-> "1" InfoDisplay MainWindow *-down-> "1" PersonListPanel MainWindow *-down-> "1" StatusBarFooter -MainWindow --> "0..1" HelpWindow +MainWindow *-down-> "0...1" UrlLauncher -PersonListPanel -down-> "*" PersonCard +PersonListPanel -down--> " *" PersonCard -MainWindow -left-|> UiPart +MainWindow --|> UiPart ResultDisplay --|> UiPart +InfoDisplay --|> UiPart CommandBox --|> UiPart PersonListPanel --|> UiPart PersonCard --|> UiPart StatusBarFooter --|> UiPart -HelpWindow --|> UiPart PersonCard ..> Model +InfoDisplay ..> Model UiManager -right-> Logic MainWindow -left-> Logic -PersonListPanel -[hidden]left- HelpWindow -HelpWindow -[hidden]left- CommandBox CommandBox -[hidden]left- ResultDisplay ResultDisplay -[hidden]left- StatusBarFooter +InfoDisplay -[hidden]left- PersonListPanel +UrlLauncher -[hidden]left- StatusBarFooter MainWindow -[hidden]-|> UiPart @enduml diff --git a/docs/diagrams/UndoRedoState0.puml b/docs/diagrams/UndoRedoState0.puml index 96e30744d24..34885420931 100644 --- a/docs/diagrams/UndoRedoState0.puml +++ b/docs/diagrams/UndoRedoState0.puml @@ -15,6 +15,6 @@ State2 -[hidden]right-> State3 hide State2 hide State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State1 @end diff --git a/docs/diagrams/UndoRedoState1.puml b/docs/diagrams/UndoRedoState1.puml index 01fcb9b2b96..0e2c8c72d33 100644 --- a/docs/diagrams/UndoRedoState1.puml +++ b/docs/diagrams/UndoRedoState1.puml @@ -16,7 +16,7 @@ State2 -[hidden]right-> State3 hide State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State2 @end diff --git a/docs/diagrams/UndoRedoState2.puml b/docs/diagrams/UndoRedoState2.puml index bccc230a5d1..0ce7073e187 100644 --- a/docs/diagrams/UndoRedoState2.puml +++ b/docs/diagrams/UndoRedoState2.puml @@ -14,7 +14,7 @@ package States <> { State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State3 @end diff --git a/docs/diagrams/UndoRedoState3.puml b/docs/diagrams/UndoRedoState3.puml index ea29c9483e4..50bf43b3f34 100644 --- a/docs/diagrams/UndoRedoState3.puml +++ b/docs/diagrams/UndoRedoState3.puml @@ -14,7 +14,7 @@ package States <> { State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State2 @end diff --git a/docs/diagrams/UndoRedoState4.puml b/docs/diagrams/UndoRedoState4.puml index 1b784cece80..83cbe4c740c 100644 --- a/docs/diagrams/UndoRedoState4.puml +++ b/docs/diagrams/UndoRedoState4.puml @@ -14,7 +14,7 @@ package States <> { State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State2 @end diff --git a/docs/diagrams/UndoRedoState5.puml b/docs/diagrams/UndoRedoState5.puml index 88927be32bc..fc89dd99d2d 100644 --- a/docs/diagrams/UndoRedoState5.puml +++ b/docs/diagrams/UndoRedoState5.puml @@ -14,7 +14,7 @@ package States <> { State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State3 note right on link: State ab2 deleted. diff --git a/docs/diagrams/style.puml b/docs/diagrams/style.puml index fad8b0adeaa..658edca53d2 100644 --- a/docs/diagrams/style.puml +++ b/docs/diagrams/style.puml @@ -13,6 +13,12 @@ !define UI_COLOR_T3 #166800 !define UI_COLOR_T4 #0E4100 +!define PREF_COLOR #Violet +!define PREF_COLOR_T1 #DarkOrchid +!define PREF_COLOR_T2 #DarkMagenta +!define PREF_COLOR_T3 #DarkSlateBlue +!define PREF_COLOR_T4 #Indigo + !define LOGIC_COLOR #3333C4 !define LOGIC_COLOR_T1 #C8C8FA !define LOGIC_COLOR_T2 #6A6ADC diff --git a/docs/images/AddAssignmentsDiagram1.png b/docs/images/AddAssignmentsDiagram1.png new file mode 100644 index 00000000000..f46b0156bcb Binary files /dev/null and b/docs/images/AddAssignmentsDiagram1.png differ diff --git a/docs/images/AddAssignmentsDiagram2.png b/docs/images/AddAssignmentsDiagram2.png new file mode 100644 index 00000000000..7884586a97e Binary files /dev/null and b/docs/images/AddAssignmentsDiagram2.png differ diff --git a/docs/images/AddAssignmentsDiagram3.png b/docs/images/AddAssignmentsDiagram3.png new file mode 100644 index 00000000000..f667363bdb8 Binary files /dev/null and b/docs/images/AddAssignmentsDiagram3.png differ diff --git a/docs/images/AddAssignmentsDiagram4.png b/docs/images/AddAssignmentsDiagram4.png new file mode 100644 index 00000000000..5a500b949e0 Binary files /dev/null and b/docs/images/AddAssignmentsDiagram4.png differ diff --git a/docs/images/AddAttendanceSequenceDiagram.png b/docs/images/AddAttendanceSequenceDiagram.png new file mode 100644 index 00000000000..6efe00357ef Binary files /dev/null and b/docs/images/AddAttendanceSequenceDiagram.png differ diff --git a/docs/images/AddAvailabilitySequenceDiagram.png b/docs/images/AddAvailabilitySequenceDiagram.png new file mode 100644 index 00000000000..58a00a2a8cd Binary files /dev/null and b/docs/images/AddAvailabilitySequenceDiagram.png differ diff --git a/docs/images/AddCommandSequence.png b/docs/images/AddCommandSequence.png new file mode 100644 index 00000000000..08c14c41852 Binary files /dev/null and b/docs/images/AddCommandSequence.png differ diff --git a/docs/images/AddGradeDiagram0.png b/docs/images/AddGradeDiagram0.png new file mode 100644 index 00000000000..07c4f2cf941 Binary files /dev/null and b/docs/images/AddGradeDiagram0.png differ diff --git a/docs/images/AddGradeDiagram1.png b/docs/images/AddGradeDiagram1.png new file mode 100644 index 00000000000..4a285212701 Binary files /dev/null and b/docs/images/AddGradeDiagram1.png differ diff --git a/docs/images/AddGradeDiagram2.png b/docs/images/AddGradeDiagram2.png new file mode 100644 index 00000000000..a934ad40c14 Binary files /dev/null and b/docs/images/AddGradeDiagram2.png differ diff --git a/docs/images/AddGradeSequenceDiagram.png b/docs/images/AddGradeSequenceDiagram.png new file mode 100644 index 00000000000..26e907441f8 Binary files /dev/null and b/docs/images/AddGradeSequenceDiagram.png differ diff --git a/docs/images/AddRolesSequenceDiagram.png b/docs/images/AddRolesSequenceDiagram.png new file mode 100644 index 00000000000..7c9aae812fa Binary files /dev/null and b/docs/images/AddRolesSequenceDiagram.png differ diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png index 1ec62caa2a5..ddfbdf634a7 100644 Binary files a/docs/images/BetterModelClassDiagram.png and b/docs/images/BetterModelClassDiagram.png differ diff --git a/docs/images/CreateNewBookSequenceDiagram.png b/docs/images/CreateNewBookSequenceDiagram.png new file mode 100644 index 00000000000..2885b4ea839 Binary files /dev/null and b/docs/images/CreateNewBookSequenceDiagram.png differ diff --git a/docs/images/CreatingNewBookState0.png b/docs/images/CreatingNewBookState0.png new file mode 100644 index 00000000000..6aacdfec827 Binary files /dev/null and b/docs/images/CreatingNewBookState0.png differ diff --git a/docs/images/CreatingNewBookState1.png b/docs/images/CreatingNewBookState1.png new file mode 100644 index 00000000000..0787173753e Binary files /dev/null and b/docs/images/CreatingNewBookState1.png differ diff --git a/docs/images/EditCommandSequence.png b/docs/images/EditCommandSequence.png new file mode 100644 index 00000000000..46f2f8e81ef Binary files /dev/null and b/docs/images/EditCommandSequence.png differ diff --git a/docs/images/FAQ_Identify_New_Book.png b/docs/images/FAQ_Identify_New_Book.png new file mode 100644 index 00000000000..d0be547fb7e Binary files /dev/null and b/docs/images/FAQ_Identify_New_Book.png differ diff --git a/docs/images/FilterDiagram1.png b/docs/images/FilterDiagram1.png new file mode 100644 index 00000000000..ee11542b84f Binary files /dev/null and b/docs/images/FilterDiagram1.png differ diff --git a/docs/images/FilterDiagram2.png b/docs/images/FilterDiagram2.png new file mode 100644 index 00000000000..a648a1d42c0 Binary files /dev/null and b/docs/images/FilterDiagram2.png differ diff --git a/docs/images/FilterDiagram3.png b/docs/images/FilterDiagram3.png new file mode 100644 index 00000000000..2eaf0ab7e5d Binary files /dev/null and b/docs/images/FilterDiagram3.png differ diff --git a/docs/images/FilterDiagram4.png b/docs/images/FilterDiagram4.png new file mode 100644 index 00000000000..44f6696761c Binary files /dev/null and b/docs/images/FilterDiagram4.png differ diff --git a/docs/images/FilterDiagram5.png b/docs/images/FilterDiagram5.png new file mode 100644 index 00000000000..a9f1a5b4922 Binary files /dev/null and b/docs/images/FilterDiagram5.png differ diff --git a/docs/images/FilterDiagram6.png b/docs/images/FilterDiagram6.png new file mode 100644 index 00000000000..ab4be01e8b0 Binary files /dev/null and b/docs/images/FilterDiagram6.png differ diff --git a/docs/images/FilterDiagram7.png b/docs/images/FilterDiagram7.png new file mode 100644 index 00000000000..d6101e3fc3e Binary files /dev/null and b/docs/images/FilterDiagram7.png differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index 04070af60d8..8b3c153e511 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/ShowDiagram1.png b/docs/images/ShowDiagram1.png new file mode 100644 index 00000000000..097cb16590d Binary files /dev/null and b/docs/images/ShowDiagram1.png differ diff --git a/docs/images/ShowDiagram2.png b/docs/images/ShowDiagram2.png new file mode 100644 index 00000000000..558c5737891 Binary files /dev/null and b/docs/images/ShowDiagram2.png differ diff --git a/docs/images/ShowDiagram3.png b/docs/images/ShowDiagram3.png new file mode 100644 index 00000000000..856921226a8 Binary files /dev/null and b/docs/images/ShowDiagram3.png differ diff --git a/docs/images/ShowSequenceDiagram.png b/docs/images/ShowSequenceDiagram.png new file mode 100644 index 00000000000..279c336ecae Binary files /dev/null and b/docs/images/ShowSequenceDiagram.png differ diff --git a/docs/images/SwapBooksSequenceDiagram.png b/docs/images/SwapBooksSequenceDiagram.png new file mode 100644 index 00000000000..5926946bf05 Binary files /dev/null and b/docs/images/SwapBooksSequenceDiagram.png differ diff --git a/docs/images/SwapState0.png b/docs/images/SwapState0.png new file mode 100644 index 00000000000..e5fa39f12b1 Binary files /dev/null and b/docs/images/SwapState0.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 5bd77847aa2..726c4893ad2 100644 Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png index 785e04dbab4..ad7e230922b 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/cxyterence.png b/docs/images/cxyterence.png new file mode 100644 index 00000000000..3338248acb0 Binary files /dev/null and b/docs/images/cxyterence.png differ diff --git a/docs/images/delete_step_1a.png b/docs/images/delete_step_1a.png new file mode 100755 index 00000000000..1a714927dab Binary files /dev/null and b/docs/images/delete_step_1a.png differ diff --git a/docs/images/delete_step_1b.png b/docs/images/delete_step_1b.png new file mode 100755 index 00000000000..76100a0e061 Binary files /dev/null and b/docs/images/delete_step_1b.png differ diff --git a/docs/images/delete_step_2a.png b/docs/images/delete_step_2a.png new file mode 100755 index 00000000000..e852347f994 Binary files /dev/null and b/docs/images/delete_step_2a.png differ diff --git a/docs/images/delete_step_2b.png b/docs/images/delete_step_2b.png new file mode 100755 index 00000000000..64447d94270 Binary files /dev/null and b/docs/images/delete_step_2b.png differ diff --git a/docs/images/delete_step_3a.png b/docs/images/delete_step_3a.png new file mode 100755 index 00000000000..0b65e952a23 Binary files /dev/null and b/docs/images/delete_step_3a.png differ diff --git a/docs/images/delete_step_3b.png b/docs/images/delete_step_3b.png new file mode 100755 index 00000000000..359e5cfb69d Binary files /dev/null and b/docs/images/delete_step_3b.png differ diff --git a/docs/images/delete_step_3c.png b/docs/images/delete_step_3c.png new file mode 100755 index 00000000000..3a8f2b058d5 Binary files /dev/null and b/docs/images/delete_step_3c.png differ diff --git a/docs/images/findAlexDavidResult.png b/docs/images/findAlexDavidResult.png index 235da1c273e..094321c6528 100644 Binary files a/docs/images/findAlexDavidResult.png and b/docs/images/findAlexDavidResult.png differ diff --git a/docs/images/guowei42.png b/docs/images/guowei42.png new file mode 100644 index 00000000000..ab3d0c807eb Binary files /dev/null and b/docs/images/guowei42.png differ diff --git a/docs/images/helpMessage.png b/docs/images/helpMessage.png deleted file mode 100644 index b1f70470137..00000000000 Binary files a/docs/images/helpMessage.png and /dev/null differ diff --git a/docs/images/kjunwei.png b/docs/images/kjunwei.png new file mode 100644 index 00000000000..8cfb7c71f9d Binary files /dev/null and b/docs/images/kjunwei.png differ diff --git a/docs/images/userGuideWindow.png b/docs/images/userGuideWindow.png new file mode 100644 index 00000000000..f0ddb8abd2b Binary files /dev/null and b/docs/images/userGuideWindow.png differ diff --git a/docs/images/vantemoon.png b/docs/images/vantemoon.png new file mode 100644 index 00000000000..d0499e055e7 Binary files /dev/null and b/docs/images/vantemoon.png differ diff --git a/docs/images/yellow-294.png b/docs/images/yellow-294.png new file mode 100644 index 00000000000..077853d7bd4 Binary files /dev/null and b/docs/images/yellow-294.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..8d23bf9068e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,6 @@ --- layout: page -title: AddressBook Level-3 +title: TAB (Teacher's Address Book) --- [![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) @@ -8,10 +8,10 @@ title: AddressBook Level-3 ![Ui](images/Ui.png) -**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). +**TAB (Teacher's Address Book) is a desktop application for managing your students' contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). -* If you are interested in using AddressBook, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). -* If you are interested about developing AddressBook, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. +* If you are interested in using TAB, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). +* If you are interested about developing TAB, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. **Acknowledgements** diff --git a/docs/team/cxyterence.md b/docs/team/cxyterence.md new file mode 100644 index 00000000000..7903f277310 --- /dev/null +++ b/docs/team/cxyterence.md @@ -0,0 +1,59 @@ +--- +layout: page +title: Terence Chong's Project Portfolio Page +--- + +### Project: Teacher’s Address Book (TAB) + +Teacher’s Address Book (TAB) is a **desktop app made for teachers, teaching assistants(TA) and professors for managing +contacts of each other, as well as their students, 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, TAB can get your contact management tasks +done faster than traditional GUI apps. + +Given below are my contributions to the project. + +* **New Feature**: Implemented the `Position` class + * What it does: Abstract class to facilitate implementation of `Student`, `TeachingAssistant` and `Professor` classes, + which serve to encapsulate relevant details of students, teaching assistants and professors respectively. + * Highlights: The `Student`, `TeachingAssistant` and `Professor` classes extend the abstract `Position` class. + Being the first subclass of `Position` to be implemented, the design of the `TeachingAssistant` class provided a + guideline for the implementation of the other two subclasses (i.e. `Student` class and `Professor` class). +* **New Feature**: Added `ShowCommand` class + * What it does: Allows the user to display additional details of a specific `Person`. For instance, `grades` and + `attendance` will be displayed for `Student`, `availability` will be displayed for `TeachingAssistant`, and `roles` + will be displayed for `Professor`. + * Highlights: Displays the aforementioned details in a secondary panel beside the primary panel that displays the list + of `Persons`, thus keeping the primary panel compact. +* **New Feature**: Changed the UI of TAB + * Highlights: The primary panel now displays key information like name, position and tag. On the other hand, the theme + is now more aesthetically pleasing and simple to the eye. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s1.github.io/tp-dashboard/?search=cxyterence&breakdown=true) + +* **Project management**: + * Owner/Manager of the team's repository + +* **Enhancements to existing features**: + * Refined the appearance of the graph for `Student` to be more aesthetically pleasing: + [#91](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/91) + +* **Documentation**: + * Developer Guide: + * Added and modified UML class and sequence diagrams for existing classes: + [#163](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/163) + * Added documentation for implementation of: + 1. `AddCommand` + 2. `EditCommand` + 3. `AttendanceCommand` + 4. `AvailabilityCommand` + 5. `AddAssignmentCommand` + 6. `RolesCommand` + 7. `ShowCommand` + [#163](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/163) + * Added documentation for use cases from UC01-UC11: + [#162](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/162) +* **Community**: + * PRs reviewed (with non-trivial review comments): + [#148](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/148) + * Reported bugs and suggestions for other teams during [ped](https://github.com/cxyterence/ped) (example: + [#1](https://github.com/cxyterence/ped/issues/1)) diff --git a/docs/team/guowei42.md b/docs/team/guowei42.md new file mode 100644 index 00000000000..4fc8214260b --- /dev/null +++ b/docs/team/guowei42.md @@ -0,0 +1,55 @@ +--- +layout: page +title: Guo Wei's Project Portfolio Page +--- + +### Project: TAB + +Teacher’s Address Book (TAB) is a **desktop app made for teachers, teaching assistants(TA) and professors for managing +contacts of each other, as well as their students, 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, TAB can get your contact management tasks +done faster than traditional GUI apps. + +Given below are my contributions to the project. + +* **New Feature**: Creating New TAB + * What it does: Allow users to create new TABs to store more names. + * Justification: This is important as some TAs or professors might be teaching different modules or different tutorial groups. Therefore, this helps them to separate and differentiate them. + This reflects similarly to features offered by Vim to easily create new tabs without needing to open separate shells. + * Highlights: This enhancement's main challenge was that it is compulsory to integrate other core features, like swap and rename. +* **New Feature**: Rename Current TAB + * What it does: Allows users to rename the current TAB to something else. + * Justification: This allows users to easily differentiate the address books created. + * Highlights: This was challenging to correctly manage and store the changes information for future use. +* **New Feature**: Swapping between TAB + * What it does: Allows users to swap between the TABs created with `ctrl+tab` shortcut or `swap` command. + * Justification: Similar to Vim's tab creation, a quick way to swap between TABs created is needed. + * Highlights: The main challenge for this was to correctly save the current TAB and swap between TABs. Also, there was a need to + correctly keep track of where the user left off for a better experience. +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s1.github.io/tp-dashboard/?search=guowei42&breakdown=true) + +* **Project management**: + * Managed releases `v1.2` + +* **Enhancements to existing features**: + * Increased code testing coverage by `+3.11%`. [#157](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/157) + * Modified Regex to fit project requirement [#32](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/32) + * Implemented Remark feature [#31](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/31) + +* **Documentation**: + * User Guide: + * Added FAQs such as proper way to delete a file + * Developer Guide: + * Added diagrams for Features implemented above + +* **Community**: + * Reviewed PRs [#61](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/61), +[#74](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/74), +[#75](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/75), +[#85](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/85), +[#92](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/92), +[#93](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/93), +[#148](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/148), +[#152](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/152), +[#158](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/158) + diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md deleted file mode 100644 index 773a07794e2..00000000000 --- a/docs/team/johndoe.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -layout: page -title: John Doe's Project Portfolio Page ---- - -### Project: AddressBook Level 3 - -AddressBook - Level 3 is a desktop address book application used for teaching Software Engineering principles. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC. - -Given below are my contributions to the project. - -* **New Feature**: Added the ability to undo/redo previous commands. - * What it does: allows the user to undo all previous commands one at a time. Preceding undo commands can be reversed by using the redo command. - * Justification: This feature improves the product significantly because a user can make mistakes in commands and the app should provide a convenient way to rectify them. - * Highlights: This enhancement affects existing commands and commands to be added in future. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing commands. - * Credits: *{mention here if you reused any code/ideas from elsewhere or if a third-party library is heavily used in the feature so that a reader can make a more accurate judgement of how much effort went into the feature}* - -* **New Feature**: Added a history command that allows the user to navigate to previous commands using up/down keys. - -* **Code contributed**: [RepoSense link]() - -* **Project management**: - * Managed releases `v1.3` - `v1.5rc` (3 releases) on GitHub - -* **Enhancements to existing features**: - * Updated the GUI color scheme (Pull requests [\#33](), [\#34]()) - * Wrote additional tests for existing features to increase coverage from 88% to 92% (Pull requests [\#36](), [\#38]()) - -* **Documentation**: - * User Guide: - * Added documentation for the features `delete` and `find` [\#72]() - * Did cosmetic tweaks to existing documentation of features `clear`, `exit`: [\#74]() - * Developer Guide: - * Added implementation details of the `delete` feature. - -* **Community**: - * PRs reviewed (with non-trivial review comments): [\#12](), [\#32](), [\#19](), [\#42]() - * Contributed to forum discussions (examples: [1](), [2](), [3](), [4]()) - * Reported bugs and suggestions for other teams in the class (examples: [1](), [2](), [3]()) - * Some parts of the history feature I added was adopted by several other class mates ([1](), [2]()) - -* **Tools**: - * Integrated a third party library (Natty) to the project ([\#42]()) - * Integrated a new Github plugin (CircleCI) to the team repo - -* _{you can add/remove categories in the list above}_ diff --git a/docs/team/kjunwei.md b/docs/team/kjunwei.md new file mode 100644 index 00000000000..c70cf961e5f --- /dev/null +++ b/docs/team/kjunwei.md @@ -0,0 +1,61 @@ +--- +layout: page +title: Khor Jun Wei's Project Portfolio Page +--- + +### Project: Teacher’s Address Book (TAB) + +Teacher’s Address Book (TAB) is a **desktop app made for teachers, teaching assistants(TA) and professors for managing +contacts of each other, as well as their students, 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, TAB can get your contact management tasks +done faster than traditional GUI apps. + +Given below are my contributions to the project. + +* **New Feature**: Implemented the `Student` class + * What it does: encapsulates the relevant details of a student (i.e. their attendance and grades). + * Justification: the product can be used for managing the contact information of persons in three positions, + with student being one of them. + * Highlights: the `Student` class extends the abstract `Position` class. +* **New Feature**: Added the `AttendanceCommand` to edit the attendance of a student in the address book. + * What it does: allows the user to edit the attendance of a student in the address book. + * Justification: As a teaching assistant, one has to keep track of the attendance of a student, to know how frequent he/she attends lessons and present the information to the professors. The attendance command allows the user to update the attendance of a student to their + current status. + * Highlights: the implementation of the command execution took inspiration from the existing commands' execution, + after an in-depth analysis of the code base and design alternatives. +* **New Feature**: Added the `GradeCommand` to edit the grade of a student in the address book. + * What it does: allows the user to edit the grade of a student in the address book. + * Justification: As a teaching assistant, one has to keep track of the grade of a student, to know how well they are performing and provide help when necessary. The grade command allows the user to update the grade of a student to their + current status. + * Highlights: the implementation of the command execution took inspiration from the existing commands' execution, + after an in-depth analysis of the code base and design alternatives. +* **New Feature**: Added the `FilterCommand` to filter students with similar tags in the address book. + * What it does: allows the user to filter students with similar tags in the address book. + * Justification: As a teaching assistant, one may want to search the address book for contacts who are part of the same tutorial class. The filter command allows the user to do so efficiently, and the address book will now display the filtered list of students. + * Highlights: the implementation of the command execution took inspiration from the existing commands' execution, + after an in-depth analysis of the code base and design alternatives. For instance, the command requires the use of `TagContainsKeywordPredicate`, which took inspiration from `NameContainsKeywordsPredicate`. +* **New Feature**: Implemented the `ChartUtil` class + * What it does: Constructs a bar chart to visually display the grades of a student. + * Justification: this will be helpful to teaching assistants, as they can now easily compare the performance between students visually. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s1.github.io/tp-dashboard/?search=kjunwei&breakdown=true) + +* **Project management**: + * Authored 9 issues to help keep track of current issues and progress. + * Managed testing and writing test code: [#149](https://app.codecov.io/gh/AY2223S1-CS2103T-T17-1/tp/commit/1ffa6db3ee281ebc53304e816129d84dd8017f3d) + +* **Enhancements to existing features**: + * Updated `addAssignmentsCommand` to raise more issues with invalid user inputs: [#143](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/143) + +* **Documentation**: + * User Guide: + * Added documentation for the `remark` feature: [#70](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/70) + + * Developer Guide: + * Added implementation details for the filter feature: [#70](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/70) + +* **Community**: + * Reviewed 14 pull requests (with non-trivial comments) to ensure quality and consistency across the database (#19, #25, #28, #31, #47, #49, [#55](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/55), #56, #89, #91, #94, #96, #98, #153). + * Reported bugs and suggestions for other teams in the class (examples: [#4](https://github.com/KJunWei/ped/issues/4)) + +* **Tools**: diff --git a/docs/team/vantemoon.md b/docs/team/vantemoon.md new file mode 100644 index 00000000000..ab9c7c37959 --- /dev/null +++ b/docs/team/vantemoon.md @@ -0,0 +1,66 @@ +--- +layout: page +title: Sun Ruoxin's Project Portfolio Page +--- + +### Project: Teacher’s Address Book (TAB) + +Teacher’s Address Book (TAB) is a **desktop app made for teachers, teaching assistants(TA) and professors for managing +contacts of each other, as well as their students, 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, TAB can get your contact management tasks +done faster than traditional GUI apps. + +Given below are my contributions to the project. + +* **New Feature**: Implemented the `TeachingAssistant` class + * What it does: encapsulates the relevant details of a teaching assistant (i.e. their availability). + * Justification: the product can be used for managing the contact information of persons in three positions, + with teaching assistants being one of them. + * Highlights: the `TeachingAssistant` class extends the abstract `Position` class. + Being the first subclass of `Position` to be implemented, the design of the `TeachingAssistant` class provided a + guideline for the implementation of the other two subclasses (i.e. `Student` class and `Professor` class). +* **New Feature**: Added the `AvailabilityCommand` to edit the availability of a teaching assistant in the address book. + * What it does: allows the user to edit the availability of a teaching assistant in the address book. + * Justification: when a teaching assistant needs to find a relief teacher when they are unavailable for a class, the + professor or the teaching assistant themselves can check the availability of other teaching assistants and know who + they can contact. The availability command allows the user to update the availability of a teaching assistant to their + current status. + * Highlights: the implementation of the command execution took inspiration from the existing commands' execution, + after an in-depth analysis of the code base and design alternatives. Being the first new command related to the + subclasses of `Position` to be implemented, the design of the `AvailabilityCommand` and `AvailabilityCommandParser` + provided a guidance for the implementation of other new commands related to the other two subclasses + (i.e. `AttendanceCommand`, `GradeCommand` and `RolesCommand`). +* **New Feature**: Modified the `GradeCommand` to accommodate product design changes. + * What it does: with `Assignment` class added in release `v1.3`, the modified `GradeCommand` adds grade to an + assignment of a student in the address book. The overall grade of the student, which is displayed on the detail page + of the student, is automatically calculated and stored in the address book when the grade of an assignment is updated. + * Justification: the modified grade command improves the usability of the product, as in a school's context the grades + are associated with assignments instead of directly with a student on their own. + * Highlights: the implementation was challenging as it required changes to the existing `GradeCommand` and other + related methods in the `Student` class. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s1.github.io/tp-dashboard/?search=vantemoon&breakdown=true) + +* **Enhancements to existing features**: + * Wrote additional tests for existing features to increase coverage by +3.47%: + [#147](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/147) + +* **Documentation**: + * User Guide: + * Added documentation for features `help`, `show`, `attendance`, `assignments`, `grade`, + `avail`, `roles`, `filter`, `new`, `swap`, and `rename`: + [#49](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/49), [#96](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/96) + * Did cosmetic tweaks to existing documentation of features `add`, `list`, `edit`, `delete`, `clear`, and `exit`: + [#49](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/49), + * Developer Guide: + * Added implementation details of the grade feature: [#75](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/75) + +* **Community**: + * PRs reviewed (with non-trivial review comments): [#55](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/55), + [148](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/148) + * Reported bugs and suggestions for other teams in the class during [ped](https://github.com/vantemoon/ped) and pe + (examples: [1](https://github.com/vantemoon/ped/issues/1), [2](https://github.com/vantemoon/ped/issues/2), + [3](https://github.com/vantemoon/ped/issues/6), [4](https://github.com/vantemoon/ped/issues/8)) + +* **Others**: + * Created UI prototype for the product diff --git a/docs/team/yellow-294.md b/docs/team/yellow-294.md new file mode 100755 index 00000000000..5bc8577d7c1 --- /dev/null +++ b/docs/team/yellow-294.md @@ -0,0 +1,55 @@ +--- +layout: page +title: Wee Xin Yang, Markus' Project Portfolio Page +--- + +### Project: Teacher’s Address Book (TAB) + +Teacher’s Address Book (TAB) is a **desktop app made for teachers, teaching assistants(TA) and professors for managing +contacts of each other, as well as their students, 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, TAB can get your contact management tasks +done faster than traditional GUI apps. + +Given below are my contributions to the project. + +* **New Feature**: Implemented the `Professor` class + * What it does: It encapsulates the details of a Professor (i.e. their roles). + * Justification: Allows each person in TAB to be differentiated by their roles, with Professor being one of them + * Highlights: The `Professor` class extends the abstract `Position` class. Roles such as `Coordinator`, `Lecturer`, `Tutor` and `Advisor` can be added so the user can know the roles of each Professor + +* **New Feature**: Implemented the `Assignment` class + * What it does: It encapsulates the name, grade and weightage of an assignment. + * Justification: Each student will be given graded assignments in the course of their learning and as such it is of absolute necessity that this is implemented + * Highlights: With its encapsulated grade and weightage, we can gauge how well the student is doing academically. This is the part of the AddAssignments feature which will be explained later +* **New Feature**: Added the `AddAssignmentsCommand` command + * What it does: It adds a list of `Assignment` to every `Student` in TAB + * Justification: Assignments can be added to every `Student` after they are added. This saves the trouble of adding assignments to each `Student` individually, which can be time consuming + * Highlights: This command only needs to be ran once. Since each TAB consists of students of the same module, subsequent students added to TAB will be automatically instantiated with assignments if the AddAssignmentsCommand has already been ran before + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s1.github.io/tp-dashboard/?search=yellow-294&breakdown=true) + +* **Enhancements to existing features**: + * AB3 only checks for similarity in `Name` when determining duplicate `Person`. I made changes to check for `Phone` and `Email` as well as it is only logical that these field should also be unique for each `Person`: + [#156](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/156) + +* **Documentation**: + * User Guide: + * Fixed UG related bugs after PED: [#148](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/148), [#179](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/179) + * Developer Guide: + * Added implementation details of the AddAssignments feature: [#72](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/72) + * Added portion in APpendix regarding effort: [#182](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/182) + +* **Community**: + * PRs reviewed: + [#34](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/34), + [#37](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/37), + [#70](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/70), + [#81](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/81), + [#155](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/155), + [#164](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/164), + [#171](https://github.com/AY2223S1-CS2103T-T17-1/tp/pull/171), + + + + + diff --git a/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md index 4fb62a83ef6..148dde99e29 100644 --- a/docs/tutorials/TracingCode.md +++ b/docs/tutorials/TracingCode.md @@ -85,7 +85,7 @@ Now let’s set the breakpoint. First, double-click the item to reach the corres ## Tracing the execution path -Recall from the User Guide that the `edit` command has the format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` For this tutorial we will be issuing the command `edit 1 n/Alice Yeoh`. +Recall from the User Guide that the `edit` command has the format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS]…​` For this tutorial we will be issuing the command `edit 1 n/Alice Yeoh`.
diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/seedu/address/Main.java index 052a5068631..1a1aef768e4 100644 --- a/src/main/java/seedu/address/Main.java +++ b/src/main/java/seedu/address/Main.java @@ -18,7 +18,9 @@ * By having a separate main class (Main) that doesn't extend Application * to be the entry point of the application, we avoid this issue. */ + public class Main { + public static void main(String[] args) { Application.launch(MainApp.class, args); } diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java index 4133aaa0151..d6a0412dec8 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/address/MainApp.java @@ -78,7 +78,7 @@ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { ReadOnlyAddressBook initialData; try { addressBookOptional = storage.readAddressBook(); - if (!addressBookOptional.isPresent()) { + if (addressBookOptional.isEmpty()) { logger.info("Data file not found. Will be starting with a sample AddressBook"); } initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); @@ -146,6 +146,9 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { try { Optional prefsOptional = storage.readUserPrefs(); initializedPrefs = prefsOptional.orElse(new UserPrefs()); + if (initializedPrefs.getAllAddressBookFilePath().length == 0) { + initializedPrefs.addAddressBook(); + } } catch (DataConversionException e) { logger.warning("UserPrefs file at " + prefsFilePath + " is not in the correct format. " + "Using default user prefs"); diff --git a/src/main/java/seedu/address/commons/util/ChartUtil.java b/src/main/java/seedu/address/commons/util/ChartUtil.java new file mode 100644 index 00000000000..649a848a2ea --- /dev/null +++ b/src/main/java/seedu/address/commons/util/ChartUtil.java @@ -0,0 +1,55 @@ +package seedu.address.commons.util; + +import java.util.ArrayList; +import java.util.Map; + +import javafx.collections.FXCollections; +import javafx.scene.chart.CategoryAxis; +import javafx.scene.chart.NumberAxis; +import javafx.scene.chart.StackedBarChart; +import javafx.scene.chart.XYChart; + +/** + * Creates JavaFX charts. + */ +public class ChartUtil { + + /** + * Creates a JavaFX BarChart with the given title, axis labels and data points. + */ + public static StackedBarChart createBarChart(String title, String xLabel, String yLabel, + Map results, Map maxResult) { + final CategoryAxis xAxis = new CategoryAxis(); + xAxis.setLabel(xLabel); + ArrayList categories = new ArrayList<>(); + for (String string: results.keySet()) { + categories.add(string); + } + xAxis.setCategories(FXCollections.observableArrayList(categories)); + final NumberAxis yAxis = new NumberAxis(); + yAxis.setLabel(yLabel); + // Disable auto-ranging so that we can configure our own tick units + yAxis.setAutoRanging(false); + + final StackedBarChart barChart = new StackedBarChart(xAxis, yAxis); + barChart.setTitle(title); + + XYChart.Series series = new XYChart.Series<>(); + series.setName("Your Score"); + for (Map.Entry entry : results.entrySet()) { + series.getData().add(new XYChart.Data<>(entry.getKey(), entry.getValue())); + } + barChart.getData().add(series); + + XYChart.Series seriesResult = new XYChart.Series<>(); + seriesResult.setName("Maximum Score"); + for (Map.Entry entry : maxResult.entrySet()) { + Number x = results.get(entry.getKey()); + seriesResult.getData().add(new XYChart.Data<>(entry.getKey(), + entry.getValue().doubleValue() - x.doubleValue())); + } + barChart.getData().add(seriesResult); + barChart.setLegendVisible(false); + return barChart; + } +} diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java index 92cd8fa605a..64c436ba3eb 100644 --- a/src/main/java/seedu/address/logic/Logic.java +++ b/src/main/java/seedu/address/logic/Logic.java @@ -1,9 +1,11 @@ package seedu.address.logic; +import java.io.IOException; import java.nio.file.Path; import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; +import seedu.address.commons.exceptions.DataConversionException; import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; @@ -47,4 +49,26 @@ public interface Logic { * Set the user prefs' GUI settings. */ void setGuiSettings(GuiSettings guiSettings); + + /** + * Set the user prefs' Stored address book array setting. + */ + void setAllAddressBookFilePath(Path[] updatedPaths); + + void resetCurrentAddressBook(); + + /** + * Add and create a new address book + */ + boolean addAddressBook() throws IOException, DataConversionException; + + /** + * Swaps between the address book + */ + void swapAddressBook(); + + /** + * Swaps to the address book + */ + void swapToAddressBook(Path nextAddressBook); } diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java index 9d9c6d15bdc..8359d246b30 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/address/logic/LogicManager.java @@ -2,19 +2,24 @@ import java.io.IOException; import java.nio.file.Path; +import java.util.Optional; import java.util.logging.Logger; import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.exceptions.DataConversionException; import seedu.address.logic.commands.Command; import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.AddressBookParser; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.AddressBook; import seedu.address.model.Model; import seedu.address.model.ReadOnlyAddressBook; import seedu.address.model.person.Person; +import seedu.address.model.util.SampleDataUtil; +import seedu.address.storage.JsonAddressBookStorage; import seedu.address.storage.Storage; /** @@ -78,4 +83,79 @@ public GuiSettings getGuiSettings() { public void setGuiSettings(GuiSettings guiSettings) { model.setGuiSettings(guiSettings); } + + @Override + public void setAllAddressBookFilePath(Path[] updatedPaths) { + model.setAllAddressBookFilePath(updatedPaths); + } + + @Override + public void resetCurrentAddressBook() { + int index = model.getUserPrefs().getStoredIndex(); + Path newPath = model.getAllAddressBookFilePath()[index]; + model.setAddressBookFilePath(newPath); + swapToAddressBook(newPath); + } + + private void setActiveAddressBook(Path latestBook, ReadOnlyAddressBook initialData) { + storage.setAddressBook(new JsonAddressBookStorage(latestBook)); + model.setAddressBook(initialData); + } + + @Override + public boolean addAddressBook() throws IOException, DataConversionException { + boolean result = model.addAddressBook(); + Optional addressBookOptional; + ReadOnlyAddressBook initialData; + if (result) { + Path[] allBooks = model.getAllAddressBookFilePath(); + Path latestBook = allBooks[allBooks.length - 1]; + initialData = new AddressBook(); + setActiveAddressBook(latestBook, initialData); + model.setStoredIndex(allBooks.length - 1); + model.setAddressBookFilePath(latestBook); + storage.saveAddressBook(initialData); + } + return result; + } + @Override + public void swapAddressBook() { + Path nextAddressBook = model.getNextAddressBookPath(); + Optional addressBookOptional; + ReadOnlyAddressBook initialData; + try { + addressBookOptional = storage.readAddressBook(nextAddressBook); + if (!addressBookOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample AddressBook"); + } + initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); + } catch (DataConversionException e) { + logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook"); + initialData = new AddressBook(); + } catch (IOException e) { + logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); + initialData = new AddressBook(); + } + setActiveAddressBook(nextAddressBook, initialData); + } + + @Override + public void swapToAddressBook(Path nextAddressBook) { + Optional addressBookOptional; + ReadOnlyAddressBook initialData; + try { + addressBookOptional = storage.readAddressBook(nextAddressBook); + if (!addressBookOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample AddressBook"); + } + initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); + } catch (DataConversionException e) { + logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook"); + initialData = new AddressBook(); + } catch (IOException e) { + logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); + initialData = new AddressBook(); + } + setActiveAddressBook(nextAddressBook, initialData); + } } diff --git a/src/main/java/seedu/address/logic/commands/AddAssignmentsCommand.java b/src/main/java/seedu/address/logic/commands/AddAssignmentsCommand.java new file mode 100644 index 00000000000..808a16a0883 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddAssignmentsCommand.java @@ -0,0 +1,145 @@ +package seedu.address.logic.commands; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.person.Person; +import seedu.address.model.person.position.Student; + +/** + * Adds assignments to all existing students in the address book. + */ +public class AddAssignmentsCommand extends Command { + + public static final String COMMAND_WORD = "assignments"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": adds assignments. Format: [name] w/[weightage]"; + public static final String MESSAGE_ADD_ASSIGNMENTS_SUCCESS = "Added Assignments to all students."; + + public static final String MESSAGE_NO_STUDENTS = "Please ensure TAB contains at least 1 " + + "student before adding assignments."; + + private final String assignments; + + /** + * @param assignments of the student to be updated to + */ + public AddAssignmentsCommand(String assignments) { + requireAllNonNull(assignments); + + this.assignments = assignments; + } + + /** + * Executes the command and returns the result message. + * + * @param model {@code Model} which the command should operate on. + * @return feedback message of the operation result for display + * @throws CommandException If an error occurs during command execution. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + Predicate predicate = new Predicate() { + @Override + public boolean test(Person person) { + return true; + } + }; + model.updateFilteredPersonList(predicate); + List lastShownList = model.getFilteredPersonList(); + boolean containsStudent = false; + for (Person p: lastShownList) { + if (p.getPosition() instanceof Student) { + containsStudent = true; + break; + } + } + if (!containsStudent) { + throw new CommandException(MESSAGE_NO_STUDENTS); + } + int numOfPeople = lastShownList.size(); + String relativePath = "./data/"; + String txt = ".txt"; + String tag = ""; + String filePath = ""; + for (int i = 0; i < numOfPeople; i++) { + + Person personToEdit = lastShownList.get(i); + if ((personToEdit.getPosition() instanceof Student)) { + tag = personToEdit.getTags().toString(); + String[] handleTag = tag.split("-"); + String module = handleTag[0].replace("[", ""); + String filePath1 = relativePath + module + txt; + if (filePath == "") { + filePath = filePath1; + } + Student currPosition = (Student) personToEdit.getPosition(); + Student editedPosition = new Student(currPosition.getAttendance(), + "0/0", + currPosition.setAssignments(assignments), filePath1); + + + Person editedPerson = new Person(personToEdit.getName(), + personToEdit.getPhone(), + personToEdit.getEmail(), + editedPosition, + personToEdit.getAddress(), + personToEdit.getRemark(), + personToEdit.getTags()); + + model.setPerson(personToEdit, editedPerson); + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + } + + } + + File file = new File(filePath); + + try { + FileWriter fileWriter = new FileWriter(filePath); + fileWriter.write(assignments); + fileWriter.close(); + } catch (IOException e) { + System.out.println("An error occurred."); + e.printStackTrace(); + } + return new CommandResult(generateSuccessMessage()); + } + + /** + * Generates a command execution success message based on whether the assignments are added to all existing + * students in the address book + * {@code personToEdit}. + */ + private String generateSuccessMessage() { + return MESSAGE_ADD_ASSIGNMENTS_SUCCESS; + } + + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof AddAssignmentsCommand)) { + return false; + } + + // state check + AddAssignmentsCommand e = (AddAssignmentsCommand) other; + return assignments.equals(e.assignments); + } + +} diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java index 71656d7c5c8..f4b9cc73471 100644 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddCommand.java @@ -5,6 +5,7 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_POSITION; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; import seedu.address.logic.commands.exceptions.CommandException; @@ -18,23 +19,24 @@ public class AddCommand extends Command { public static final String COMMAND_WORD = "add"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. " + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to TAB. " + "Parameters: " + PREFIX_NAME + "NAME " + PREFIX_PHONE + "PHONE " + PREFIX_EMAIL + "EMAIL " + + PREFIX_POSITION + "POSITION " + PREFIX_ADDRESS + "ADDRESS " + "[" + PREFIX_TAG + "TAG]...\n" + "Example: " + COMMAND_WORD + " " + PREFIX_NAME + "John Doe " + PREFIX_PHONE + "98765432 " + PREFIX_EMAIL + "johnd@example.com " + + PREFIX_POSITION + "Student " + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " - + PREFIX_TAG + "friends " - + PREFIX_TAG + "owesMoney"; + + PREFIX_TAG + "CS2103T-T17"; public static final String MESSAGE_SUCCESS = "New person added: %1$s"; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book"; + public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in TAB"; private final Person toAdd; diff --git a/src/main/java/seedu/address/logic/commands/AttendanceCommand.java b/src/main/java/seedu/address/logic/commands/AttendanceCommand.java new file mode 100644 index 00000000000..51bbe936a31 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AttendanceCommand.java @@ -0,0 +1,110 @@ +package seedu.address.logic.commands; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ATTENDANCE; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.person.Person; +import seedu.address.model.person.position.Student; + +/** + * Changes the attendance of an existing student in the address book. + */ +public class AttendanceCommand extends Command { + public static final String COMMAND_WORD = "attendance"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Edits the attendance of the Student identified " + + "by the index number used in the last person listing.\n" + + "Parameters: INDEX (must be a positive integer) " + + PREFIX_ATTENDANCE + + "ATTENDANCE ([integer (0-100)]/[integer (0-100)])\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_ATTENDANCE + "1/2."; + public static final String MESSAGE_PERSON_NOT_STUDENT = "The person to edit is not a student, there is no " + + "attendance to be edited."; + public static final String MESSAGE_EDIT_ATTENDANCE_SUCCESS = "Edited attendance to student: %1$s"; + + private final Index index; + private final String attendance; + + /** + * @param index of the student in the filtered person list to edit the attendance + * @param attendance of the student to be updated to + */ + public AttendanceCommand(Index index, String attendance) { + requireAllNonNull(index, attendance); + this.index = index; + this.attendance = attendance; + } + + /** + * Executes the command and returns the result message. + * + * @param model {@code Model} which the command should operate on. + * @return feedback message of the operation result for display + * @throws CommandException If an error occurs during command execution. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + List lastShownList = model.getFilteredPersonList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + Person personToEdit = lastShownList.get(index.getZeroBased()); + if (!(personToEdit.getPosition() instanceof Student)) { + throw new CommandException(MESSAGE_PERSON_NOT_STUDENT); + } + Student currPosition = (Student) personToEdit.getPosition(); + Student editedPosition = new Student(attendance, + currPosition.getOverallGrade(), + currPosition.getAssignmentsList(), currPosition.getFilePath()); + Person editedPerson = new Person(personToEdit.getName(), + personToEdit.getPhone(), + personToEdit.getEmail(), + editedPosition, + personToEdit.getAddress(), + personToEdit.getRemark(), + personToEdit.getTags()); + + model.setPerson(personToEdit, editedPerson); + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + + return new CommandResult(generateSuccessMessage(editedPerson)); + } + + /** + * Generates a command execution success message based on whether the availability is edited for + * {@code personToEdit}. + */ + private String generateSuccessMessage(Person personToEdit) { + return String.format(MESSAGE_EDIT_ATTENDANCE_SUCCESS, personToEdit); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof AttendanceCommand)) { + return false; + } + + // state check + AttendanceCommand e = (AttendanceCommand) other; + return index.equals(e.index) + && attendance.equals(e.attendance); + } + +} diff --git a/src/main/java/seedu/address/logic/commands/AvailabilityCommand.java b/src/main/java/seedu/address/logic/commands/AvailabilityCommand.java new file mode 100644 index 00000000000..2311300cde2 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AvailabilityCommand.java @@ -0,0 +1,114 @@ +package seedu.address.logic.commands; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_AVAILABILITY; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.List; +import java.util.Locale; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.person.Person; +import seedu.address.model.person.position.TeachingAssistant; + +/** + * Changes the availability of an existing teaching assistant in the address book. + */ +public class AvailabilityCommand extends Command { + public static final String COMMAND_WORD = "avail"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Edits the availability of the Teaching Assistant identified " + + "by the index number used in the last person listing.\n" + + "Parameters: INDEX (must be a positive integer) " + + PREFIX_AVAILABILITY + + "[AVAILABILITY]\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_AVAILABILITY + "available."; + public static final String MESSAGE_PERSON_NOT_TA = "The person to edit is not a teaching assistant, there is no " + + "availability to be edited."; + public static final String MESSAGE_EDIT_AVAILABILITY_SUCCESS = "Edited availability to Teaching Assistant: %1$s"; + + private final Index index; + private final String availability; + + /** + * @param index of the teaching assistant in the filtered person list to edit the availability + * @param availability of the teaching assistant to be updated to + */ + public AvailabilityCommand(Index index, String availability) { + requireAllNonNull(index, availability); + + this.index = index; + switch (availability.toLowerCase(Locale.ROOT)) { + case "unavailable": + this.availability = "Unavailable"; + break; + default: + this.availability = "Available"; + } + } + + /** + * Executes the command and returns the result message. + * + * @param model {@code Model} which the command should operate on. + * @return feedback message of the operation result for display + * @throws CommandException If an error occurs during command execution. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + List lastShownList = model.getFilteredPersonList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + Person personToEdit = lastShownList.get(index.getZeroBased()); + if (!(personToEdit.getPosition() instanceof TeachingAssistant)) { + throw new CommandException(MESSAGE_PERSON_NOT_TA); + } + TeachingAssistant editedPosition = new TeachingAssistant(availability); + Person editedPerson = new Person(personToEdit.getName(), + personToEdit.getPhone(), + personToEdit.getEmail(), + editedPosition, + personToEdit.getAddress(), + personToEdit.getRemark(), + personToEdit.getTags()); + + model.setPerson(personToEdit, editedPerson); + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + + return new CommandResult(generateSuccessMessage(editedPerson)); + } + + /** + * Generates a command execution success message based on whether the availability is edited for + * {@code personToEdit}. + */ + private String generateSuccessMessage(Person personToEdit) { + return String.format(MESSAGE_EDIT_AVAILABILITY_SUCCESS, personToEdit); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof AvailabilityCommand)) { + return false; + } + + // state check + AvailabilityCommand e = (AvailabilityCommand) other; + return index.equals(e.index) + && availability.equals(e.availability); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java index 9c86b1fa6e4..9c0ac2a59a7 100644 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java @@ -11,7 +11,7 @@ public class ClearCommand extends Command { public static final String COMMAND_WORD = "clear"; - public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; + public static final String MESSAGE_SUCCESS = "TAB has been cleared!"; @Override diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java index 92f900b7916..57b08fadfb5 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/address/logic/commands/CommandResult.java @@ -4,6 +4,8 @@ import java.util.Objects; +import seedu.address.model.person.Person; + /** * Represents the result of a command execution. */ @@ -11,18 +13,30 @@ public class CommandResult { private final String feedbackToUser; + private final Person personToShow; + /** Help information should be shown to the user. */ private final boolean showHelp; + /** New Book should be created for the user. */ + private final boolean showNewBook; + + /** Application should swap between address books */ + private final boolean showSwap; + /** The application should exit. */ private final boolean exit; /** * Constructs a {@code CommandResult} with the specified fields. */ - public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { + public CommandResult(String feedbackToUser, Person personToShow, boolean showHelp, boolean showNewBook, + boolean swap, boolean exit) { this.feedbackToUser = requireNonNull(feedbackToUser); + this.personToShow = personToShow; this.showHelp = showHelp; + this.showNewBook = showNewBook; + this.showSwap = swap; this.exit = exit; } @@ -31,21 +45,50 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { * and other fields set to their default value. */ public CommandResult(String feedbackToUser) { - this(feedbackToUser, false, false); + this(feedbackToUser, null, false, false, false, false); + } + + /** + * Constructs a {@code CommandResult} with the specified {@code personToShow}, + * and other fields set to their default value. + */ + public CommandResult(String feedbackToUser, Person personToShow) { + this(feedbackToUser, personToShow, false, false, false, false); + } + + public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { + this(feedbackToUser, null, showHelp, false, false, exit); } public String getFeedbackToUser() { return feedbackToUser; } + public Person getPersonToShow() { + return personToShow; + } + + public boolean hasPersonToShow() { + return personToShow != null; + } + public boolean isShowHelp() { return showHelp; } + public boolean isNewBook() { + return showNewBook; + } + + public boolean isSwap() { + return showSwap; + } + public boolean isExit() { return exit; } + @Override public boolean equals(Object other) { if (other == this) { @@ -68,4 +111,5 @@ public int hashCode() { return Objects.hash(feedbackToUser, showHelp, exit); } + } diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java index 7e36114902f..853c3823d19 100644 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ b/src/main/java/seedu/address/logic/commands/EditCommand.java @@ -5,7 +5,6 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; import java.util.Collections; @@ -24,6 +23,8 @@ import seedu.address.model.person.Name; import seedu.address.model.person.Person; import seedu.address.model.person.Phone; +import seedu.address.model.person.Remark; +import seedu.address.model.person.position.Position; import seedu.address.model.tag.Tag; /** @@ -40,15 +41,17 @@ public class EditCommand extends Command { + "[" + PREFIX_NAME + "NAME] " + "[" + PREFIX_PHONE + "PHONE] " + "[" + PREFIX_EMAIL + "EMAIL] " - + "[" + PREFIX_ADDRESS + "ADDRESS] " - + "[" + PREFIX_TAG + "TAG]...\n" + + "[" + PREFIX_ADDRESS + "ADDRESS] \n" + + "Tags and Position cannot be edited\n" + "Example: " + COMMAND_WORD + " 1 " + PREFIX_PHONE + "91234567 " + PREFIX_EMAIL + "johndoe@example.com"; public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s"; public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book."; + public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in TAB."; + public static final String MESSAGE_DUPLICATE_PHONE = "This person's Phone already exists in TAB."; + public static final String MESSAGE_DUPLICATE_EMAIL = "This person's Email already exists in TAB."; private final Index index; private final EditPersonDescriptor editPersonDescriptor; @@ -81,6 +84,14 @@ public CommandResult execute(Model model) throws CommandException { throw new CommandException(MESSAGE_DUPLICATE_PERSON); } + if (!personToEdit.hasSameEmail(editedPerson) && model.hasEmail(editedPerson)) { + throw new CommandException(MESSAGE_DUPLICATE_EMAIL); + } + + if (!personToEdit.hasSamePhone(editedPerson) && model.hasPhone(editedPerson)) { + throw new CommandException(MESSAGE_DUPLICATE_PHONE); + } + model.setPerson(personToEdit, editedPerson); model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson)); @@ -96,10 +107,13 @@ private static Person createEditedPerson(Person personToEdit, EditPersonDescript Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName()); Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); + Position updatedPosition = editPersonDescriptor.getPosition().orElse(personToEdit.getPosition()); Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress()); + Remark updatedRemark = personToEdit.getRemark(); Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags()); - return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); + return new Person(updatedName, updatedPhone, updatedEmail, updatedPosition, updatedAddress, + updatedRemark, updatedTags); } @Override @@ -128,6 +142,7 @@ public static class EditPersonDescriptor { private Name name; private Phone phone; private Email email; + private Position position; private Address address; private Set tags; @@ -141,6 +156,7 @@ public EditPersonDescriptor(EditPersonDescriptor toCopy) { setName(toCopy.name); setPhone(toCopy.phone); setEmail(toCopy.email); + setPosition(toCopy.position); setAddress(toCopy.address); setTags(toCopy.tags); } @@ -149,7 +165,7 @@ public EditPersonDescriptor(EditPersonDescriptor toCopy) { * Returns true if at least one field is edited. */ public boolean isAnyFieldEdited() { - return CollectionUtil.isAnyNonNull(name, phone, email, address, tags); + return CollectionUtil.isAnyNonNull(name, phone, email, position, address, tags); } public void setName(Name name) { @@ -176,6 +192,14 @@ public Optional getEmail() { return Optional.ofNullable(email); } + public void setPosition(Position position) { + this.position = position; + } + + public Optional getPosition() { + return Optional.ofNullable(position); + } + public void setAddress(Address address) { this.address = address; } @@ -219,6 +243,7 @@ public boolean equals(Object other) { return getName().equals(e.getName()) && getPhone().equals(e.getPhone()) && getEmail().equals(e.getEmail()) + && getPosition().equals(e.getPosition()) && getAddress().equals(e.getAddress()) && getTags().equals(e.getTags()); } diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java index 3dd85a8ba90..9b7b3d2c8d1 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java @@ -9,11 +9,11 @@ public class ExitCommand extends Command { public static final String COMMAND_WORD = "exit"; - public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; + public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting TAB as requested ..."; @Override public CommandResult execute(Model model) { - return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); + return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, null, false, false, false, true); } } diff --git a/src/main/java/seedu/address/logic/commands/FilterCommand.java b/src/main/java/seedu/address/logic/commands/FilterCommand.java new file mode 100644 index 00000000000..ed87c240ba3 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/FilterCommand.java @@ -0,0 +1,43 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.commons.core.Messages; +import seedu.address.model.Model; +import seedu.address.model.tag.TagContainsKeywordPredicate; + + +/** + * Finds and lists all persons in address book whose tag(s) contain the argument keyword. + * Keyword matching is case insensitive. + */ +public class FilterCommand extends Command { + + public static final String COMMAND_WORD = "filter"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Filters all persons whose tag(s) contain " + + "the specified keyword (case-insensitive) and displays them as a list with index numbers.\n" + + "Parameters: KEYWORD\n" + + "Example: " + COMMAND_WORD + " alice"; + + private final TagContainsKeywordPredicate predicate; + + public FilterCommand(TagContainsKeywordPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredPersonList(predicate); + return new CommandResult( + String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FilterCommand // instanceof handles nulls + && predicate.equals(((FilterCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/GradeCommand.java b/src/main/java/seedu/address/logic/commands/GradeCommand.java new file mode 100644 index 00000000000..16c19020394 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/GradeCommand.java @@ -0,0 +1,124 @@ +package seedu.address.logic.commands; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ASSIGNMENT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_GRADE; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.person.Person; +import seedu.address.model.person.position.Student; + +/** + * Changes the grade of an existing student in the address book. + */ +public class GradeCommand extends Command { + public static final String COMMAND_WORD = "grade"; + + public static final String EMPTY_FIELD = "Please provide an assignment field."; + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Edits the grade of the Assignment of a student" + + " identified by the index number used in the last person listing.\n" + + "Parameters: " + + "INDEX (must be a positive integer) " + + PREFIX_ASSIGNMENT + "INDEX (must be a positive integer) " + + PREFIX_GRADE + "GRADE ([integer (0-100)]/[integer (0-100)])\n" + + "Example: " + COMMAND_WORD + " " + + "1 " + PREFIX_ASSIGNMENT + "1 " + + PREFIX_GRADE + "18/20."; + public static final String MESSAGE_PERSON_NOT_STUDENT = "The person to edit is not a student, there is no " + + "grade to be edited."; + public static final String MESSAGE_EDIT_GRADE_SUCCESS = "Edited grade to student: %1$s"; + public static final String MESSAGE_INVALID_INDEX = "The assignment number is too big, " + + "please input a smaller number."; + + private final Index indexOfStudent; + private final Index indexOfAssignment; + private final String grade; + + /** + * @param indexOfStudent Index of the student in the filtered person list to edit the grade + * @param indexOfAssignment Index of the assignment in the student's assignment list + * @param grade of the student to be updated to + */ + public GradeCommand(Index indexOfStudent, Index indexOfAssignment, String grade) { + requireAllNonNull(indexOfStudent, indexOfAssignment, grade); + + this.indexOfStudent = indexOfStudent; + this.indexOfAssignment = indexOfAssignment; + this.grade = grade; + } + + /** + * Executes the command and returns the result message. + * + * @param model {@code Model} which the command should operate on. + * @return feedback message of the operation result for display + * @throws CommandException If an error occurs during command execution. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + List lastShownList = model.getFilteredPersonList(); + + if (indexOfStudent.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + Person personToEdit = lastShownList.get(indexOfStudent.getZeroBased()); + if (!(personToEdit.getPosition() instanceof Student)) { + throw new CommandException(MESSAGE_PERSON_NOT_STUDENT); + } + Student currPosition = (Student) personToEdit.getPosition(); + if (!currPosition.isValidAssignmentIndex(indexOfAssignment)) { + throw new CommandException(MESSAGE_INVALID_INDEX); + } + Student editedPosition = new Student(currPosition.getAttendance(), + currPosition.updateOverallGrade(indexOfAssignment, grade), + currPosition.getAssignmentsList(), currPosition.getFilePath()); + Person editedPerson = new Person(personToEdit.getName(), + personToEdit.getPhone(), + personToEdit.getEmail(), + editedPosition, + personToEdit.getAddress(), + personToEdit.getRemark(), + personToEdit.getTags()); + + model.setPerson(personToEdit, editedPerson); + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + + return new CommandResult(generateSuccessMessage(editedPerson)); + } + + /** + * Generates a command execution success message based on whether the availability is edited for + * {@code personToEdit}. + */ + private String generateSuccessMessage(Person personToEdit) { + return String.format(MESSAGE_EDIT_GRADE_SUCCESS, personToEdit); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof GradeCommand)) { + return false; + } + + // state check + GradeCommand e = (GradeCommand) other; + return indexOfStudent.equals(e.indexOfStudent) + && indexOfAssignment.equals(e.indexOfAssignment) + && grade.equals(e.grade); + } + +} diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java index bf824f91bd0..18e1f5035bf 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java @@ -16,6 +16,6 @@ public class HelpCommand extends Command { @Override public CommandResult execute(Model model) { - return new CommandResult(SHOWING_HELP_MESSAGE, true, false); + return new CommandResult(SHOWING_HELP_MESSAGE, null, true, false, false, false); } } diff --git a/src/main/java/seedu/address/logic/commands/NewBookCommand.java b/src/main/java/seedu/address/logic/commands/NewBookCommand.java new file mode 100644 index 00000000000..c046d9b861b --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/NewBookCommand.java @@ -0,0 +1,20 @@ +package seedu.address.logic.commands; + +import seedu.address.model.Model; + +/** + * Creates a new Address Book. + */ +public class NewBookCommand extends Command { + + public static final String COMMAND_WORD = "new"; + + public static final String MESSAGE_NEW_BOOK_ACKNOWLEDGEMENT = "Creating New Address Book as requested ..."; + + @Override + public CommandResult execute(Model model) { + return new CommandResult(MESSAGE_NEW_BOOK_ACKNOWLEDGEMENT, null, + false, true, false, false); + } + +} diff --git a/src/main/java/seedu/address/logic/commands/RemarkCommand.java b/src/main/java/seedu/address/logic/commands/RemarkCommand.java new file mode 100644 index 00000000000..422bec236db --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/RemarkCommand.java @@ -0,0 +1,91 @@ +package seedu.address.logic.commands; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_REMARK; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.person.Person; +import seedu.address.model.person.Remark; + +/** + * Changes the remark of an existing person in the address book. + */ +public class RemarkCommand extends Command { + public static final String COMMAND_WORD = "remark"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Edits the remark of the person identified " + + "by the index number used in the last person listing. " + + "Existing remark will be overwritten by the input.\n" + + "Parameters: INDEX (must be a positive integer) " + + PREFIX_REMARK + "[REMARK]\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_REMARK + "Likes to swim."; + public static final String MESSAGE_ADD_REMARK_SUCCESS = "Added remark to Person: %1$s"; + public static final String MESSAGE_DELETE_REMARK_SUCCESS = "Removed remark from Person: %1$s"; + + private final Index index; + private final Remark remark; + + /** + * @param index of the person in the filtered person list to edit the remark + * @param remark of the person to be updated to + */ + public RemarkCommand(Index index, Remark remark) { + requireAllNonNull(index, remark); + + this.index = index; + this.remark = remark; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + List lastShownList = model.getFilteredPersonList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + Person personToEdit = lastShownList.get(index.getZeroBased()); + Person editedPerson = new Person(personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(), + personToEdit.getPosition(), personToEdit.getAddress(), remark, personToEdit.getTags()); + + model.setPerson(personToEdit, editedPerson); + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + + return new CommandResult(generateSuccessMessage(editedPerson)); + } + + /** + * Generates a command execution success message based on whether the remark is added to or removed from + * {@code personToEdit}. + */ + private String generateSuccessMessage(Person personToEdit) { + String message = !remark.value.isEmpty() ? MESSAGE_ADD_REMARK_SUCCESS : MESSAGE_DELETE_REMARK_SUCCESS; + return String.format(message, personToEdit); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof RemarkCommand)) { + return false; + } + + // state check + RemarkCommand e = (RemarkCommand) other; + return index.equals(e.index) + && remark.equals(e.remark); + } +} diff --git a/src/main/java/seedu/address/logic/commands/RenameCommand.java b/src/main/java/seedu/address/logic/commands/RenameCommand.java new file mode 100644 index 00000000000..17fe6dfd160 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/RenameCommand.java @@ -0,0 +1,70 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; + +/** + * Renames the currently stored folder name to a new name + */ +public class RenameCommand extends Command { + public static final String COMMAND_WORD = "rename"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Renames the current folder " + + "by the input string.\n" + + "Parameters: NAME (must be an alphanumeric String with only '-' and '_' allowed)"; + + public static final String MESSAGE_RENAME_SUCCESS = "Renamed successfully!"; + public static final String MESSAGE_RENAME_FAILURE = "There is an error creating the file!\n" + + "The file has to be in alphanumeric format with only '-' and '_' allowed\n" + + "The file cannot be a duplicate"; + public static final String MESSAGE_RENAME_SAME = "They are the same name!"; + + private final String name; + + /** + * @param name of the new book + */ + public RenameCommand(String name) { + requireNonNull(name); + this.name = name; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + try { + Path newAddressBookFilePath = Paths.get("data" , name + ".json"); + if (newAddressBookFilePath.toString().equals(model.getUserPrefs().getAddressBookFilePath().toString())) { + return new CommandResult(MESSAGE_RENAME_SAME, null, false, false, false, false); + } + model.renameAddressBook(this.name); + return new CommandResult(MESSAGE_RENAME_SUCCESS, null, false, false, false, false); + } catch (IOException e) { + return new CommandResult(MESSAGE_RENAME_FAILURE, null, false, false, false, false); + } + + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof RenameCommand)) { + return false; + } + + // state check + RenameCommand s = (RenameCommand) other; + return name.equals(s.name); + } +} diff --git a/src/main/java/seedu/address/logic/commands/RolesCommand.java b/src/main/java/seedu/address/logic/commands/RolesCommand.java new file mode 100644 index 00000000000..535c313f5a3 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/RolesCommand.java @@ -0,0 +1,123 @@ +package seedu.address.logic.commands; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLES; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.List; +import java.util.Locale; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.person.Person; +import seedu.address.model.person.position.Professor; + +/** + * Changes the roles of an existing professor in the address book. + */ +public class RolesCommand extends Command { + public static final String COMMAND_WORD = "roles"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Edits the roles of the Professor identified " + + "by the index number used in the last person listing.\n" + + "Multiple roles may be added and must be separated by a comma.\n" + + "Parameters: INDEX (must be a positive integer) " + + PREFIX_ROLES + + "[ROLES]\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_ROLES + "Coordinator, Lecturer, Advisor"; + public static final String MESSAGE_PERSON_NOT_PROFESSOR = "The person to edit is not a Professor, there are no " + + "roles to be edited."; + public static final String MESSAGE_EDIT_ROLES_SUCCESS = "Edited roles to Professor: %1$s"; + + private final Index index; + private final String role; + + /** + * @param index of the professor in the filtered person list to edit the roles + * @param roles of the professor to be updated to + */ + public RolesCommand(Index index, String roles) { + requireAllNonNull(index, roles); + this.index = index; + switch (roles.toLowerCase(Locale.ROOT)) { + case "coordinator": + this.role = "Coordinator"; + break; + case "lecturer": + this.role = "Lecturer"; + break; + case "tutor": + this.role = "Tutor"; + break; + case "advisor": + this.role = "Advisor"; + break; + default: + this.role = "Unassigned"; + } + } + + /** + * Executes the command and returns the result message. + * + * @param model {@code Model} which the command should operate on. + * @return feedback message of the operation result for display + * @throws CommandException If an error occurs during command execution. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + List lastShownList = model.getFilteredPersonList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + Person personToEdit = lastShownList.get(index.getZeroBased()); + if (!(personToEdit.getPosition() instanceof Professor)) { + throw new CommandException(MESSAGE_PERSON_NOT_PROFESSOR); + } + Professor editedPosition = new Professor(role); + Person editedPerson = new Person(personToEdit.getName(), + personToEdit.getPhone(), + personToEdit.getEmail(), + editedPosition, + personToEdit.getAddress(), + personToEdit.getRemark(), + personToEdit.getTags()); + + model.setPerson(personToEdit, editedPerson); + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + + return new CommandResult(generateSuccessMessage(editedPerson)); + } + + /** + * Generates a command execution success message based on whether the roles are edited for + * {@code personToEdit}. + */ + private String generateSuccessMessage(Person personToEdit) { + return String.format(MESSAGE_EDIT_ROLES_SUCCESS, personToEdit); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof RolesCommand)) { + return false; + } + + // state check + RolesCommand e = (RolesCommand) other; + return index.equals(e.index) + && role.equals(e.role); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ShowCommand.java b/src/main/java/seedu/address/logic/commands/ShowCommand.java new file mode 100644 index 00000000000..50e9454a2a5 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ShowCommand.java @@ -0,0 +1,65 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.person.Person; + +/** + * Shows the details of an existing person inside TAB + */ +public class ShowCommand extends Command { + public static final String COMMAND_WORD = "show"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Shows the details of the person identified " + + "by the index number used in the displayed person list.\n" + + "Parameters: INDEX (must be a positive integer)"; + + public static final String MESSAGE_SHOW_PERSON_SUCCESS = "Details displayed successfully!"; + + private final Index index; + + /** + * @param index of the person in the displayed persons list to show details of + */ + public ShowCommand(Index index) { + requireNonNull(index); + this.index = index; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredPersonList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + Person personToShow = lastShownList.get(index.getZeroBased()); + + return new CommandResult(MESSAGE_SHOW_PERSON_SUCCESS, personToShow, false, false, false, false); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ShowCommand)) { + return false; + } + + // state check + ShowCommand s = (ShowCommand) other; + return index.equals(s.index); + } +} diff --git a/src/main/java/seedu/address/logic/commands/SwapCommand.java b/src/main/java/seedu/address/logic/commands/SwapCommand.java new file mode 100644 index 00000000000..f3c504b1ea8 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/SwapCommand.java @@ -0,0 +1,19 @@ +package seedu.address.logic.commands; + +import seedu.address.model.Model; + +/** + * Swaps between Address Books. + */ +public class SwapCommand extends Command { + + public static final String COMMAND_WORD = "swap"; + + public static final String MESSAGE_SWAP_ACKNOWLEDGEMENT = "Swapping Address Book as requested ..."; + + @Override + public CommandResult execute(Model model) { + return new CommandResult(MESSAGE_SWAP_ACKNOWLEDGEMENT, null, false, false, true, false); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/AddAssignmentsCommandParser.java b/src/main/java/seedu/address/logic/parser/AddAssignmentsCommandParser.java new file mode 100644 index 00000000000..b0be538fa4b --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/AddAssignmentsCommandParser.java @@ -0,0 +1,31 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ASSIGNMENTS; + +import seedu.address.logic.commands.AddAssignmentsCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.position.Student; + +/** + * Parses input arguments and creates a new AddAssignmentsCommand object + */ +public class AddAssignmentsCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the {@code AddAssignmentsCommand} + * and returns a {@code AddAssignmentsCommand} object for execution. + * + * @throws ParseException if the user input does not conform the expected format + */ + public AddAssignmentsCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_ASSIGNMENTS); + + String assignments = argMultimap.getValue(PREFIX_ASSIGNMENTS).orElse(""); + if (!Student.isValidAssignments(assignments)) { + throw new ParseException(Student.findAssignmentIssue(assignments)); + } + + return new AddAssignmentsCommand(assignments); + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java index 3b8bfa035e8..bdd09d40797 100644 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java @@ -5,6 +5,7 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_POSITION; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; import java.util.Set; @@ -17,6 +18,8 @@ import seedu.address.model.person.Name; import seedu.address.model.person.Person; import seedu.address.model.person.Phone; +import seedu.address.model.person.Remark; +import seedu.address.model.person.position.Position; import seedu.address.model.tag.Tag; /** @@ -31,9 +34,11 @@ public class AddCommandParser implements Parser { */ public AddCommand parse(String args) throws ParseException { ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_POSITION, + PREFIX_ADDRESS, PREFIX_TAG); - if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_POSITION, PREFIX_PHONE, PREFIX_EMAIL, + PREFIX_TAG) || !argMultimap.getPreamble().isEmpty()) { throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); } @@ -42,9 +47,10 @@ public AddCommand parse(String args) throws ParseException { Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()); Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()); Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()); + Remark remark = new Remark(""); Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); - - Person person = new Person(name, phone, email, address, tagList); + Position position = ParserUtil.parsePosition(argMultimap.getValue(PREFIX_POSITION).get(), tagList.toString()); + Person person = new Person(name, phone, email, position, address, remark, tagList); return new AddCommand(person); } diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java index 1e466792b46..3a4e983ab95 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java @@ -6,15 +6,26 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import seedu.address.logic.commands.AddAssignmentsCommand; import seedu.address.logic.commands.AddCommand; +import seedu.address.logic.commands.AttendanceCommand; +import seedu.address.logic.commands.AvailabilityCommand; import seedu.address.logic.commands.ClearCommand; import seedu.address.logic.commands.Command; import seedu.address.logic.commands.DeleteCommand; import seedu.address.logic.commands.EditCommand; import seedu.address.logic.commands.ExitCommand; +import seedu.address.logic.commands.FilterCommand; import seedu.address.logic.commands.FindCommand; +import seedu.address.logic.commands.GradeCommand; import seedu.address.logic.commands.HelpCommand; import seedu.address.logic.commands.ListCommand; +import seedu.address.logic.commands.NewBookCommand; +import seedu.address.logic.commands.RemarkCommand; +import seedu.address.logic.commands.RenameCommand; +import seedu.address.logic.commands.RolesCommand; +import seedu.address.logic.commands.ShowCommand; +import seedu.address.logic.commands.SwapCommand; import seedu.address.logic.parser.exceptions.ParseException; /** @@ -59,15 +70,47 @@ public Command parseCommand(String userInput) throws ParseException { case FindCommand.COMMAND_WORD: return new FindCommandParser().parse(arguments); + case FilterCommand.COMMAND_WORD: + return new FilterCommandParser().parse(arguments); + case ListCommand.COMMAND_WORD: return new ListCommand(); + case NewBookCommand.COMMAND_WORD: + return new NewBookCommand(); + + case SwapCommand.COMMAND_WORD: + return new SwapCommand(); + case ExitCommand.COMMAND_WORD: return new ExitCommand(); case HelpCommand.COMMAND_WORD: return new HelpCommand(); + case RemarkCommand.COMMAND_WORD: + return new RemarkCommandParser().parse(arguments); + + case AvailabilityCommand.COMMAND_WORD: + return new AvailabilityCommandParser().parse(arguments); + + case AttendanceCommand.COMMAND_WORD: + return new AttendanceCommandParser().parse(arguments); + + case GradeCommand.COMMAND_WORD: + return new GradeCommandParser().parse(arguments); + + case RolesCommand.COMMAND_WORD: + return new RolesCommandParser().parse(arguments); + + case RenameCommand.COMMAND_WORD: + return new RenameCommandParser().parse(arguments); + + case ShowCommand.COMMAND_WORD: + return new ShowCommandParser().parse(arguments); + + case AddAssignmentsCommand.COMMAND_WORD: + return new AddAssignmentsCommandParser().parse(arguments); default: throw new ParseException(MESSAGE_UNKNOWN_COMMAND); } diff --git a/src/main/java/seedu/address/logic/parser/AttendanceCommandParser.java b/src/main/java/seedu/address/logic/parser/AttendanceCommandParser.java new file mode 100644 index 00000000000..e15e34e6037 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/AttendanceCommandParser.java @@ -0,0 +1,47 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ATTENDANCE; + +import java.util.NoSuchElementException; + +import seedu.address.commons.core.index.Index; +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.logic.commands.AttendanceCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.position.Student; + +/** + * Parses input arguments and creates a new AttendanceCommand object + */ +public class AttendanceCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the {@code AttendanceCommand} + * and returns a {@code AttendanceCommand} object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AttendanceCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_ATTENDANCE); + + Index index; + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (IllegalValueException ive) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AttendanceCommand.MESSAGE_USAGE), ive); + } catch (NoSuchElementException e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AttendanceCommand.MESSAGE_USAGE), e); + } + + String attendance = argMultimap.getValue(PREFIX_ATTENDANCE).orElse(""); + if (!Student.isValidAttendance(attendance)) { + throw new ParseException(Student.ATTENDANCE_CONSTRAINTS); + } + + return new AttendanceCommand(index, attendance); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/AvailabilityCommandParser.java b/src/main/java/seedu/address/logic/parser/AvailabilityCommandParser.java new file mode 100644 index 00000000000..2059ec0b9de --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/AvailabilityCommandParser.java @@ -0,0 +1,46 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_AVAILABILITY; + +import java.util.NoSuchElementException; + +import seedu.address.commons.core.index.Index; +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.logic.commands.AvailabilityCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.position.TeachingAssistant; + +/** + * Parses input arguments and creates a new AvailabilityCommand object + */ +public class AvailabilityCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the {@code AvailabilityCommand} + * and returns a {@code AvailabilityCommand} object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AvailabilityCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_AVAILABILITY); + + Index index; + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (IllegalValueException ive) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AvailabilityCommand.MESSAGE_USAGE), ive); + } catch (NoSuchElementException e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AvailabilityCommand.MESSAGE_USAGE), e); + } + + String availability = argMultimap.getValue(PREFIX_AVAILABILITY).orElse(""); + if (!TeachingAssistant.isValidAvailability(availability)) { + throw new ParseException(TeachingAssistant.MESSAGE_CONSTRAINTS); + } + + return new AvailabilityCommand(index, availability); + } +} diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java index 75b1a9bf119..abcf4b750f3 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java @@ -9,7 +9,15 @@ public class CliSyntax { 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/"); + public static final Prefix PREFIX_POSITION = new Prefix("pos/"); + public static final Prefix PREFIX_AVAILABILITY = new Prefix("avail/"); + public static final Prefix PREFIX_ATTENDANCE = new Prefix("attendance/"); + public static final Prefix PREFIX_GRADE = new Prefix("grade/"); + public static final Prefix PREFIX_ROLES = new Prefix("roles/"); public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); public static final Prefix PREFIX_TAG = new Prefix("t/"); + public static final Prefix PREFIX_REMARK = new Prefix("r/"); + public static final Prefix PREFIX_ASSIGNMENT = new Prefix("assignment/"); + public static final Prefix PREFIX_ASSIGNMENTS = new Prefix("assignments/"); } diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java index 845644b7dea..67977876a4e 100644 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/EditCommandParser.java @@ -6,18 +6,11 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; - -import java.util.Collection; -import java.util.Collections; -import java.util.Optional; -import java.util.Set; import seedu.address.commons.core.index.Index; import seedu.address.logic.commands.EditCommand; import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.tag.Tag; /** * Parses input arguments and creates a new EditCommand object @@ -32,7 +25,7 @@ public class EditCommandParser implements Parser { public EditCommand parse(String args) throws ParseException { requireNonNull(args); ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS); Index index; @@ -52,10 +45,10 @@ public EditCommand parse(String args) throws ParseException { if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get())); } + if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); } - parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags); if (!editPersonDescriptor.isAnyFieldEdited()) { throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); @@ -64,19 +57,5 @@ public EditCommand parse(String args) throws ParseException { return new EditCommand(index, editPersonDescriptor); } - /** - * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty. - * If {@code tags} contain only one element which is an empty string, it will be parsed into a - * {@code Set} containing zero tags. - */ - private Optional> parseTagsForEdit(Collection tags) throws ParseException { - assert tags != null; - - if (tags.isEmpty()) { - return Optional.empty(); - } - Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; - return Optional.of(ParserUtil.parseTags(tagSet)); - } } diff --git a/src/main/java/seedu/address/logic/parser/FilterCommandParser.java b/src/main/java/seedu/address/logic/parser/FilterCommandParser.java new file mode 100644 index 00000000000..07c620c18a4 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/FilterCommandParser.java @@ -0,0 +1,29 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.logic.commands.FilterCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.tag.TagContainsKeywordPredicate; + +/** + * Parses input arguments and creates a new FilterCommand object + */ +public class FilterCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FilterCommand + * and returns a FilterCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FilterCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterCommand.MESSAGE_USAGE)); + } + + return new FilterCommand(new TagContainsKeywordPredicate(trimmedArgs)); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/GradeCommandParser.java b/src/main/java/seedu/address/logic/parser/GradeCommandParser.java new file mode 100644 index 00000000000..7e6cffe8b7d --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/GradeCommandParser.java @@ -0,0 +1,62 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.commands.GradeCommand.EMPTY_FIELD; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ASSIGNMENT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_GRADE; + +import java.util.NoSuchElementException; + +import seedu.address.commons.core.index.Index; +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.logic.commands.GradeCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Assignment; + +/** + * Parses input arguments and creates a new GradeCommand object + */ +public class GradeCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the {@code GradeCommand} + * and returns a {@code GradeCommand} object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public GradeCommand parse(String args) throws ParseException { + requireNonNull(args); + if (args.trim().split(" ").length == 1 && !args.trim().equals("")) { + throw new ParseException(EMPTY_FIELD); + } + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_ASSIGNMENT, PREFIX_GRADE); + Index indexOfStudent; + try { + indexOfStudent = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (IllegalValueException ive) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + GradeCommand.MESSAGE_USAGE), ive); + } catch (NoSuchElementException e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + GradeCommand.MESSAGE_USAGE), e); + } + + Index indexOfAssignment; + try { + indexOfAssignment = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_ASSIGNMENT).get()); + } catch (IllegalValueException ive) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + GradeCommand.MESSAGE_USAGE), ive); + } catch (NoSuchElementException e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + GradeCommand.MESSAGE_USAGE), e); + } + + String grade = argMultimap.getValue(PREFIX_GRADE).orElse(""); + if (!Assignment.isValidInputGrade(grade)) { + throw new ParseException(Assignment.GRADE_CONSTRAINTS); + } + + return new GradeCommand(indexOfStudent, indexOfAssignment, grade); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java index b117acb9c55..b5ddf311364 100644 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java @@ -13,6 +13,7 @@ import seedu.address.model.person.Email; import seedu.address.model.person.Name; import seedu.address.model.person.Phone; +import seedu.address.model.person.position.Position; import seedu.address.model.tag.Tag; /** @@ -95,6 +96,28 @@ public static Email parseEmail(String email) throws ParseException { return new Email(trimmedEmail); } + /** + * Parses a {@code String positionLine} into an {@code Position}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code positionLine} is invalid. + */ + public static Position parsePosition(String position, String tags) throws ParseException { + requireNonNull(position); + requireNonNull(tags); + + String relativePath = "./data/"; + String txt = ".txt"; + String tag = ""; + String[] handleTag = tags.split("-"); + String module = handleTag[0].replace("[", ""); + String filePath = relativePath + module + txt; + if (!Position.isValidPosition(position)) { + throw new ParseException(Position.MESSAGE_CONSTRAINTS); + } + return Position.buildPosition(position, filePath); + } + /** * Parses a {@code String tag} into a {@code Tag}. * Leading and trailing whitespaces will be trimmed. @@ -104,9 +127,11 @@ public static Email parseEmail(String email) throws ParseException { public static Tag parseTag(String tag) throws ParseException { requireNonNull(tag); String trimmedTag = tag.trim(); + String module = trimmedTag.split("-")[0]; if (!Tag.isValidTagName(trimmedTag)) { throw new ParseException(Tag.MESSAGE_CONSTRAINTS); } + return new Tag(trimmedTag); } @@ -121,4 +146,20 @@ public static Set parseTags(Collection tags) throws ParseException } return tagSet; } + + /** + * Parses and checks the validity of file name. + */ + public static String parseRename(String newName) throws ParseException { + requireNonNull(newName); + String regexPattern = "^[A-za-z0-9-_]{1,255}$"; + String messageConstraints = "File name is limited to alphanumeric characters, '-' and '_'"; + String trimmedTag = newName.trim(); + String lowerCase = trimmedTag.toLowerCase(); + if (!lowerCase.matches(regexPattern)) { + throw new ParseException(messageConstraints); + } + return lowerCase; + } + } diff --git a/src/main/java/seedu/address/logic/parser/RemarkCommandParser.java b/src/main/java/seedu/address/logic/parser/RemarkCommandParser.java new file mode 100644 index 00000000000..793f36fd856 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/RemarkCommandParser.java @@ -0,0 +1,42 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_REMARK; + +import java.util.NoSuchElementException; + +import seedu.address.commons.core.index.Index; +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.logic.commands.RemarkCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Remark; + +/** + * Parses input arguments and creates a new RemarkCommand object + */ +public class RemarkCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the {@code RemarkCommand} + * and returns a {@code RemarkCommand} object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public RemarkCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_REMARK); + + Index index; + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (IllegalValueException ive) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, RemarkCommand.MESSAGE_USAGE), ive); + } catch (NoSuchElementException e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + RemarkCommand.MESSAGE_USAGE), e); + } + + String remark = argMultimap.getValue(PREFIX_REMARK).orElse(""); + + return new RemarkCommand(index, new Remark(remark)); + } +} diff --git a/src/main/java/seedu/address/logic/parser/RenameCommandParser.java b/src/main/java/seedu/address/logic/parser/RenameCommandParser.java new file mode 100644 index 00000000000..d160bed7b3f --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/RenameCommandParser.java @@ -0,0 +1,27 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.logic.commands.RenameCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new RenameCommand object. + */ +public class RenameCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the RenameCommand + * and returns a RenameCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public RenameCommand parse(String args) throws ParseException { + try { + String name = ParserUtil.parseRename(args); + return new RenameCommand(name); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, RenameCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/RolesCommandParser.java b/src/main/java/seedu/address/logic/parser/RolesCommandParser.java new file mode 100644 index 00000000000..1744dbabe57 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/RolesCommandParser.java @@ -0,0 +1,51 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLES; + +import java.util.NoSuchElementException; + +import seedu.address.commons.core.index.Index; +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.logic.commands.RolesCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.position.Professor; + +/** + * Parses input arguments and creates a new RolesCommand object + */ +public class RolesCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the {@code RolesCommand} + * and returns a {@code RolesCommand} object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public RolesCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_ROLES); + + Index index; + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (IllegalValueException ive) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + RolesCommand.MESSAGE_USAGE), ive); + } catch (NoSuchElementException e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + RolesCommand.MESSAGE_USAGE), e); + } + + + String roles = argMultimap.getValue(PREFIX_ROLES).orElse(""); + String[] rolesArray = roles.split(", "); + for (String role: rolesArray) { + + if (!Professor.isValidRole(role)) { + throw new ParseException(Professor.MESSAGE_CONSTRAINTS); + } + } + + return new RolesCommand(index, roles); + } +} diff --git a/src/main/java/seedu/address/logic/parser/ShowCommandParser.java b/src/main/java/seedu/address/logic/parser/ShowCommandParser.java new file mode 100644 index 00000000000..35e60a045ac --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ShowCommandParser.java @@ -0,0 +1,28 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.ShowCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new ShowCommand object. + */ +public class ShowCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ShowCommand + * and returns a ShowCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ShowCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new ShowCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ShowCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java index 1a943a0781a..594b7ccd7e0 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/address/model/AddressBook.java @@ -66,6 +66,21 @@ public boolean hasPerson(Person person) { return persons.contains(person); } + /** + * Returns true if a person with the same email as {@code person} exists in the address book. + */ + public boolean hasEmail(Person person) { + requireNonNull(person); + return persons.containsEmail(person); + } + + /** + * Returns true if a person with the same phone as {@code person} exists in the address book. + */ + public boolean hasPhone(Person person) { + requireNonNull(person); + return persons.containsPhone(person); + } /** * Adds a person to the address book. * The person must not already exist in the address book. diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index d54df471c1f..ce3814d4169 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -1,5 +1,6 @@ package seedu.address.model; +import java.io.IOException; import java.nio.file.Path; import java.util.function.Predicate; @@ -44,19 +45,50 @@ public interface Model { */ void setAddressBookFilePath(Path addressBookFilePath); + /** Returns the AddressBook */ + ReadOnlyAddressBook getAddressBook(); + /** * Replaces address book data with the data in {@code addressBook}. */ void setAddressBook(ReadOnlyAddressBook addressBook); - /** Returns the AddressBook */ - ReadOnlyAddressBook getAddressBook(); + void setStoredIndex(int index); + + /** Returns all address book's paths*/ + Path[] getAllAddressBookFilePath(); + + /** + * Replaces all address book data with the data in {@code addressBook}. + */ + void setAllAddressBookFilePath(Path[] updatedPaths); + + /** Returns the next AddressBook */ + Path getNextAddressBookPath(); + + /** Renames the current AddressBook */ + void renameAddressBook(String newName) throws IOException; + + /** + * Creates a new Address Book + */ + boolean addAddressBook(); /** * Returns true if a person with the same identity as {@code person} exists in the address book. */ boolean hasPerson(Person person); + /** + * Returns true if a person with the same email as {@code person} exists in the address book. + */ + boolean hasEmail(Person person); + + /** + * Returns true if a person with the same phone as {@code person} exists in the address book. + */ + boolean hasPhone(Person person); + /** * Deletes the given person. * The person must exist in the address book. diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 86c1df298d7..ae70755ce05 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -3,6 +3,7 @@ import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import java.io.IOException; import java.nio.file.Path; import java.util.function.Predicate; import java.util.logging.Logger; @@ -29,7 +30,7 @@ public class ModelManager implements Model { public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) { requireAllNonNull(addressBook, userPrefs); - logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs); + logger.fine("Initializing with TAB: " + addressBook + " and user prefs " + userPrefs); this.addressBook = new AddressBook(addressBook); this.userPrefs = new UserPrefs(userPrefs); @@ -75,6 +76,40 @@ public void setAddressBookFilePath(Path addressBookFilePath) { userPrefs.setAddressBookFilePath(addressBookFilePath); } + @Override + public void setStoredIndex(int index) { + assert(index >= 0); + userPrefs.setStoredIndex(index); + } + + @Override + public Path[] getAllAddressBookFilePath() { + return userPrefs.getAllAddressBookFilePath(); + } + + @Override + public void setAllAddressBookFilePath(Path[] updatedPaths) { + userPrefs.setAllAddressBookFilePath(updatedPaths); + } + @Override + public boolean addAddressBook() { + if (!userPrefs.addAddressBook()) { + logger.warning("Maximum amount of TAB created"); + return false; + } + return true; + } + + @Override + public void renameAddressBook(String name) throws IOException { + userPrefs.renameFile(name); + } + + @Override + public Path getNextAddressBookPath() { + return userPrefs.getNextAddressBookPath(); + } + //=========== AddressBook ================================================================================ @Override @@ -93,6 +128,18 @@ public boolean hasPerson(Person person) { return addressBook.hasPerson(person); } + @Override + public boolean hasPhone(Person person) { + requireNonNull(person); + return addressBook.hasPhone(person); + } + + @Override + public boolean hasEmail(Person person) { + requireNonNull(person); + return addressBook.hasEmail(person); + } + @Override public void deletePerson(Person target) { addressBook.removePerson(target); diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java index befd58a4c73..a5a9e9e4eb5 100644 --- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java +++ b/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java @@ -13,4 +13,8 @@ public interface ReadOnlyUserPrefs { Path getAddressBookFilePath(); + int getStoredIndex(); + + Path[] getAllAddressBookFilePath(); + } diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/address/model/UserPrefs.java index 25a5fd6eab9..52fff5bb2af 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/address/model/UserPrefs.java @@ -2,8 +2,12 @@ import static java.util.Objects.requireNonNull; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; import seedu.address.commons.core.GuiSettings; @@ -13,13 +17,19 @@ */ public class UserPrefs implements ReadOnlyUserPrefs { + private static final int MAX_ADDRESS_BOOK_LIMIT = 5; + private static final String DEFAULT_ADDRESS_BOOK_NAME = "addressbook"; private GuiSettings guiSettings = new GuiSettings(); - private Path addressBookFilePath = Paths.get("data" , "addressbook.json"); + private int addressBookIndex = 0; + private Path addressBookFilePath = Paths.get("data" , DEFAULT_ADDRESS_BOOK_NAME + ".json"); + private ArrayList allAddressBookFilePath = new ArrayList(); /** * Creates a {@code UserPrefs} with default values. */ - public UserPrefs() {} + public UserPrefs() { + allAddressBookFilePath.add(addressBookFilePath); + } /** * Creates a {@code UserPrefs} with the prefs in {@code userPrefs}. @@ -36,6 +46,8 @@ public void resetData(ReadOnlyUserPrefs newUserPrefs) { requireNonNull(newUserPrefs); setGuiSettings(newUserPrefs.getGuiSettings()); setAddressBookFilePath(newUserPrefs.getAddressBookFilePath()); + setAllAddressBookFilePath(newUserPrefs.getAllAddressBookFilePath()); + setStoredIndex(newUserPrefs.getStoredIndex() % newUserPrefs.getAllAddressBookFilePath().length); } public GuiSettings getGuiSettings() { @@ -56,6 +68,65 @@ public void setAddressBookFilePath(Path addressBookFilePath) { this.addressBookFilePath = addressBookFilePath; } + public Path[] getAllAddressBookFilePath() { + return allAddressBookFilePath.toArray(new Path[allAddressBookFilePath.size()]); + } + + public void setAllAddressBookFilePath(Path[] allAddressBookFilePath) { + requireNonNull(allAddressBookFilePath); + this.allAddressBookFilePath = new ArrayList<>(List.of(allAddressBookFilePath)); + } + + /** + * Adds a brand new Address Book to the data directory + * + * @return boolean value indicating {@code true} = Success or {@code false} = Limit reached + */ + public boolean addAddressBook() { + if (allAddressBookFilePath.size() == MAX_ADDRESS_BOOK_LIMIT) { + return false; + } else { + String newBookName; + if (allAddressBookFilePath.size() != 0) { + newBookName = DEFAULT_ADDRESS_BOOK_NAME + System.currentTimeMillis() + ".json"; + } else { + newBookName = DEFAULT_ADDRESS_BOOK_NAME + ".json"; + } + Path newBook = Paths.get("data" , newBookName); + allAddressBookFilePath.add(newBook); + return true; + } + } + + /** + * Renames the current address book + */ + public void renameFile(String name) throws IOException { + Path newAddressBookFilePath = Paths.get("data" , name + ".json"); + Files.move(this.addressBookFilePath, newAddressBookFilePath); + this.allAddressBookFilePath.set(addressBookIndex, newAddressBookFilePath); + } + + + public Path getNextAddressBookPath() { + incrementIndex(); + Path nextAddressBook = allAddressBookFilePath.get(addressBookIndex); + setAddressBookFilePath(nextAddressBook); + return nextAddressBook; + } + + public int getStoredIndex() { + return addressBookIndex; + } + + public void setStoredIndex(int index) { + addressBookIndex = index; + } + + private void incrementIndex() { + addressBookIndex = (addressBookIndex + 1) % allAddressBookFilePath.size(); + } + @Override public boolean equals(Object other) { if (other == this) { diff --git a/src/main/java/seedu/address/model/person/Assignment.java b/src/main/java/seedu/address/model/person/Assignment.java new file mode 100644 index 00000000000..ed5a1d1cacc --- /dev/null +++ b/src/main/java/seedu/address/model/person/Assignment.java @@ -0,0 +1,152 @@ +package seedu.address.model.person; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents an Assignment object that encapsulates the + * Assignment's name, grade and weightage + */ +public class Assignment { + + public static final String GRADE_VALIDATION_REGEX = "\\d{1,3}" + "/" + "\\d{1,3}"; + + public static final String WEIGHTAGE_VALIDATION_REGEX = "^(100|[1-9]?[0-9])$"; + + public static final String GRADE_CONSTRAINTS = + "Grade should be in the format [integer (0-100)]/[integer (0-100)], where the first number is smaller than " + + "or equal to the second number."; + + public static final String WEIGHTAGE_CONSTRAINTS = + "Weightage should be in terms of percentages, i.e. 0 - 100"; + + private final String name; + private String grade; + private final String weightage; + private boolean isGradeUpdated; + + /** + * Constructs an {@code Assignment}. + * + * @param name An assignment name. + * @param weightage A valid weightage. + */ + + public Assignment(String name, String weightage) { + requireNonNull(name); + requireNonNull(weightage); + checkArgument(isValidWeightage(weightage), WEIGHTAGE_CONSTRAINTS); + this.name = name; + this.grade = "0/0"; + this.weightage = weightage; + this.isGradeUpdated = false; + } + + /** + * Constructs an {@code Assignment}. + * + * @param name An assignment name. + * @param grade A valid grade. + * @param weightage A valid weightage. + */ + public Assignment(String name, String grade, String weightage) { + requireNonNull(name); + requireNonNull(grade); + requireNonNull(weightage); + checkArgument(isValidRetrievedGrade(grade), GRADE_CONSTRAINTS); + checkArgument(isValidWeightage(weightage), WEIGHTAGE_CONSTRAINTS); + this.name = name; + this.grade = grade; + this.weightage = weightage; + this.isGradeUpdated = false; + } + /** + * Returns if a given string is a valid grade. + */ + public static boolean isValidInputGrade(String test) { + if (!test.matches(GRADE_VALIDATION_REGEX)) { + return false; + } else { + String[] split = test.split("/"); + Integer firstNumber = Integer.parseInt(split[0]); + Integer secondNumber = Integer.parseInt(split[1]); + return firstNumber <= secondNumber && secondNumber <= 100; + } + } + + /** + * Returns if a given string is a valid grade. + */ + public static boolean isValidRetrievedGrade(String test) { + if (!test.matches(GRADE_VALIDATION_REGEX)) { + return false; + } else { + String[] split = test.split("/"); + Integer firstNumber = Integer.parseInt(split[0]); + Integer secondNumber = Integer.parseInt(split[1]); + return firstNumber <= secondNumber && secondNumber <= 100; + } + } + + /** + * Returns if a given string is a valid weightage. + */ + public static boolean isValidWeightage(String test) { + return test.matches(WEIGHTAGE_VALIDATION_REGEX); + } + + public void setGrade(String grade) { + this.grade = grade; + this.isGradeUpdated = true; + } + + public int getWeightage() { + return Integer.parseInt(weightage); + } + + public float getGradePercentage() { + float gradeAchieved = (float) Integer.parseInt(grade.split("/")[0]); + float fullMark = (float) Integer.parseInt(grade.split("/")[1]); + if (fullMark == 0.0) { + return 0; + } + float gradePercentage = gradeAchieved / fullMark; + return gradePercentage; + } + + public String getAssignmentName() { + return name; + } + + public Integer getScore() { + return Integer.valueOf(grade.split("/")[0]); + } + + public Integer getMaximumScore() { + return Integer.valueOf(grade.split("/")[1]); + } + + public boolean getIsGradeUpdated() { + return isGradeUpdated; + } + + @Override + public String toString() { + return "(" + name + " Score: " + grade + " Weightage: " + weightage + "%" + ")"; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Assignment // instanceof handles nulls + && name.equals(((Assignment) other).name)); // state check + } + + @Override + public int hashCode() { + return toString().hashCode(); + } + + + +} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java index 8ff1d83fe89..9a25e316622 100644 --- a/src/main/java/seedu/address/model/person/Person.java +++ b/src/main/java/seedu/address/model/person/Person.java @@ -7,6 +7,8 @@ import java.util.Objects; import java.util.Set; +import seedu.address.model.person.position.Position; +import seedu.address.model.person.position.Student; import seedu.address.model.tag.Tag; /** @@ -19,21 +21,31 @@ public class Person { private final Name name; private final Phone phone; private final Email email; + private final Position position; // Data fields private final Address address; + + private final Remark remark; private final Set tags = new HashSet<>(); /** * Every field must be present and not null. */ - public Person(Name name, Phone phone, Email email, Address address, Set tags) { - requireAllNonNull(name, phone, email, address, tags); + public Person(Name name, Phone phone, Email email, Position position, Address address, + Remark remark, Set tags) { + requireAllNonNull(name, phone, email, address, tags, remark); this.name = name; this.phone = phone; this.email = email; + this.position = position; this.address = address; - this.tags.addAll(tags); + this.remark = remark; + if (position instanceof Student && tags.size() != 0) { + this.tags.add(tags.iterator().next()); + } else { + this.tags.addAll(tags); + } } public Name getName() { @@ -48,10 +60,21 @@ public Email getEmail() { return email; } + public Position getPosition() { + return position; + } + public String getDetails() { + return getPosition().getDetails(); + } + public Address getAddress() { return address; } + public Remark getRemark() { + return remark; + } + /** * Returns an immutable tag set, which throws {@code UnsupportedOperationException} * if modification is attempted. @@ -73,6 +96,20 @@ public boolean isSamePerson(Person otherPerson) { && otherPerson.getName().equals(getName()); } + /** + * Returns true if both persons have the same phone. + */ + public boolean hasSamePhone(Person otherPerson) { + return phone.equals(otherPerson.getPhone()); + } + + /** + * Returns true if both persons have the same email. + */ + public boolean hasSameEmail(Person otherPerson) { + return email.equals(otherPerson.getEmail()); + } + /** * Returns true if both persons have the same identity and data fields. * This defines a stronger notion of equality between two persons. @@ -91,6 +128,7 @@ public boolean equals(Object other) { return otherPerson.getName().equals(getName()) && otherPerson.getPhone().equals(getPhone()) && otherPerson.getEmail().equals(getEmail()) + && otherPerson.getPosition().equals(getPosition()) && otherPerson.getAddress().equals(getAddress()) && otherPerson.getTags().equals(getTags()); } @@ -98,7 +136,7 @@ public boolean equals(Object other) { @Override public int hashCode() { // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); + return Objects.hash(name, phone, email, position, address, tags); } @Override @@ -109,8 +147,12 @@ public String toString() { .append(getPhone()) .append("; Email: ") .append(getEmail()) + .append("; Position: ") + .append(getPosition()) .append("; Address: ") - .append(getAddress()); + .append(getAddress()) + .append(" Remark: ") + .append(getRemark()); Set tags = getTags(); if (!tags.isEmpty()) { diff --git a/src/main/java/seedu/address/model/person/Remark.java b/src/main/java/seedu/address/model/person/Remark.java new file mode 100644 index 00000000000..2451bd937fb --- /dev/null +++ b/src/main/java/seedu/address/model/person/Remark.java @@ -0,0 +1,40 @@ +package seedu.address.model.person; + +import static java.util.Objects.requireNonNull; + +/** + * Represents a Person's remark in the address book. + * Guarantees: immutable; is always valid + */ +public class Remark { + + public final String value; + + /** + * Constructs an {@code Address}. + * + * @param remark A valid address. + */ + public Remark(String remark) { + requireNonNull(remark); + value = remark; + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Remark // instanceof handles nulls + && value.equals(((Remark) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java index 0fee4fe57e6..cd9c99de694 100644 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ b/src/main/java/seedu/address/model/person/UniquePersonList.java @@ -36,6 +36,22 @@ public boolean contains(Person toCheck) { return internalList.stream().anyMatch(toCheck::isSamePerson); } + /** + * Returns true if the list contains an equivalent email as the given argument. + */ + public boolean containsEmail(Person toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::hasSameEmail); + } + + /** + * Returns true if the list contains an equivalent phone as the given argument. + */ + public boolean containsPhone(Person toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::hasSamePhone); + } + /** * Adds a person to the list. * The person must not already exist in the list. diff --git a/src/main/java/seedu/address/model/person/position/Position.java b/src/main/java/seedu/address/model/person/position/Position.java new file mode 100644 index 00000000000..c159b51bef3 --- /dev/null +++ b/src/main/java/seedu/address/model/person/position/Position.java @@ -0,0 +1,76 @@ +package seedu.address.model.person.position; + +import static java.util.Objects.requireNonNull; + +import java.util.Set; + +import seedu.address.model.tag.Tag; + + +/** + * Represents a Person's position in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidPosition(String)} + */ +public abstract class Position { + + enum Positions { + STUDENT, + TA, + PROFESSOR + } + + public static final String MESSAGE_CONSTRAINTS = + "Positions can only be one of the following: Student, TA, Professor (non case-sensitive)."; + + public static final String DETAILS_MESSAGE_CONSTRAINTS = + "Details cannot be empty"; + public final String value; + /** + * Constructor for Position + */ + public Position(String value) { + requireNonNull(value); + this.value = value; + } + + /** + * Returns true if a given string is a valid position. + */ + public static boolean isValidPosition(String test) { + for (Positions position : Positions.values()) { + if (position.name().equalsIgnoreCase(test)) { + return true; + } + } + return false; + } + + /** + * Class method that returns the type of position given by the value. + * @param position A valid position. + * @return The desired Position descendant + */ + public static Position buildPosition(String position, String filePath) { + if (Positions.STUDENT.name().equalsIgnoreCase(position)) { + return new Student(filePath); + } else if (Positions.TA.name().equalsIgnoreCase(position)) { + return new TeachingAssistant(); + } else if (Positions.PROFESSOR.name().equalsIgnoreCase(position)) { + return new Professor(); + } else { + return null; + } + } + + public abstract String toShow(); + + public abstract String toString(); + + public abstract boolean equals(Object other); + + public abstract int hashcode(); + + public abstract String getDetails(); + public abstract void setDetails(String details); + public abstract void setFilePath(Set modelTags); +} diff --git a/src/main/java/seedu/address/model/person/position/Professor.java b/src/main/java/seedu/address/model/person/position/Professor.java new file mode 100644 index 00000000000..181fbbcc469 --- /dev/null +++ b/src/main/java/seedu/address/model/person/position/Professor.java @@ -0,0 +1,99 @@ +package seedu.address.model.person.position; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +import java.util.Set; + +import seedu.address.model.tag.Tag; + +/** + * Represents the Professor position in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidPosition(String)} + */ +public class Professor extends Position { + /** + * Roles of the Professor. + */ + public enum Role { + COORDINATOR, + LECTURER, + TUTOR, + ADVISOR, + UNASSIGNED; + } + + public static final String MESSAGE_CONSTRAINTS = + "Role can only be Unassigned, Coordinator, Lecturer, Tutor, and/or Advisor (non case-sensitive)"; + + private String role; + + /** + * Creates a professor. Its roles at initiation are empty. + */ + public Professor() { + super("Professor"); + role = "Unassigned"; + } + + /** + * Creates a professor with the given details. + * @param role of the professor + */ + public Professor(String role) { + super("Professor"); + requireNonNull(role); + checkArgument(isValidRole(role), MESSAGE_CONSTRAINTS); + + this.role = role; + } + + @Override + public void setDetails(String role) { + requireNonNull(role); + this.role = role; + } + + /** + * Returns true if a given string is a valid role. + */ + public static boolean isValidRole(String test) { + requireNonNull(test); + for (Role role: Role.values()) { + if (role.name().equalsIgnoreCase(test)) { + return true; + } + } + return false; + } + + @Override + public String toShow() { + return "Role: " + role; + } + + @Override + public String toString() { + return "Professor: " + role; + } + + @Override + public boolean equals(Object other) { + return true; + } + + @Override + public int hashcode() { + return 0; + } + + @Override + public String getDetails() { + return role; + } + + @Override + public void setFilePath(Set modelTags) { + + } +} diff --git a/src/main/java/seedu/address/model/person/position/Student.java b/src/main/java/seedu/address/model/person/position/Student.java new file mode 100644 index 00000000000..9450e488af1 --- /dev/null +++ b/src/main/java/seedu/address/model/person/position/Student.java @@ -0,0 +1,400 @@ +package seedu.address.model.person.position; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.address.model.person.Assignment.WEIGHTAGE_CONSTRAINTS; +import static seedu.address.model.person.Assignment.WEIGHTAGE_VALIDATION_REGEX; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Scanner; +import java.util.Set; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.person.Assignment; +import seedu.address.model.tag.Tag; + +/** + * Represents the Student position in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidPosition(String)} + */ +public class Student extends Position { + + public static final String ATTENDANCE_CONSTRAINTS = + "Attendance should be in the format [integer (0-100)]/[integer (0-100)], where the first number is " + + "smaller than or equal to the second number."; + public static final String ATTENDANCE_VALIDATION_REGEX = "\\d{1,3}" + "/" + "\\d{1,3}"; + public static final String MESSAGE_ASSIGNMENT_INVALID = "The index of the assignment is invalid."; + public static final String ASSIGNMENT_CONSTRAINTS = + "Incorrect Assignment inputs. Please make sure your input is in the right format " + + "\n i.e. assignments assignments/ Assignment 1 w/20, Assignments 2 w/20, Finals w/60"; + public static final String ASSIGNMENT_DUPLICATE = + "Please ensure you do not have multiple assignments with the same name."; + public static final String ASSIGNMENT_INVALID_SUM_OF_WEIGHTAGE = + "Please ensure that your weightages add up to 100."; + public static final String TOO_MANY_ASSIGNMENT = + "Please ensure you only have a maximum of 10 assignments."; + + private String attendance; + private String overallGrade; + private ArrayList assignmentsList; + + private String filePath; + + + /** + * Creates a student and initialises their attendance to 0/0. + */ + public Student(String filePath) { + super("Student"); + requireNonNull(filePath); + this.attendance = "0/0"; + this.overallGrade = "0/0"; + this.assignmentsList = new ArrayList<>(); + this.filePath = filePath; + File file = new File(filePath); + String dir = System.getProperty("user.dir"); + Path path = Paths.get(dir, filePath); + + + + if (Files.exists(path)) { + try { + Scanner scanner = new Scanner(file); + if (scanner.hasNext()) { + String assignments = scanner.nextLine(); + setAssignments(assignments); + } + } catch (IOException e) { + System.out.println("An error occurred."); + e.printStackTrace(); + } + } + } + + /** + * Creates a student with the given details. + * @param attendance of the student + * @param overallGrade of the student + * @param assignmentsList Assignments that have been assigned to the student + */ + public Student(String attendance, String overallGrade, ArrayList assignmentsList, String filePath) { + super("Student"); + requireNonNull(attendance); + requireNonNull(overallGrade); + requireNonNull(assignmentsList); + requireNonNull(filePath); + String[] array = attendance.split("/"); + array[0] = array[0].replaceFirst("^0+(?!$)", ""); + array[1] = array[1].replaceFirst("^0+(?!$)", ""); + this.attendance = array[0] + "/" + array[1]; + this.overallGrade = overallGrade; + this.assignmentsList = assignmentsList; + this.filePath = filePath; + } + + public String getAttendance() { + return attendance; + } + + public String getOverallGrade() { + return overallGrade; + } + + public ArrayList getAssignmentsList() { + return assignmentsList; + } + + public String getFilePath() { + return filePath; + } + + public void setFilePath(String filePath) { + requireNonNull(filePath); + this.filePath = filePath; + } + + public void setFilePath(Set modelTags) { + requireNonNull(modelTags); + String str = modelTags.toString(); + String data = "./data/"; + String txt = ".txt"; + String module = str.split("-")[0].replace("[", ""); + String filePath = data + module + txt; + this.filePath = filePath; + } + + public void setAttendance(String attendance) { + requireNonNull(attendance); + this.attendance = attendance; + } + + /** + * Returns true if a given string is a valid attendance. + */ + public static boolean isValidAttendance(String test) { + if (!test.matches(ATTENDANCE_VALIDATION_REGEX)) { + return false; + } else { + String[] split = test.split("/"); + Integer firstNumber = Integer.parseInt(split[0]); + Integer secondNumber = Integer.parseInt(split[1]); + return firstNumber <= secondNumber && secondNumber <= 100; + } + } + + /** + * Returns true if a given string is a valid string of Assignments + */ + public static boolean isValidAssignments(String test) { + Set assignmentNames = new HashSet<>(); + String[] splitStr = test.split(", "); + int len = splitStr.length; + int totalWeightage = 0; + + for (int i = 0; i < len; i++) { + if (i >= 10) { + return false; + } + + String[] nameAndWeight = splitStr[i].split(" w/"); + if (nameAndWeight.length != 2) { + return false; + } + + if (!nameAndWeight[1].matches(WEIGHTAGE_VALIDATION_REGEX)) { + return false; + } + + int weightage = Integer.parseInt(nameAndWeight[1]); + + if (weightage <= 0) { + return false; + } + + if (!assignmentNames.add(nameAndWeight[0])) { + return false; + } + + totalWeightage += weightage; + } + + return totalWeightage == 100; + } + + /** + * Returns true if a given string is a valid string of Assignments + */ + public static String findAssignmentIssue(String test) { + Set assignmentNames = new HashSet<>(); + String[] splitStr = test.split(", "); + int len = splitStr.length; + int totalWeightage = 0; + + for (int i = 0; i < len; i++) { + if (i >= 10) { + return TOO_MANY_ASSIGNMENT; + } + String[] nameAndWeight = splitStr[i].split(" w/"); + if (nameAndWeight.length != 2) { + return ASSIGNMENT_CONSTRAINTS; + } + + if (!nameAndWeight[1].matches(WEIGHTAGE_VALIDATION_REGEX)) { + return WEIGHTAGE_CONSTRAINTS; + } + + int weightage = Integer.parseInt(nameAndWeight[1]); + if (weightage <= 0) { + return WEIGHTAGE_CONSTRAINTS; + } + if (!assignmentNames.add(nameAndWeight[0])) { + return ASSIGNMENT_DUPLICATE; + } + + totalWeightage += weightage; + } + + if (!(totalWeightage == 100)) { + return ASSIGNMENT_INVALID_SUM_OF_WEIGHTAGE; + } + return null; + } + + /** + * Returns true if the given index of assignment to be edited is valid. + * @param indexOfAssignment Index of the assignment to be edited + * @return whether the given index is valid + */ + public boolean isValidAssignmentIndex(Index indexOfAssignment) { + return indexOfAssignment.getZeroBased() >= 0 + && indexOfAssignment.getZeroBased() < assignmentsList.size(); + } + + public void setOverallGrade(String overallGrade) { + requireNonNull(overallGrade); + this.overallGrade = overallGrade; + } + + /** + * Updates the overall grade of the student when the grade of + * one of their assignments in changed. + */ + public String updateOverallGrade(Index indexOfAssignment, String grade) throws CommandException { + this.setAssignmentGrade(indexOfAssignment, grade); + int totalWeightage = 0; + float totalGrade = 0; + for (Assignment assignment: assignmentsList) { + if (assignment.getIsGradeUpdated()) { + totalWeightage += assignment.getWeightage(); + totalGrade += assignment.getGradePercentage() * assignment.getWeightage(); + } + } + return String.format("%.2f/%d", totalGrade, totalWeightage); + } + + public Map getAssignmentsAndGrade() { + Map map = new LinkedHashMap<>(); + for (Assignment a: assignmentsList) { + map.put(a.getAssignmentName() + " (" + a.getWeightage() + "%)", a.getScore()); + } + return map; + } + + public Map getAssignmentsAndMaximumGrade() { + Map map = new LinkedHashMap<>(); + for (Assignment a: assignmentsList) { + map.put(a.getAssignmentName() + " (" + a.getWeightage() + "%)", a.getMaximumScore()); + } + return map; + } + + private void setAssignmentGrade(Index indexOfAssignment, String grade) throws CommandException { + checkArgument(isValidAssignmentIndex(indexOfAssignment), MESSAGE_ASSIGNMENT_INVALID); + checkArgument(Assignment.isValidInputGrade(grade), Assignment.GRADE_CONSTRAINTS); + + Assignment assignmentToEdit = assignmentsList.get(indexOfAssignment.getZeroBased()); + assignmentToEdit.setGrade(grade); + } + + @Override + public void setDetails(String details) { + String[] gradeAndAttendance = isolateDetails(details); + String overallGrade = gradeAndAttendance[0]; + String attendance = gradeAndAttendance[1]; + String assignments = gradeAndAttendance[2]; + setOverallGrade(overallGrade); + setAttendance(attendance); + setPreviousAssignments(assignments); + } + + /** + * Isolates the grade, attendance, and assignments values in the details + * @param details A valid detail. + * @return a String[] with its first element being the grade and the 2nd element the attendance + */ + public String[] isolateDetails(String details) { + String[] gradeAttendanceAssignments = new String[3]; + String[] splitDetails = details.split(", grade - "); + String[] splitDetails2 = splitDetails[0].split("attendance - "); + String[] splitDetails3 = splitDetails[1].split(" Assignments: "); + gradeAttendanceAssignments[0] = splitDetails3[0]; + gradeAttendanceAssignments[1] = splitDetails2[1]; + gradeAttendanceAssignments[2] = splitDetails3[1]; + return gradeAttendanceAssignments; + } + + public ArrayList setAssignments(String assignments) { + requireNonNull(assignments); + this.overallGrade = "0/0"; + String[] splitStr = assignments.split(", "); + int len = splitStr.length; + if (assignmentsList.size() > 0) { + assignmentsList = new ArrayList<>(); + } + for (int i = 0; i < len; i++) { + String[] weightageStr = splitStr[i].split(" w/"); + String name = weightageStr[0]; + String weightage = weightageStr[1]; + Assignment a = new Assignment(name, weightage); + addAssignments(a); + } + return assignmentsList; + } + + public void setPreviousAssignments(String assignments) { + + String trimmedAssignments = trimAssignments(assignments); + + if (trimmedAssignments.equals("")) { + return; + } + + String[] assignmentsArr = trimmedAssignments.split(", "); + int assignmentArrLen = assignmentsArr.length; + + for (int i = 0; i < assignmentArrLen; i++) { + String curr = assignmentsArr[i]; + String[] splitStr = curr.split(" Score: "); + String[] splitStr2 = splitStr[1].split(" Weightage: "); + String name = splitStr[0]; + String grade = splitStr2[0]; + String weightage = splitStr2[1].replace("%", ""); + Assignment a = new Assignment(name, grade, weightage); + addAssignments(a); + } + } + + /** + * Returns true if a given string is a valid grade. + */ + public String trimAssignments(String assignments) { + String trimmedAssignments = assignments.replace("[", "") + .replace("]", "") + .replace("(", "") + .replace(")", ""); + + return trimmedAssignments; + } + + public void addAssignments(Assignment assignment) { + this.assignmentsList.add(assignment); + } + + @Override + public String toShow() { + return "Attendance: " + attendance + "\n" + + "Grade: " + overallGrade; + } + + @Override + public String toString() { + return "Student: attendance - " + attendance + ", grade - " + overallGrade + + "\nAssignments: " + assignmentsList.toString(); + } + + @Override + public boolean equals(Object other) { + return true; + } + + @Override + public int hashcode() { + return 0; + } + + @Override + public String getDetails() { + return "attendance - " + attendance + ", grade - " + overallGrade + " Assignments: " + assignmentsList; + + } + +} diff --git a/src/main/java/seedu/address/model/person/position/TeachingAssistant.java b/src/main/java/seedu/address/model/person/position/TeachingAssistant.java new file mode 100644 index 00000000000..91ebbaf7d8d --- /dev/null +++ b/src/main/java/seedu/address/model/person/position/TeachingAssistant.java @@ -0,0 +1,96 @@ +package seedu.address.model.person.position; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +import java.util.Set; + +import seedu.address.model.tag.Tag; + +/** + * Represents the Teaching Assistant position in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidPosition(String)} + */ +public class TeachingAssistant extends Position { + + /** + * Availability of the teaching assistant. + */ + public enum Availability { + AVAILABLE, + UNAVAILABLE + } + + public static final String MESSAGE_CONSTRAINTS = + "Availability can only be available or unavailable (non case-sensitive)."; + + private String availability; + + /** + * Creates a teaching assistant and initialises their availability to available. + */ + public TeachingAssistant() { + super("TA"); + availability = "Available"; + } + + /** + * Creates a teaching assistant with the given details. + * @param availability of the teaching assistant + */ + public TeachingAssistant(String availability) { + super("TA"); + requireNonNull(availability); + checkArgument(isValidAvailability(availability), MESSAGE_CONSTRAINTS); + this.availability = availability; + } + + @Override + public void setDetails(String availability) { + requireNonNull(availability); + checkArgument(isValidAvailability(availability), MESSAGE_CONSTRAINTS); + this.availability = availability; + } + + + /** + * Returns true if a given string is a valid availability. + */ + public static boolean isValidAvailability(String test) { + requireNonNull(test); + for (Availability availability: Availability.values()) { + if (availability.name().equalsIgnoreCase(test)) { + return true; + } + } + return false; + } + + @Override + public String toShow() { + return "Availability: " + availability; + } + + @Override + public String toString() { + return "Teaching Assistant: " + availability; + } + + @Override + public boolean equals(Object other) { + return true; + } + + @Override + public int hashcode() { + return 0; + } + + @Override + public String getDetails() { + return availability; + } + + public void setFilePath(Set modelTags) { + } +} diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java index b0ea7e7dad7..8dacc06b67a 100644 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ b/src/main/java/seedu/address/model/tag/Tag.java @@ -9,8 +9,10 @@ */ public class Tag { - public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric"; - public static final String VALIDATION_REGEX = "\\p{Alnum}+"; + public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric " + + "or in {Module}-{Tutorial Group} format"; + + public static final String VALIDATION_REGEX = "^[a-zA-Z0-9]+[-][a-zA-Z0-9]+$"; // "\p{Alnum}+" public final String tagName; @@ -23,6 +25,7 @@ public Tag(String tagName) { requireNonNull(tagName); checkArgument(isValidTagName(tagName), MESSAGE_CONSTRAINTS); this.tagName = tagName; + String module1 = tagName.split("-")[0]; } /** diff --git a/src/main/java/seedu/address/model/tag/TagContainsKeywordPredicate.java b/src/main/java/seedu/address/model/tag/TagContainsKeywordPredicate.java new file mode 100644 index 00000000000..9b8af330baa --- /dev/null +++ b/src/main/java/seedu/address/model/tag/TagContainsKeywordPredicate.java @@ -0,0 +1,31 @@ +package seedu.address.model.tag; + +import java.util.function.Predicate; + +import seedu.address.model.person.Person; + + +/** + * Tests that any of a {@code Person}'s {@code Tags} match any of the keywords given. + */ +public class TagContainsKeywordPredicate implements Predicate { + private final String keyword; + + public TagContainsKeywordPredicate(String keyword) { + this.keyword = keyword; + } + + @Override + public boolean test(Person person) { + return person.getTags().stream() + .anyMatch(tag -> tag.tagName.equalsIgnoreCase(keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TagContainsKeywordPredicate // instanceof handles nulls + && keyword.equals(((TagContainsKeywordPredicate) other).keyword)); // state check + } + +} diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java index 1806da4facf..c67b12cf3ca 100644 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java @@ -11,32 +11,38 @@ import seedu.address.model.person.Name; import seedu.address.model.person.Person; import seedu.address.model.person.Phone; +import seedu.address.model.person.Remark; +import seedu.address.model.person.position.Professor; +import seedu.address.model.person.position.Student; +import seedu.address.model.person.position.TeachingAssistant; import seedu.address.model.tag.Tag; /** * Contains utility methods for populating {@code AddressBook} with sample data. */ public class SampleDataUtil { + public static final Remark EMPTY_REMARK = new Remark(""); public static Person[] getSamplePersons() { return new Person[] { new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), - new Address("Blk 30 Geylang Street 29, #06-40"), - getTagSet("friends")), + new Student("./data/CS2103T.txt"), new Address("Blk 30 Geylang Street 29, #06-40"), EMPTY_REMARK, + getTagSet("CS2103T-T01")), new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), - new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), - getTagSet("colleagues", "friends")), + new TeachingAssistant(), new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), EMPTY_REMARK, + getTagSet("CS2103T-T05", "CS2103T-T01")), new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), - new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), - getTagSet("neighbours")), + new Professor(), new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), EMPTY_REMARK, + getTagSet("CS2103T-T10")), new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), - new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), - getTagSet("family")), + new Student("./data/CS2103T.txt"), new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), + EMPTY_REMARK, + getTagSet("CS2103T-T12")), new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), - new Address("Blk 47 Tampines Street 20, #17-35"), - getTagSet("classmates")), + new TeachingAssistant(), new Address("Blk 47 Tampines Street 20, #17-35"), EMPTY_REMARK, + getTagSet("CS2103T-T01")), new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), - new Address("Blk 45 Aljunied Street 85, #11-31"), - getTagSet("colleagues")) + new Professor(), new Address("Blk 45 Aljunied Street 85, #11-31"), EMPTY_REMARK, + getTagSet("CS2103T-T02")) }; } diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java index a6321cec2ea..06d150f4942 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java @@ -15,32 +15,43 @@ import seedu.address.model.person.Name; import seedu.address.model.person.Person; import seedu.address.model.person.Phone; +import seedu.address.model.person.Remark; +import seedu.address.model.person.position.Position; import seedu.address.model.tag.Tag; /** * Jackson-friendly version of {@link Person}. */ class JsonAdaptedPerson { - public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!"; private final String name; private final String phone; private final String email; + private final String position; + private final String details; private final String address; + private final String remark; private final List tagged = new ArrayList<>(); + /** * Constructs a {@code JsonAdaptedPerson} with the given person details. */ @JsonCreator public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone, - @JsonProperty("email") String email, @JsonProperty("address") String address, - @JsonProperty("tagged") List tagged) { + @JsonProperty("email") String email, @JsonProperty("position") String position, + @JsonProperty("address") String address, @JsonProperty("remark") String remark, + @JsonProperty("details") String details, + @JsonProperty("tagged") List tagged) { + this.name = name; this.phone = phone; this.email = email; + this.position = position; + this.details = details; this.address = address; + this.remark = remark; if (tagged != null) { this.tagged.addAll(tagged); } @@ -53,7 +64,10 @@ public JsonAdaptedPerson(Person source) { name = source.getName().fullName; phone = source.getPhone().value; email = source.getEmail().value; + position = source.getPosition().value; + details = source.getDetails(); address = source.getAddress().value; + remark = source.getRemark().value; tagged.addAll(source.getTags().stream() .map(JsonAdaptedTag::new) .collect(Collectors.toList())); @@ -94,16 +108,38 @@ public Person toModelType() throws IllegalValueException { } final Email modelEmail = new Email(email); + if (position == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + Position.class.getSimpleName())); + } + if (!Position.isValidPosition(position)) { + throw new IllegalValueException(Position.MESSAGE_CONSTRAINTS); + } + final Position modelPosition = Position.buildPosition(position, tagged.toString()); + if (address == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + Address.class.getSimpleName())); } if (!Address.isValidAddress(address)) { throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); } final Address modelAddress = new Address(address); + if (remark == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + Remark.class.getSimpleName())); + } + final Remark modelRemark = new Remark(remark); + + if (details == null) { + throw new IllegalValueException(Position.DETAILS_MESSAGE_CONSTRAINTS); + } + final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); + modelPosition.setDetails(details); + modelPosition.setFilePath(modelTags); + return new Person(modelName, modelPhone, modelEmail, modelPosition, modelAddress, modelRemark, modelTags); } } diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/seedu/address/storage/Storage.java index beda8bd9f11..f7c9a0efa73 100644 --- a/src/main/java/seedu/address/storage/Storage.java +++ b/src/main/java/seedu/address/storage/Storage.java @@ -23,6 +23,8 @@ public interface Storage extends AddressBookStorage, UserPrefsStorage { @Override Path getAddressBookFilePath(); + void setAddressBook(AddressBookStorage addressBookStorage); + @Override Optional readAddressBook() throws DataConversionException, IOException; diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java index 6cfa0162164..b35b5f505e0 100644 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ b/src/main/java/seedu/address/storage/StorageManager.java @@ -1,5 +1,7 @@ package seedu.address.storage; +import static java.util.Objects.requireNonNull; + import java.io.IOException; import java.nio.file.Path; import java.util.Optional; @@ -53,6 +55,12 @@ public Path getAddressBookFilePath() { return addressBookStorage.getAddressBookFilePath(); } + @Override + public void setAddressBook(AddressBookStorage addressBookStorage) { + requireNonNull(addressBookStorage); + this.addressBookStorage = addressBookStorage; + } + @Override public Optional readAddressBook() throws DataConversionException, IOException { return readAddressBook(addressBookStorage.getAddressBookFilePath()); diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/address/ui/HelpWindow.java deleted file mode 100644 index 3f16b2fcf26..00000000000 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ /dev/null @@ -1,102 +0,0 @@ -package seedu.address.ui; - -import java.util.logging.Logger; - -import javafx.fxml.FXML; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.input.Clipboard; -import javafx.scene.input.ClipboardContent; -import javafx.stage.Stage; -import seedu.address.commons.core.LogsCenter; - -/** - * Controller for a help page - */ -public class HelpWindow extends UiPart { - - public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html"; - public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL; - - private static final Logger logger = LogsCenter.getLogger(HelpWindow.class); - private static final String FXML = "HelpWindow.fxml"; - - @FXML - private Button copyButton; - - @FXML - private Label helpMessage; - - /** - * Creates a new HelpWindow. - * - * @param root Stage to use as the root of the HelpWindow. - */ - public HelpWindow(Stage root) { - super(FXML, root); - helpMessage.setText(HELP_MESSAGE); - } - - /** - * Creates a new HelpWindow. - */ - public HelpWindow() { - this(new Stage()); - } - - /** - * Shows the help window. - * @throws IllegalStateException - *
    - *
  • - * if this method is called on a thread other than the JavaFX Application Thread. - *
  • - *
  • - * if this method is called during animation or layout processing. - *
  • - *
  • - * if this method is called on the primary stage. - *
  • - *
  • - * if {@code dialogStage} is already showing. - *
  • - *
- */ - public void show() { - logger.fine("Showing help page about the application."); - getRoot().show(); - getRoot().centerOnScreen(); - } - - /** - * Returns true if the help window is currently being shown. - */ - public boolean isShowing() { - return getRoot().isShowing(); - } - - /** - * Hides the help window. - */ - public void hide() { - getRoot().hide(); - } - - /** - * Focuses on the help window. - */ - public void focus() { - getRoot().requestFocus(); - } - - /** - * Copies the URL to the user guide to the clipboard. - */ - @FXML - private void copyUrl() { - final Clipboard clipboard = Clipboard.getSystemClipboard(); - final ClipboardContent url = new ClipboardContent(); - url.putString(USERGUIDE_URL); - clipboard.setContent(url); - } -} diff --git a/src/main/java/seedu/address/ui/InfoDisplay.java b/src/main/java/seedu/address/ui/InfoDisplay.java new file mode 100644 index 00000000000..667d2965218 --- /dev/null +++ b/src/main/java/seedu/address/ui/InfoDisplay.java @@ -0,0 +1,133 @@ +package seedu.address.ui; + +import java.util.Comparator; + +import javafx.fxml.FXML; +import javafx.scene.Group; +import javafx.scene.Scene; +import javafx.scene.chart.StackedBarChart; +import javafx.scene.control.Label; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import javafx.stage.Stage; +import seedu.address.commons.util.ChartUtil; +import seedu.address.model.person.Person; +import seedu.address.model.person.position.Student; + +/** + * A ui for the details of a specified person displayed at the right panel of TAB. + */ +public class InfoDisplay extends UiPart { + + private static final String FXML = "InfoDisplay.fxml"; + + /** + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + + private Person person; + + private StackedBarChart chart; + + @FXML + private Label name; + @FXML + private Label position; + @FXML + private Label phone; + @FXML + private Label email; + @FXML + private FlowPane tags; + @FXML + private Label misc; + @FXML + private StackPane placeHolder; + @FXML + private Label address; + @FXML + private Label remark; + + public InfoDisplay() { + super(FXML); + } + + /** + * Displays information of a specified person. + */ + public void setInfo(Person person) { + this.person = person; + name.setText(person.getName().fullName); + position.setText(person.getPosition().value); + phone.setText("Phone: " + person.getPhone().value); + email.setText("Email: " + person.getEmail().value); + person.getTags().stream().sorted(Comparator.comparing(tag -> tag.tagName)) + .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + misc.setText(person.getPosition().toShow()); + address.setText("Address: " + person.getAddress().value); + remark.setText("Remarks: " + person.getRemark().value); + if (person.getPosition() instanceof Student) { + Student s = (Student) person.getPosition(); + StackedBarChart chart = ChartUtil.createBarChart("Results", + "Assignments", "Score", s.getAssignmentsAndGrade(), + s.getAssignmentsAndMaximumGrade()); + chart.setAnimated(false); + this.chart = chart; + placeHolder.getChildren().add(chart); + } else { + this.chart = null; + placeHolder.getChildren().clear(); + } + } + + /** + * Displays a Graph in a new window. + */ + public void displayGraph() { + if (chart != null && name != null) { + Stage stage = new Stage(); + stage.setTitle("Grades of " + name.getText()); + Group root = new Group(chart); + Scene scene = new Scene(root); + stage.setScene(scene); + stage.show(); + } + } + + /** + * Clears any information being displayed. + */ + public void clearInfo() { + name.setText(null); + position.setText(null); + phone.setText(null); + email.setText(null); + tags.getChildren().clear(); + misc.setText(null); + address.setText(null); + remark.setText(null); + placeHolder.getChildren().clear(); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof InfoDisplay)) { + return false; + } + + // state check + InfoDisplay current = (InfoDisplay) other; + return person.equals(current.person); + } +} diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index 9106c3aa6e5..edcda190a8c 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -1,5 +1,7 @@ package seedu.address.ui; +import java.io.IOException; +import java.nio.file.Files; import java.util.logging.Logger; import javafx.event.ActionEvent; @@ -12,10 +14,13 @@ import javafx.stage.Stage; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.exceptions.DataConversionException; import seedu.address.logic.Logic; import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.RenameCommand; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Person; /** * The Main Window. Provides the basic application layout containing @@ -24,6 +29,7 @@ public class MainWindow extends UiPart { private static final String FXML = "MainWindow.fxml"; + private static final String USERGUIDE_URL = "https://ay2223s1-cs2103t-t17-1.github.io/tp/UserGuide.html"; private final Logger logger = LogsCenter.getLogger(getClass()); @@ -33,7 +39,7 @@ public class MainWindow extends UiPart { // Independent Ui parts residing in this Ui container private PersonListPanel personListPanel; private ResultDisplay resultDisplay; - private HelpWindow helpWindow; + private InfoDisplay infoDisplay; @FXML private StackPane commandBoxPlaceholder; @@ -41,6 +47,12 @@ public class MainWindow extends UiPart { @FXML private MenuItem helpMenuItem; + @FXML + private MenuItem swapBook; + + @FXML + private MenuItem newBook; + @FXML private StackPane personListPanelPlaceholder; @@ -50,6 +62,9 @@ public class MainWindow extends UiPart { @FXML private StackPane statusbarPlaceholder; + @FXML + private StackPane infoDisplayPlaceholder; + /** * Creates a {@code MainWindow} with the given {@code Stage} and {@code Logic}. */ @@ -64,8 +79,6 @@ public MainWindow(Stage primaryStage, Logic logic) { setWindowDefaultSize(logic.getGuiSettings()); setAccelerators(); - - helpWindow = new HelpWindow(); } public Stage getPrimaryStage() { @@ -74,6 +87,8 @@ public Stage getPrimaryStage() { private void setAccelerators() { setAccelerator(helpMenuItem, KeyCombination.valueOf("F1")); + setAccelerator(swapBook, KeyCombination.valueOf("Shift+Tab")); + setAccelerator(newBook, KeyCombination.valueOf("Ctrl+Shift+N")); } /** @@ -110,7 +125,7 @@ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { * Fills up all the placeholders of this window. */ void fillInnerParts() { - personListPanel = new PersonListPanel(logic.getFilteredPersonList()); + personListPanel = new PersonListPanel(logic.getFilteredPersonList(), this); personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); resultDisplay = new ResultDisplay(); @@ -121,6 +136,9 @@ void fillInnerParts() { CommandBox commandBox = new CommandBox(this::executeCommand); commandBoxPlaceholder.getChildren().add(commandBox.getRoot()); + + infoDisplay = new InfoDisplay(); + infoDisplayPlaceholder.getChildren().add(infoDisplay.getRoot()); } /** @@ -136,21 +154,65 @@ private void setWindowDefaultSize(GuiSettings guiSettings) { } /** - * Opens the help window or focuses on it if it's already opened. + * Resets what the status bar shows. + */ + public void refreshStatusBar() { + StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath()); + statusbarPlaceholder.getChildren().set(0, statusBarFooter.getRoot()); + } + + /** + * Opens the help window in a new browser window */ @FXML public void handleHelp() { - if (!helpWindow.isShowing()) { - helpWindow.show(); - } else { - helpWindow.focus(); - } + UrlLauncher helpController = new UrlLauncher(); + helpController.launchWebPage(USERGUIDE_URL); } void show() { primaryStage.show(); } + /** + * Creates a new address book + */ + @FXML + private void handleNewBook() { + try { + if (!logic.addAddressBook()) { + resultDisplay.setFeedbackToUser("Maximum amount of TAB created"); + } else { + refreshStatusBar(); + } + } catch (IOException | DataConversionException e) { + resultDisplay.setFeedbackToUser("Sorry! Error creating File"); + } + } + + /** + * Swaps between the Books + */ + @FXML + private void handleRename() { + try { + Files.delete(logic.getAddressBookFilePath()); + logic.resetCurrentAddressBook(); + refreshStatusBar(); + } catch (IOException e) { + resultDisplay.setFeedbackToUser("Sorry! Error deleting File"); + } + } + + /** + * Swaps between the Books + */ + @FXML + private void handleSwap() { + logic.swapAddressBook(); + refreshStatusBar(); + } + /** * Closes the application. */ @@ -159,7 +221,6 @@ private void handleExit() { GuiSettings guiSettings = new GuiSettings(primaryStage.getWidth(), primaryStage.getHeight(), (int) primaryStage.getX(), (int) primaryStage.getY()); logic.setGuiSettings(guiSettings); - helpWindow.hide(); primaryStage.hide(); } @@ -177,11 +238,30 @@ private CommandResult executeCommand(String commandText) throws CommandException CommandResult commandResult = logic.execute(commandText); logger.info("Result: " + commandResult.getFeedbackToUser()); resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser()); + infoDisplay.clearInfo(); + + if (commandResult.hasPersonToShow()) { + Person personToShow = commandResult.getPersonToShow(); + assert personToShow != null; + infoDisplay.setInfo(personToShow); + } if (commandResult.isShowHelp()) { handleHelp(); } + if (commandResult.isNewBook()) { + handleNewBook(); + } + + if (commandResult.getFeedbackToUser().equals(RenameCommand.MESSAGE_RENAME_SUCCESS)) { + handleRename(); + } + + if (commandResult.isSwap()) { + handleSwap(); + } + if (commandResult.isExit()) { handleExit(); } @@ -193,4 +273,8 @@ private CommandResult executeCommand(String commandText) throws CommandException throw e; } } + + public void handlePersonListPanelEdits(String s) throws CommandException, ParseException { + this.executeCommand(s); + } } diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java index 7fc927bc5d9..4f0834c4e5c 100644 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ b/src/main/java/seedu/address/ui/PersonCard.java @@ -33,11 +33,7 @@ public class PersonCard extends UiPart { @FXML private Label id; @FXML - private Label phone; - @FXML - private Label address; - @FXML - private Label email; + private Label position; @FXML private FlowPane tags; @@ -49,11 +45,8 @@ public PersonCard(Person person, int displayedIndex) { this.person = person; id.setText(displayedIndex + ". "); name.setText(person.getName().fullName); - phone.setText(person.getPhone().value); - address.setText(person.getAddress().value); - email.setText(person.getEmail().value); - person.getTags().stream() - .sorted(Comparator.comparing(tag -> tag.tagName)) + position.setText(person.getPosition().value); + person.getTags().stream().sorted(Comparator.comparing(tag -> tag.tagName)) .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); } diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java index f4c501a897b..89aee719fcb 100644 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ b/src/main/java/seedu/address/ui/PersonListPanel.java @@ -8,6 +8,8 @@ import javafx.scene.control.ListView; import javafx.scene.layout.Region; import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.person.Person; /** @@ -20,13 +22,24 @@ public class PersonListPanel extends UiPart { @FXML private ListView personListView; + private MainWindow mw; /** * Creates a {@code PersonListPanel} with the given {@code ObservableList}. */ - public PersonListPanel(ObservableList personList) { + public PersonListPanel(ObservableList personList, MainWindow mw) { super(FXML); personListView.setItems(personList); personListView.setCellFactory(listView -> new PersonListViewCell()); + this.mw = mw; + } + + @FXML + private void displayInfo() throws CommandException, ParseException { + int personIndex = personListView.getSelectionModel().getSelectedIndex() + 1; + int size = personListView.getItems().size(); + if (personIndex > 0 && personIndex <= size) { + mw.handlePersonListPanelEdits("show " + personIndex); + } } /** diff --git a/src/main/java/seedu/address/ui/UrlLauncher.java b/src/main/java/seedu/address/ui/UrlLauncher.java new file mode 100644 index 00000000000..77c77d74d1e --- /dev/null +++ b/src/main/java/seedu/address/ui/UrlLauncher.java @@ -0,0 +1,21 @@ +package seedu.address.ui; + +import java.io.IOException; + +import javafx.application.Application; +import javafx.stage.Stage; + + +/** + * Launches a URL in the default desktop browser + * + * */ +public class UrlLauncher extends Application { + + @Override + public void start(Stage primaryStage) throws IOException {} + + public void launchWebPage(String url) { + getHostServices().showDocument(url); + } +} diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 36e6b001cd8..0ae5b3b97da 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -1,6 +1,6 @@ .background { - -fx-background-color: derive(#1d1d1d, 20%); - background-color: #383838; /* Used in the default.html file */ + -fx-background-color: derive(#1d2433, 20%); + background-color: #1d2433; /* Used in the default.html file */ } .label { @@ -17,6 +17,13 @@ -fx-opacity: 1; } +.label-blue { + -fx-font-size: 11pt; + -fx-font-family: "Segoe UI Semibold"; + -fx-text-fill: #2f3b54; + -fx-opacity: 1; +} + .label-header { -fx-font-size: 32pt; -fx-font-family: "Segoe UI Light"; @@ -24,6 +31,13 @@ -fx-opacity: 1; } +.label-header-blue { + -fx-font-size: 32pt; + -fx-font-family: "Segoe UI Light"; + -fx-text-fill: #2f3b54; + -fx-opacity: 1; +} + .text-field { -fx-font-size: 12pt; -fx-font-family: "Segoe UI Semibold"; @@ -77,34 +91,37 @@ } .split-pane:horizontal .split-pane-divider { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#d7dce2, 20%); -fx-border-color: transparent transparent transparent #4d4d4d; } .split-pane { -fx-border-radius: 1; -fx-border-width: 1; - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#1d2433, 20%); } .list-view { - -fx-background-insets: 0; - -fx-padding: 0; - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-insets: 10; + -fx-padding: 10; + -fx-background-color: derive(#d7dce2, 20%); } .list-cell { - -fx-label-padding: 0 0 0 0; - -fx-graphic-text-gap : 0; - -fx-padding: 0 0 0 0; + -fx-label-padding: 5 5; + -fx-graphic-text-gap : 5; + -fx-padding: 5px; + -fx-background-radius: 15px; + -fx-background-insets: 3px; + -fx-background-color: transparent; } .list-cell:filled:even { - -fx-background-color: #3c3e3f; + -fx-background-color: #6679a4; } .list-cell:filled:odd { - -fx-background-color: #515658; + -fx-background-color: #8695b7; } .list-cell:filled:selected { @@ -112,8 +129,9 @@ } .list-cell:filled:selected #cardPane { - -fx-border-color: #3e7b91; - -fx-border-width: 1; + -fx-border-color: transparent transparent transparent #5ccfe6; + -fx-border-width: 3; + -fx-border-radius: 10; } .list-cell .label { @@ -133,13 +151,30 @@ } .stack-pane { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#2f3b54, 20%); } .pane-with-border { - -fx-background-color: derive(#1d1d1d, 20%); - -fx-border-color: derive(#1d1d1d, 10%); - -fx-border-top-width: 1px; + -fx-background-color: derive(#2f3b54, 20%); + -fx-border-color: derive(#2f3b54, 10%); + -fx-border-top-width: 0px; +} + +.pane-with-border-alt { + -fx-background-color: derive(#d7dce2, 20%); + -fx-border-color: derive(#d7dce2, 10%); + -fx-border-top-width: 0px; +} + +.pane-light { + -fx-background-color: derive(#d7dce2, 20%); + -fx-background-radius: 10px; + -fx-border-top-width: 0px; +} + +.pane-transparent { + -fx-background-color: transparent; + -fx-border-radius: 10; } .status-bar { @@ -147,7 +182,7 @@ } .result-display { - -fx-background-color: transparent; + -fx-background-color: #2f3b54; -fx-font-family: "Segoe UI Light"; -fx-font-size: 13pt; -fx-text-fill: white; @@ -185,7 +220,7 @@ } .context-menu { - -fx-background-color: derive(#1d1d1d, 50%); + -fx-background-color: derive(#2f3b54, 50%); } .context-menu .label { @@ -193,7 +228,7 @@ } .menu-bar { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#2f3b54, 20%); } .menu-bar .label { @@ -282,11 +317,11 @@ } .scroll-bar { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: transparent; } .scroll-bar .thumb { - -fx-background-color: derive(#1d1d1d, 50%); + -fx-background-color: derive(#2f3b54, 50%); -fx-background-insets: 3; } @@ -318,9 +353,10 @@ } #commandTextField { - -fx-background-color: transparent #383838 transparent #383838; + -fx-background-color: #1d2433; -fx-background-insets: 0; - -fx-border-color: #383838 #383838 #ffffff #383838; + -fx-background-radius: 15; + -fx-border-color: transparent; -fx-border-insets: 0; -fx-border-width: 1; -fx-font-family: "Segoe UI Light"; @@ -333,7 +369,7 @@ } #resultDisplay .content { - -fx-background-color: transparent, #383838, transparent, #383838; + -fx-background-color: #6679a4; -fx-background-radius: 0; } @@ -343,8 +379,8 @@ } #tags .label { - -fx-text-fill: white; - -fx-background-color: #3e7b91; + -fx-text-fill: black; + -fx-background-color: #5ccfe6; -fx-padding: 1 3 1 3; -fx-border-radius: 2; -fx-background-radius: 2; diff --git a/src/main/resources/view/HelpWindow.css b/src/main/resources/view/HelpWindow.css deleted file mode 100644 index 17e8a8722cd..00000000000 --- a/src/main/resources/view/HelpWindow.css +++ /dev/null @@ -1,19 +0,0 @@ -#copyButton, #helpMessage { - -fx-text-fill: white; -} - -#copyButton { - -fx-background-color: dimgray; -} - -#copyButton:hover { - -fx-background-color: gray; -} - -#copyButton:armed { - -fx-background-color: darkgray; -} - -#helpMessageContainer { - -fx-background-color: derive(#1d1d1d, 20%); -} diff --git a/src/main/resources/view/HelpWindow.fxml b/src/main/resources/view/HelpWindow.fxml deleted file mode 100644 index 5dea0adef70..00000000000 --- a/src/main/resources/view/HelpWindow.fxml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/view/InfoDisplay.fxml b/src/main/resources/view/InfoDisplay.fxml new file mode 100644 index 00000000000..873dd444eb4 --- /dev/null +++ b/src/main/resources/view/InfoDisplay.fxml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index a431648f6c0..5fe866c6a32 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -12,7 +12,7 @@ + title="TAB" minWidth="950" minHeight="800" onCloseRequest="#handleExit"> @@ -25,33 +25,52 @@ - + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml index f08ea32ad55..0da8f5493b3 100644 --- a/src/main/resources/view/PersonListCard.fxml +++ b/src/main/resources/view/PersonListCard.fxml @@ -7,30 +7,36 @@ + - + - + - + - + - - - + + + diff --git a/src/main/resources/view/PersonListPanel.fxml b/src/main/resources/view/PersonListPanel.fxml index 8836d323cc5..972f7423c84 100644 --- a/src/main/resources/view/PersonListPanel.fxml +++ b/src/main/resources/view/PersonListPanel.fxml @@ -4,5 +4,5 @@ - + diff --git a/src/main/resources/view/ResultDisplay.fxml b/src/main/resources/view/ResultDisplay.fxml index 58d5ad3dc56..3fbf6d0adf4 100644 --- a/src/main/resources/view/ResultDisplay.fxml +++ b/src/main/resources/view/ResultDisplay.fxml @@ -3,7 +3,7 @@ -