diff --git a/README.md b/README.md index 13f5c77403f..74694a396fa 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,26 @@ -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) - -![Ui](docs/images/Ui.png) - -* This is **a sample project for Software Engineering (SE) students**.
- Example usages: - * as a starting point of a course project (as opposed to writing everything from scratch) - * as a case study -* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details. - * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big. - * It comes with a **reasonable level of user and developer documentation**. -* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...). -* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**. -* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info. +[![CI Status](https://github.com/AY2122S1-CS2103T-W12-4/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2122S1-CS2103T-W12-4/tp/actions) + +# ModuLink + +### The **best** module partner finder for CS students. + +#### Overview +- [X] Text based CLI commands - for the fast coders +- [X] Simple - learn in seconds +- [X] Fastest - 0 ping + +> Better than Tinder! - SoC CS students + +#### Features +1. Create and edit your own profile +2. Add and remove as favourite +3. Find by name +4. Filter by module +5. Filter by grouping status +6. And many more! + +For a more detailed documentation of this project, please visit our [Product Website](https://ay2122s1-cs2103t-w12-4.github.io/tp/UserGuide)! + +#### Ui Mockup for ModuLink + +![Ui Mockup](docs/images/screenshots/Ui.png) diff --git a/build.gradle b/build.gradle index be2d2905dde..165fe1152dc 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { id 'jacoco' } -mainClassName = 'seedu.address.Main' +mainClassName = 'seedu.modulink.Main' sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 @@ -66,7 +66,11 @@ dependencies { } shadowJar { - archiveName = 'addressbook.jar' + archiveName = 'ModuLink.jar' } defaultTasks 'clean', 'test' + +run { + enableAssertions = true +} diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..38425c24216 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -5,55 +5,53 @@ 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` - ## Project team -### John Doe - - +### Aakansha Narain -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] + -* Role: Project Advisor +[[github](https://github.com/aakanshanarain)] +[[portfolio](team/aakanshanarain.md)] -### Jane Doe +* Role: Developer +* Responsibilities: Delivarables +### Charlton Tan - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/charltonator)] +[[portfolio](team/charltonator.md)] -* Role: Team Lead -* Responsibilities: UI +* Role: Developer +* Responsibilities: Testing -### Johnny Doe +### Ethan Wong - + -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] +[[github](http://github.com/ethanwong6362)] +[[portfolio](team/ethanwong6362.md)] * Role: Developer -* Responsibilities: Data +* Responsibilities: Logic flow -### Jean Doe +### Ng Jia Yuan - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/ngjiayuan)] +[[portfolio](team/ngjiayuan.md)] -* Role: Developer -* Responsibilities: Dev Ops + Threading +* Role: Team Lead +* Responsibilities: Code quality and team operations -### James Doe +### Zachary Lau - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/zacharylwy)] +[[portfolio](team/zacharylwy.md)] * Role: Developer * Responsibilities: UI diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 46eae8ee565..092ac3903c9 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,15 +1,24 @@ --- layout: page -title: Developer Guide +title: ModuLink - Developer Guide --- -* Table of Contents -{:toc} + +**Table of Contents** +1. [Acknowledgements](#acknowledgements) +2. [Setting up, getting started](#setting-up-getting-started) +3. [Design](#design) +4. [Implementation](#implementation) +5. [Documentation, logging, testing, configuration, dev-ops](#documentation-logging-testing-configuration-dev-ops) +6. [Appendix](#appendix-requirements) + 1. [Requirements](#appendix-requirements) + 2. [Instructions for manual testing](#appendix-instructions-for-manual-testing) -------------------------------------------------------------------------------------------------------------------- ## **Acknowledgements** -* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +* [Favourite Star Icon](https://imgbin.com/png/X9hfA1CP/five-pointed-star-yellow-png) +* [Profile Icon](http://www.stickpng.com/img/icons-logos-emojis/users/simple-user-icon) -------------------------------------------------------------------------------------------------------------------- @@ -23,7 +32,7 @@ Refer to the guide [_Setting up and getting started_](SettingUp.md).
-:bulb: **Tip:** The `.puml` files used to create diagrams in this document can be found in the [diagrams](https://github.com/se-edu/addressbook-level3/tree/master/docs/diagrams/) folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams. +:bulb: **Tip:** The `.puml` files used to create diagrams in this document can be found in the [diagrams](https://github.com/AY2122S1-CS2103T-W12-4/tp/tree/master/docs/diagrams) folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams.
### Architecture @@ -36,7 +45,7 @@ Given below is a quick overview of main components and how they interact with ea **Main components of the architecture** -**`Main`** has two classes called [`Main`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java). It is responsible for, +**`Main`** has two classes called [`Main`](https://github.com/AY2122S1-CS2103T-W12-4/tp/blob/master/src/main/java/seedu/modulink/Main.java) and [`MainApp`](https://github.com/AY2122S1-CS2103T-W12-4/tp/blob/master/src/main/java/seedu/modulink/MainApp.java). It is responsible for, * At app launch: Initializes the components in the correct sequence, and connects them up with each other. * At shut down: Shuts down the components and invokes cleanup methods where necessary. @@ -52,7 +61,7 @@ The rest of the App consists of four components. **How the architecture components interact with each other** -The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete 1`. +The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `addFav A0123456A`. @@ -69,13 +78,13 @@ The sections below give more details of each component. ### UI component -The **API** of this component is specified in [`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java) +The **API** of this component is specified in [`Ui.java`](https://github.com/AY2122S1-CS2103T-W12-4/tp/tree/master/src/main/java/seedu/modulink/ui/Ui.java) ![Structure of the UI Component](images/UiClassDiagram.png) The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. -The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml) +The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/AY2122S1-CS2103T-W12-4/tp/blob/master/src/main/java/seedu/modulink/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/AY2122S1-CS2103T-W12-4/tp/blob/master/src/main/resources/view/MainWindow.fxml) The `UI` component, @@ -86,23 +95,23 @@ The `UI` component, ### Logic component -**API** : [`Logic.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/logic/Logic.java) +**API** : [`Logic.java`](https://github.com/AY2122S1-CS2103T-W12-4/tp/tree/master/src/main/java/seedu/modulink/logic/Logic.java) Here's a (partial) class diagram of the `Logic` component: How the `Logic` component works: -1. When `Logic` is called upon to execute a command, it uses the `AddressBookParser` class to parse the user command. -1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddCommand`) which is executed by the `LogicManager`. +1. When `Logic` is called upon to execute a command, it uses the `ModuLinkParser` class to parse the user command. +1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `CreateCommand`) which is executed by the `LogicManager`. 1. The command can communicate with the `Model` when it is executed (e.g. to add a person). 1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. -The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("delete 1")` API call. +The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("addFav")` API call. -![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png) +![Interactions Inside the Logic Component for the `addFav` Command](images/AddFavSequenceDiagram.png) -
:information_source: **Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. +
:information_source: **Note:** The lifeline for `AddFavCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command: @@ -110,43 +119,39 @@ Here are the other classes in `Logic` (omitted from the class diagram above) tha How the parsing works: -* When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `AddressBookParser` returns back as a `Command` object. -* All `XYZCommandParser` classes (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing. +* When called upon to parse a user command, the `ModuLinkParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `CreateCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `CreateCommand`) which the `ModuLinkParser` returns back as a `Command` object. +* All `XYZCommandParser` classes (e.g., `CreateCommandParser`, `EditCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing. ### Model component -**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java) +**API** : [`Model.java`](https://github.com/AY2122S1-CS2103T-W12-4/tp/tree/master/src/main/java/seedu/modulink/model/Model.java) The `Model` component, -* stores the address book data i.e., all `Person` objects (which are contained in a `UniquePersonList` object). +* stores ModuLink data i.e., all `Person` objects (which are contained in a `UniquePersonList` object). * stores the currently 'selected' `Person` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. * stores a `UserPref` object that represents the user’s preferences. This is exposed to the outside as a `ReadOnlyUserPref` objects. * does not depend on any of the other three components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components) -
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Person` references. This allows `AddressBook` to only require one `Tag` object per unique tag, instead of each `Person` needing their own `Tag` objects.
- - -
### Storage component -**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java) +**API** : [`Storage.java`](https://github.com/AY2122S1-CS2103T-W12-4/tp/tree/master/src/main/java/seedu/modulink/storage/Storage.java) The `Storage` component, -* can save both address book data and user preference data in json format, and read them back into corresponding objects. -* inherits from both `AddressBookStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed). +* can save both ModuLink data and user preference data in json format, and read them back into corresponding objects. +* inherits from both `ModuLinkStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed). * depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`) ### Common classes -Classes used by multiple components are in the `seedu.addressbook.commons` package. +Classes used by multiple components are in the `seedu.modulink.commons` package. -------------------------------------------------------------------------------------------------------------------- @@ -154,89 +159,104 @@ 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 +### Create A Profile +#### Implementation +The `create` mechanism will allow the user to create a personal profile. -#### Proposed Implementation +The implementation required the creation of a parser for `CreateCommand` as the command does takes in parameters (personal information, module information etc..). +`CreateCommand` class updates the `Model` class and then returns a new instance of the `CommandResult` class. +The GUI will then update to include the created profile and the current user's profile will be highlighted in the GUI. +After creating a profile, the user can now use other commands in ModuLink. -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: +#### Usage +To use this function and create a profile, simply enter the command and the required parameters in the command line in the following format. +`create n/NAME id/STUDENT_ID p/PHONE e/EMAIL [mod/TAG]...`
+Example: `create n/John Doe id/A1234567Z p/98765432 e/johnd@example.com mod/CS2100 mod/CS2101` -* `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. +The following sequence diagram shows how the `create` mechanism works: +![CreateSequenceDiagram](images/CreateSequenceDiagram.png) -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. -Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. +### Add A Module Tag +#### Implementation +The `addMod` mechanism will allow the user to add a module tag to their profile. -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. +The implementation required the creation of a parser for `addModCommand` as the command does takes in parameters. +`addModCommand` class updates the `Model` class and then returns a new instance of the `CommandResult` class. +The GUI will then update to include the tags for the user profile. -![UndoRedoState0](images/UndoRedoState0.png) +#### Usage +To use this function and create a profile, simply enter the command and the required parameters in the command line in the following format. +`addMod [mod/MOD]...`
+Example: `addMod mod/CS2103T` -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. +The following sequence diagram shows how the `addMod` mechanism works: +![AddModSequenceDiagram](images/AddModSequenceDiagram.png) -![UndoRedoState1](images/UndoRedoState1.png) -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`. +### Add A Profile As Favourite +#### Implementation +The `addFav` mechanism will allow the user to add a module tag to their profile. -![UndoRedoState2](images/UndoRedoState2.png) +The implementation required the creation of a parser for `addFavCommand` as the command does takes in parameters. +`addFavCommand` class updates the `Model` class and then returns a new instance of the `CommandResult` class. +The GUI will then update to show the specified profile as a favourite. -
: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`. - -
+#### Usage +To use this function and create a profile, simply enter the command and the required parameters in the command line in the following format. +`addFav Student_ID`
+Example: `addFav A1234567X` -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. - -![UndoRedoState3](images/UndoRedoState3.png) - -
: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. - -
+The following sequence diagram shows how the `addFav` mechanism works: +![AddFavSequenceDiagram](images/AddFavSequenceDiagram.png) -The following sequence diagram shows how the undo operation works: - -![UndoSequenceDiagram](images/UndoSequenceDiagram.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. - -
- -The `redo` command does the opposite — it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state. - -
:information_source: **Note:** If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone AddressBook states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo. - -
-Step 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. +### List All Favourited Profiles +#### Implementation +The `listFav` mechanism will allow the user to view all the students that he/she has favourited. -![UndoRedoState4](images/UndoRedoState4.png) +The implementation did not require the creation of a parser for `ListFavCommand` as the command does not take in any parameters. +`ListFavCommand` class updates the `Model` class and then returns a new instance of the `CommandResult` class. +The GUI will then change the content to display all favourited profiles. -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. +#### Usage +To use this function to see all favourited profiles, simply enter `listFav` in the command line. -![UndoRedoState5](images/UndoRedoState5.png) +The following sequence diagram shows how the `listFav` mechanism works: +![ListFavSequenceDiagram](images/ListFavSequenceDiagram.png) -The following activity diagram summarizes what happens when a user executes a new command: - +### Filter Profiles By Module and Optionally Group Status +#### Implementation +The `filter` mechanism will allow the user to filter profiles by module and optionally group status. -#### Design considerations: +The implementation required the creation of a parser for `filterCommand` as the command does takes in parameters. +`filterCommand` class updates the `Model` class and then returns a new instance of the `CommandResult` class. +The GUI will then update to show the profiles that contain the specified modules and/or group status. -**Aspect: How undo & redo executes:** +#### Usage +To use this function and create a profile, simply enter the command and the required parameters in the command line in the following format. +`filter mod/MODULE [group/GROUP_STATUS]`
+Example: `filter mod/CS2103T group/SM` -* **Alternative 1 (current choice):** Saves the entire address book. - * Pros: Easy to implement. - * Cons: May have performance issues in terms of memory usage. +The following sequence diagram shows how the `filter` mechanism works: +![FilterSequenceDiagram](images/FilterSequenceDiagram.png) -* **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. +### Edit Group Status of Modules +#### Implementation +The `editGroupStatus` mechanism will allow the user to edit the group status of the modules on their profile. -_{more aspects and alternatives to be added}_ +The implementation required the creation of a parser for `editGroupStatusCommand` as the command does takes in parameters. +`editGroupStatusCommand` class updates the `Model` class and then returns a new instance of the `CommandResult` class. +The GUI will then update to show the updated group status for the specified module. -### \[Proposed\] Data archiving +#### Usage +To use this function and create a profile, simply enter the command and the required parameters in the command line in the following format. +`editGroupStatus mod/MODULE group/GROUP_STATUS`
+Example: `editGroupStatus mod/CS2103T group/Need member` -_{Explain here how the data archiving feature will be implemented}_ +The following sequence diagram shows how the `editGroupStatus` mechanism works: +![EditGroupStatusDiagram](images/EditGroupStatusCommandSequenceDiagram.png) -------------------------------------------------------------------------------------------------------------------- @@ -262,8 +282,9 @@ _{Explain here how the data archiving feature will be implemented}_ * can type fast * prefers typing to mouse interactions * is reasonably comfortable using CLI apps +* wants to find people to form groups with for his CS modules -**Value proposition**: manage contacts faster than a typical mouse/GUI driven app +**Value proposition**: search for and contact students in the same modules to quickly form groups. ### User stories @@ -272,56 +293,236 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli | 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 | +| `* * *` | user | add a contact as a favourite | easily keep track of my favourites' activity | +| `* * *` | user | remove a contact as a favourite| remove users I no longer am interested in | +| `* * *` | new user | create a profile | start using the app | +| `* * ` | potential user exploring the app | view those taking similar mods | easily find potential groupmates | +| `* * *` | user | list the modules I am taking | allow other users to view me as a potential groupmate | +| `* *` | user who formed a group | update group status for my modules | let other users know I have a group for a module | +| `* *` | proficient user | filter profiles by module | save time browsing profiles | +| `* *` | long time user | update the modules I am taking | find new favourites and groupmates for new modules that I am taking | +| `*` | user searching for groupmates | view potential groupmates' github| browse their work to decide if we would work well together | +| `*` | user looking for a specific profile | find a profile by student ID | quickly view their profile | + + -*{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 `ModuLink` and the **Actor** is the `user`, unless specified otherwise.
+**Preconditions:** User is logged in.) -**Use case: Delete a person** +
+ +**Use case: UC1 - Create user profile** **MSS** -1. User requests to list persons -2. AddressBook shows a list of persons -3. User requests to delete a specific person in the list -4. AddressBook deletes the person +1. User boots up ModuLink for the first time. +2. User enters their details. +3. ModuLink creates a new profile. Use case ends. **Extensions** -* 2a. The list is empty. - - Use case ends. +* 2a. The given details are invalid. -* 3a. The given index is invalid. + * 2a1. ModuLink shows an error message. + * 2a2. ModuLink requests for the correct details. - * 3a1. AddressBook shows an error message. + Steps 2a1 - 2a2 are repeated until the correct details are entered. Use case resumes at step 2. -*{More to be added}* -### Non-Functional Requirements +**Use case: UC2 - Add a profile to Favourites list** + +
+ +**MSS** + +1. User requests to add a specific profile to their favourites list. +2. ModuLink adds the profile. + + Use case ends. + +**Extensions** + +* 1a. The requested profile ID is invalid. + + * 1a1. ModuLink shows an error message. + * 1a2. ModuLink requests for the correct ID. + + Steps 1a1 - 1a2 are repeated until the correct details are entered. + + Use case resumes at step 1. + +
+ +**Use case: UC3 - Remove a profile from Favourites list** + +**MSS** + +1. User requests to remove a specific profile from their favourites list. +2. ModuLink removes the profile from their favourites. + + Use case ends. + +**Extensions** + +* 2a. The requested profile ID is invalid. + + * 2a1. ModuLink shows an error message. + * 2a2. ModuLink requests for the correct ID. + + Steps 2a1 - 2a2 are repeated until the correct details are entered. + + Use case resumes at step 1. + +
+ +**Use case: UC4 - View Favourites list** + +**MSS** + +1. User requests to display all profiles in their favourites list. +2. ModuLink shows a list of all profiles that the user has added to their favourites list. + + Use case ends. + +
+ +**Use case: UC5 - Remove a module from the user's profile** + +**MSS** + +1. User requests to remove a module from their profile. +2. ModuLink removes the module from the profile. + + Use case ends. + +**Extensions** + +* 1a. The requested Module is invalid. + + * 1a1. ModuLink shows an error message. + * 1a2. ModuLink requests for the correct ID. -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. +* 1b. The requested Module is not present in the users current module list. -*{More to be added}* + * 1b1. ModuLink shows an error message. + * 1b2. ModuLink requests for the correct ID. + +
+ +**Use case: UC6 - Add a module to the user's profile** + +**MSS** + +1. User requests to add a module from their profile. +2. ModuLink adds the module from the profile. + + Use case ends. + +**Extensions** + +* 1a. The requested Module is invalid. + + * 1a1. ModuLink shows an error message. + * 1a2. ModuLink requests for the correct ID. + +
+ +**Use case: UC7 - Update group status for modules** + +**MSS** + +1. User requests to change a tag for a specific module with their updated group status. +2. ModuLink deletes the existing tag for the module in the user's profile. +3. ModuLink adds the new tag for the module in the user's profile. + + Use case ends. + +**Extensions** + +* 1a. The requested module does not exist in the user's profile. + + * 1a1. ModuLink shows an error message. + * 1a2. ModuLink asks the user if they would like to add the module to their profile (UC6) . + + Use case resumes at step 1. + +
+ +**Use case: UC8 - Filter profiles by mods** + +**MSS** + +1. User requests to find all profiles with a particular tag. +2. ModuLink shows the list of profiles with the requested tag. + + Use case ends. + +**Extensions** + +* 1a. The requested tag does not exist. + + * 1a1. ModuLink shows an error message. + * 1a2. ModuLink requests for the correct tag. + + Steps 1a1 - 1a2 are repeated until the correct tag is entered. + + Use case resumes at step 1. + +
+ +**Use case: UC9 - Filter profiles by module** + +**MSS** + +1. User requests to find all profiles which have a particular module(s). +2. ModuLink shows the list of profiles with the requested module(s). + + Use case ends. + +
+ +**Use case: UC10 - Find a profile by student ID** + +**MSS** + +1. User requests to find a profile with the specified student ID. +2. ModuLink shows the profile with the requested student ID. + + Use case ends. + +**Extensions** + +* 1a. The requested student ID does not exist as a profile. + + * 1a1. ModuLink shows an error message. + + Use case ends. + + +### Non-Functional Requirements + +1. **Interoperability**: Should work on any _mainstream OS_ as long as it has Java `11` or above installed. +2. **Capacity/Efficiency**: Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage. +3. **Quality**: 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. +4. **Performance**: Should be able to respond to a command within a response time of 2 seconds. +5. **Quality**: A new user should be able to understand and use ModuLink easily with the help of the [UserGuide](https://ay2122s1-cs2103t-w12-4.github.io/tp/UserGuide.html). ### Glossary * **Mainstream OS**: Windows, Linux, Unix, OS-X -* **Private contact detail**: A contact detail that is not meant to be shared with others +* **Module**: A NUS CS module listed on [NUSmods](https://nusmods.com/modules?sem[0]=1&sem[1]=2&sem[2]=3&sem[3]=4) +* **Student**: A NUS CS student + **Profile**: A student registered in ModuLink. +* **Group status**: The group status of a student for a group project in a module +* **Command**: A command for the program. A full list of command can be seen in the [UserGuide](https://ay2122s1-cs2103t-w12-4.github.io/tp/UserGuide.html). +* **Contact detail**: Contact details consist of the user names, email, telegram handle. -------------------------------------------------------------------------------------------------------------------- @@ -340,38 +541,32 @@ testers are expected to do more *exploratory* testing. 1. Download the jar file and copy into an empty folder - 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. + 2. Double-click the jar file + Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. -1. Saving window preferences +2. Saving window preferences 1. Resize the window to an optimum size. Move the window to a different location. Close the window. 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 …​ }_ - -### Deleting a person - -1. Deleting a person while all persons are being shown - - 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. - - 1. Test case: `delete 1`
- Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated. - - 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. - - 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 …​ }_ +3. Closing the app + + 1. Type "exit" into the Command Line or click the X in the upper right-hand corner. + Expected: The app will close out and a `.json` file will be created in the `data` folder. ### Saving data 1. Dealing with missing/corrupted data files - 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ + 1. Open up ModuLink and run a few commands. + 2. Exit out of ModuLink and a `.json` file will appear under the `data` folder. + 3. You can now edit the `.json` file to have invalid data (e.g. having 2 profiles with the same ID, having invalid emails etc.) + 4. Re-launch the app by double-clicking the jar file.
+ Expected: ModuLink will now appear with no data. + + 1. To fix this, delete the `.json` file and re-launch the app. + Expected: ModuLink will work as normal now, with all changes the user previously made reverted. -1. _{ more test cases …​ }_ +[Back to top](#modulink-developer-guide) diff --git a/docs/SettingUp.md b/docs/SettingUp.md index 275445bd551..c7f0764f6cd 100644 --- a/docs/SettingUp.md +++ b/docs/SettingUp.md @@ -23,7 +23,7 @@ If you plan to use Intellij IDEA (highly recommended): 1. **Import the project as a Gradle project**: Follow the guide [_[se-edu/guides] IDEA: Importing a Gradle project_](https://se-education.org/guides/tutorials/intellijImportGradleProject.html) to import the project into IDEA.
:exclamation: Note: Importing a Gradle project is slightly different from importing a normal Java project. 1. **Verify the setup**: - 1. Run the `seedu.address.Main` and try a few commands. + 1. Run the `seedu.modulink.Main` and try a few commands. 1. [Run the tests](Testing.md) to ensure they all pass. -------------------------------------------------------------------------------------------------------------------- diff --git a/docs/Testing.md b/docs/Testing.md index 8a99e82438a..f2de109fa09 100644 --- a/docs/Testing.md +++ b/docs/Testing.md @@ -29,8 +29,8 @@ There are two ways to run tests. This project has three types of tests: 1. *Unit tests* targeting the lowest level methods/classes.
- e.g. `seedu.address.commons.StringUtilTest` + e.g. `seedu.modulink.commons.StringUtilTest` 1. *Integration tests* that are checking the integration of multiple code units (those code units are assumed to be working).
- e.g. `seedu.address.storage.StorageManagerTest` + e.g. `seedu.modulink.storage.StorageManagerTest` 1. Hybrids of unit and integration tests. These test are checking multiple code units as well as how the are connected together.
- e.g. `seedu.address.logic.LogicManagerTest` + e.g. `seedu.modulink.logic.LogicManagerTest` diff --git a/docs/Ui2.png b/docs/Ui2.png new file mode 100644 index 00000000000..73d1ba01e70 Binary files /dev/null and b/docs/Ui2.png differ diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 3716f3ca8a4..f8161534314 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -1,44 +1,115 @@ --- layout: page -title: User Guide +title: ModuLink - User Guide --- +## Welcome to ModuLink! -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. +ModuLink is a **desktop-based** application for **Computer Science (CS) students** at NUS to facilitate finding teammates for group-based modules. With a powerful integrated contact management platform, connecting with peers to form project groups has never been easier. ModuLink allows you to find students taking the modules you are interested in, search by their group status (to find students available to form or join groups), and much more. +
-* Table of Contents -{:toc} +The app is configured to use the **Command Line Interface (CLI)**, which means most of the commands you will interact with will be typed in a command box, rather than using a Graphical User Interface (GUI) with graphical tools and icons. If you are unaccustomed to the CLI, do not worry, as ModuLink still provides a lot of the benefits of a GUI to make working with text-based inputs easy to understand. +
+ +This User Guide will show you everything you need to know to optimize your experience with ModuLink, whether you are a new or proficient user. We recommend reading through **[Section 1: Using this User Guide](#section-1-using-this-user-guide)** if you are a first-time user. If you would like to skip to the features ModuLink provides, you can head to **[Section 3: Features - Understanding what ModuLink can do for you](#section-3-features---understanding-what-modulink-can-do-for-you)**. -------------------------------------------------------------------------------------------------------------------- -## Quick start +## Section 1: Using this User Guide +This guide has been designed to make sure you can quickly access any information you may need. At a glance, here is how it is structured: + +1. [Quick Start - Getting started with ModuLink (Recommended for first-time users)](#section-2-quick-start---getting-started-with-modulink) +2. [Features - Understanding what ModuLink can do for you](#section-3-features---understanding-what-modulink-can-do-for-you) +3. [Managing Data](#section-4-data-storage-in-modulink) +4. [Summaries](#section-5-summaries) +5. [Frequently Asked Questions](#section-6-frequently-asked-questions) + +We further recommend taking a glance at **[Section 1.1 Syntax and symbols](#section-11-syntax-and-symbols)** to familiarize yourself with the formats, icons and other terms this guide uses. + +### Section 1.1: Syntax and Symbols +Here are the commonly used syntax and symbols throughout the UG: + +`inline code`
denotes a command for the application.
+`prefix/`
denotes a ModuLink-supported prefix. -1. Ensure you have Java `11` or above installed in your Computer. +
:exclamation: Important
denotes an important information for the functioning of the app. +
-1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases). +
:warning: Caution
denotes a possible fault that could occur. +
-1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook. +
:information_source: Notes
denotes extra information to aid you in understanding the app. +
-1. Double-click the file to start the app. The GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
- ![Ui](images/Ui.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.
- Some example commands you can try: +-------------------------------------------------------------------------------------------------------------------- + +## Section 2: Quick Start - Getting Started with ModuLink + +### Section 2.1 One-time Set-Up +If you are using ModuLink for the first time, we will first walk you through a one-time set-up to get started. - * **`list`** : Lists all contacts. +1. Ensure you have Java 11 or above installed on your computer. You can easily do this via the **Terminal** app for Mac users, or the **Command Prompt** app for Windows users, and simply type and enter `java -version`. +
+Alternatively, if you prefer, you could also check the version via the GUI for both Mac and Windows systems, as mentioned in this [guide](https://phoenixnap.com/kb/check-java-version-on-mac-windows). +
+
+
:exclamation: Important: + ModuLink will not run on older versions of Java! If you do not have Java 11 installed, please install it by following the instructions in the official [guide](https://www.oracle.com/java/technologies/javase/jdk11-archive-downloads.html). +
+
+2. Download the latest **ModuLink.jar** from our website [here](https://github.com/AY2122S1-CS2103T-W12-4/tp/releases). +3. Move the file to the folder you want to use as the home folder for ModuLink on your computer. - * **`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. +### Section 2.2 Working with ModuLink +To run the app, simply double click the file. - * **`delete`**`3` : Deletes the 3rd contact shown in the current list. +Alternatively, you can open the app via the **Terminal** app for Mac users, or the **Command Prompt** app for Windows users. To do this, +1. `cd` into the directory you saved ModuLink in. +2. Type and enter `java -jar ModuLink.jar`. - * **`clear`** : Deletes all contacts. +You should see a GUI similar to the one shown below in a few seconds. The app will already contain data. Here’s a quick look at the different elements in the app. - * **`exit`** : Exits the app. +![Ui](images/screenshots/Ui2.png) -1. Refer to the [Features](#features) below for details of each command. +As mentioned earlier, ModuLink works as a CLI, which means you will need to type and enter commands in the command box. To get accustomed to the interface, try the following commands (in the given the order). + +* **`create n/John Doe id/A0123456A p/24680135 e/johndoe@example.com`** : Creates your ModuLink user profile with the name John Doe and the relevant student ID, phone number and email. +* **`addMod mod/CS2103T need group`**, followed by **`addMod mod/CS2101 need member`**: Adds CS2103T and CS2101 module tags to your profile and indicates that you need to form or join a group for CS2103T and that you need member(s) for your CS2101 group. +* **`list`**: Lists all the profiles on ModuLink. +* **`filter mod/CS2101`**: Filters all profiles who have CS2101 as one of their module tags. +* **`filter mod/CS2101 need group`**: Filters all profiles who have CS2101 as one of their module tags AND need to form or join a group for it. +* **`addFav A1234567R`**: Adds the profile with the student ID A1234567R to your favorites list. +* **`listFav`**: Lists all your favorite profiles. -------------------------------------------------------------------------------------------------------------------- -## Features +## Section 3: Features - Understanding what ModuLink can do for you + +To know what you can do with ModuLink, and what ModuLink can do for you, refer to the table of contents below to quickly navigate between sections.
+ +
:exclamation: Important: +If you are a first-time user, we recommend starting with [Create your own profile](#11-create-your-own-profile--create). This is because you can only use other commands in ModuLink after creating your own profile. +
+ +1. [Profiles](#1-profiles)
+ 1.1. [Create your own profile](#11-create-your-own-profile--create)
+ 1.2. [Edit your profile](#12-edit-your-profile--edit)
+2. [Manage module tags](#2-manage-module-tags)
+ 2.1. [Add modules to your profile](#21-add-modules-to-your-profile--addmod)
+ 2.2. [Edit the group status of existing modules on your profile](#22-edit-the-group-status-of-existing-modules-on-your-profile--editgroupstatus)
+ 2.3. [Remove modules from your profile](#23-remove-modules-from-your-profile--remmod)
+3. [Manage favourites](#3-manage-favorites)
+ 3.1. [Add a profile as a favourite](#31-add-a-profile-as-a-favorite--addfav)
+ 3.2. [Remove a profile from favourites list](#32-remove-a-profile-from-favourites-list--remfav)
+4. [Viewing options](#4-viewing-options)
+ 4.1. [List all profiles](#41-list-all-profiles--list)
+ 4.2. [List all profiles marked as favorite](#42-list-all-profiles-marked-as-favorite--listfav)
+ 4.3. [Find profiles by name](#43-find-profiles-by-name--find)
+ 4.4. [Find profiles by Student ID](#44-find-profiles-by-student-id--findid)
+ 4.5. [Filter profiles by module and group status](#45-filter-profiles-by-module-and-group-status--filter)
+5. [Utility commands](#5-utility-commands)
+ 5.1 [Viewing help](#51-viewing-help--help)
+ 5.2 [Exiting ModuLink](#52-exiting-modulink--exit)
@@ -46,147 +117,325 @@ AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized fo * 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`. + +* Prefixes and commands are case-sensitive.
* 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`. + e.g `n/NAME [mod/MODULE]` can be used as `n/John Doe mod/CS2103T` 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. `[mod/MODULE]…​` can be used as ` ` (i.e. 0 times), `mod/CS2100`, `mod/CS2100 mod/CS2103T` etc. * 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.
- 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.
+* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, and `exit`) will be ignored.
e.g. if the command specifies `help 123`, it will be interpreted as `help`.
-### Viewing help : `help` +### 1. Profiles -Shows a message explaning how to access the help page. +#### 1.1 Create your own profile : `create` -![help message](images/helpMessage.png) +Creates your user profile. You can also choose to add modules and indicate your group status for each module. +
:exclamation: Important: +in order to start using ModuLink, you are **required** to create a new profile should you not have one. +
-Format: `help` +Format: `create n/NAME id/STUDENT_ID p/PHONE_NUMBER e/EMAIL [github/GITHUB_USERNAME] [tele/TELEGRAM_HANDLE] [mod/MODULE [GROUP_STATUS]]...` +
+**:information_source: Notes:** +* Every STUDENT_ID in the database must be unique. +* You can choose to input your telegram handle as either starting with '@' (e.g.: @teleHandle), or just the handle itself (e.g.: teleHandle) +* The group statuses available are: Need member, Need group, Don't need group/Not looking for group. +* The default status when a module is added without a description is 'Don't need group/Not looking for group'. The respective module will be displayed as a blue tag. +* To indicate you need members for your group, you can include the description 'need member'. The respective module will be displayed as a yellow tag. +* To indicate you are looking for a group, you can include the description 'need group'. The respective module will be displayed as a red tag. +
+ +Examples: +* `create n/John Doe id/A0222594A p/12345678 e/john.doe@example.com github/johndoe mod/CS2103T` +* `create n/Jane Doe id/A0222594A p/87654321 e/jane_doe@example.com tele/@janedoe mod/CS2101 need group` +* `create n/Alexa Tan id/A0012367N p/998877662 e/alexa.tan@example.com mod/CS2030S need member mod/CS2100` +
+
+![result for 'create n/Alexa Tan id/A0012367N p/998877662 e/alexa.tan@example.com mod/CS2030S need member mod/CS2100'](images/screenshots/createProfile.png) -### Adding a person: `add` +#### 1.2 Edit your profile : `edit` -Adds a person to the address book. +Edits your own profile. You can choose to edit any attribute in your own profile, except for module tags which use separate commands. You can edit multiple attributes at once. -Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` +Format: `edit EDITED_ATTRIBUTE [MORE ATTRIBUTES]...` -
:bulb: **Tip:** -A person can have any number of tags (including 0) +Prefixes for editable attributes: +* NAME: `n/` +* STUDENT ID: `id/` +* PHONE: `p/` +* EMAIL: `e/` +* GITHUB: `github/` +* TELE: `tele/` + +Examples: +* `edit p/123321432 e/changedemail@example.com` +* `edit github/alexatan` +
+
+![result for 'edit p/99881234'](images/screenshots/editProfile.png) + +### 2. Manage module tags +Module tags are colour coded. +* Blue tags indicate that the user is not looking for a group for that specific module. +* Yellow tags indicate that the user is looking for other students to join their group for that specific module. +* Red tags indicate that the user has no group and is looking to join one for that specific module.
+![tag explanation](images/screenshots/tagexplanation.PNG) + +#### 2.1 Add modules to your profile : `addMod` + +Adds the user-specified module to your own profile. + +Format: `addMod mod/MODULE [GROUP_STATUS]` + +Examples: +* `addMod mod/CS2103T` +* `addMod mod/CS2220 need group` +
+
+ ![result for 'addMod mod/CS2220 need group'](images/screenshots/addMod.png) + + +#### 2.2 Edit the group status of existing modules on your profile : `editGroupStatus` + +Edits the group status of user-specified module in your module list. + +Format: `editGroupStatus mod/MODULE [NEW_STATUS_DESCRIPTION]` + +
+**:information_source: Notes:** +* If no updated status description is given, the group status is set to the default 'Don't need group/Not looking for group'.
+ +Examples: +* `editGroupStatus mod/CS2220` +* `editGroupStatus mod/CS2030S need member` +* `editGroupStatus mod/CS2100 need group` +
+
+ Original profile: + ![result for 'original'](images/screenshots/originalForegsc.png) + + Updated profile: + ![result for 'updated'](images/screenshots/egsc.png) + + +#### 2.3 Remove modules from your profile : `remMod` + +Removes the user-specified module from your own profile. + +Format: `remMod mod/MODULE` 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` +* `remMod mod/CS2103T` +* `remMod mod/CS2220` +
+
+ ![result for 'remMod mod/CS2220'](images/screenshots/remMod.png) + + -### Listing all persons : `list` +### 3. Manage favorites -Shows a list of all persons in the address book. +#### 3.1 Add a profile as a favorite : `addFav` -Format: `list` +Adds a profile to your favourites list. Favourited profiles will have a star beside their name. -### Editing a person : `edit` +Format: `addFav STUDENT_ID` -Edits an existing person in the address book. +
+**:information_source: Notes:** +* You cannot add your own profile as a favourite. +
-Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` +Examples: +* `addFav A0222594A` +* `addFav A1234567R` +
+
+ ![result for 'addfav A1234967R'](images/screenshots/addFav.png) -* 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. +### 3.2 Remove a profile from favourites list : `remFav` + +Removes a user-specified profile from your favourites list. + +Format: `remFav STUDENT_ID` 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. +* `remFav A0212345X` +* `remFav A1234567R` +
+
+ ![result for 'remfav A1234567R'](images/screenshots/remFav.png) + +### 4. Viewing options + +#### 4.1 List all profiles : `list` -### Locating persons by name: `find` +Shows a list of all profiles in ModuLink. -Finds persons whose names contain any of the given keywords. +Format: `list` +
+
+![result for 'list'](images/screenshots/list.png) + +#### 4.2 List all profiles marked as favorite : `listFav` + +Shows a list of all profiles that you have added to your favourites list. + +Format: `listFav` +
+
+![result for 'listFav'](images/screenshots/listFav.png) + +#### 4.3 Find profiles by name : `find` -Format: `find KEYWORD [MORE_KEYWORDS]` +Finds profiles whose names contain any of the entered keywords. -* 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` +Format: `find KEYWORD [MORE_KEYWORDS]...` + +* The search is not case-sensitive. 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` -* Persons matching at least one keyword will be returned (i.e. `OR` search). - e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang` +* Only alphanumeric characters can be entered. +* Only full words will be matched e.g. Han will not match Hans +* Profiles matching at least one of the specified keywords will be returned. Examples: -* `find John` returns `john` and `John Doe` -* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png) +* `find John` returns `John` and `John Doe` +* `find bernice` returns `Bernice Yu` +* `find alex david` returns `Alex Berenson` and `David Li` +
+
+ ![result for 'find bernice'](images/screenshots/find.png) -### Deleting a person : `delete` +#### 4.4 Find profiles by student ID : `findId` -Deletes the specified person from the address book. +Finds profiles whose student ID number matches any of the entered keywords. -Format: `delete INDEX` +Format: `findId STUDENT_ID [MORE_STUDENT_IDS]...` -* Deletes 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, …​ +* The search is not case-sensitive. e.g a0123456a will match A023456A +* The order of the student IDs does not matter. e.g. `findId A0123456A A0654321A` will show the profiles whose student Id number matches either A0123456A or A0654321A. +* Only the student ID numbers are searched. +* Profiles matching any keywords will be returned. Examples: -* `list` followed by `delete 2` deletes the 2nd person in the address book. -* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command. +* `findId A1204567S` returns the person whose student ID number matches A1204567S exactly. +* `findId A1234567R A1234567H` returns the profiles whose student ID number matches either A1234567R or A1234567H exactly. +
+
+ ![result for 'findId A1234567R A1234567H'](images/screenshots/findId.png) + +#### 4.5 Filter profiles by module and group status : `filter` + +Show profiles filtered by module code and _optionally_ by group status. + +Format: `filter mod/MODULE_CODE [GROUP_STATUS]` + +* You can filter by profiles who need to join or form a group ('need group') or profiles who need members for their group ('need member'). +* `MODULE_CODE` is required for filtering by group status. The filter will return the profiles with the specified group status of the specified module. +
+**:information_source: Notes:** +* Your own profile does not appear when filtering. +
+Examples +* `filter mod/CS2100` +* `filter mod/CS2100 need member` +* `filter mod/CS2100 need group` +
+
+ ![result for 'filter mod/CS2100'](images/screenshots/filtermod.png) +
+
+ ![result for 'filter mod/CS210' need memmber](images/screenshots/filtermodgroup.png) -### Clearing all entries : `clear` -Clears all entries from the address book. +### 5. Utility commands -Format: `clear` +#### 5.1 Viewing help : `help` -### Exiting the program : `exit` +Shows a message explaning how to access the help page. Upon entering the command, a pop-up window will appear. Follow the link on the window to access the User Guide. + +Format: `help` +
+
+![help message](images/screenshots/helpMessage.png) + +#### 5.2 Exiting ModuLink : `exit` Exits the program. Format: `exit` +## Section 4: Data Storage in ModuLink + ### 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. +ModuLink's 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. +ModuLink's data are saved as a JSON file `[JAR file location]/data/modulink.json`. -
: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. +
:warning: Caution: +If your changes to the data file makes its format invalid, ModuLink will discard all data and start with an empty data file at the next run. +In this event, please locate the data file, delete it, and relaunch the app.
-### Archiving data files `[coming in v2.0]` - -_Details coming soon ..._ +
:warning: Caution: +We advise against changing the `isMyProfile` and `isFavourite` fields of your own profile should you wish to edit the JSON file directly. +
-------------------------------------------------------------------------------------------------------------------- -## FAQ +## Section 5: Summaries -**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. +### Parameter summary --------------------------------------------------------------------------------------------------------------------- +Prefix | Parameter +-------|----------- +**`n/`** | NAME +**`id/`** | STUDENT ID +**`p/`** | PHONE NUMBER +**`e/`** | EMAIL +**`github/`** | GITHUB USERNAME +**`tele/`** | TELEGRAM HANDLE +**`mod/`** | MODULE -## Command summary +### Command summary Action | Format, Examples ---------|------------------ -**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​`
e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague` -**Clear** | `clear` -**Delete** | `delete INDEX`
e.g., `delete 3` -**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…​`
e.g.,`edit 2 n/James Lee e/jameslee@example.com` -**Find** | `find KEYWORD [MORE_KEYWORDS]`
e.g., `find James Jake` +-------|----------------- +**Create** | `create n/NAME id/STUDENT_ID p/PHONE_NUMBER e/EMAIL [github/GITHUB_USERNAME] [tele/TELEGRAM_HANDLE] [mod/MODULE [GROUP STATUS]]...`

e.g., `create n/John Doe id/A0222594A p/12345678 e/john.doe@example.com mod/CS2100` +**Edit** | `edit EDITED_ATTRIBUTE [MORE_ATTRIBUTES]...`

e.g., `edit p/123321432 e/changedemail@example.com` +**Add module** | `addMod mod/MODULE [GROUP_STATUS]`

e.g., `addMod mod/CS2103T need member` +**Edit module group status** | `editGroupStatus mod/MODULE [NEW_STATUS_DESCRIPTION]`

e.g., `editGroupStatus mod/CS2103T need group` +**Remove module** | `remMod mod/MODULE`

e.g., `remMod mod/CS2100` +**Add Favourite** | `addFav STUDENT_ID`

e.g., `addFav A0222594A` +**Remove Favourite** | `remFav STUDENT_ID`

e.g., `remFav A0222594A` **List** | `list` +**List favorites** | `listFav` +**Find by name** | `find KEYWORD [MORE_KEYWORDS]...`
e.g., `find alex david` returns `Alex Yeoh, David Li` +**Find by student ID** | `findId STUDENT_ID [MORE_STUDENT_IDS]...`
e.g., `findId A0222594A` returns person with student Id matching A0222594A. +**Filter** | `filter mod/MODULE_CODE [group/GROUP_STATUS]`
e.g. no group filter: `filter mod/CS2030`
with group filter: `filter mod/CS2030 need group` **Help** | `help` +**Exit** | `exit` + +-------------------------------------------------------------------------------------------------------------------- + +## Section 6: Frequently Asked Questions + +**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 ModuLink home folder. + +-------------------------------------------------------------------------------------------------------------------- diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..188ad5ab3bc 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: "AB-3" +title: "ModuLink" theme: minima header_pages: @@ -8,7 +8,7 @@ header_pages: markdown: kramdown -repository: "se-edu/addressbook-level3" +repository: "AY2122S1-CS2103T-W12-4/tp" github_icon: "images/github-icon.png" plugins: diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss index 0d3f6e80ced..e13c60d4b93 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: "ModuLink"; font-size: 32px; } } diff --git a/docs/diagrams/AddFavSequenceDiagram.puml b/docs/diagrams/AddFavSequenceDiagram.puml new file mode 100644 index 00000000000..0845e4ccc1a --- /dev/null +++ b/docs/diagrams/AddFavSequenceDiagram.puml @@ -0,0 +1,75 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":ModuLinkParser" as ModuLinkParser LOGIC_COLOR +participant ":AddFavCommandParser" as AddFavCommandParser LOGIC_COLOR +participant "a:AddFavCommand" as AddFavCommand 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("addFav A0123456A") +activate LogicManager + +LogicManager -> ModuLinkParser : parseCommand("addFav A0123456A") +activate ModuLinkParser + +create AddFavCommandParser +ModuLinkParser -> AddFavCommandParser +activate AddFavCommandParser + +AddFavCommandParser --> ModuLinkParser +deactivate AddFavCommandParser + +ModuLinkParser -> AddFavCommandParser : parse("A0123456A") +activate AddFavCommandParser + +create AddFavCommand +AddFavCommandParser -> AddFavCommand +activate AddFavCommand + +AddFavCommand --> AddFavCommandParser : a +deactivate AddFavCommand + +AddFavCommandParser --> ModuLinkParser : a +deactivate AddFavCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +AddFavCommandParser -[hidden]-> ModuLinkParser +destroy AddFavCommandParser + +ModuLinkParser --> LogicManager : a +deactivate ModuLinkParser + +LogicManager -> AddFavCommand : execute() +activate AddFavCommand + +AddFavCommand -> Model : getPersonList() +activate Model + +Model --> AddFavCommand +deactivate Model + +AddFavCommand -> Model : updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS) +activate Model + +Model --> AddFavCommand +deactivate Model + +create CommandResult +AddFavCommand -> CommandResult +activate CommandResult + +CommandResult --> AddFavCommand +deactivate CommandResult + +AddFavCommand --> LogicManager : result +deactivate AddFavCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/AddModSequenceDiagram.puml b/docs/diagrams/AddModSequenceDiagram.puml new file mode 100644 index 00000000000..e19ce0f4c70 --- /dev/null +++ b/docs/diagrams/AddModSequenceDiagram.puml @@ -0,0 +1,75 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":ModuLinkParser" as ModuLinkParser LOGIC_COLOR +participant ":AddModCommandParser" as AddModCommandParser LOGIC_COLOR +participant "a:AddModCommand" as AddModCommand 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("addMod mod/CS2101") +activate LogicManager + +LogicManager -> ModuLinkParser : parseCommand("addMod mod/CS2101") +activate ModuLinkParser + +create AddModCommandParser +ModuLinkParser -> AddModCommandParser +activate AddModCommandParser + +AddModCommandParser --> ModuLinkParser +deactivate AddModCommandParser + +ModuLinkParser -> AddModCommandParser : parse("mod/CS2101") +activate AddModCommandParser + +create AddModCommand +AddModCommandParser -> AddModCommand +activate AddModCommand + +AddModCommand --> AddModCommandParser : a +deactivate AddModCommand + +AddModCommandParser --> ModuLinkParser : a +deactivate AddModCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +AddModCommandParser -[hidden]-> ModuLinkParser +destroy AddModCommandParser + +ModuLinkParser --> LogicManager : a +deactivate ModuLinkParser + +LogicManager -> AddModCommand : execute() +activate AddModCommand + +AddModCommand -> Model : setPerson(myProfile, editedProfile) +activate Model + +Model --> AddModCommand +deactivate Model + +AddModCommand -> Model : updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS) +activate Model + +Model --> AddModCommand +deactivate Model + +create CommandResult +AddModCommand -> CommandResult +activate CommandResult + +CommandResult --> AddModCommand +deactivate CommandResult + +AddModCommand --> LogicManager : result +deactivate AddModCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml index ef81d18c337..10166f8c88b 100644 --- a/docs/diagrams/ArchitectureSequenceDiagram.puml +++ b/docs/diagrams/ArchitectureSequenceDiagram.puml @@ -7,19 +7,19 @@ Participant ":Logic" as logic LOGIC_COLOR Participant ":Model" as model MODEL_COLOR Participant ":Storage" as storage STORAGE_COLOR -user -[USER_COLOR]> ui : "delete 1" +user -[USER_COLOR]> ui : "addFav A0123456A" activate ui UI_COLOR -ui -[UI_COLOR]> logic : execute("delete 1") +ui -[UI_COLOR]> logic : execute("addFav A0123456A") activate logic LOGIC_COLOR -logic -[LOGIC_COLOR]> model : deletePerson(p) +logic -[LOGIC_COLOR]> model : addFav(A0123456A) activate model MODEL_COLOR model -[MODEL_COLOR]-> logic deactivate model -logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook) +logic -[LOGIC_COLOR]> storage : saveModuLink(moduLink) activate storage STORAGE_COLOR storage -[STORAGE_COLOR]> storage : Save to file diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml index 5731f9cbaa1..bb1bcf254d8 100644 --- a/docs/diagrams/BetterModelClassDiagram.puml +++ b/docs/diagrams/BetterModelClassDiagram.puml @@ -4,8 +4,8 @@ skinparam arrowThickness 1.1 skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR -AddressBook *-right-> "1" UniquePersonList -AddressBook *-right-> "1" UniqueTagList +ModuLink *-right-> "1" UniquePersonList +ModuLink *-right-> "1" UniqueTagList UniqueTagList -[hidden]down- UniquePersonList UniqueTagList -[hidden]down- UniquePersonList @@ -17,5 +17,5 @@ Person -up-> "*" Tag Person *--> Name Person *--> Phone Person *--> Email -Person *--> Address + @enduml diff --git a/docs/diagrams/CommitActivityDiagram.puml b/docs/diagrams/CommitActivityDiagram.puml index 6a6b23a006f..83f64f2bb57 100644 --- a/docs/diagrams/CommitActivityDiagram.puml +++ b/docs/diagrams/CommitActivityDiagram.puml @@ -5,10 +5,10 @@ start 'Since the beta syntax does not support placing the condition outside the 'diamond we place it as the true branch instead. -if () then ([command commits AddressBook]) +if () then ([command commits ModuLink]) :Purge redundant states; - :Save AddressBook to - addressBookStateList; + :Save ModuLink to + moduLinkStateList; else ([else]) endif stop diff --git a/docs/diagrams/CreateSequenceDiagram.puml b/docs/diagrams/CreateSequenceDiagram.puml new file mode 100644 index 00000000000..d650cef2730 --- /dev/null +++ b/docs/diagrams/CreateSequenceDiagram.puml @@ -0,0 +1,69 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":ModuLinkParser" as ModuLinkParser LOGIC_COLOR +participant ":CreateCommandParser" as CreateCommandParser LOGIC_COLOR +participant "p:CreateCommand" as CreateCommand 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("create n/... id/... p/... e/...") +activate LogicManager + +LogicManager -> ModuLinkParser : parseCommand("create n/... id/... p/... e/...") +activate ModuLinkParser + +create CreateCommandParser +ModuLinkParser -> CreateCommandParser +activate CreateCommandParser + +CreateCommandParser --> ModuLinkParser +deactivate CreateCommandParser + +ModuLinkParser -> CreateCommandParser : parse("n/... id/... p/... e/...") +activate CreateCommandParser + +create CreateCommand +CreateCommandParser -> CreateCommand +activate CreateCommand + +CreateCommand --> CreateCommandParser : p +deactivate CreateCommand + +CreateCommandParser --> ModuLinkParser : p +deactivate CreateCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +CreateCommandParser -[hidden]-> ModuLinkParser +destroy CreateCommandParser + +ModuLinkParser --> LogicManager : p +deactivate ModuLinkParser + +LogicManager -> CreateCommand : execute() +activate CreateCommand + +CreateCommand -> Model : createProfile(myProfile) +activate Model + +Model --> CreateCommand +deactivate Model + +create CommandResult +CreateCommand -> CommandResult +activate CommandResult + +CommandResult --> CreateCommand +deactivate CommandResult + +CreateCommand --> LogicManager : result +deactivate CreateCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/EditGroupStatusCommandSequenceDiagram.puml b/docs/diagrams/EditGroupStatusCommandSequenceDiagram.puml new file mode 100644 index 00000000000..d5e3cabbc16 --- /dev/null +++ b/docs/diagrams/EditGroupStatusCommandSequenceDiagram.puml @@ -0,0 +1,75 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":ModuLinkParser" as ModuLinkParser LOGIC_COLOR +participant ":EditGroupStatusCommandParser" as EditGroupStatusCommandParser LOGIC_COLOR +participant "p:EditGroupStatusCommand" as EditGroupStatusCommand 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("editGroupStatus mod/CS2101 need member") +activate LogicManager + +LogicManager -> ModuLinkParser : parseCommand("editGroupStatus mod/CS2101 need member") +activate ModuLinkParser + +create EditGroupStatusCommandParser +ModuLinkParser -> EditGroupStatusCommandParser +activate EditGroupStatusCommandParser + +EditGroupStatusCommandParser --> ModuLinkParser +deactivate EditGroupStatusCommandParser + +ModuLinkParser -> EditGroupStatusCommandParser : parse("mod/CS2101 need member") +activate EditGroupStatusCommandParser + +create EditGroupStatusCommand +EditCommandParser -> EditGroupStatusCommand +activate EditGroupStatusCommand + +EditGroupStatusCommand --> EditGroupStatusCommandParser : e +deactivate EditGroupStatusCommand + +EditGroupStatusCommandParser --> ModuLinkParser : e +deactivate EditGroupStatusCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +EditGroupStatusCommandParser -[hidden]-> ModuLinkParser +destroy EditGroupStatusCommandParser + +ModuLinkParser --> LogicManager : e +deactivate ModuLinkParser + +LogicManager -> EditGroupStatusCommand : execute() +activate EditGroupStatusCommand + +EditCommand -> Model : setPerson(myProfile, editedProfile) +activate Model + +Model --> EditGroupStatusCommand +deactivate Model + +EditCommand -> Model : updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS) +activate Model + +Model --> EditGroupStatusCommand +deactivate Model + +create CommandResult +EditCommand -> CommandResult +activate CommandResult + +CommandResult --> EditGroupStatusCommand +deactivate CommandResult + +EditCommand --> LogicManager : result +deactivate EditGroupStatusCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/EditSequenceDiagram.puml b/docs/diagrams/EditSequenceDiagram.puml new file mode 100644 index 00000000000..4b6d1ba0dc7 --- /dev/null +++ b/docs/diagrams/EditSequenceDiagram.puml @@ -0,0 +1,75 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":ModuLinkParser" as ModuLinkParser LOGIC_COLOR +participant ":EditCommandParser" as EditCommandParser LOGIC_COLOR +participant "p: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("edit n/...") +activate LogicManager + +LogicManager -> ModuLinkParser : parseCommand("edit n/...") +activate ModuLinkParser + +create EditCommandParser +ModuLinkParser -> EditCommandParser +activate EditCommandParser + +EditCommandParser --> ModuLinkParser +deactivate EditCommandParser + +ModuLinkParser -> EditCommandParser : parse("n/...") +activate EditCommandParser + +create EditCommand +EditCommandParser -> EditCommand +activate EditCommand + +EditCommand --> EditCommandParser : p +deactivate EditCommand + +EditCommandParser --> ModuLinkParser : p +deactivate EditCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +EditCommandParser -[hidden]-> ModuLinkParser +destroy EditCommandParser + +ModuLinkParser --> LogicManager : p +deactivate ModuLinkParser + +LogicManager -> EditCommand : execute() +activate EditCommand + +EditCommand -> Model : setPerson(myProfile, editedProfile) +activate Model + +Model --> EditCommand +deactivate Model + +EditCommand -> Model : updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS) +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/FilterSequenceDiagram.puml b/docs/diagrams/FilterSequenceDiagram.puml new file mode 100644 index 00000000000..b2c6c1206fd --- /dev/null +++ b/docs/diagrams/FilterSequenceDiagram.puml @@ -0,0 +1,79 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":ModuLinkParser" as ModuLinkParser LOGIC_COLOR +participant ":FilterCommandParser" as FilterCommandParser LOGIC_COLOR +participant "f:FilterCommand" as FilterCommand LOGIC_COLOR +participant "pred:ModuleContainsKeywordsPredicate" as NameContainsKeywordsPredicate 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("filter mod/CS2101") +activate LogicManager + +LogicManager -> ModuLinkParser : parseCommand("filter mod/CS2101")") +activate ModuLinkParser + +create FilterCommandParser +ModuLinkParser -> FilterCommandParser +activate FilterCommandParser + +FilterCommandParser --> ModuLinkParser +deactivate FilterCommandParser + +ModuLinkParser -> FilterCommandParser : parse("mod/CS2101") +activate FilterCommandParser + +create FilterCommand +FilterCommandParser -> FilterCommand +activate FilterCommand + + +create NameContainsKeywordsPredicate +FilterCommand -> NameContainsKeywordsPredicate +activate NameContainsKeywordsPredicate + +NameContainsKeywordsPredicate --> FilterCommand : pred +deactivate NameContainsKeywordsPredicate + +FilterCommand --> FilterCommandParser : f +deactivate FilterCommand + +FilterCommandParser --> ModuLinkParser : f +deactivate FilterCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +FilterCommandParser -[hidden]-> ModuLinkParser +destroy FilterCommandParser + +ModuLinkParser --> LogicManager : f +deactivate ModuLinkParser + +LogicManager -> FilterCommand : execute() +activate FilterCommand + + +FilterCommand -> Model : updateFilteredPersonList(pred) +activate Model + +Model --> FilterCommand +deactivate Model + +create CommandResult +FilterCommand -> CommandResult +activate CommandResult + +CommandResult --> FilterCommand +deactivate CommandResult + +FilterCommand --> LogicManager : result +deactivate FilterCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/FindIdSequenceDiagram.puml b/docs/diagrams/FindIdSequenceDiagram.puml new file mode 100644 index 00000000000..0eb81ba539d --- /dev/null +++ b/docs/diagrams/FindIdSequenceDiagram.puml @@ -0,0 +1,79 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":ModuLinkParser" as ModuLinkParser LOGIC_COLOR +participant ":FindIdCommandParser" as FindIdCommandParser LOGIC_COLOR +participant "f:FindIdCommand" as FindIdCommand LOGIC_COLOR +participant "pred:StudentIdContainsKeywordsPredicate" as StudentIdContainsKeywordsPredicate 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("findId A0123456A") +activate LogicManager + +LogicManager -> ModuLinkParser : parseCommand("findId A0123456A") +activate ModuLinkParser + +create FindIdCommandParser +ModuLinkParser -> FindIdCommandParser +activate FindIdCommandParser + +FindIdCommandParser --> ModuLinkParser +deactivate FindIdCommandParser + +ModuLinkParser -> FindIdCommandParser : parse("A0123456A") +activate FindIdCommandParser + +create FindIdCommand +FindIdCommandParser -> FindIdCommand +activate FindIdCommand + + +create StudentIdContainsKeywordsPredicate +FindIdCommand -> StudentIdContainsKeywordsPredicate +activate StudentIdContainsKeywordsPredicate + +StudentIdContainsKeywordsPredicate --> FindIdCommand : pred +deactivate StudentIdContainsKeywordsPredicate + +FindIdCommand --> FindIdCommandParser : f +deactivate FindIdCommand + +FindIdCommandParser --> ModuLinkParser : f +deactivate FindIdCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +FindIdCommandParser -[hidden]-> ModuLinkParser +destroy FindIdCommandParser + +ModuLinkParser --> LogicManager : f +deactivate ModuLinkParser + +LogicManager -> FindIdCommand : execute() +activate FindIdCommand + + +FindIdCommand -> Model : updateFilteredPersonList(pred) +activate Model + +Model --> FindIdCommand +deactivate Model + +create CommandResult +FindIdCommand -> CommandResult +activate CommandResult + +CommandResult --> FindIdCommand +deactivate CommandResult + +FindIdCommand --> LogicManager : result +deactivate FindIdCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/FindSequenceDiagram.puml b/docs/diagrams/FindSequenceDiagram.puml new file mode 100644 index 00000000000..785636af85d --- /dev/null +++ b/docs/diagrams/FindSequenceDiagram.puml @@ -0,0 +1,79 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":ModuLinkParser" as ModuLinkParser LOGIC_COLOR +participant ":FindCommandParser" as FindCommandParser LOGIC_COLOR +participant "f:FindCommand" as FindCommand LOGIC_COLOR +participant "pred:NameContainsKeywordsPredicate" as NameContainsKeywordsPredicate 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("find Roy") +activate LogicManager + +LogicManager -> ModuLinkParser : parseCommand("find Roy") +activate ModuLinkParser + +create FindCommandParser +ModuLinkParser -> FindCommandParser +activate FindCommandParser + +FindCommandParser --> ModuLinkParser +deactivate FindCommandParser + +ModuLinkParser -> FindCommandParser : parse("Roy") +activate FindCommandParser + +create FindCommand +FindCommandParser -> FindCommand +activate FindCommand + + +create NameContainsKeywordsPredicate +FindCommand -> NameContainsKeywordsPredicate +activate NameContainsKeywordsPredicate + +NameContainsKeywordsPredicate --> FindCommand : pred +deactivate NameContainsKeywordsPredicate + +FindCommand --> FindCommandParser : f +deactivate FindCommand + +FindCommandParser --> ModuLinkParser : f +deactivate FindCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +FindCommandParser -[hidden]-> ModuLinkParser +destroy FindCommandParser + +ModuLinkParser --> LogicManager : f +deactivate ModuLinkParser + +LogicManager -> FindCommand : execute() +activate FindCommand + + +FindCommand -> Model : updateFilteredPersonList(pred) +activate Model + +Model --> FindCommand +deactivate Model + +create CommandResult +FindCommand -> CommandResult +activate CommandResult + +CommandResult --> FindCommand +deactivate CommandResult + +FindCommand --> LogicManager : result +deactivate FindCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ListFavSequenceDiagram.puml b/docs/diagrams/ListFavSequenceDiagram.puml new file mode 100644 index 00000000000..61f8496c743 --- /dev/null +++ b/docs/diagrams/ListFavSequenceDiagram.puml @@ -0,0 +1,61 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":ModuLinkParser" as ModuLinkParser LOGIC_COLOR +participant ":ListFavCommand" as ListFavCommand LOGIC_COLOR +participant "pred:IsFavouritePredicate" as IsFavouritePredicate 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("listFav") +activate LogicManager + +LogicManager -> ModuLinkParser : parseCommand("listFav") +activate ModuLinkParser + +create ListFavCommand +ModuLinkParser -> ListFavCommand +activate ListFavCommand + +ListFavCommand --> ModuLinkParser +deactivate ListFavCommand + +ModuLinkParser --> LogicManager +deactivate ModuLinkParser + +LogicManager -> ListFavCommand : execute() +activate ListFavCommand + +create IsFavouritePredicate +ListFavCommand -> IsFavouritePredicate +activate IsFavouritePredicate + +IsFavouritePredicate --> ListFavCommand :pred +deactivate IsFavouritePredicate + +ListFavCommand -> Model : updateFilteredPersonList(pred) +activate Model + +Model --> ListFavCommand +deactivate Model + +create CommandResult +ListFavCommand -> CommandResult +activate CommandResult + +CommandResult --> ListFavCommand +deactivate CommandResult + +ListFavCommand --> LogicManager +deactivate ListFavCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml index 6d14b17b361..49137041e0e 100644 --- a/docs/diagrams/LogicClassDiagram.puml +++ b/docs/diagrams/LogicClassDiagram.puml @@ -6,7 +6,7 @@ skinparam classBackgroundColor LOGIC_COLOR package Logic { -Class AddressBookParser +Class ModuLinkParser Class XYZCommand Class CommandResult Class "{abstract}\nCommand" as Command @@ -27,8 +27,8 @@ Class HiddenOutside #FFFFFF HiddenOutside ..> Logic LogicManager .right.|> Logic -LogicManager -right->"1" AddressBookParser -AddressBookParser ..> XYZCommand : creates > +LogicManager -right->"1" ModuLinkParser +ModuLinkParser ..> XYZCommand : creates > XYZCommand -up-|> Command LogicManager .left.> Command : executes > diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 1122257bd9a..ab601eaa83f 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -5,11 +5,11 @@ skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR Package Model <>{ -Interface ReadOnlyAddressBook <> +Interface ReadOnlyModuLink <> Interface ReadOnlyUserPrefs <> Interface Model <> -Class AddressBook -Class ReadOnlyAddressBook +Class ModuLink +Class ReadOnlyModuLink Class Model Class ModelManager Class UserPrefs @@ -18,37 +18,40 @@ Class ReadOnlyUserPrefs Class UniquePersonList Class Person -Class Address +Class StudentId Class Email Class Name Class Phone -Class Tag +Class Telegram +Class Github +Class Mod } Class HiddenOutside #FFFFFF HiddenOutside ..> Model -AddressBook .up.|> ReadOnlyAddressBook +ModuLink .up.|> ReadOnlyModuLink ModelManager .up.|> Model Model .right.> ReadOnlyUserPrefs -Model .left.> ReadOnlyAddressBook -ModelManager -left-> "1" AddressBook +Model .left.> ReadOnlyModuLink +ModelManager -left-> "1" ModuLink ModelManager -right-> "1" UserPrefs UserPrefs .up.|> ReadOnlyUserPrefs -AddressBook *--> "1" UniquePersonList +ModuLink *--> "1" UniquePersonList UniquePersonList --> "~* all" Person Person *--> Name Person *--> Phone Person *--> Email -Person *--> Address -Person *--> "*" Tag +Person *--> StudentId +Person *--> "0..1" Telegram +Person *--> "0..1" Github +Person *--> "*" Mod Name -[hidden]right-> Phone -Phone -[hidden]right-> Address -Address -[hidden]right-> Email + ModelManager -->"~* filtered" Person @enduml diff --git a/docs/diagrams/ParserClasses.puml b/docs/diagrams/ParserClasses.puml index 6ba585cba01..228a1c1d485 100644 --- a/docs/diagrams/ParserClasses.puml +++ b/docs/diagrams/ParserClasses.puml @@ -9,7 +9,7 @@ Class XYZCommand package "Parser classes"{ Interface Parser <> -Class AddressBookParser +Class ModuLinkParser Class XYZCommandParser Class CliSyntax Class ParserUtil @@ -19,12 +19,12 @@ Class Prefix } Class HiddenOutside #FFFFFF -HiddenOutside ..> AddressBookParser +HiddenOutside ..> ModuLinkParser -AddressBookParser .down.> XYZCommandParser: creates > +ModuLinkParser .down.> XYZCommandParser: creates > XYZCommandParser ..> XYZCommand : creates > -AddressBookParser ..> Command : returns > +ModuLinkParser ..> Command : returns > XYZCommandParser .up.|> Parser XYZCommandParser ..> ArgumentMultimap XYZCommandParser ..> ArgumentTokenizer diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml index 85ac3ea2dee..67a036abbe5 100644 --- a/docs/diagrams/StorageClassDiagram.puml +++ b/docs/diagrams/StorageClassDiagram.puml @@ -14,10 +14,10 @@ Class JsonUserPrefsStorage Interface Storage <> Class StorageManager -package "AddressBook Storage" #F4F6F6{ -Interface AddressBookStorage <> -Class JsonAddressBookStorage -Class JsonSerializableAddressBook +package "ModuLink Storage" #F4F6F6{ +Interface ModuLinkStorage <> +Class JsonModuLinkStorage +Class JsonSerializableModuLink Class JsonAdaptedPerson Class JsonAdaptedTag } @@ -29,15 +29,15 @@ HiddenOutside ..> Storage StorageManager .up.|> Storage StorageManager -up-> "1" UserPrefsStorage -StorageManager -up-> "1" AddressBookStorage +StorageManager -up-> "1" ModuLinkStorage Storage -left-|> UserPrefsStorage -Storage -right-|> AddressBookStorage +Storage -right-|> ModuLinkStorage JsonUserPrefsStorage .up.|> UserPrefsStorage -JsonAddressBookStorage .up.|> AddressBookStorage -JsonAddressBookStorage ..> JsonSerializableAddressBook -JsonSerializableAddressBook --> "*" JsonAdaptedPerson +JsonModuLinkStorage .up.|> ModuLinkStorage +JsonModuLinkStorage ..> JsonSerializableModuLink +JsonSerializableModuLink --> "*" JsonAdaptedPerson JsonAdaptedPerson --> "*" JsonAdaptedTag @enduml diff --git a/docs/diagrams/UndoRedoState0.puml b/docs/diagrams/UndoRedoState0.puml index 96e30744d24..f8ff7ca791c 100644 --- a/docs/diagrams/UndoRedoState0.puml +++ b/docs/diagrams/UndoRedoState0.puml @@ -6,15 +6,15 @@ skinparam ClassBorderColor #000000 title Initial state package States { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__ab0:ModuLink__" + class State2 as "__ab1:ModuLink__" + class State3 as "__ab2:ModuLink__" } State1 -[hidden]right-> State2 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..397ea284118 100644 --- a/docs/diagrams/UndoRedoState1.puml +++ b/docs/diagrams/UndoRedoState1.puml @@ -6,9 +6,9 @@ skinparam ClassBorderColor #000000 title After command "delete 5" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__ab0:ModuLink__" + class State2 as "__ab1:ModuLink__" + class State3 as "__ab2:ModuLink__" } State1 -[hidden]right-> State2 @@ -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..781b48790b5 100644 --- a/docs/diagrams/UndoRedoState2.puml +++ b/docs/diagrams/UndoRedoState2.puml @@ -6,15 +6,15 @@ skinparam ClassBorderColor #000000 title After command "add n/David" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__ab0:ModuLink__" + class State2 as "__ab1:ModuLink__" + class State3 as "__ab2:ModuLink__" } 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..d248f0c951d 100644 --- a/docs/diagrams/UndoRedoState3.puml +++ b/docs/diagrams/UndoRedoState3.puml @@ -6,15 +6,15 @@ skinparam ClassBorderColor #000000 title After command "undo" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__ab0:ModuLink__" + class State2 as "__ab1:ModuLink__" + class State3 as "__ab2:ModuLink__" } 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..fe538f94c94 100644 --- a/docs/diagrams/UndoRedoState4.puml +++ b/docs/diagrams/UndoRedoState4.puml @@ -6,15 +6,15 @@ skinparam ClassBorderColor #000000 title After command "list" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__ab0:ModuLink__" + class State2 as "__ab1:ModuLink__" + class State3 as "__ab2:ModuLink__" } 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..7d2231c8257 100644 --- a/docs/diagrams/UndoRedoState5.puml +++ b/docs/diagrams/UndoRedoState5.puml @@ -6,15 +6,15 @@ skinparam ClassBorderColor #000000 title After command "clear" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab3:AddressBook__" + class State1 as "__ab0:ModuLink__" + class State2 as "__ab1:ModuLink__" + class State3 as "__ab3:ModuLink__" } 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/UndoSequenceDiagram.puml b/docs/diagrams/UndoSequenceDiagram.puml index 410aab4e412..20cd8a636d4 100644 --- a/docs/diagrams/UndoSequenceDiagram.puml +++ b/docs/diagrams/UndoSequenceDiagram.puml @@ -3,42 +3,42 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":ModuLinkParser" as ModuLinkParser LOGIC_COLOR participant "u:UndoCommand" as UndoCommand LOGIC_COLOR end box box Model MODEL_COLOR_T1 participant ":Model" as Model MODEL_COLOR -participant ":VersionedAddressBook" as VersionedAddressBook MODEL_COLOR +participant ":VersionedModuLink" as VersionedModuLink MODEL_COLOR end box [-> LogicManager : execute(undo) activate LogicManager -LogicManager -> AddressBookParser : parseCommand(undo) -activate AddressBookParser +LogicManager -> ModuLinkParser : parseCommand(undo) +activate ModuLinkParser create UndoCommand -AddressBookParser -> UndoCommand +ModuLinkParser -> UndoCommand activate UndoCommand -UndoCommand --> AddressBookParser +UndoCommand --> ModuLinkParser deactivate UndoCommand -AddressBookParser --> LogicManager : u -deactivate AddressBookParser +ModuLinkParser --> LogicManager : u +deactivate ModuLinkParser LogicManager -> UndoCommand : execute() activate UndoCommand -UndoCommand -> Model : undoAddressBook() +UndoCommand -> Model : undoModuLink() activate Model -Model -> VersionedAddressBook : undo() -activate VersionedAddressBook +Model -> VersionedModuLink : undo() +activate VersionedModuLink -VersionedAddressBook -> VersionedAddressBook :resetData(ReadOnlyAddressBook) -VersionedAddressBook --> Model : -deactivate VersionedAddressBook +VersionedModuLink -> VersionedModuLink :resetData(ReadOnlyModuLink) +VersionedModuLink --> Model : +deactivate VersionedModuLink Model --> UndoCommand deactivate Model diff --git a/docs/images/AddFavSequenceDiagram.png b/docs/images/AddFavSequenceDiagram.png new file mode 100644 index 00000000000..a505b55f34a Binary files /dev/null and b/docs/images/AddFavSequenceDiagram.png differ diff --git a/docs/images/AddModSequenceDiagram.png b/docs/images/AddModSequenceDiagram.png new file mode 100644 index 00000000000..e6001041754 Binary files /dev/null and b/docs/images/AddModSequenceDiagram.png differ diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png index 2f1346869d0..caa768bbb5e 100644 Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png index 1ec62caa2a5..8bf71fc7cdd 100644 Binary files a/docs/images/BetterModelClassDiagram.png and b/docs/images/BetterModelClassDiagram.png differ diff --git a/docs/images/CommitActivityDiagram.png b/docs/images/CommitActivityDiagram.png index c08c13f5c8b..6bf98428ad1 100644 Binary files a/docs/images/CommitActivityDiagram.png and b/docs/images/CommitActivityDiagram.png differ diff --git a/docs/images/CreateSequenceDiagram.png b/docs/images/CreateSequenceDiagram.png new file mode 100644 index 00000000000..80c34251f6d Binary files /dev/null and b/docs/images/CreateSequenceDiagram.png differ diff --git a/docs/images/EditGroupStatusCommandSequenceDiagram.png b/docs/images/EditGroupStatusCommandSequenceDiagram.png new file mode 100644 index 00000000000..e711dcfb3c1 Binary files /dev/null and b/docs/images/EditGroupStatusCommandSequenceDiagram.png differ diff --git a/docs/images/EditSequenceDiagram.png b/docs/images/EditSequenceDiagram.png new file mode 100644 index 00000000000..1f2175f2c71 Binary files /dev/null and b/docs/images/EditSequenceDiagram.png differ diff --git a/docs/images/FilterSequenceDiagram.png b/docs/images/FilterSequenceDiagram.png new file mode 100644 index 00000000000..da856309b3c Binary files /dev/null and b/docs/images/FilterSequenceDiagram.png differ diff --git a/docs/images/FindIdSequenceDiagram.png b/docs/images/FindIdSequenceDiagram.png new file mode 100644 index 00000000000..6f96200889b Binary files /dev/null and b/docs/images/FindIdSequenceDiagram.png differ diff --git a/docs/images/FindSequenceDiagram.png b/docs/images/FindSequenceDiagram.png new file mode 100644 index 00000000000..e2b26c811b1 Binary files /dev/null and b/docs/images/FindSequenceDiagram.png differ diff --git a/docs/images/ListFavSequenceDiagram.png b/docs/images/ListFavSequenceDiagram.png new file mode 100644 index 00000000000..4137e1d2d78 Binary files /dev/null and b/docs/images/ListFavSequenceDiagram.png differ diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png index c3028aa1cda..6b951d82c68 100644 Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index 39d7aec4b33..d763f2033c5 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/ParserClasses.png b/docs/images/ParserClasses.png index 58ad22ce16a..476655e7017 100644 Binary files a/docs/images/ParserClasses.png and b/docs/images/ParserClasses.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png index 82c66f8f16e..94283354a61 100644 Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png deleted file mode 100644 index 91488fd1a0f..00000000000 Binary files a/docs/images/Ui.png and /dev/null differ diff --git a/docs/images/UndoRedoState0-Initial_state.png b/docs/images/UndoRedoState0-Initial_state.png new file mode 100644 index 00000000000..2da30ac2819 Binary files /dev/null and b/docs/images/UndoRedoState0-Initial_state.png differ diff --git a/docs/images/UndoRedoState1-After_command__delete_5_.png b/docs/images/UndoRedoState1-After_command__delete_5_.png new file mode 100644 index 00000000000..16618b2099e Binary files /dev/null and b/docs/images/UndoRedoState1-After_command__delete_5_.png differ diff --git a/docs/images/UndoRedoState2-After_command__add_n_David_.png b/docs/images/UndoRedoState2-After_command__add_n_David_.png new file mode 100644 index 00000000000..fb83a730aec Binary files /dev/null and b/docs/images/UndoRedoState2-After_command__add_n_David_.png differ diff --git a/docs/images/UndoRedoState3-After_command__undo_.png b/docs/images/UndoRedoState3-After_command__undo_.png new file mode 100644 index 00000000000..d1088f999c8 Binary files /dev/null and b/docs/images/UndoRedoState3-After_command__undo_.png differ diff --git a/docs/images/UndoRedoState4-After_command__list_.png b/docs/images/UndoRedoState4-After_command__list_.png new file mode 100644 index 00000000000..0febd1613fe Binary files /dev/null and b/docs/images/UndoRedoState4-After_command__list_.png differ diff --git a/docs/images/UndoRedoState5-After_command__clear_.png b/docs/images/UndoRedoState5-After_command__clear_.png new file mode 100644 index 00000000000..d7dfdadee69 Binary files /dev/null and b/docs/images/UndoRedoState5-After_command__clear_.png differ diff --git a/docs/images/UndoSequenceDiagram.png b/docs/images/UndoSequenceDiagram.png index 6addcd3a8d9..f97efb8b80e 100644 Binary files a/docs/images/UndoSequenceDiagram.png and b/docs/images/UndoSequenceDiagram.png differ diff --git a/docs/images/aakanshanarain.png b/docs/images/aakanshanarain.png new file mode 100644 index 00000000000..f2d483c218f Binary files /dev/null and b/docs/images/aakanshanarain.png differ diff --git a/docs/images/charltonator.png b/docs/images/charltonator.png new file mode 100644 index 00000000000..2053ece6dc3 Binary files /dev/null and b/docs/images/charltonator.png differ diff --git a/docs/images/developerPhotos/aakanshanarain.png b/docs/images/developerPhotos/aakanshanarain.png new file mode 100644 index 00000000000..f2d483c218f Binary files /dev/null and b/docs/images/developerPhotos/aakanshanarain.png differ diff --git a/docs/images/developerPhotos/charltonator.png b/docs/images/developerPhotos/charltonator.png new file mode 100644 index 00000000000..2053ece6dc3 Binary files /dev/null and b/docs/images/developerPhotos/charltonator.png differ diff --git a/docs/images/developerPhotos/ethanwong6362.png b/docs/images/developerPhotos/ethanwong6362.png new file mode 100644 index 00000000000..9544c56e627 Binary files /dev/null and b/docs/images/developerPhotos/ethanwong6362.png differ diff --git a/docs/images/developerPhotos/ngjiayuan.png b/docs/images/developerPhotos/ngjiayuan.png new file mode 100644 index 00000000000..7d350cdf0c1 Binary files /dev/null and b/docs/images/developerPhotos/ngjiayuan.png differ diff --git a/docs/images/developerPhotos/zacharylwy.png b/docs/images/developerPhotos/zacharylwy.png new file mode 100644 index 00000000000..6421cf5e67e Binary files /dev/null and b/docs/images/developerPhotos/zacharylwy.png differ diff --git a/docs/images/ethanwong6362.png b/docs/images/ethanwong6362.png new file mode 100644 index 00000000000..9544c56e627 Binary files /dev/null and b/docs/images/ethanwong6362.png differ diff --git a/docs/images/findAlexDavidResult.png b/docs/images/findAlexDavidResult.png deleted file mode 100644 index 235da1c273e..00000000000 Binary files a/docs/images/findAlexDavidResult.png and /dev/null 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/ngjiayuan.png b/docs/images/ngjiayuan.png new file mode 100644 index 00000000000..7d350cdf0c1 Binary files /dev/null and b/docs/images/ngjiayuan.png differ diff --git a/docs/images/screenshots/Ui.png b/docs/images/screenshots/Ui.png new file mode 100644 index 00000000000..dd8ec3aca53 Binary files /dev/null and b/docs/images/screenshots/Ui.png differ diff --git a/docs/images/screenshots/Ui2.png b/docs/images/screenshots/Ui2.png new file mode 100644 index 00000000000..9d7806e9810 Binary files /dev/null and b/docs/images/screenshots/Ui2.png differ diff --git a/docs/images/screenshots/addFav.png b/docs/images/screenshots/addFav.png new file mode 100644 index 00000000000..a8c3843927d Binary files /dev/null and b/docs/images/screenshots/addFav.png differ diff --git a/docs/images/screenshots/addMod.png b/docs/images/screenshots/addMod.png new file mode 100644 index 00000000000..ed5cd9ca354 Binary files /dev/null and b/docs/images/screenshots/addMod.png differ diff --git a/docs/images/screenshots/createProfile.png b/docs/images/screenshots/createProfile.png new file mode 100644 index 00000000000..077ae844084 Binary files /dev/null and b/docs/images/screenshots/createProfile.png differ diff --git a/docs/images/screenshots/editProfile.png b/docs/images/screenshots/editProfile.png new file mode 100644 index 00000000000..bf6e14f5361 Binary files /dev/null and b/docs/images/screenshots/editProfile.png differ diff --git a/docs/images/screenshots/egsc.png b/docs/images/screenshots/egsc.png new file mode 100644 index 00000000000..54a8a69c81c Binary files /dev/null and b/docs/images/screenshots/egsc.png differ diff --git a/docs/images/screenshots/filtermod.png b/docs/images/screenshots/filtermod.png new file mode 100644 index 00000000000..28b147a2f37 Binary files /dev/null and b/docs/images/screenshots/filtermod.png differ diff --git a/docs/images/screenshots/filtermodgroup.png b/docs/images/screenshots/filtermodgroup.png new file mode 100644 index 00000000000..4f4bec0c7a8 Binary files /dev/null and b/docs/images/screenshots/filtermodgroup.png differ diff --git a/docs/images/screenshots/find.png b/docs/images/screenshots/find.png new file mode 100644 index 00000000000..50901055e08 Binary files /dev/null and b/docs/images/screenshots/find.png differ diff --git a/docs/images/screenshots/findId.png b/docs/images/screenshots/findId.png new file mode 100644 index 00000000000..5b001509b9f Binary files /dev/null and b/docs/images/screenshots/findId.png differ diff --git a/docs/images/screenshots/helpMessage.png b/docs/images/screenshots/helpMessage.png new file mode 100644 index 00000000000..78b5285dfbe Binary files /dev/null and b/docs/images/screenshots/helpMessage.png differ diff --git a/docs/images/screenshots/list.png b/docs/images/screenshots/list.png new file mode 100644 index 00000000000..78683111662 Binary files /dev/null and b/docs/images/screenshots/list.png differ diff --git a/docs/images/screenshots/listFav.png b/docs/images/screenshots/listFav.png new file mode 100644 index 00000000000..09ffdb79862 Binary files /dev/null and b/docs/images/screenshots/listFav.png differ diff --git a/docs/images/screenshots/originalForegsc.png b/docs/images/screenshots/originalForegsc.png new file mode 100644 index 00000000000..8d747826a32 Binary files /dev/null and b/docs/images/screenshots/originalForegsc.png differ diff --git a/docs/images/screenshots/remFav.png b/docs/images/screenshots/remFav.png new file mode 100644 index 00000000000..aa2fa5a83ac Binary files /dev/null and b/docs/images/screenshots/remFav.png differ diff --git a/docs/images/screenshots/remMod.png b/docs/images/screenshots/remMod.png new file mode 100644 index 00000000000..9e7f92ea9fa Binary files /dev/null and b/docs/images/screenshots/remMod.png differ diff --git a/docs/images/screenshots/tagexplanation.PNG b/docs/images/screenshots/tagexplanation.PNG new file mode 100644 index 00000000000..1e0c95f6c47 Binary files /dev/null and b/docs/images/screenshots/tagexplanation.PNG differ diff --git a/docs/images/zacharylwy.png b/docs/images/zacharylwy.png new file mode 100644 index 00000000000..6421cf5e67e Binary files /dev/null and b/docs/images/zacharylwy.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..4263a91158a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,17 +1,18 @@ --- layout: page -title: AddressBook Level-3 +title: ModuLink --- [![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) [![codecov](https://codecov.io/gh/se-edu/addressbook-level3/branch/master/graph/badge.svg)](https://codecov.io/gh/se-edu/addressbook-level3) +[![codecov](https://codecov.io/gh/AY2122S1-CS2103T-W12-4/tp/branch/master/graph/badge.svg?token=RS2T7CZCPN)](https://codecov.io/gh/AY2122S1-CS2103T-W12-4/tp) -![Ui](images/Ui.png) +![Ui](images/screenshots/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). +**ModuLink is a desktop-based application for Computer Science (CS) students at NUS to facilitate finding teammates for group-based modules.** 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 ModuLink, head over to the [**User Guide**](UserGuide.html). +* If you are interested in learning how we developed ModuLink, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. **Acknowledgements** diff --git a/docs/team/aakanshanarain.md b/docs/team/aakanshanarain.md new file mode 100644 index 00000000000..54851251eed --- /dev/null +++ b/docs/team/aakanshanarain.md @@ -0,0 +1,42 @@ +--- +layout: page +title: Aakansha Narain's Project Portfolio Page +--- + +### Project: ModuLink + +ModuLink is a desktop-based application for Computer Science (CS) students at NUS to facilitate finding teammates for group-based modules. It allows you to find students taking the modules you are interested in, search by their group status (to find students available to form or join groups), and much more. ModuLink has been developed using Java and has about 18k LoC. + +Given below are my contributions to the project. + +* **New Feature**: Filter by module and optionally group status + * What it does: Allows the user to filter the list of profiles by a specific module and optionally a group status. + * Justification: This feature is crucial as it is the basis of what ModuLink aims to do - simplify search for students in a specific module needing to either form, join or add members to their groups. With the filter feature, users can easily find exactly which students are taking the specific module and if they are open to forming/adding members to their groups. + * Highlights: This was a new command to implement, and took about 400 LoC to develop. + +* **New Feature**: Edit group status for modules on your profile + * What it does: Allows the user to edit the group status of a module on their profile. + * Justification: This feature makes updating the module tags easier instead of having to delete and add a new module tag every time the user’s group status changes. + * Highlights: This was a new command to implement, and took about 340 LoC to develop. + +* **Feature Enhancements**: Added the Student ID, Telegram handle and GitHub username attribute fields to the profiles. + * Justification: These identity fields are pivotal for our target audience (CS students) while forming their groups. + * Highlights: All three fields follow the conventions required by the actual organization (i.e. The required Student ID is in the format of the actual NUS-prescribed format of the Matriculation number, the required GitHub username and Telegram handle follow most of the actual constraints of GitHub usernames and Telegram handles respectively. In total, implementing these 3 fields took about 1k LoC. + +* **Other Code Enhancements**: + * Wrote additional tests for features to increase coverage. (Continually, and also specifically Pull Request: [\#190](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/190)) + * Added more specific and situation-appropriate error messages for better UX. + +* **Code contributed**: [RepoSense link - aakanshanarain](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/?search=aakanshanarain&sort=groupTitle&sortWithin=title&since=2021-09-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=false) + +* **Project management**: + * Managed release v1.3.1 on GitHub + * Opened issues for the team + +* **Documentation**: + * User Guide: + * Edited documentation for all new features. (Pull Requests: [\#100](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/100), [\#103](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/103), [\#105](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/105), [\#107](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/107), [\#109](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/109)) + * Added the Introduction and made other cosmetic changes throughout the document. (Pull Requests: [\#111](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/111), [\#184](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/184)) + * Developer Guide: + * Added use cases (UC5 - UC10). (Pull Requests: [\#52](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/52)) + * Added details + sequence diagram for editGroupStatus command. (Pull Requests: [\#96](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/96), [\#196](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/196)) diff --git a/docs/team/charltonator.md b/docs/team/charltonator.md new file mode 100644 index 00000000000..1c4b3e1120d --- /dev/null +++ b/docs/team/charltonator.md @@ -0,0 +1,38 @@ +--- +layout: page +title: Charlton Tan's Project Portfolio Page +--- + +### Project: ModuLink + +ModuLink is a desktop-based application for Computer Science (CS) students at NUS to facilitate finding teammates for group-based modules. It allows you to find students taking the modules you are interested in, search by their group status (to find students available to form or join groups), and much more. ModuLink has been developed using Java and has about 18k LoC. + +Given below are my contributions to the project. + +* **New Feature**: Added the findId feature. + * What it does: allows users to search for other profiles by Student ID. + * Justification: This feature was necessary as each profile has a unique Student ID and thus searching for a specific profile is much easier. + * Highlights: Only profiles whose student ID match the specified ID exactly will be shown. Implementation was also relatively simple as it was simply checking every profile. + +* **New Feature**: Added the addFav feature. + * What it does: allows users to add another profile as a favourite in ModuLink. + * Justification: This feature was necessary as having favourited profiles is a key feature of ModuLink. It allows users to keep track of profiles they are interested in. + * Highlights: This feature is complemented by the remFav feature. Together they form a key part of ModuLink's functionality. + +* **Feature Enhancements**: Enhanced the GUI by adding rounded corners to each Card in the list, expanding result box and adding colour to highlight the current User's profile. + * Justification: This feature was necessary as it would make the GUI more aesthetically pleasing and also allow the User to easily identify his own profile. + * Highlights: These enhancements were done by gaining a better understanding of how JavaFx works. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/?search=charltonator&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-17) + +* **Project management**: + * Managed release `v1.3.trial` on GitHub + * Assigned issues and merged PRs for team. + +* **Documentation**: + * User Guide: + * Fixed unclear instructions. + * Added features to User Guide (Pull requests [\#39](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/39)) + * Developer Guide: + * Added function table to Developer Guide. (Pull requests [\#48](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/48)) + * Checked for areas which needed diagrams to be updated. (Pull requests [\#84](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/84)) diff --git a/docs/team/ethanwong6362.md b/docs/team/ethanwong6362.md new file mode 100644 index 00000000000..2f1e76cedff --- /dev/null +++ b/docs/team/ethanwong6362.md @@ -0,0 +1,45 @@ +--- +layout: page +title: Ethan Wong's Project Portfolio Page +--- + +### Project: ModuLink + +ModuLink is a desktop-based application for Computer Science (CS) students at NUS to facilitate finding teammates for group-based modules. It allows you to find students taking the modules you are interested in, search by their group status (to find students available to form or join groups), and much more. ModuLink has been developed using Java and has about 18k LoC. + +Given below are my contributions to the project. + +* **New Feature**: Added the `addMod`/`remMod` commands. (Pull Request: [#81](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/81)) + * What it does: Allows the user to add a module to their profile. Modules already present in the users profile list can be removed using the `remMod` command. + * Justification: Previously, users could only edit their module tags with the `Edit` command, which required them to re-enter their entire updated list. This feature allows users to edit their module list one-by-one, making it much more convenient. + * Highlights: These were new commands, and took roughly 400 LoC to develop both of them. Previous iterations of these commands allowed users to add and remove multiple modules at a time, but in the end we decided on only allowing one module at a time to keep in line with the input conventions of our other commands, as well as to eliminate possible bugs. + +* **Feature Enhancements**: Changed `Tag`s to `Mod`s. `Mod`s now also have an additional `Status` field, which determines what colour it has in the UI. (Pull Request: [#55](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/55)) + * Justification: ModuLink is meant to be used by NUS CS students, thus specialising Tags to act as Modules, and having those Modules show their grouping status, makes it even easier for our users to find group mates. + * Highlights: Mods follow naming conventions of NUS Modules. Previous experience with HTML and CSS helped a lot when tweaking things in JavaFX. + +* **Feature Enhancements**: Added the `isFavourite` field to the profiles. (Pull Request: [#63](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/63)) + * Justification: To allow users to be able to keep track of people they want to work together with in the future. + * Highlights: A star icon is added in the UI to indicate a favourited profile. + * Credits: The icon used was pulled from [this](https://imgbin.com/png/X9hfA1CP/five-pointed-star-yellow-png) website. + +* **Feature Enhancements**: Changed the `Edit` command to only allow users to edit their own profile and removed the ability to edit module tags. (Pull Request: [#94](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/94)) + * Justification: As the data present in ModuLink is meant to act as profiles others have created, it wouldn't make sense for users to edit others' profiles. Our team also decided to separate editing module tags from editing other fields in the user profile + +* **Other Code Enhancements**: + * Fixed bugs pointed out to us during the PED. (Pull Requests: [#174](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/174), [#178](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/178), [#179](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/179)) + * Updated all commands (except `Edit`) to strictly take one input per parameter. + * Added several utility functions to `Model` and `ModelManger` such as `getProfile()` and `refreshFilteredPersonList()`. + +* **Code contributed**: [RepoSense link - ethanwong6362](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/?search=Ethan&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-17) + +* **Project management**: + * Managed release `v1.3.3` on GitHub. + +* **Documentation**: + * User Guide: + * Updated documentation for all features with the latest examples and screenshots to help users use our app. (Pull Request: [#179](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/179)) + * Proofread and edited our user guide for grammatical/technical errors and tone. + * Developer Guide: + * Added use cases (UC1 - UC4) and edited use cases (UC5 - UC6) + * Added test cases for Manual Testing. 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/ngjiayuan.md b/docs/team/ngjiayuan.md new file mode 100644 index 00000000000..fd8538891bf --- /dev/null +++ b/docs/team/ngjiayuan.md @@ -0,0 +1,39 @@ +--- +layout: page +title: Ng Jia Yuan's Project Portfolio Page +--- + +### Project: ModuLink + +ModuLink is a desktop-based application for Computer Science (CS) students at NUS to facilitate finding teammates for group-based modules. It allows you to find students taking the modules you are interested in, search by their group status (to find students available to form or join groups), and much more. ModuLink has been developed using Java and has about 6k LoC. + +Given below are my contributions to the project. + +* **New Feature**: Added the create feature to allow new users to create their profile + * What it does: allows new user to create their profile for the first time. + * Justification: This feature was necessary as each user should be required to create their profile with their contact details. This allows other users to search them up and obtain their contact details in order to reach out to them to form groups. + * Highlights: This command is restricted to be called only once as we expect only 1 profile creation per new user. Any amendments should be made with the edit command instead. Users are also required to create their profile prior to using other commands of ModuLink as part of the Get Started process. + +* **New Feature**: Added the remFav feature to remove a favourited profile as a favourite + * What it does: allows user to remove a favourited profile as their favourites. + * Justification: This feature was necessary as users may add a profile to favourite by accident or has a change in mind of keeping a profile as their favourites. + * Highlights: This command is a new command and complements the addFav command. + +* **Feature Enhancements**: Moved the user profile to the top of the list + * Justification: This feature was necessary as it provides users a better user experience to be able to view their own profile at the top of the list. + * Highlights: This feature was achieved by adding an addToTop function for the personsList. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/?search=ngjiayuan&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-17) + +* **Project management**: + * Organised weekly meetings + * Opening of issues and delegation of tasks + * Managed release `v1.3.2` on GitHub + * Checking and merging of PRs + +* **Documentation**: + * User Guide: + * Updated instructions on UG to be more reader focused and reader friendly. + * Fixed documentation bugs from PE-D. + * Developer Guide: + * Assisted Zachary in crafting sequence diagrams. diff --git a/docs/team/zacharylwy.md b/docs/team/zacharylwy.md new file mode 100644 index 00000000000..c7872e68b58 --- /dev/null +++ b/docs/team/zacharylwy.md @@ -0,0 +1,31 @@ +--- +layout: page +title: Zachary Lau's Project Portfolio Page +--- + +### Project: ModuLink + +ModuLink is a desktop-based application for Computer Science (CS) students at NUS to facilitate finding teammates for group-based modules. It allows you to find students taking the modules you are interested in, search by their group status (to find students available to form or join groups), and much more. ModuLink has been developed using Java and has about 18k LoC. + +Given below are my contributions to the project. + +* **New Feature**: Added the ability to for a user to see all his/her favourited profiles. + * What it does: allows the user to view all favourites. + * Justification: This feature was necessary as it had a significant importance in the purpose of our product. A user is highly likely to want to be able to view all favourited profiles as there would be too many profiles of little/no importance shown on the GUI. Scrolling through all profiles to find the few that are favourited would be very tedious. + * Highlights: This enhancement does not affect existing commands and commands to be added in future. It required an in-depth analysis of design alternatives. The implementation was not too challenging as our team had the foresight to include helpful attributes in all profiles. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/?search=zacharylwy&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-17) + +* **Project management**: + * Managed releases `v1.2` - `v1.4` (3 releases) on GitHub + +* **Enhancements to existing features**: + * Removed the unused address field in the product (Pull requests [\#62](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/62)) + +* **Documentation**: + * User Guide: + * Edited documentation for all new features. (Pull requests [\#44](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/44), [\#72](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/72), [\#90](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/90), [\#172](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/172)) + * Did cosmetic tweaks to existing screenshots used in documentation of most features. (Pull requests [\#173](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/173), [\#180](https://github.com/AY2122S1-CS2103T-W12-4/tp/pull/180)) + * Developer Guide: + * Added sequence diagrams for all new features. (Credit to Ng Jia Yuan who was helping me in the creation of all used diagrams) + * Handled most of the DG documentation and ammends. (Credit to Ng Jia Yuan who editted the documentation with me) diff --git a/docs/tutorials/AddRemark.md b/docs/tutorials/AddRemark.md index 8919d8eaa17..fed54079bae 100644 --- a/docs/tutorials/AddRemark.md +++ b/docs/tutorials/AddRemark.md @@ -23,9 +23,9 @@ For now, let’s keep `RemarkCommand` as simple as possible and print some outpu **`RemarkCommand.java`:** ``` java -package seedu.address.logic.commands; +package seedu.modulink.logic.commands; -import seedu.address.model.Model; +import seedu.modulink.model.Model; /** * Changes the remark of an existing person in the address book. @@ -91,7 +91,7 @@ Let’s change `RemarkCommand` to parse input from the user. We start by modifying the constructor of `RemarkCommand` to accept an `Index` and a `String`. While we are at it, let’s change the error message to echo the values. While this is not a replacement for tests, it is an obvious way to tell if our code is functioning as intended. ``` java -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.modulink.commons.util.CollectionUtil.requireAllNonNull; //... public class RemarkCommand extends Command { //... @@ -142,7 +142,7 @@ Your code should look something like [this](https://github.com/se-edu/addressboo Now let’s move on to writing a parser that will extract the index and remark from the input provided by the user. -Create a `RemarkCommandParser` class in the `seedu.address.logic.parser` package. The class must extend the `Parser` interface. +Create a `RemarkCommandParser` class in the `seedu.modulink.logic.parser` package. The class must extend the `Parser` interface. ![The relationship between Parser and RemarkCommandParser](../images/add-remark/ParserInterface.png) @@ -229,7 +229,7 @@ Now that we have all the information that we need, let’s lay the groundwork fo ### Add a new `Remark` class -Create a new `Remark` in `seedu.address.model.person`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code. +Create a new `Remark` in `seedu.modulink.model.person`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code. A copy-paste and search-replace later, you should have something like [this](https://github.com/se-edu/addressbook-level3/commit/4516e099699baa9e2d51801bd26f016d812dedcc#diff-af2f075d24dfcd333876f0fbce321f25). Note how `Remark` has no constrains and thus does not require input validation. @@ -242,7 +242,7 @@ Let’s change `RemarkCommand` and `RemarkCommandParser` to use the new `Remark` Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each person. -Simply add the following to [`seedu.address.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-0c6b6abcfac8c205e075294f25e851fe). +Simply add the following to [`seedu.modulink.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-0c6b6abcfac8c205e075294f25e851fe). **`PersonCard.java`:** diff --git a/docs/tutorials/RemovingFields.md b/docs/tutorials/RemovingFields.md index f29169bc924..b7748d166c3 100644 --- a/docs/tutorials/RemovingFields.md +++ b/docs/tutorials/RemovingFields.md @@ -28,7 +28,7 @@ IntelliJ IDEA provides a refactoring tool that can identify *most* parts of a re ### Assisted refactoring -The `address` field in `Person` is actually an instance of the `seedu.address.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu. +The `address` field in `Person` is actually an instance of the `seedu.modulink.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu. * :bulb: To make things simpler, you can unselect the options `Search in comments and strings` and `Search for text occurrences` ![Usages detected](../images/remove/UnsafeDelete.png) diff --git a/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md index 4fb62a83ef6..3072711fb68 100644 --- a/docs/tutorials/TracingCode.md +++ b/docs/tutorials/TracingCode.md @@ -39,7 +39,7 @@ In our case, we would want to begin the tracing at the very point where the App -According to the sequence diagram you saw earlier (and repeated above for reference), the `UI` component yields control to the `Logic` component through a method named `execute`. Searching through the code base for an `execute()` method that belongs to the `Logic` component yields a promising candidate in `seedu.address.logic.Logic`. +According to the sequence diagram you saw earlier (and repeated above for reference), the `UI` component yields control to the `Logic` component through a method named `execute`. Searching through the code base for an `execute()` method that belongs to the `Logic` component yields a promising candidate in `seedu.modulink.logic.Logic`. @@ -48,7 +48,7 @@ According to the sequence diagram you saw earlier (and repeated above for refere :bulb: **Intellij Tip:** The ['**Search Everywhere**' feature](https://www.jetbrains.com/help/idea/searching-everywhere.html) can be used here. In particular, the '**Find Symbol**' ('Symbol' here refers to methods, variables, classes etc.) variant of that feature is quite useful here as we are looking for a _method_ named `execute`, not simply the text `execute`.
-A quick look at the `seedu.address.logic.Logic` (an extract given below) confirms that this indeed might be what we’re looking for. +A quick look at the `seedu.modulink.logic.Logic` (an extract given below) confirms that this indeed might be what we’re looking for. ```java public interface Logic { diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java deleted file mode 100644 index 1deb3a1e469..00000000000 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ /dev/null @@ -1,13 +0,0 @@ -package seedu.address.commons.core; - -/** - * Container for user visible messages. - */ -public class Messages { - - public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; - public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; - public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; - public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; - -} diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java deleted file mode 100644 index 71656d7c5c8..00000000000 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ /dev/null @@ -1,67 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_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 seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; - -/** - * Adds a person to the address book. - */ -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. " - + "Parameters: " - + PREFIX_NAME + "NAME " - + PREFIX_PHONE + "PHONE " - + PREFIX_EMAIL + "EMAIL " - + PREFIX_ADDRESS + "ADDRESS " - + "[" + PREFIX_TAG + "TAG]...\n" - + "Example: " + COMMAND_WORD + " " - + PREFIX_NAME + "John Doe " - + PREFIX_PHONE + "98765432 " - + PREFIX_EMAIL + "johnd@example.com " - + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " - + PREFIX_TAG + "friends " - + PREFIX_TAG + "owesMoney"; - - 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"; - - private final Person toAdd; - - /** - * Creates an AddCommand to add the specified {@code Person} - */ - public AddCommand(Person person) { - requireNonNull(person); - toAdd = person; - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - - if (model.hasPerson(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } - - model.addPerson(toAdd); - return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof AddCommand // instanceof handles nulls - && toAdd.equals(((AddCommand) other).toAdd)); - } -} diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java deleted file mode 100644 index 9c86b1fa6e4..00000000000 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ /dev/null @@ -1,23 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; - -import seedu.address.model.AddressBook; -import seedu.address.model.Model; - -/** - * Clears the address book. - */ -public class ClearCommand extends Command { - - public static final String COMMAND_WORD = "clear"; - public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; - - - @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.setAddressBook(new AddressBook()); - return new CommandResult(MESSAGE_SUCCESS); - } -} diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java deleted file mode 100644 index 3b8bfa035e8..00000000000 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -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.Set; -import java.util.stream.Stream; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Parses input arguments and creates a new AddCommand object - */ -public class AddCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the AddCommand - * and returns an AddCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public AddCommand parse(String args) throws ParseException { - ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); - - if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) - || !argMultimap.getPreamble().isEmpty()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); - } - - Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); - 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()); - Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); - - Person person = new Person(name, phone, email, address, tagList); - - return new AddCommand(person); - } - - /** - * Returns true if none of the prefixes contains empty {@code Optional} values in the given - * {@code ArgumentMultimap}. - */ - private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { - return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); - } - -} diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java deleted file mode 100644 index 1e466792b46..00000000000 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ /dev/null @@ -1,76 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import seedu.address.logic.commands.AddCommand; -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.FindCommand; -import seedu.address.logic.commands.HelpCommand; -import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.parser.exceptions.ParseException; - -/** - * Parses user input. - */ -public class AddressBookParser { - - /** - * Used for initial separation of command word and args. - */ - private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); - - /** - * Parses user input into command for execution. - * - * @param userInput full user input string - * @return the command based on the user input - * @throws ParseException if the user input does not conform the expected format - */ - public Command parseCommand(String userInput) throws ParseException { - final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); - if (!matcher.matches()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); - } - - final String commandWord = matcher.group("commandWord"); - final String arguments = matcher.group("arguments"); - switch (commandWord) { - - case AddCommand.COMMAND_WORD: - return new AddCommandParser().parse(arguments); - - case EditCommand.COMMAND_WORD: - return new EditCommandParser().parse(arguments); - - case DeleteCommand.COMMAND_WORD: - return new DeleteCommandParser().parse(arguments); - - case ClearCommand.COMMAND_WORD: - return new ClearCommand(); - - case FindCommand.COMMAND_WORD: - return new FindCommandParser().parse(arguments); - - case ListCommand.COMMAND_WORD: - return new ListCommand(); - - case ExitCommand.COMMAND_WORD: - return new ExitCommand(); - - case HelpCommand.COMMAND_WORD: - return new HelpCommand(); - - default: - throw new ParseException(MESSAGE_UNKNOWN_COMMAND); - } - } - -} diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java deleted file mode 100644 index 845644b7dea..00000000000 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ /dev/null @@ -1,82 +0,0 @@ -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_ADDRESS; -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 - */ -public class EditCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the EditCommand - * and returns an EditCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public EditCommand parse(String args) throws ParseException { - requireNonNull(args); - ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); - - Index index; - - try { - index = ParserUtil.parseIndex(argMultimap.getPreamble()); - } catch (ParseException pe) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe); - } - - EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); - if (argMultimap.getValue(PREFIX_NAME).isPresent()) { - editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); - } - if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { - editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); - } - if (argMultimap.getValue(PREFIX_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); - } - - 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/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java deleted file mode 100644 index b117acb9c55..00000000000 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ /dev/null @@ -1,124 +0,0 @@ -package seedu.address.logic.parser; - -import static java.util.Objects.requireNonNull; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Contains utility methods used for parsing strings in the various *Parser classes. - */ -public class ParserUtil { - - public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer."; - - /** - * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be - * trimmed. - * @throws ParseException if the specified index is invalid (not non-zero unsigned integer). - */ - public static Index parseIndex(String oneBasedIndex) throws ParseException { - String trimmedIndex = oneBasedIndex.trim(); - if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) { - throw new ParseException(MESSAGE_INVALID_INDEX); - } - return Index.fromOneBased(Integer.parseInt(trimmedIndex)); - } - - /** - * Parses a {@code String name} into a {@code Name}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code name} is invalid. - */ - public static Name parseName(String name) throws ParseException { - requireNonNull(name); - String trimmedName = name.trim(); - if (!Name.isValidName(trimmedName)) { - throw new ParseException(Name.MESSAGE_CONSTRAINTS); - } - return new Name(trimmedName); - } - - /** - * Parses a {@code String phone} into a {@code Phone}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code phone} is invalid. - */ - public static Phone parsePhone(String phone) throws ParseException { - requireNonNull(phone); - String trimmedPhone = phone.trim(); - if (!Phone.isValidPhone(trimmedPhone)) { - throw new ParseException(Phone.MESSAGE_CONSTRAINTS); - } - return new Phone(trimmedPhone); - } - - /** - * Parses a {@code String address} into an {@code Address}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code address} is invalid. - */ - public static Address parseAddress(String address) throws ParseException { - requireNonNull(address); - String trimmedAddress = address.trim(); - if (!Address.isValidAddress(trimmedAddress)) { - throw new ParseException(Address.MESSAGE_CONSTRAINTS); - } - return new Address(trimmedAddress); - } - - /** - * Parses a {@code String email} into an {@code Email}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code email} is invalid. - */ - public static Email parseEmail(String email) throws ParseException { - requireNonNull(email); - String trimmedEmail = email.trim(); - if (!Email.isValidEmail(trimmedEmail)) { - throw new ParseException(Email.MESSAGE_CONSTRAINTS); - } - return new Email(trimmedEmail); - } - - /** - * Parses a {@code String tag} into a {@code Tag}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code tag} is invalid. - */ - public static Tag parseTag(String tag) throws ParseException { - requireNonNull(tag); - String trimmedTag = tag.trim(); - if (!Tag.isValidTagName(trimmedTag)) { - throw new ParseException(Tag.MESSAGE_CONSTRAINTS); - } - return new Tag(trimmedTag); - } - - /** - * Parses {@code Collection tags} into a {@code Set}. - */ - public static Set parseTags(Collection tags) throws ParseException { - requireNonNull(tags); - final Set tagSet = new HashSet<>(); - for (String tagName : tags) { - tagSet.add(parseTag(tagName)); - } - return tagSet; - } -} diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/person/Address.java deleted file mode 100644 index 60472ca22a0..00000000000 --- a/src/main/java/seedu/address/model/person/Address.java +++ /dev/null @@ -1,57 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's address in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)} - */ -public class Address { - - public static final String MESSAGE_CONSTRAINTS = "Addresses can take any values, and it should not be blank"; - - /* - * The first character of the address must not be a whitespace, - * otherwise " " (a blank string) becomes a valid input. - */ - public static final String VALIDATION_REGEX = "[^\\s].*"; - - public final String value; - - /** - * Constructs an {@code Address}. - * - * @param address A valid address. - */ - public Address(String address) { - requireNonNull(address); - checkArgument(isValidAddress(address), MESSAGE_CONSTRAINTS); - value = address; - } - - /** - * Returns true if a given string is a valid email. - */ - public static boolean isValidAddress(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Address // instanceof handles nulls - && value.equals(((Address) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java deleted file mode 100644 index 8ff1d83fe89..00000000000 --- a/src/main/java/seedu/address/model/person/Person.java +++ /dev/null @@ -1,123 +0,0 @@ -package seedu.address.model.person; - -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import seedu.address.model.tag.Tag; - -/** - * Represents a Person in the address book. - * Guarantees: details are present and not null, field values are validated, immutable. - */ -public class Person { - - // Identity fields - private final Name name; - private final Phone phone; - private final Email email; - - // Data fields - private final Address address; - private final Set tags = new HashSet<>(); - - /** - * 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); - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - this.tags.addAll(tags); - } - - public Name getName() { - return name; - } - - public Phone getPhone() { - return phone; - } - - public Email getEmail() { - return email; - } - - public Address getAddress() { - return address; - } - - /** - * Returns an immutable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - */ - public Set getTags() { - return Collections.unmodifiableSet(tags); - } - - /** - * Returns true if both persons have the same name. - * This defines a weaker notion of equality between two persons. - */ - public boolean isSamePerson(Person otherPerson) { - if (otherPerson == this) { - return true; - } - - return otherPerson != null - && otherPerson.getName().equals(getName()); - } - - /** - * Returns true if both persons have the same identity and data fields. - * This defines a stronger notion of equality between two persons. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof Person)) { - return false; - } - - Person otherPerson = (Person) other; - return otherPerson.getName().equals(getName()) - && otherPerson.getPhone().equals(getPhone()) - && otherPerson.getEmail().equals(getEmail()) - && otherPerson.getAddress().equals(getAddress()) - && otherPerson.getTags().equals(getTags()); - } - - @Override - public int hashCode() { - // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append(getName()) - .append("; Phone: ") - .append(getPhone()) - .append("; Email: ") - .append(getEmail()) - .append("; Address: ") - .append(getAddress()); - - Set tags = getTags(); - if (!tags.isEmpty()) { - builder.append("; Tags: "); - tags.forEach(builder::append); - } - return builder.toString(); - } - -} diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java deleted file mode 100644 index b0ea7e7dad7..00000000000 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ /dev/null @@ -1,54 +0,0 @@ -package seedu.address.model.tag; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Tag in the address book. - * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)} - */ -public class Tag { - - public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric"; - public static final String VALIDATION_REGEX = "\\p{Alnum}+"; - - public final String tagName; - - /** - * Constructs a {@code Tag}. - * - * @param tagName A valid tag name. - */ - public Tag(String tagName) { - requireNonNull(tagName); - checkArgument(isValidTagName(tagName), MESSAGE_CONSTRAINTS); - this.tagName = tagName; - } - - /** - * Returns true if a given string is a valid tag name. - */ - public static boolean isValidTagName(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Tag // instanceof handles nulls - && tagName.equals(((Tag) other).tagName)); // state check - } - - @Override - public int hashCode() { - return tagName.hashCode(); - } - - /** - * Format state as text for viewing. - */ - public String toString() { - return '[' + tagName + ']'; - } - -} diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java deleted file mode 100644 index 1806da4facf..00000000000 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.model.util; - -import java.util.Arrays; -import java.util.Set; -import java.util.stream.Collectors; - -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Contains utility methods for populating {@code AddressBook} with sample data. - */ -public class SampleDataUtil { - public static Person[] getSamplePersons() { - return new Person[] { - new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), - new Address("Blk 30 Geylang Street 29, #06-40"), - getTagSet("friends")), - new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), - new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), - getTagSet("colleagues", "friends")), - new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), - new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), - getTagSet("neighbours")), - new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), - new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), - getTagSet("family")), - new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), - new Address("Blk 47 Tampines Street 20, #17-35"), - getTagSet("classmates")), - new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), - new Address("Blk 45 Aljunied Street 85, #11-31"), - getTagSet("colleagues")) - }; - } - - public static ReadOnlyAddressBook getSampleAddressBook() { - AddressBook sampleAb = new AddressBook(); - for (Person samplePerson : getSamplePersons()) { - sampleAb.addPerson(samplePerson); - } - return sampleAb; - } - - /** - * Returns a tag set containing the list of strings given. - */ - public static Set getTagSet(String... strings) { - return Arrays.stream(strings) - .map(Tag::new) - .collect(Collectors.toSet()); - } - -} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java deleted file mode 100644 index a6321cec2ea..00000000000 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ /dev/null @@ -1,109 +0,0 @@ -package seedu.address.storage; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -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 address; - 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) { - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - if (tagged != null) { - this.tagged.addAll(tagged); - } - } - - /** - * Converts a given {@code Person} into this class for Jackson use. - */ - public JsonAdaptedPerson(Person source) { - name = source.getName().fullName; - phone = source.getPhone().value; - email = source.getEmail().value; - address = source.getAddress().value; - tagged.addAll(source.getTags().stream() - .map(JsonAdaptedTag::new) - .collect(Collectors.toList())); - } - - /** - * Converts this Jackson-friendly adapted person object into the model's {@code Person} object. - * - * @throws IllegalValueException if there were any data constraints violated in the adapted person. - */ - public Person toModelType() throws IllegalValueException { - final List personTags = new ArrayList<>(); - for (JsonAdaptedTag tag : tagged) { - personTags.add(tag.toModelType()); - } - - if (name == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); - } - if (!Name.isValidName(name)) { - throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS); - } - final Name modelName = new Name(name); - - if (phone == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName())); - } - if (!Phone.isValidPhone(phone)) { - throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS); - } - final Phone modelPhone = new Phone(phone); - - if (email == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName())); - } - if (!Email.isValidEmail(email)) { - throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS); - } - final Email modelEmail = new Email(email); - - if (address == null) { - 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); - - final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); - } - -} diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java deleted file mode 100644 index 7fc927bc5d9..00000000000 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ /dev/null @@ -1,77 +0,0 @@ -package seedu.address.ui; - -import java.util.Comparator; - -import javafx.fxml.FXML; -import javafx.scene.control.Label; -import javafx.scene.layout.FlowPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Region; -import seedu.address.model.person.Person; - -/** - * An UI component that displays information of a {@code Person}. - */ -public class PersonCard extends UiPart { - - private static final String FXML = "PersonListCard.fxml"; - - /** - * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. - * As a consequence, UI elements' variable names cannot be set to such keywords - * or an exception will be thrown by JavaFX during runtime. - * - * @see The issue on AddressBook level 4 - */ - - public final Person person; - - @FXML - private HBox cardPane; - @FXML - private Label name; - @FXML - private Label id; - @FXML - private Label phone; - @FXML - private Label address; - @FXML - private Label email; - @FXML - private FlowPane tags; - - /** - * Creates a {@code PersonCode} with the given {@code Person} and index to display. - */ - public PersonCard(Person person, int displayedIndex) { - super(FXML); - 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)) - .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof PersonCard)) { - return false; - } - - // state check - PersonCard card = (PersonCard) other; - return id.getText().equals(card.id.getText()) - && person.equals(card.person); - } -} diff --git a/src/main/java/seedu/address/AppParameters.java b/src/main/java/seedu/modulink/AppParameters.java similarity index 93% rename from src/main/java/seedu/address/AppParameters.java rename to src/main/java/seedu/modulink/AppParameters.java index ab552c398f3..f4bd6b587d1 100644 --- a/src/main/java/seedu/address/AppParameters.java +++ b/src/main/java/seedu/modulink/AppParameters.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.modulink; import java.nio.file.Path; import java.nio.file.Paths; @@ -7,8 +7,8 @@ import java.util.logging.Logger; import javafx.application.Application; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.util.FileUtil; +import seedu.modulink.commons.core.LogsCenter; +import seedu.modulink.commons.util.FileUtil; /** * Represents the parsed command-line parameters given to the application. diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/seedu/modulink/Main.java similarity index 88% rename from src/main/java/seedu/address/Main.java rename to src/main/java/seedu/modulink/Main.java index 052a5068631..4436920c18d 100644 --- a/src/main/java/seedu/address/Main.java +++ b/src/main/java/seedu/modulink/Main.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.modulink; import javafx.application.Application; @@ -19,6 +19,10 @@ * to be the entry point of the application, we avoid this issue. */ public class Main { + /** + * Main method. + * @param args parameter for main method. + */ 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/modulink/MainApp.java similarity index 70% rename from src/main/java/seedu/address/MainApp.java rename to src/main/java/seedu/modulink/MainApp.java index 4133aaa0151..f32382d7a78 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/modulink/MainApp.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.modulink; import java.io.IOException; import java.nio.file.Path; @@ -6,30 +6,35 @@ import java.util.logging.Logger; import javafx.application.Application; +import javafx.collections.ObservableList; import javafx.stage.Stage; -import seedu.address.commons.core.Config; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.core.Version; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.ConfigUtil; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; -import seedu.address.logic.LogicManager; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; -import seedu.address.model.util.SampleDataUtil; -import seedu.address.storage.AddressBookStorage; -import seedu.address.storage.JsonAddressBookStorage; -import seedu.address.storage.JsonUserPrefsStorage; -import seedu.address.storage.Storage; -import seedu.address.storage.StorageManager; -import seedu.address.storage.UserPrefsStorage; -import seedu.address.ui.Ui; -import seedu.address.ui.UiManager; +import seedu.modulink.commons.core.Config; +import seedu.modulink.commons.core.LogsCenter; +import seedu.modulink.commons.core.Version; +import seedu.modulink.commons.exceptions.DataConversionException; +import seedu.modulink.commons.util.ConfigUtil; +import seedu.modulink.commons.util.StringUtil; +import seedu.modulink.logic.Logic; +import seedu.modulink.logic.LogicManager; +import seedu.modulink.model.AddressBook; +import seedu.modulink.model.Model; +import seedu.modulink.model.ModelManager; +import seedu.modulink.model.ReadOnlyAddressBook; +import seedu.modulink.model.ReadOnlyUserPrefs; +import seedu.modulink.model.UserPrefs; +import seedu.modulink.model.person.Person; +import seedu.modulink.model.person.StudentId; +import seedu.modulink.model.person.exceptions.DuplicatePersonException; +import seedu.modulink.model.person.exceptions.UserProfileIsFavouriteException; +import seedu.modulink.model.util.SampleDataUtil; +import seedu.modulink.storage.AddressBookStorage; +import seedu.modulink.storage.JsonAddressBookStorage; +import seedu.modulink.storage.JsonUserPrefsStorage; +import seedu.modulink.storage.Storage; +import seedu.modulink.storage.StorageManager; +import seedu.modulink.storage.UserPrefsStorage; +import seedu.modulink.ui.Ui; +import seedu.modulink.ui.UiManager; /** * Runs the application. @@ -48,6 +53,7 @@ public class MainApp extends Application { @Override public void init() throws Exception { + logger.info("=============================[ Initializing AddressBook ]==========================="); super.init(); @@ -82,6 +88,28 @@ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { logger.info("Data file not found. Will be starting with a sample AddressBook"); } initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); + Model currentData = new ModelManager(initialData, userPrefs); + ObservableList allPersons = currentData.getFilteredPersonList(); + int noOfPeople = allPersons.size(); + for (int i = 0; i < noOfPeople; i++) { + StudentId currentId = allPersons.get(i).getStudentId(); + if (allPersons.get(i).getIsMyProfile() && allPersons.get(i).getIsFavourite()) { + throw new UserProfileIsFavouriteException(); + } + for (int j = i + 1; j < noOfPeople; j++) { + StudentId comparingId = allPersons.get(j).getStudentId(); + if (currentId.equals(comparingId)) { + throw new DuplicatePersonException(); + } + } + } + } catch (UserProfileIsFavouriteException err) { + logger.warning("Data file has a profile that is both the User Profile and a favourited profile."); + initialData = new AddressBook(); + } + catch (DuplicatePersonException err) { + logger.warning("Data file consists of duplicate StudentIds. Will be starting with an empty AddressBook"); + initialData = new AddressBook(); } catch (DataConversionException e) { logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook"); initialData = new AddressBook(); diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/seedu/modulink/commons/core/Config.java similarity index 97% rename from src/main/java/seedu/address/commons/core/Config.java rename to src/main/java/seedu/modulink/commons/core/Config.java index 91145745521..8b98cba3ea9 100644 --- a/src/main/java/seedu/address/commons/core/Config.java +++ b/src/main/java/seedu/modulink/commons/core/Config.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.modulink.commons.core; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/modulink/commons/core/GuiSettings.java similarity index 98% rename from src/main/java/seedu/address/commons/core/GuiSettings.java rename to src/main/java/seedu/modulink/commons/core/GuiSettings.java index ba33653be67..542b137a0d4 100644 --- a/src/main/java/seedu/address/commons/core/GuiSettings.java +++ b/src/main/java/seedu/modulink/commons/core/GuiSettings.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.modulink.commons.core; import java.awt.Point; import java.io.Serializable; diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/modulink/commons/core/LogsCenter.java similarity index 99% rename from src/main/java/seedu/address/commons/core/LogsCenter.java rename to src/main/java/seedu/modulink/commons/core/LogsCenter.java index 431e7185e76..0a92f740c8a 100644 --- a/src/main/java/seedu/address/commons/core/LogsCenter.java +++ b/src/main/java/seedu/modulink/commons/core/LogsCenter.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.modulink.commons.core; import java.io.IOException; import java.util.Arrays; diff --git a/src/main/java/seedu/modulink/commons/core/Messages.java b/src/main/java/seedu/modulink/commons/core/Messages.java new file mode 100644 index 00000000000..758e2f3801b --- /dev/null +++ b/src/main/java/seedu/modulink/commons/core/Messages.java @@ -0,0 +1,27 @@ +package seedu.modulink.commons.core; + +/** + * Container for user visible messages. + */ +public class Messages { + + public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; + public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; + public static final String MESSAGE_UNEXPECTED_INPUT_FORMAT = "Unexpected input found: %s. \n%s"; + public static final String MESSAGE_UNKNOWN_PREFIX_FORMAT = "Unknown prefix(es) inputted! \n%s"; + public static final String MESSAGE_DUPLICATE_PREFIX_FORMAT = "The following prefixes were " + + "specified more than once: %s. Only one occurrence of a prefix is allowed. \n%s"; + public static final String MESSAGE_MISSING_PREFIXES_FORMAT = "The following prefixes are missing: %s. \n%s"; + public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; + public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d person(s) listed!"; + public static final String MESSAGE_NO_PERSON_LISTED = "We couldn't find anyone %s"; + public static final String MESSAGE_NO_SUCH_ID_FOUND = "No such Student ID found!"; + public static final String MESSAGE_FAVOURITE_ADDED = "%s is added as a favourite!"; + public static final String MESSAGE_PERSON_ALREADY_FAVOURITE = "The Student is already a favourite!"; + public static final String MESSAGE_FAVOURITE_REMOVED = "%s is removed from favourites."; + public static final String MESSAGE_PERSON_IS_NOT_FAVOURITE = "The Student is not a favourite."; + public static final String MESSAGE_NO_FAVOURITES = "There is currently no one in your favourites list."; + public static final String MESSAGE_DUPLICATE_ON_START = "Duplicate Student IDs were detected in the data file." + + " A new addressbook was created"; + +} diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/seedu/modulink/commons/core/Version.java similarity index 98% rename from src/main/java/seedu/address/commons/core/Version.java rename to src/main/java/seedu/modulink/commons/core/Version.java index 12142ec1e32..409a2923da5 100644 --- a/src/main/java/seedu/address/commons/core/Version.java +++ b/src/main/java/seedu/modulink/commons/core/Version.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.modulink.commons.core; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/src/main/java/seedu/address/commons/core/index/Index.java b/src/main/java/seedu/modulink/commons/core/index/Index.java similarity index 97% rename from src/main/java/seedu/address/commons/core/index/Index.java rename to src/main/java/seedu/modulink/commons/core/index/Index.java index 19536439c09..ae8b8828b3f 100644 --- a/src/main/java/seedu/address/commons/core/index/Index.java +++ b/src/main/java/seedu/modulink/commons/core/index/Index.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core.index; +package seedu.modulink.commons.core.index; /** * Represents a zero-based or one-based index. diff --git a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java b/src/main/java/seedu/modulink/commons/exceptions/DataConversionException.java similarity index 84% rename from src/main/java/seedu/address/commons/exceptions/DataConversionException.java rename to src/main/java/seedu/modulink/commons/exceptions/DataConversionException.java index 1f689bd8e3f..73677b245ca 100644 --- a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java +++ b/src/main/java/seedu/modulink/commons/exceptions/DataConversionException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.modulink.commons.exceptions; /** * Represents an error during conversion of data from one format to another diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java b/src/main/java/seedu/modulink/commons/exceptions/IllegalValueException.java similarity index 92% rename from src/main/java/seedu/address/commons/exceptions/IllegalValueException.java rename to src/main/java/seedu/modulink/commons/exceptions/IllegalValueException.java index 19124db485c..b6744dfceeb 100644 --- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java +++ b/src/main/java/seedu/modulink/commons/exceptions/IllegalValueException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.modulink.commons.exceptions; /** * Signals that some given data does not fulfill some constraints. diff --git a/src/main/java/seedu/address/commons/util/AppUtil.java b/src/main/java/seedu/modulink/commons/util/AppUtil.java similarity index 94% rename from src/main/java/seedu/address/commons/util/AppUtil.java rename to src/main/java/seedu/modulink/commons/util/AppUtil.java index 87aa89c0326..e0db8d027f9 100644 --- a/src/main/java/seedu/address/commons/util/AppUtil.java +++ b/src/main/java/seedu/modulink/commons/util/AppUtil.java @@ -1,9 +1,9 @@ -package seedu.address.commons.util; +package seedu.modulink.commons.util; import static java.util.Objects.requireNonNull; import javafx.scene.image.Image; -import seedu.address.MainApp; +import seedu.modulink.MainApp; /** * A container for App specific utility functions diff --git a/src/main/java/seedu/address/commons/util/CollectionUtil.java b/src/main/java/seedu/modulink/commons/util/CollectionUtil.java similarity index 96% rename from src/main/java/seedu/address/commons/util/CollectionUtil.java rename to src/main/java/seedu/modulink/commons/util/CollectionUtil.java index eafe4dfd681..91e44c3390a 100644 --- a/src/main/java/seedu/address/commons/util/CollectionUtil.java +++ b/src/main/java/seedu/modulink/commons/util/CollectionUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.modulink.commons.util; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/commons/util/ConfigUtil.java b/src/main/java/seedu/modulink/commons/util/ConfigUtil.java similarity index 77% rename from src/main/java/seedu/address/commons/util/ConfigUtil.java rename to src/main/java/seedu/modulink/commons/util/ConfigUtil.java index f7f8a2bd44c..a59f035bb15 100644 --- a/src/main/java/seedu/address/commons/util/ConfigUtil.java +++ b/src/main/java/seedu/modulink/commons/util/ConfigUtil.java @@ -1,11 +1,11 @@ -package seedu.address.commons.util; +package seedu.modulink.commons.util; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.core.Config; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.modulink.commons.core.Config; +import seedu.modulink.commons.exceptions.DataConversionException; /** * A class for accessing the Config File. diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/seedu/modulink/commons/util/FileUtil.java similarity index 98% rename from src/main/java/seedu/address/commons/util/FileUtil.java rename to src/main/java/seedu/modulink/commons/util/FileUtil.java index b1e2767cdd9..b550b3b3530 100644 --- a/src/main/java/seedu/address/commons/util/FileUtil.java +++ b/src/main/java/seedu/modulink/commons/util/FileUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.modulink.commons.util; import java.io.IOException; import java.nio.file.Files; diff --git a/src/main/java/seedu/address/commons/util/JsonUtil.java b/src/main/java/seedu/modulink/commons/util/JsonUtil.java similarity index 97% rename from src/main/java/seedu/address/commons/util/JsonUtil.java rename to src/main/java/seedu/modulink/commons/util/JsonUtil.java index 8ef609f055d..04b4cc70dd4 100644 --- a/src/main/java/seedu/address/commons/util/JsonUtil.java +++ b/src/main/java/seedu/modulink/commons/util/JsonUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.modulink.commons.util; import static java.util.Objects.requireNonNull; @@ -20,8 +20,8 @@ import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.modulink.commons.core.LogsCenter; +import seedu.modulink.commons.exceptions.DataConversionException; /** * Converts a Java object instance to JSON and vice versa diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/modulink/commons/util/StringUtil.java similarity index 74% rename from src/main/java/seedu/address/commons/util/StringUtil.java rename to src/main/java/seedu/modulink/commons/util/StringUtil.java index 61cc8c9a1cb..a78df8d5eb9 100644 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ b/src/main/java/seedu/modulink/commons/util/StringUtil.java @@ -1,12 +1,13 @@ -package seedu.address.commons.util; +package seedu.modulink.commons.util; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.modulink.commons.util.AppUtil.checkArgument; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Arrays; + /** * Helper functions for handling strings. */ @@ -65,4 +66,30 @@ public static boolean isNonZeroUnsignedInteger(String s) { return false; } } + + /** + * Returns the number of occurrences of {@param c} in {@param s}. + */ + public static int countMatch(String s, char c) { + int n = 0; + for (int i = 0; i < s.length(); i++) { + if (c == s.charAt(i)) { + n++; + } + } + return n; + } + + /** + * Returns the number of occurrences of {@param substring} in {@param s}. + */ + public static int countMatch(String s, String substring) { + int n = 0; + for (int i = 0; i < s.length() - substring.length(); i++) { + if (substring.equals(s.substring(i, i + substring.length()))) { + n++; + } + } + return n; + } } diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/modulink/logic/Logic.java similarity index 68% rename from src/main/java/seedu/address/logic/Logic.java rename to src/main/java/seedu/modulink/logic/Logic.java index 92cd8fa605a..05f3e610fdf 100644 --- a/src/main/java/seedu/address/logic/Logic.java +++ b/src/main/java/seedu/modulink/logic/Logic.java @@ -1,14 +1,14 @@ -package seedu.address.logic; +package seedu.modulink.logic; import java.nio.file.Path; import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; +import seedu.modulink.commons.core.GuiSettings; +import seedu.modulink.logic.commands.CommandResult; +import seedu.modulink.logic.commands.exceptions.CommandException; +import seedu.modulink.logic.parser.exceptions.ParseException; +import seedu.modulink.model.ReadOnlyAddressBook; +import seedu.modulink.model.person.Person; /** * API of the Logic component @@ -26,7 +26,7 @@ public interface Logic { /** * Returns the AddressBook. * - * @see seedu.address.model.Model#getAddressBook() + * @see seedu.modulink.model.Model#getAddressBook() */ ReadOnlyAddressBook getAddressBook(); @@ -47,4 +47,9 @@ public interface Logic { * Set the user prefs' GUI settings. */ void setGuiSettings(GuiSettings guiSettings); + + /** + * Returns true if a profile has been created. + */ + boolean hasCreatedProfile(); } diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/modulink/logic/LogicManager.java similarity index 56% rename from src/main/java/seedu/address/logic/LogicManager.java rename to src/main/java/seedu/modulink/logic/LogicManager.java index 9d9c6d15bdc..a66b692dbd5 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/modulink/logic/LogicManager.java @@ -1,27 +1,34 @@ -package seedu.address.logic; +package seedu.modulink.logic; import java.io.IOException; import java.nio.file.Path; import java.util.logging.Logger; import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -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.Model; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; -import seedu.address.storage.Storage; +import seedu.modulink.commons.core.GuiSettings; +import seedu.modulink.commons.core.LogsCenter; +import seedu.modulink.logic.commands.Command; +import seedu.modulink.logic.commands.CommandResult; +import seedu.modulink.logic.commands.CreateCommand; +import seedu.modulink.logic.commands.EditCommand; +import seedu.modulink.logic.commands.exceptions.CommandException; +import seedu.modulink.logic.parser.AddressBookParser; +import seedu.modulink.logic.parser.exceptions.ParseException; +import seedu.modulink.model.Model; +import seedu.modulink.model.ReadOnlyAddressBook; +import seedu.modulink.model.person.Person; +import seedu.modulink.storage.Storage; + /** * The main LogicManager of the app. */ public class LogicManager implements Logic { public static final String FILE_OPS_ERROR_MESSAGE = "Could not save data to file: "; + public static final String PROFILE_NOT_CREATED_ERROR_MESSAGE = + "Please create your profile using the create command first!"; + public static final String PROFILE_CREATED_ERROR_MESSAGE = + "Profile already created! Use the edit command to edit it!"; private final Logger logger = LogsCenter.getLogger(LogicManager.class); private final Model model; @@ -41,8 +48,15 @@ public LogicManager(Model model, Storage storage) { public CommandResult execute(String commandText) throws CommandException, ParseException { logger.info("----------------[USER COMMAND][" + commandText + "]"); + if (!hasCreatedProfile() && !commandText.toLowerCase().startsWith("create") && !commandText.equals("list")) { + throw new CommandException(PROFILE_NOT_CREATED_ERROR_MESSAGE + "\n" + CreateCommand.MESSAGE_USAGE); + } else if (hasCreatedProfile() && commandText.toLowerCase().startsWith("create")) { + throw new CommandException(PROFILE_CREATED_ERROR_MESSAGE + "\n" + EditCommand.MESSAGE_USAGE); + } + CommandResult commandResult; Command command = addressBookParser.parseCommand(commandText); + commandResult = command.execute(model); try { @@ -78,4 +92,8 @@ public GuiSettings getGuiSettings() { public void setGuiSettings(GuiSettings guiSettings) { model.setGuiSettings(guiSettings); } + + public boolean hasCreatedProfile() { + return !model.hasPerson(Person.getPlaceholder()); + } } diff --git a/src/main/java/seedu/modulink/logic/commands/AddFavCommand.java b/src/main/java/seedu/modulink/logic/commands/AddFavCommand.java new file mode 100644 index 00000000000..7defdb71063 --- /dev/null +++ b/src/main/java/seedu/modulink/logic/commands/AddFavCommand.java @@ -0,0 +1,75 @@ +package seedu.modulink.logic.commands; + +import javafx.collections.ObservableList; +import seedu.modulink.commons.core.Messages; +import seedu.modulink.model.Model; +import seedu.modulink.model.person.Person; + + +/** + * Adds person whose student ID matches the user input to favourites.. + * ID matching is case insensitive. + */ +public class AddFavCommand extends Command { + + public static final String COMMAND_WORD = "addFav"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person as a favourite.\n " + + "Parameters: Student_ID\n" + + "Example: " + COMMAND_WORD + " A1234567X"; + + public static final String FAVOURITING_PROFILE_ERROR = + "You cannot add your own profile to your Favourites list."; + + public static final String MULTIPLE_ID_ERROR = + "You can only add one person to your Favourites list at a time."; + + private final String studentId; + + public AddFavCommand(String studentId) { + this.studentId = studentId; + } + + @Override + public CommandResult execute(Model model) { + boolean noPersonFound = true; + ObservableList personList = model.getPersonList(); + + // Look for the Person with the student ID, and if he is + // not a favourite, make him a favourite. If he already is, + // return a message saying he already is. + for (Person person : personList) { + if (person.getStudentId().toString().equalsIgnoreCase(studentId)) { + if (person.getIsFavourite()) { + return new CommandResult(String.format(Messages.MESSAGE_PERSON_ALREADY_FAVOURITE)); + } else if (person.getIsMyProfile()) { + return new CommandResult(FAVOURITING_PROFILE_ERROR); + } else { + person.setFavouriteTrue(); + noPersonFound = false; + } + } + } + + if (noPersonFound) { + return new CommandResult( + String.format(Messages.MESSAGE_NO_SUCH_ID_FOUND)); + } else { + // included this so the list will be properly updated + model.refreshFilteredPersonList(); + return new CommandResult( + String.format(Messages.MESSAGE_FAVOURITE_ADDED, studentId)); + } + + } + + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddFavCommand // instanceof handles nulls + && this.studentId.equals(((AddFavCommand) other).studentId)); + } + + +} diff --git a/src/main/java/seedu/modulink/logic/commands/AddModCommand.java b/src/main/java/seedu/modulink/logic/commands/AddModCommand.java new file mode 100644 index 00000000000..d6f786864cf --- /dev/null +++ b/src/main/java/seedu/modulink/logic/commands/AddModCommand.java @@ -0,0 +1,111 @@ +package seedu.modulink.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_MOD; +import static seedu.modulink.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import seedu.modulink.logic.commands.EditCommand.EditPersonDescriptor; +import seedu.modulink.logic.commands.exceptions.CommandException; +import seedu.modulink.model.Model; +import seedu.modulink.model.person.Email; +import seedu.modulink.model.person.GitHubUsername; +import seedu.modulink.model.person.Name; +import seedu.modulink.model.person.Person; +import seedu.modulink.model.person.Phone; +import seedu.modulink.model.person.StudentId; +import seedu.modulink.model.person.TelegramHandle; +import seedu.modulink.model.tag.Mod; + +public class AddModCommand extends Command { + public static final String COMMAND_WORD = "addMod"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Adds a module to your module list. " + + "You may optionally add your grouping status. " + + "Existing modules will not be overwritten.\n" + + "Duplicate modules cannot be added.\n" + + "Parameters: " + + "[" + PREFIX_MOD + "MOD]\n" + + "Example: \n- " + COMMAND_WORD + + " " + PREFIX_MOD + "CS2103T\n- " + + COMMAND_WORD + " " + PREFIX_MOD + "CS2100 need group"; + + public static final String MESSAGE_ADD_MODULE_SUCCESS = "Added module: %1$s"; + public static final String MESSAGE_NO_CHANGE = + "Please provide a module to add with the \"mod/\" prefix."; + public static final String MESSAGE_DUPLICATE_MODULE = "You have already added this module to your list.\n" + + "Please use the \"editGroupStatus\" command to change your grouping status."; + + + private final EditPersonDescriptor editPersonDescriptor; + + /** + * Constructor. + */ + public AddModCommand(EditPersonDescriptor editPersonDescriptor) { + requireNonNull(editPersonDescriptor); + + this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + Person myProfile = model.getProfile(); + + Person editedProfile = createEditedPerson(myProfile, editPersonDescriptor); + + if (myProfile.equals(editedProfile)) { + throw new CommandException(MESSAGE_DUPLICATE_MODULE); + } + + model.setPerson(myProfile, editedProfile); + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + return new CommandResult(String.format(MESSAGE_ADD_MODULE_SUCCESS, editPersonDescriptor.getTags().get())); + } + + /** + * Creates and returns a {@code Person} with the details of {@code myProfile} + * edited with {@code editPersonDescriptor}. + */ + private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) { + assert personToEdit != null; + + Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName()); + StudentId updatedId = editPersonDescriptor.getStudentId().orElse(personToEdit.getStudentId()); + Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); + Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); + GitHubUsername updatedGitHubUsername = editPersonDescriptor.getGitHubUsername() + .orElse(personToEdit.getGithubUsername()); + TelegramHandle updatedTelegramHandle = editPersonDescriptor.getTelegramHandle() + .orElse(personToEdit.getTelegramHandle()); + Set updatedMods = new HashSet<>(Collections.emptySet()); + updatedMods.addAll(personToEdit.getMods()); + updatedMods.addAll(editPersonDescriptor.getTags().get()); + + return new Person(updatedName, updatedId, updatedPhone, updatedEmail, + updatedGitHubUsername, updatedTelegramHandle, false, updatedMods, true); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof AddModCommand)) { + return false; + } + + // state check + AddModCommand amc = (AddModCommand) other; + return editPersonDescriptor.equals(amc.editPersonDescriptor); + } +} diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/modulink/logic/commands/Command.java similarity index 78% rename from src/main/java/seedu/address/logic/commands/Command.java rename to src/main/java/seedu/modulink/logic/commands/Command.java index 64f18992160..4080a1dff27 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/seedu/modulink/logic/commands/Command.java @@ -1,7 +1,7 @@ -package seedu.address.logic.commands; +package seedu.modulink.logic.commands; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; +import seedu.modulink.logic.commands.exceptions.CommandException; +import seedu.modulink.model.Model; /** * Represents a command with hidden internal logic and the ability to be executed. diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/modulink/logic/commands/CommandResult.java similarity index 97% rename from src/main/java/seedu/address/logic/commands/CommandResult.java rename to src/main/java/seedu/modulink/logic/commands/CommandResult.java index 92f900b7916..acfc8cc27f7 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/modulink/logic/commands/CommandResult.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.modulink.logic.commands; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/modulink/logic/commands/CreateCommand.java b/src/main/java/seedu/modulink/logic/commands/CreateCommand.java new file mode 100644 index 00000000000..d9ad909eb38 --- /dev/null +++ b/src/main/java/seedu/modulink/logic/commands/CreateCommand.java @@ -0,0 +1,83 @@ +package seedu.modulink.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_GITHUB_USERNAME; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_MOD; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_TELEGRAM_HANDLE; + +import seedu.modulink.logic.commands.exceptions.CommandException; +import seedu.modulink.model.Model; +import seedu.modulink.model.person.Person; + +/** + * Adds a person to the address book. + */ +public class CreateCommand extends Command { + + public static final String COMMAND_WORD = "create"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Creates the user's profile in ModuLink. " + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_ID + "STUDENT ID " + + PREFIX_PHONE + "PHONE " + + PREFIX_EMAIL + "EMAIL " + + "[" + PREFIX_GITHUB_USERNAME + "GITHUB USERNAME] " + + "[" + PREFIX_TELEGRAM_HANDLE + "TELEGRAM HANDLE] " + + "[" + PREFIX_MOD + "TAG]...\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "John Doe " + + PREFIX_ID + "A1234567Z " + + PREFIX_PHONE + "98765432 " + + PREFIX_EMAIL + "johnd@example.com " + + PREFIX_GITHUB_USERNAME + "johnd " + + PREFIX_TELEGRAM_HANDLE + "@johndoe " + + PREFIX_MOD + "CS2100 " + + PREFIX_MOD + "CS2101"; + + public static final String MESSAGE_SUCCESS = "Profile created: %1$s"; + public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book"; + public static final String MESSAGE_DUPLICATE_STUDENT_ID = "There is already a person with this Student ID."; + + private final Person myProfile; + + /** + * Creates an AddCommand to add the specified {@code Person} + */ + public CreateCommand(Person person) { + requireNonNull(person); + myProfile = person; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasPerson(myProfile)) { + throw new CommandException(MESSAGE_DUPLICATE_PERSON); + } + + if (model.hasStudentIdNotProfile(myProfile)) { + throw new CommandException(MESSAGE_DUPLICATE_STUDENT_ID); + } + + // deletes the placeholder and adds the user profile + if (!model.getFilteredPersonList().isEmpty()) { + Person placeholder = model.getFilteredPersonList().get(0); + model.deletePerson(placeholder); + } + model.addProfile(myProfile); + return new CommandResult(String.format(MESSAGE_SUCCESS, myProfile)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof CreateCommand // instanceof handles nulls + && myProfile.equals(((CreateCommand) other).myProfile)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/modulink/logic/commands/DeleteCommand.java similarity index 85% rename from src/main/java/seedu/address/logic/commands/DeleteCommand.java rename to src/main/java/seedu/modulink/logic/commands/DeleteCommand.java index 02fd256acba..33ab7442af5 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ b/src/main/java/seedu/modulink/logic/commands/DeleteCommand.java @@ -1,14 +1,14 @@ -package seedu.address.logic.commands; +package seedu.modulink.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; +import seedu.modulink.commons.core.Messages; +import seedu.modulink.commons.core.index.Index; +import seedu.modulink.logic.commands.exceptions.CommandException; +import seedu.modulink.model.Model; +import seedu.modulink.model.person.Person; /** * Deletes a person identified using it's displayed index from the address book. diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/modulink/logic/commands/EditCommand.java similarity index 50% rename from src/main/java/seedu/address/logic/commands/EditCommand.java rename to src/main/java/seedu/modulink/logic/commands/EditCommand.java index 7e36114902f..022eea739fc 100644 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ b/src/main/java/seedu/modulink/logic/commands/EditCommand.java @@ -1,30 +1,31 @@ -package seedu.address.logic.commands; +package seedu.modulink.logic.commands; import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_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 static seedu.modulink.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_GITHUB_USERNAME; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_TELEGRAM_HANDLE; +import static seedu.modulink.model.Model.PREDICATE_SHOW_ALL_PERSONS; import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Optional; import java.util.Set; -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.CollectionUtil; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; +import seedu.modulink.commons.util.CollectionUtil; +import seedu.modulink.logic.commands.exceptions.CommandException; +import seedu.modulink.model.Model; +import seedu.modulink.model.person.Email; +import seedu.modulink.model.person.GitHubUsername; +import seedu.modulink.model.person.Name; +import seedu.modulink.model.person.Person; +import seedu.modulink.model.person.Phone; +import seedu.modulink.model.person.StudentId; +import seedu.modulink.model.person.TelegramHandle; +import seedu.modulink.model.tag.Mod; + /** * Edits the details of an existing person in the address book. @@ -33,52 +34,46 @@ public class EditCommand extends Command { public static final String COMMAND_WORD = "edit"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified " - + "by the index number used in the displayed person list. " + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of your profile.\n" + "Existing values will be overwritten by the input values.\n" - + "Parameters: INDEX (must be a positive integer) " + + "Parameters: " + "[" + PREFIX_NAME + "NAME] " + + "[" + PREFIX_ID + "STUDENT ID] " + "[" + PREFIX_PHONE + "PHONE] " + "[" + PREFIX_EMAIL + "EMAIL] " - + "[" + PREFIX_ADDRESS + "ADDRESS] " - + "[" + PREFIX_TAG + "TAG]...\n" - + "Example: " + COMMAND_WORD + " 1 " + + "[" + PREFIX_GITHUB_USERNAME + "GITHUB] " + + "[" + PREFIX_TELEGRAM_HANDLE + "TELEGRAM]\n" + + "Example: " + COMMAND_WORD + " " + PREFIX_PHONE + "91234567 " - + PREFIX_EMAIL + "johndoe@example.com"; + + PREFIX_EMAIL + "johndoe@example.com\n" + + "Note: Module tags cannot be edited using this command.\n " + + "To edit your module list, " + + "please use the \"addMod\", \"remMod\", or \"editGroupStatus\" commands."; - public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s"; + public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited your profile: %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_STUDENT_ID = "There is already a profile with this Student ID."; - private final Index index; private final EditPersonDescriptor editPersonDescriptor; /** - * @param index of the person in the filtered person list to edit * @param editPersonDescriptor details to edit the person with */ - public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) { - requireNonNull(index); + public EditCommand(EditPersonDescriptor editPersonDescriptor) { requireNonNull(editPersonDescriptor); - this.index = index; this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor); } @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 personToEdit = lastShownList.get(index.getZeroBased()); + Person personToEdit = model.getProfile(); Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor); - if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); + if (model.hasStudentIdNotProfile(editedPerson)) { + throw new CommandException(MESSAGE_DUPLICATE_STUDENT_ID); } model.setPerson(personToEdit, editedPerson); @@ -94,12 +89,17 @@ private static Person createEditedPerson(Person personToEdit, EditPersonDescript assert personToEdit != null; Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName()); + StudentId updatedId = editPersonDescriptor.getStudentId().orElse(personToEdit.getStudentId()); Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); - Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress()); - Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags()); - - return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); + GitHubUsername updatedGitHubUsername = editPersonDescriptor.getGitHubUsername() + .orElse(personToEdit.getGithubUsername()); + TelegramHandle updatedTelegramHandle = editPersonDescriptor.getTelegramHandle() + .orElse(personToEdit.getTelegramHandle()); + Set updatedMods = personToEdit.getMods(); + + return new Person(updatedName, updatedId, updatedPhone, updatedEmail, + updatedGitHubUsername, updatedTelegramHandle, false, updatedMods, true); } @Override @@ -116,8 +116,7 @@ public boolean equals(Object other) { // state check EditCommand e = (EditCommand) other; - return index.equals(e.index) - && editPersonDescriptor.equals(e.editPersonDescriptor); + return editPersonDescriptor.equals(e.editPersonDescriptor); } /** @@ -126,30 +125,35 @@ public boolean equals(Object other) { */ public static class EditPersonDescriptor { private Name name; + private StudentId id; private Phone phone; private Email email; - private Address address; - private Set tags; + private GitHubUsername gitHubUsername; + private TelegramHandle telegramHandle; + private Set mods; public EditPersonDescriptor() {} /** * Copy constructor. - * A defensive copy of {@code tags} is used internally. + * A defensive copy of {@code mods} is used internally. */ public EditPersonDescriptor(EditPersonDescriptor toCopy) { setName(toCopy.name); + setStudentId(toCopy.id); setPhone(toCopy.phone); setEmail(toCopy.email); - setAddress(toCopy.address); - setTags(toCopy.tags); + setGitHubUsername(toCopy.gitHubUsername); + setTelegramHandle(toCopy.telegramHandle); + setTags(toCopy.mods); } + /** * Returns true if at least one field is edited. */ public boolean isAnyFieldEdited() { - return CollectionUtil.isAnyNonNull(name, phone, email, address, tags); + return CollectionUtil.isAnyNonNull(name, id, phone, email, gitHubUsername, telegramHandle, mods); } public void setName(Name name) { @@ -160,6 +164,14 @@ public Optional getName() { return Optional.ofNullable(name); } + public void setStudentId(StudentId id) { + this.id = id; + } + + public Optional getStudentId() { + return Optional.ofNullable(id); + } + public void setPhone(Phone phone) { this.phone = phone; } @@ -176,29 +188,37 @@ public Optional getEmail() { return Optional.ofNullable(email); } - public void setAddress(Address address) { - this.address = address; + public void setGitHubUsername(GitHubUsername gitHubUsername) { + this.gitHubUsername = gitHubUsername; + } + + public Optional getGitHubUsername() { + return Optional.ofNullable(gitHubUsername); + } + + public void setTelegramHandle(TelegramHandle telegramHandle) { + this.telegramHandle = telegramHandle; } - public Optional
getAddress() { - return Optional.ofNullable(address); + public Optional getTelegramHandle() { + return Optional.ofNullable(telegramHandle); } /** - * Sets {@code tags} to this object's {@code tags}. - * A defensive copy of {@code tags} is used internally. + * Sets {@code mods} to this object's {@code mods}. + * A defensive copy of {@code mods} is used internally. */ - public void setTags(Set tags) { - this.tags = (tags != null) ? new HashSet<>(tags) : null; + public void setTags(Set mods) { + this.mods = (mods != null) ? new HashSet<>(mods) : null; } /** * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException} * if modification is attempted. - * Returns {@code Optional#empty()} if {@code tags} is null. + * Returns {@code Optional#empty()} if {@code mods} is null. */ - public Optional> getTags() { - return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); + public Optional> getTags() { + return (mods != null) ? Optional.of(Collections.unmodifiableSet(mods)) : Optional.empty(); } @Override @@ -217,9 +237,11 @@ public boolean equals(Object other) { EditPersonDescriptor e = (EditPersonDescriptor) other; return getName().equals(e.getName()) + && getStudentId().equals(e.getStudentId()) && getPhone().equals(e.getPhone()) && getEmail().equals(e.getEmail()) - && getAddress().equals(e.getAddress()) + && getGitHubUsername().equals(e.getGitHubUsername()) + && getTelegramHandle().equals(e.getTelegramHandle()) && getTags().equals(e.getTags()); } } diff --git a/src/main/java/seedu/modulink/logic/commands/EditGroupStatusCommand.java b/src/main/java/seedu/modulink/logic/commands/EditGroupStatusCommand.java new file mode 100644 index 00000000000..6179668ff49 --- /dev/null +++ b/src/main/java/seedu/modulink/logic/commands/EditGroupStatusCommand.java @@ -0,0 +1,128 @@ +package seedu.modulink.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_MOD; +import static seedu.modulink.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import seedu.modulink.logic.commands.exceptions.CommandException; +import seedu.modulink.model.Model; +import seedu.modulink.model.person.Email; +import seedu.modulink.model.person.GitHubUsername; +import seedu.modulink.model.person.Name; +import seedu.modulink.model.person.Person; +import seedu.modulink.model.person.Phone; +import seedu.modulink.model.person.StudentId; +import seedu.modulink.model.person.TelegramHandle; +import seedu.modulink.model.tag.Mod; + +public class EditGroupStatusCommand extends Command { + + public static final String COMMAND_WORD = "editGroupStatus"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Edits the group status of the specified module. " + + "To change the grouping status to 'don't need group/not looking for group', " + + "please leave the description blank\n" + + "Parameters: " + + PREFIX_MOD + "MOD \n" + + "Example: \n- " + COMMAND_WORD + " " + PREFIX_MOD + "CS2100 need group\n" + + "- " + COMMAND_WORD + " " + PREFIX_MOD + "CS2101 need member\n"; + + public static final String MESSAGE_EDIT_GROUP_STATUS_SUCCESS = "Group status changed to: %1$s"; + public static final String MESSAGE_MODULE_DOES_NOT_EXIST = "The specified module does not exist on your profile. " + + "To add the module to your profile, use the addMod command."; + public static final String MESSAGE_NO_MODULE_SPECIFIED = "One module must be provided."; + public static final String MESSAGE_MULTIPLE_MODULES_SPECIFIED = "Only one module must be provided."; + public static final String MESSAGE_NO_STATUS_CHANGED = "The status specified is the same " + + "as what currently exists for the specified module."; + + + private final EditCommand.EditPersonDescriptor editPersonDescriptor; + + /** + * Constructor. + */ + public EditGroupStatusCommand(EditCommand.EditPersonDescriptor editPersonDescriptor) { + requireNonNull(editPersonDescriptor); + + this.editPersonDescriptor = new EditCommand.EditPersonDescriptor(editPersonDescriptor); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + Person myProfile = model.getProfile(); + + for (Mod mod : myProfile.getMods()) { + for (Mod toEditModule : editPersonDescriptor.getTags().get()) { + if (toEditModule.equals(mod)) { + if (toEditModule.equalsStatus(mod)) { + throw new CommandException(MESSAGE_NO_STATUS_CHANGED); + } + } + } + } + + Person editedProfile = createEditedPerson(myProfile, editPersonDescriptor); + + if (!myProfile.getMods().containsAll(editPersonDescriptor.getTags().get())) { + throw new CommandException(MESSAGE_MODULE_DOES_NOT_EXIST); + } + + model.setPerson(myProfile, editedProfile); + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + return new CommandResult(String.format(MESSAGE_EDIT_GROUP_STATUS_SUCCESS, + editPersonDescriptor.getTags().get())); + } + + /** + * Creates and returns a {@code Person} with the details of {@code myProfile} + * edited with {@code editPersonDescriptor}. + */ + private static Person createEditedPerson(Person personToEdit, + EditCommand.EditPersonDescriptor editPersonDescriptor) { + assert personToEdit != null; + + Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName()); + StudentId updatedId = editPersonDescriptor.getStudentId().orElse(personToEdit.getStudentId()); + Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); + Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); + GitHubUsername updatedGitHubUsername = editPersonDescriptor.getGitHubUsername() + .orElse(personToEdit.getGithubUsername()); + TelegramHandle updatedTelegramHandle = editPersonDescriptor.getTelegramHandle() + .orElse(personToEdit.getTelegramHandle()); + Set updatedMods = new HashSet<>(Collections.emptySet()); + + for (Mod mod : personToEdit.getMods()) { + if (!editPersonDescriptor.getTags().get().contains(mod)) { + updatedMods.add(mod); + } + } + updatedMods.addAll(editPersonDescriptor.getTags().get()); + + return new Person(updatedName, updatedId, updatedPhone, updatedEmail, + updatedGitHubUsername, updatedTelegramHandle, false, updatedMods, true); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditGroupStatusCommand)) { + return false; + } + + // state check + EditGroupStatusCommand egsc = (EditGroupStatusCommand) other; + return editPersonDescriptor.equals(egsc.editPersonDescriptor); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/modulink/logic/commands/ExitCommand.java similarity index 83% rename from src/main/java/seedu/address/logic/commands/ExitCommand.java rename to src/main/java/seedu/modulink/logic/commands/ExitCommand.java index 3dd85a8ba90..3bf49617a2b 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/modulink/logic/commands/ExitCommand.java @@ -1,6 +1,6 @@ -package seedu.address.logic.commands; +package seedu.modulink.logic.commands; -import seedu.address.model.Model; +import seedu.modulink.model.Model; /** * Terminates the program. diff --git a/src/main/java/seedu/modulink/logic/commands/FilterCommand.java b/src/main/java/seedu/modulink/logic/commands/FilterCommand.java new file mode 100644 index 00000000000..0f3d17dca7a --- /dev/null +++ b/src/main/java/seedu/modulink/logic/commands/FilterCommand.java @@ -0,0 +1,51 @@ +package seedu.modulink.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.modulink.commons.core.Messages; +import seedu.modulink.model.Model; +import seedu.modulink.model.person.ModuleContainsKeywordsPredicate; + +/** + * Filters and lists all persons in address book who have a certain module (and optionally group status) as their tag. + * 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 + ": Show user profiles filtered by module code " + + "and optionally by group status (need member or need group).\n" + + "MODULE_CODE is required for filtering by group status. " + + "The filter will return the profiles with the specified group status of the specified module.\n" + + "Parameters: filter mod/MODULE_CODE [GROUP_STATUS]\n" + + "Example: " + COMMAND_WORD + " mod/CS2030 need member\n"; + + private final ModuleContainsKeywordsPredicate predicate; + + public FilterCommand(ModuleContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredPersonList(predicate); + int result = model.getFilteredPersonList().size(); + if (result <= 0) { + return new CommandResult( + String.format(Messages.MESSAGE_NO_PERSON_LISTED, "with this module.")); + } else { + 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/FindCommand.java b/src/main/java/seedu/modulink/logic/commands/FindCommand.java similarity index 68% rename from src/main/java/seedu/address/logic/commands/FindCommand.java rename to src/main/java/seedu/modulink/logic/commands/FindCommand.java index d6b19b0a0de..279c5bac377 100644 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ b/src/main/java/seedu/modulink/logic/commands/FindCommand.java @@ -1,10 +1,10 @@ -package seedu.address.logic.commands; +package seedu.modulink.logic.commands; import static java.util.Objects.requireNonNull; -import seedu.address.commons.core.Messages; -import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.modulink.commons.core.Messages; +import seedu.modulink.model.Model; +import seedu.modulink.model.person.NameContainsKeywordsPredicate; /** * Finds and lists all persons in address book whose name contains any of the argument keywords. @@ -29,8 +29,14 @@ public FindCommand(NameContainsKeywordsPredicate predicate) { public CommandResult execute(Model model) { requireNonNull(model); model.updateFilteredPersonList(predicate); - return new CommandResult( - String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); + int result = model.getFilteredPersonList().size(); + if (result <= 0) { + return new CommandResult( + String.format(Messages.MESSAGE_NO_PERSON_LISTED, "with this name.")); + } else { + return new CommandResult( + String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); + } } @Override diff --git a/src/main/java/seedu/modulink/logic/commands/FindIdCommand.java b/src/main/java/seedu/modulink/logic/commands/FindIdCommand.java new file mode 100644 index 00000000000..fea87924a6b --- /dev/null +++ b/src/main/java/seedu/modulink/logic/commands/FindIdCommand.java @@ -0,0 +1,48 @@ +package seedu.modulink.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.modulink.commons.core.Messages; +import seedu.modulink.model.Model; +import seedu.modulink.model.person.StudentIdContainsKeywordsPredicate; + +/** + * Finds and lists all persons in address book whose Student ID contains any of the argument keywords. + * Keyword matching is case insensitive. + */ +public class FindIdCommand extends Command { + + public static final String COMMAND_WORD = "findId"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds profiles whose Student ID matches " + + "the specified ID (case-insensitive) exactly and displays them as a list with index numbers.\n" + + "Parameters: STUDENT_ID [MORE_STUDENT_IDS]...\n" + + "Example: " + COMMAND_WORD + " A1234567F"; + + private final StudentIdContainsKeywordsPredicate predicate; + + public FindIdCommand(StudentIdContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredPersonList(predicate); + int result = model.getFilteredPersonList().size(); + if (result <= 0) { + return new CommandResult( + String.format(Messages.MESSAGE_NO_PERSON_LISTED, "with this ID.")); + } else { + 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 FindIdCommand // instanceof handles nulls + && predicate.equals(((FindIdCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/modulink/logic/commands/HelpCommand.java similarity index 87% rename from src/main/java/seedu/address/logic/commands/HelpCommand.java rename to src/main/java/seedu/modulink/logic/commands/HelpCommand.java index bf824f91bd0..0956973e301 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/modulink/logic/commands/HelpCommand.java @@ -1,6 +1,6 @@ -package seedu.address.logic.commands; +package seedu.modulink.logic.commands; -import seedu.address.model.Model; +import seedu.modulink.model.Model; /** * Format full help instructions for every command for display. diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/modulink/logic/commands/ListCommand.java similarity index 76% rename from src/main/java/seedu/address/logic/commands/ListCommand.java rename to src/main/java/seedu/modulink/logic/commands/ListCommand.java index 84be6ad2596..fb47aca93d3 100644 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ b/src/main/java/seedu/modulink/logic/commands/ListCommand.java @@ -1,9 +1,9 @@ -package seedu.address.logic.commands; +package seedu.modulink.logic.commands; import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import static seedu.modulink.model.Model.PREDICATE_SHOW_ALL_PERSONS; -import seedu.address.model.Model; +import seedu.modulink.model.Model; /** * Lists all persons in the address book to the user. @@ -12,7 +12,7 @@ public class ListCommand extends Command { public static final String COMMAND_WORD = "list"; - public static final String MESSAGE_SUCCESS = "Listed all persons"; + public static final String MESSAGE_SUCCESS = "Listed all person(s)"; @Override diff --git a/src/main/java/seedu/modulink/logic/commands/ListFavCommand.java b/src/main/java/seedu/modulink/logic/commands/ListFavCommand.java new file mode 100644 index 00000000000..fed78b17fe0 --- /dev/null +++ b/src/main/java/seedu/modulink/logic/commands/ListFavCommand.java @@ -0,0 +1,34 @@ +package seedu.modulink.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.modulink.commons.core.Messages; +import seedu.modulink.model.Model; +import seedu.modulink.model.person.IsFavouritePredicate; + +/** + * Finds and lists all persons in ModuLink whose Student ID contains any of the argument keywords. + * Keyword matching is case insensitive. + */ +public class ListFavCommand extends Command { + + public static final String COMMAND_WORD = "listFav"; + + public static final String MESSAGE_SUCCESS = "Listed all favourites."; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all favourites of the current user " + + "and displays them as a list with index numbers.\n" + + "Example: " + COMMAND_WORD; + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredPersonList(new IsFavouritePredicate()); + int result = model.getFilteredPersonList().size(); + if (result <= 0) { + return new CommandResult(Messages.MESSAGE_NO_FAVOURITES); + } else { + return new CommandResult(MESSAGE_SUCCESS); + } + } +} diff --git a/src/main/java/seedu/modulink/logic/commands/RemFavCommand.java b/src/main/java/seedu/modulink/logic/commands/RemFavCommand.java new file mode 100644 index 00000000000..db7b1dfa6b0 --- /dev/null +++ b/src/main/java/seedu/modulink/logic/commands/RemFavCommand.java @@ -0,0 +1,66 @@ +package seedu.modulink.logic.commands; + +import javafx.collections.ObservableList; +import seedu.modulink.commons.core.Messages; +import seedu.modulink.model.Model; +import seedu.modulink.model.person.Person; + + +/** + * Removes person whose student ID matches the user input from favourites. + * ID matching is case insensitive. + */ +public class RemFavCommand extends Command { + public static final String COMMAND_WORD = "remFav"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Removes a person as a favourite.\n " + + "Parameters: Student_ID\n" + + "Example: " + COMMAND_WORD + " A1234567X"; + + public static final String MULTIPLE_ID_ERROR = + "You can only remove one person from your Favourites list at a time."; + + private final String studentId; + + public RemFavCommand(String studentId) { + this.studentId = studentId; + } + + @Override + public CommandResult execute(Model model) { + boolean noPersonFound = true; + ObservableList personList = model.getPersonList(); + + // Look for the Person with the student ID, and if he is + // not a favourite, make him a favourite. If he already is, + // return a message saying he already is. + for (Person person : personList) { + if (person.getStudentId().toString().equalsIgnoreCase(studentId)) { + if (!person.getIsFavourite()) { + return new CommandResult(Messages.MESSAGE_PERSON_IS_NOT_FAVOURITE); + } else { + person.setFavouriteFalse(); + noPersonFound = false; + } + } + } + + if (noPersonFound) { + return new CommandResult(Messages.MESSAGE_NO_SUCH_ID_FOUND); + } else { + // included this so the list will be properly updated + model.refreshFilteredPersonList(); + return new CommandResult( + String.format(Messages.MESSAGE_FAVOURITE_REMOVED, studentId)); + } + + } + + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof RemFavCommand // instanceof handles nulls + && this.studentId.equals(((RemFavCommand) other).studentId)); + } +} diff --git a/src/main/java/seedu/modulink/logic/commands/RemoveModCommand.java b/src/main/java/seedu/modulink/logic/commands/RemoveModCommand.java new file mode 100644 index 00000000000..77cf0a4b1eb --- /dev/null +++ b/src/main/java/seedu/modulink/logic/commands/RemoveModCommand.java @@ -0,0 +1,112 @@ +package seedu.modulink.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_MOD; +import static seedu.modulink.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import seedu.modulink.logic.commands.EditCommand.EditPersonDescriptor; +import seedu.modulink.logic.commands.exceptions.CommandException; +import seedu.modulink.model.Model; +import seedu.modulink.model.person.Email; +import seedu.modulink.model.person.GitHubUsername; +import seedu.modulink.model.person.Name; +import seedu.modulink.model.person.Person; +import seedu.modulink.model.person.Phone; +import seedu.modulink.model.person.StudentId; +import seedu.modulink.model.person.TelegramHandle; +import seedu.modulink.model.tag.Mod; + +public class RemoveModCommand extends Command { + public static final String COMMAND_WORD = "remMod"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Removes a module from your module list. " + + "Remaining modules will not be affected.\n" + + "Parameters: " + + "[" + PREFIX_MOD + "MOD]\n" + + "Example: " + COMMAND_WORD + + " " + PREFIX_MOD + "CS2103T"; + + public static final String MESSAGE_ADD_MODULE_SUCCESS = "Removed module: %1$s"; + public static final String MESSAGE_NO_CHANGE = + "Please provide a module to remove with the \"mod/\" prefix."; + public static final String MESSAGE_MODULE_DOES_NOT_EXIST = + "You can only remove existing modules in your module list."; + + + private final EditPersonDescriptor editPersonDescriptor; + + /** + * Constructor for RemoveModCommand. + */ + public RemoveModCommand(EditPersonDescriptor editPersonDescriptor) { + requireNonNull(editPersonDescriptor); + + this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + Person myProfile = model.getProfile(); + + if (!myProfile.getMods().containsAll(editPersonDescriptor.getTags().get())) { + throw new CommandException(MESSAGE_MODULE_DOES_NOT_EXIST); + } + + Person editedProfile = createEditedPerson(myProfile, editPersonDescriptor); + + model.setPerson(myProfile, editedProfile); + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + return new CommandResult(String.format(MESSAGE_ADD_MODULE_SUCCESS, editPersonDescriptor.getTags().get())); + } + + /** + * Creates and returns a {@code Person} with the details of {@code personToEdit} + * edited with {@code editPersonDescriptor}. + */ + private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) { + assert personToEdit != null; + + Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName()); + StudentId updatedId = editPersonDescriptor.getStudentId().orElse(personToEdit.getStudentId()); + Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); + Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); + GitHubUsername updatedGitHubUsername = editPersonDescriptor.getGitHubUsername() + .orElse(personToEdit.getGithubUsername()); + TelegramHandle updatedTelegramHandle = editPersonDescriptor.getTelegramHandle() + .orElse(personToEdit.getTelegramHandle()); + Set updatedMods = new HashSet<>(Collections.emptySet()); + + for (Mod mod : personToEdit.getMods()) { + if (!editPersonDescriptor.getTags().get().contains(mod)) { + updatedMods.add(mod); + } + } + + return new Person(updatedName, updatedId, updatedPhone, updatedEmail, + updatedGitHubUsername, updatedTelegramHandle, false, updatedMods, true); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof RemoveModCommand)) { + return false; + } + + // state check + RemoveModCommand rmc = (RemoveModCommand) other; + return editPersonDescriptor.equals(rmc.editPersonDescriptor); + } +} diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/seedu/modulink/logic/commands/exceptions/CommandException.java similarity index 89% rename from src/main/java/seedu/address/logic/commands/exceptions/CommandException.java rename to src/main/java/seedu/modulink/logic/commands/exceptions/CommandException.java index a16bd14f2cd..1b08307591d 100644 --- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java +++ b/src/main/java/seedu/modulink/logic/commands/exceptions/CommandException.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands.exceptions; +package seedu.modulink.logic.commands.exceptions; /** * Represents an error which occurs during execution of a {@link Command}. diff --git a/src/main/java/seedu/modulink/logic/parser/AddFavCommandParser.java b/src/main/java/seedu/modulink/logic/parser/AddFavCommandParser.java new file mode 100644 index 00000000000..be1c01ae0c7 --- /dev/null +++ b/src/main/java/seedu/modulink/logic/parser/AddFavCommandParser.java @@ -0,0 +1,44 @@ +package seedu.modulink.logic.parser; + +import static seedu.modulink.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.modulink.commons.core.Messages; +import seedu.modulink.logic.commands.AddFavCommand; +import seedu.modulink.logic.parser.exceptions.ParseException; +import seedu.modulink.model.person.StudentId; + +/** + * Parses input arguments and creates a new AddFavCommand object + */ +public class AddFavCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddFavCommand + * and returns a AddFavCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddFavCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddFavCommand.MESSAGE_USAGE)); + } + + String[] nameKeywords = trimmedArgs.split("\\s+"); + if (!nameKeywords[0].matches(StudentId.VALIDATION_REGEX)) { + throw new ParseException(String.format(Messages.MESSAGE_UNEXPECTED_INPUT_FORMAT, + nameKeywords[0], AddFavCommand.MESSAGE_USAGE)); + } + if (nameKeywords.length > 1) { + if (nameKeywords[1].matches(StudentId.VALIDATION_REGEX)) { + throw new ParseException(AddFavCommand.MULTIPLE_ID_ERROR); + } else { + throw new ParseException(String.format(Messages.MESSAGE_UNEXPECTED_INPUT_FORMAT, + nameKeywords[1], AddFavCommand.MESSAGE_USAGE)); + } + } + + return new AddFavCommand(nameKeywords[0]); + } + +} diff --git a/src/main/java/seedu/modulink/logic/parser/AddModCommandParser.java b/src/main/java/seedu/modulink/logic/parser/AddModCommandParser.java new file mode 100644 index 00000000000..eee89d874c7 --- /dev/null +++ b/src/main/java/seedu/modulink/logic/parser/AddModCommandParser.java @@ -0,0 +1,75 @@ +package seedu.modulink.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.modulink.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_MOD; + +import java.util.Collection; +import java.util.Optional; +import java.util.Set; + +import seedu.modulink.logic.commands.AddModCommand; +import seedu.modulink.logic.commands.EditCommand.EditPersonDescriptor; +import seedu.modulink.logic.parser.exceptions.ParseException; +import seedu.modulink.model.tag.Mod; + +public class AddModCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the AddModCommand + * and returns an AddModCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddModCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_MOD); + + String trimmedArgs = args.trim(); + + try { + if (trimmedArgs.isEmpty() + || parseModsToAdd(argMultimap.getAllValues(PREFIX_MOD)).isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddModCommand.MESSAGE_USAGE)); + } + + EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); + + parseModsToAdd(argMultimap.getAllValues(PREFIX_MOD)).ifPresent(editPersonDescriptor::setTags); + if (!editPersonDescriptor.isAnyFieldEdited()) { + throw new ParseException(AddModCommand.MESSAGE_NO_CHANGE); + } + + return new AddModCommand(editPersonDescriptor); + + } catch (ParseException e) { + throw new ParseException(String.format(e.getMessage() + "%s", + e.getMessage().startsWith("Unknown prefix(es)") ? AddModCommand.MESSAGE_USAGE : "")); + } + + } + + /** + * 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> parseModsToAdd(Collection tags) throws ParseException { + assert tags != null; + + if (tags.isEmpty()) { + throw new ParseException(AddModCommand.MESSAGE_NO_CHANGE); + } + + if (tags.size() > 1) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddModCommand.MESSAGE_USAGE)); + } + + if (tags.contains("")) { + throw new ParseException(AddModCommand.MESSAGE_NO_CHANGE); + + } else { + return Optional.of(ParserUtil.parseTags(tags)); + } + } +} diff --git a/src/main/java/seedu/modulink/logic/parser/AddressBookParser.java b/src/main/java/seedu/modulink/logic/parser/AddressBookParser.java new file mode 100644 index 00000000000..01095375d2b --- /dev/null +++ b/src/main/java/seedu/modulink/logic/parser/AddressBookParser.java @@ -0,0 +1,103 @@ +package seedu.modulink.logic.parser; + +import static seedu.modulink.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.modulink.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import seedu.modulink.logic.commands.AddFavCommand; +import seedu.modulink.logic.commands.AddModCommand; +import seedu.modulink.logic.commands.Command; +import seedu.modulink.logic.commands.CreateCommand; +import seedu.modulink.logic.commands.DeleteCommand; +import seedu.modulink.logic.commands.EditCommand; +import seedu.modulink.logic.commands.EditGroupStatusCommand; +import seedu.modulink.logic.commands.ExitCommand; +import seedu.modulink.logic.commands.FilterCommand; +import seedu.modulink.logic.commands.FindCommand; +import seedu.modulink.logic.commands.FindIdCommand; +import seedu.modulink.logic.commands.HelpCommand; +import seedu.modulink.logic.commands.ListCommand; +import seedu.modulink.logic.commands.ListFavCommand; +import seedu.modulink.logic.commands.RemFavCommand; +import seedu.modulink.logic.commands.RemoveModCommand; +import seedu.modulink.logic.parser.exceptions.ParseException; + +/** + * Parses user input. + */ +public class AddressBookParser { + + /** + * Used for initial separation of command word and args. + */ + private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); + + /** + * Parses user input into command for execution. + * + * @param userInput full user input string + * @return the command based on the user input + * @throws ParseException if the user input does not conform the expected format + */ + public Command parseCommand(String userInput) throws ParseException { + final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); + if (!matcher.matches()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + } + + final String commandWord = matcher.group("commandWord"); + final String arguments = matcher.group("arguments"); + switch (commandWord) { + + case AddFavCommand.COMMAND_WORD: + return new AddFavCommandParser().parse(arguments); + + case AddModCommand.COMMAND_WORD: + return new AddModCommandParser().parse(arguments); + + case CreateCommand.COMMAND_WORD: + return new CreateCommandParser().parse(arguments); + + case EditCommand.COMMAND_WORD: + return new EditCommandParser().parse(arguments); + + case DeleteCommand.COMMAND_WORD: + return new DeleteCommandParser().parse(arguments); + + case FindCommand.COMMAND_WORD: + return new FindCommandParser().parse(arguments); + + case FindIdCommand.COMMAND_WORD: + return new FindIdCommandParser().parse(arguments); + + case ListCommand.COMMAND_WORD: + return new ListCommand(); + + case ListFavCommand.COMMAND_WORD: + return new ListFavCommand(); + + case RemFavCommand.COMMAND_WORD: + return new RemFavCommandParser().parse(arguments); + + case RemoveModCommand.COMMAND_WORD: + return new RemoveModCommandParser().parse(arguments); + + case FilterCommand.COMMAND_WORD: + return new FilterCommandParser().parse(arguments); + + case EditGroupStatusCommand.COMMAND_WORD: + return new EditGroupStatusCommandParser().parse(arguments); + + case ExitCommand.COMMAND_WORD: + return new ExitCommand(); + + case HelpCommand.COMMAND_WORD: + return new HelpCommand(); + + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java b/src/main/java/seedu/modulink/logic/parser/ArgumentMultimap.java similarity index 98% rename from src/main/java/seedu/address/logic/parser/ArgumentMultimap.java rename to src/main/java/seedu/modulink/logic/parser/ArgumentMultimap.java index 954c8e18f8e..6edd84752c0 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java +++ b/src/main/java/seedu/modulink/logic/parser/ArgumentMultimap.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.modulink.logic.parser; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/seedu/modulink/logic/parser/ArgumentTokenizer.java similarity index 99% rename from src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java rename to src/main/java/seedu/modulink/logic/parser/ArgumentTokenizer.java index 5c9aebfa488..fcb5f2fcd9b 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java +++ b/src/main/java/seedu/modulink/logic/parser/ArgumentTokenizer.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.modulink.logic.parser; import java.util.ArrayList; import java.util.Arrays; diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/modulink/logic/parser/CliSyntax.java similarity index 51% rename from src/main/java/seedu/address/logic/parser/CliSyntax.java rename to src/main/java/seedu/modulink/logic/parser/CliSyntax.java index 75b1a9bf119..4f3ea098bf9 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/modulink/logic/parser/CliSyntax.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.modulink.logic.parser; /** * Contains Command Line Interface (CLI) syntax definitions common to multiple commands @@ -7,9 +7,10 @@ public class CliSyntax { /* Prefix definitions */ public static final Prefix PREFIX_NAME = new Prefix("n/"); + public static final Prefix PREFIX_ID = new Prefix("id/"); public static final Prefix PREFIX_PHONE = new Prefix("p/"); public static final Prefix PREFIX_EMAIL = new Prefix("e/"); - public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); - public static final Prefix PREFIX_TAG = new Prefix("t/"); - + public static final Prefix PREFIX_GITHUB_USERNAME = new Prefix("github/"); + public static final Prefix PREFIX_TELEGRAM_HANDLE = new Prefix("tele/"); + public static final Prefix PREFIX_MOD = new Prefix("mod/"); } diff --git a/src/main/java/seedu/modulink/logic/parser/CreateCommandParser.java b/src/main/java/seedu/modulink/logic/parser/CreateCommandParser.java new file mode 100644 index 00000000000..6339deedbcb --- /dev/null +++ b/src/main/java/seedu/modulink/logic/parser/CreateCommandParser.java @@ -0,0 +1,110 @@ +package seedu.modulink.logic.parser; + +import static seedu.modulink.commons.core.Messages.MESSAGE_DUPLICATE_PREFIX_FORMAT; +import static seedu.modulink.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.modulink.commons.core.Messages.MESSAGE_MISSING_PREFIXES_FORMAT; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_GITHUB_USERNAME; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_MOD; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_TELEGRAM_HANDLE; + +import java.util.Set; +import java.util.stream.Stream; + +import seedu.modulink.logic.commands.CreateCommand; +import seedu.modulink.logic.parser.exceptions.ParseException; +import seedu.modulink.model.person.Email; +import seedu.modulink.model.person.GitHubUsername; +import seedu.modulink.model.person.Name; +import seedu.modulink.model.person.Person; +import seedu.modulink.model.person.Phone; +import seedu.modulink.model.person.StudentId; +import seedu.modulink.model.person.TelegramHandle; +import seedu.modulink.model.tag.Mod; + + +/** + * Parses input arguments and creates a new AddCommand object + */ +public class CreateCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddCommand + * and returns an AddCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public CreateCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_ID, PREFIX_PHONE, + PREFIX_EMAIL, PREFIX_GITHUB_USERNAME, PREFIX_TELEGRAM_HANDLE, PREFIX_MOD); + + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ID, PREFIX_PHONE, PREFIX_EMAIL)) { + StringBuilder missingPrefixes = findMissingPrefixes(argMultimap, + PREFIX_NAME, PREFIX_ID, PREFIX_PHONE, PREFIX_EMAIL); + throw new ParseException(String.format(MESSAGE_MISSING_PREFIXES_FORMAT, + missingPrefixes, CreateCommand.MESSAGE_USAGE)); + + } + + if (!argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, CreateCommand.MESSAGE_USAGE)); + } + + // either duplicate parameters, or unsupported parameters (dealt with in next try-catch block) + try { + ParserUtil.checkDuplicate(args, argMultimap, ParserUtil.isDuplicatePrefix(args, PREFIX_NAME, PREFIX_ID, + PREFIX_PHONE, PREFIX_EMAIL, PREFIX_GITHUB_USERNAME, PREFIX_TELEGRAM_HANDLE)); + } catch (ParseException e) { + StringBuilder duplicatePrefixes = ParserUtil.findDuplicatePrefixes(args, + PREFIX_NAME, PREFIX_ID, PREFIX_PHONE, + PREFIX_EMAIL, PREFIX_GITHUB_USERNAME, PREFIX_TELEGRAM_HANDLE); + throw new ParseException(String.format(MESSAGE_DUPLICATE_PREFIX_FORMAT, + duplicatePrefixes, CreateCommand.MESSAGE_USAGE)); + } + + try { + Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); + Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()); + StudentId id = ParserUtil.parseStudentId(argMultimap.getValue(PREFIX_ID).orElse(null)); + Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()); + GitHubUsername gitHubUsername = + ParserUtil.parseGithubUsername(argMultimap.getValue(PREFIX_GITHUB_USERNAME).orElse(null)); + TelegramHandle telegramHandle = + ParserUtil.parseTelegramHandle(argMultimap.getValue(PREFIX_TELEGRAM_HANDLE).orElse(null)); + Set modList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_MOD)); + Person person = new Person(name, id, phone, email, gitHubUsername, + telegramHandle, false, modList, true); + return new CreateCommand(person); + + } catch (ParseException e) { + throw new ParseException(String.format(e.getMessage() + "%s", + e.getMessage().startsWith("Unknown prefix(es)") ? CreateCommand.MESSAGE_USAGE : "")); + } + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + + /** + * Returns any prefixes that are missing. + * {@code ArgumentMultimap}. + */ + private static StringBuilder findMissingPrefixes(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + StringBuilder missingPrefixList = new StringBuilder(); + for (Prefix prefix : prefixes) { + if (argumentMultimap.getValue(prefix).isEmpty()) { + missingPrefixList.append(prefix).append(" "); + } + } + return missingPrefixList; + } + +} diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/modulink/logic/parser/DeleteCommandParser.java similarity index 73% rename from src/main/java/seedu/address/logic/parser/DeleteCommandParser.java rename to src/main/java/seedu/modulink/logic/parser/DeleteCommandParser.java index 522b93081cc..b58b25911bc 100644 --- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java +++ b/src/main/java/seedu/modulink/logic/parser/DeleteCommandParser.java @@ -1,10 +1,10 @@ -package seedu.address.logic.parser; +package seedu.modulink.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.modulink.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.DeleteCommand; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.modulink.commons.core.index.Index; +import seedu.modulink.logic.commands.DeleteCommand; +import seedu.modulink.logic.parser.exceptions.ParseException; /** * Parses input arguments and creates a new DeleteCommand object diff --git a/src/main/java/seedu/modulink/logic/parser/EditCommandParser.java b/src/main/java/seedu/modulink/logic/parser/EditCommandParser.java new file mode 100644 index 00000000000..43a24bc296e --- /dev/null +++ b/src/main/java/seedu/modulink/logic/parser/EditCommandParser.java @@ -0,0 +1,118 @@ +package seedu.modulink.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.modulink.commons.core.Messages.MESSAGE_DUPLICATE_PREFIX_FORMAT; +import static seedu.modulink.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_GITHUB_USERNAME; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_MOD; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_TELEGRAM_HANDLE; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +import seedu.modulink.logic.commands.EditCommand; +import seedu.modulink.logic.commands.EditCommand.EditPersonDescriptor; +import seedu.modulink.logic.parser.exceptions.ParseException; +import seedu.modulink.model.tag.Mod; + +/** + * Parses input arguments and creates a new EditCommand object + */ +public class EditCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditCommand + * and returns an EditCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EditCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_ID, PREFIX_PHONE, + PREFIX_EMAIL, PREFIX_GITHUB_USERNAME, PREFIX_TELEGRAM_HANDLE, + PREFIX_MOD); + + String trimmedArgs = args.trim(); + String preamble = argMultimap.getPreamble(); + if (trimmedArgs.isEmpty() + || !preamble.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + + EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); + + // either duplicate parameters, or unsupported parameters (dealt with in next try-catch block) + try { + ParserUtil.checkDuplicate(args, argMultimap, ParserUtil.isDuplicatePrefix(args, PREFIX_NAME, PREFIX_ID, + PREFIX_PHONE, PREFIX_EMAIL, PREFIX_GITHUB_USERNAME, PREFIX_TELEGRAM_HANDLE)); + } catch (ParseException e) { + StringBuilder duplicatePrefixes = ParserUtil.findDuplicatePrefixes(args, + PREFIX_NAME, PREFIX_ID, PREFIX_PHONE, + PREFIX_EMAIL, PREFIX_GITHUB_USERNAME, PREFIX_TELEGRAM_HANDLE); + throw new ParseException(String.format(MESSAGE_DUPLICATE_PREFIX_FORMAT, + duplicatePrefixes, EditCommand.MESSAGE_USAGE)); + } + + try { + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); + } + if (argMultimap.getValue(PREFIX_ID).isPresent()) { + editPersonDescriptor.setStudentId(ParserUtil.parseStudentId(argMultimap.getValue(PREFIX_ID).get())); + } + if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { + editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); + } + if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { + editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get())); + } + if (argMultimap.getValue(PREFIX_GITHUB_USERNAME).isPresent()) { + editPersonDescriptor.setGitHubUsername(ParserUtil.parseGithubUsername + (argMultimap.getValue(PREFIX_GITHUB_USERNAME).get())); + } + if (argMultimap.getValue(PREFIX_TELEGRAM_HANDLE).isPresent()) { + editPersonDescriptor.setTelegramHandle(ParserUtil.parseTelegramHandle( + argMultimap.getValue(PREFIX_TELEGRAM_HANDLE).get())); + } + if (parseModsToEdit(argMultimap.getAllValues(PREFIX_MOD)).isPresent()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + + if (!editPersonDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); + } + + return new EditCommand(editPersonDescriptor); + } catch (ParseException e) { + throw new ParseException(String.format(e.getMessage() + "%s", + e.getMessage().startsWith("Unknown prefix(es)") ? EditCommand.MESSAGE_USAGE : "")); + } + } + + + /** + * 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. + * + * Only used for error checking. + */ + private Optional> parseModsToEdit(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/modulink/logic/parser/EditGroupStatusCommandParser.java b/src/main/java/seedu/modulink/logic/parser/EditGroupStatusCommandParser.java new file mode 100644 index 00000000000..803f633be62 --- /dev/null +++ b/src/main/java/seedu/modulink/logic/parser/EditGroupStatusCommandParser.java @@ -0,0 +1,82 @@ +package seedu.modulink.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.modulink.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.modulink.commons.core.Messages.MESSAGE_UNKNOWN_PREFIX_FORMAT; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_MOD; + +import java.util.Collection; +import java.util.Optional; +import java.util.Set; + +import seedu.modulink.logic.commands.EditCommand; +import seedu.modulink.logic.commands.EditGroupStatusCommand; +import seedu.modulink.logic.parser.exceptions.ParseException; +import seedu.modulink.model.tag.Mod; + +public class EditGroupStatusCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the EditGroupStatusCommand + * and returns an EditGroupStatusCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EditGroupStatusCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_MOD); + + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditGroupStatusCommand.MESSAGE_USAGE)); + } + + if (!trimmedArgs.startsWith("mod/") && trimmedArgs.contains("/")) { + throw new ParseException( + String.format(MESSAGE_UNKNOWN_PREFIX_FORMAT, EditGroupStatusCommand.MESSAGE_USAGE)); + } + + if (!trimmedArgs.startsWith("mod/") && !trimmedArgs.contains("/")) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditGroupStatusCommand.MESSAGE_USAGE)); + } + + EditCommand.EditPersonDescriptor editPersonDescriptor = new EditCommand.EditPersonDescriptor(); + + try { + parseModsToAdd(argMultimap.getAllValues(PREFIX_MOD)).ifPresent(editPersonDescriptor::setTags); + } catch (ParseException e) { + throw new ParseException(String.format(e.getMessage() + "%s", + e.getMessage().startsWith("Unknown prefix(es)") ? EditGroupStatusCommand.MESSAGE_USAGE : "")); + } + + if (!editPersonDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditGroupStatusCommand.MESSAGE_NO_MODULE_SPECIFIED); + } + + return new EditGroupStatusCommand(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> parseModsToAdd(Collection tags) throws ParseException { + assert tags != null; + + if (tags.isEmpty()) { + return Optional.empty(); + } + + if (tags.size() > 1) { + throw new ParseException(EditGroupStatusCommand.MESSAGE_MULTIPLE_MODULES_SPECIFIED); + } + + if (tags.contains("")) { + throw new ParseException(EditGroupStatusCommand.MESSAGE_NO_MODULE_SPECIFIED); + } else { + return Optional.of(ParserUtil.parseTags(tags)); + } + } +} diff --git a/src/main/java/seedu/modulink/logic/parser/FilterCommandParser.java b/src/main/java/seedu/modulink/logic/parser/FilterCommandParser.java new file mode 100644 index 00000000000..a188e852aee --- /dev/null +++ b/src/main/java/seedu/modulink/logic/parser/FilterCommandParser.java @@ -0,0 +1,55 @@ +package seedu.modulink.logic.parser; + +import static seedu.modulink.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_MOD; + +import java.util.Set; +import java.util.stream.Stream; + +import seedu.modulink.logic.commands.FilterCommand; +import seedu.modulink.logic.parser.exceptions.ParseException; +import seedu.modulink.model.person.ModuleContainsKeywordsPredicate; +import seedu.modulink.model.tag.Mod; + +public class FilterCommandParser implements Parser { + + public static final String MESSAGE_MORE_THAN_ONE_PARAMETER_FORMAT = "Only one module can be specified."; + + /** + * 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 { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_MOD); + + if (!arePrefixesPresent(argMultimap, PREFIX_MOD) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterCommand.MESSAGE_USAGE)); + } + + try { + Set modList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_MOD)); + if (modList.size() > 1) { + throw new ParseException(String.format(MESSAGE_MORE_THAN_ONE_PARAMETER_FORMAT, + FilterCommand.MESSAGE_USAGE)); + } + + return new FilterCommand(new ModuleContainsKeywordsPredicate(modList)); + + } catch (ParseException e) { + throw new ParseException(String.format(e.getMessage() + "%s", + e.getMessage().startsWith("Unknown prefix(es)") ? FilterCommand.MESSAGE_USAGE : "")); + } + + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/modulink/logic/parser/FindCommandParser.java similarity index 74% rename from src/main/java/seedu/address/logic/parser/FindCommandParser.java rename to src/main/java/seedu/modulink/logic/parser/FindCommandParser.java index 4fb71f23103..4f1b56d3f10 100644 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ b/src/main/java/seedu/modulink/logic/parser/FindCommandParser.java @@ -1,12 +1,12 @@ -package seedu.address.logic.parser; +package seedu.modulink.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.modulink.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import java.util.Arrays; -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.modulink.logic.commands.FindCommand; +import seedu.modulink.logic.parser.exceptions.ParseException; +import seedu.modulink.model.person.NameContainsKeywordsPredicate; /** * Parses input arguments and creates a new FindCommand object diff --git a/src/main/java/seedu/modulink/logic/parser/FindIdCommandParser.java b/src/main/java/seedu/modulink/logic/parser/FindIdCommandParser.java new file mode 100644 index 00000000000..b68f8861606 --- /dev/null +++ b/src/main/java/seedu/modulink/logic/parser/FindIdCommandParser.java @@ -0,0 +1,34 @@ +package seedu.modulink.logic.parser; + +import static seedu.modulink.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.Arrays; + +import seedu.modulink.logic.commands.FindIdCommand; +import seedu.modulink.logic.parser.exceptions.ParseException; +import seedu.modulink.model.person.StudentIdContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new FindIdCommand object + */ +public class FindIdCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FindIdCommand + * and returns a FindIdCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FindIdCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindIdCommand.MESSAGE_USAGE)); + } + + String[] nameKeywords = trimmedArgs.split("\\s+"); + + + return new FindIdCommand(new StudentIdContainsKeywordsPredicate(Arrays.asList(nameKeywords))); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/seedu/modulink/logic/parser/Parser.java similarity index 72% rename from src/main/java/seedu/address/logic/parser/Parser.java rename to src/main/java/seedu/modulink/logic/parser/Parser.java index d6551ad8e3f..0a85fcae05f 100644 --- a/src/main/java/seedu/address/logic/parser/Parser.java +++ b/src/main/java/seedu/modulink/logic/parser/Parser.java @@ -1,7 +1,7 @@ -package seedu.address.logic.parser; +package seedu.modulink.logic.parser; -import seedu.address.logic.commands.Command; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.modulink.logic.commands.Command; +import seedu.modulink.logic.parser.exceptions.ParseException; /** * Represents a Parser that is able to parse user input into a {@code Command} of type {@code T}. diff --git a/src/main/java/seedu/modulink/logic/parser/ParserUtil.java b/src/main/java/seedu/modulink/logic/parser/ParserUtil.java new file mode 100644 index 00000000000..8b5b414fb8e --- /dev/null +++ b/src/main/java/seedu/modulink/logic/parser/ParserUtil.java @@ -0,0 +1,261 @@ +package seedu.modulink.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_GITHUB_USERNAME; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_TELEGRAM_HANDLE; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import seedu.modulink.commons.core.Messages; +import seedu.modulink.commons.core.index.Index; +import seedu.modulink.commons.util.StringUtil; +import seedu.modulink.logic.parser.exceptions.ParseException; +import seedu.modulink.model.person.Email; +import seedu.modulink.model.person.GitHubUsername; +import seedu.modulink.model.person.Name; +import seedu.modulink.model.person.Phone; +import seedu.modulink.model.person.StudentId; +import seedu.modulink.model.person.TelegramHandle; +import seedu.modulink.model.tag.Mod; + +/** + * Contains utility methods used for parsing strings in the various *Parser classes. + */ +public class ParserUtil { + + public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer."; + + /** + * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be + * trimmed. + * @throws ParseException if the specified index is invalid (not non-zero unsigned integer). + */ + public static Index parseIndex(String oneBasedIndex) throws ParseException { + String trimmedIndex = oneBasedIndex.trim(); + if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) { + throw new ParseException(MESSAGE_INVALID_INDEX); + } + return Index.fromOneBased(Integer.parseInt(trimmedIndex)); + } + + /** + * Parses a {@code String name} into a {@code Name}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code name} is invalid. + */ + public static Name parseName(String name) throws ParseException { + requireNonNull(name); + String trimmedName = name.trim(); + if (trimmedName.contains("/")) { + throw new ParseException(String.format(Messages.MESSAGE_UNKNOWN_PREFIX_FORMAT, "")); + } + if (!Name.isValidName(trimmedName)) { + throw new ParseException(Name.MESSAGE_CONSTRAINTS); + } + return new Name(trimmedName); + } + + /** + * Parses a {@code String phone} into a {@code Phone}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code phone} is invalid. + */ + public static Phone parsePhone(String phone) throws ParseException { + requireNonNull(phone); + String trimmedPhone = phone.trim(); + if (trimmedPhone.contains("/")) { + throw new ParseException(String.format(Messages.MESSAGE_UNKNOWN_PREFIX_FORMAT, "")); + } + if (!Phone.isValidPhone(trimmedPhone)) { + throw new ParseException(Phone.MESSAGE_CONSTRAINTS); + } + return new Phone(trimmedPhone); + } + + /** + * Parses a {@code String email} into an {@code Email}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code email} is invalid. + */ + public static Email parseEmail(String email) throws ParseException { + requireNonNull(email); + String trimmedEmail = email.trim(); + if (trimmedEmail.contains("/")) { + throw new ParseException(String.format(Messages.MESSAGE_UNKNOWN_PREFIX_FORMAT, "")); + } + if (!Email.isValidEmail(trimmedEmail)) { + throw new ParseException(Email.MESSAGE_CONSTRAINTS); + } + return new Email(trimmedEmail); + } + + /** + * Parses a {@code String gitHubUsername} into a {@code GitHubUsername}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code gitHubUsername} is invalid. + */ + public static GitHubUsername parseGithubUsername(String gitHubUsername) throws ParseException { + if (gitHubUsername != null) { + String trimmedUsername = gitHubUsername.trim(); + if (trimmedUsername.contains("/")) { + throw new ParseException(String.format(Messages.MESSAGE_UNKNOWN_PREFIX_FORMAT, "")); + } + if (!GitHubUsername.isValidUsername(gitHubUsername)) { + throw new ParseException(GitHubUsername.MESSAGE_CONSTRAINTS); + } + return new GitHubUsername(trimmedUsername); + } else { + return new GitHubUsername(null); + } + } + + /** + * Parses a {@code String tag} into a {@code Tag}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code tag} is invalid. + */ + public static Mod parseTag(String tag) throws ParseException { + requireNonNull(tag); + String trimmedTag = tag.trim(); + if (trimmedTag.contains("/")) { + throw new ParseException(String.format(Messages.MESSAGE_UNKNOWN_PREFIX_FORMAT, "")); + } + if (!Mod.isValidTagName(trimmedTag)) { + throw new ParseException(Mod.MESSAGE_CONSTRAINTS); + } + return new Mod(trimmedTag); + } + + /** + * Parses {@code Collection tags} into a {@code Set}. + */ + public static Set parseTags(Collection tags) throws ParseException { + requireNonNull(tags); + final Set modSet = new HashSet<>(); + for (String tagName : tags) { + modSet.add(parseTag(tagName)); + } + return modSet; + } + + /** + * Parses a {@code String studentId} into a {@code StudentId}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code StudentId} is invalid. + */ + public static StudentId parseStudentId(String studentId) throws ParseException { + requireNonNull(studentId); + String trimmedId = studentId.trim(); + if (trimmedId.contains("/")) { + throw new ParseException(String.format(Messages.MESSAGE_UNKNOWN_PREFIX_FORMAT, "")); + } + if (!StudentId.isValidId(studentId)) { + throw new ParseException(StudentId.MESSAGE_CONSTRAINTS); + } + return new StudentId(trimmedId); + } + + /** + * Parses a {@code String telegramHandle} into a {@code TelegramHandle}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code telegramHandle} is invalid. + */ + public static TelegramHandle parseTelegramHandle(String telegramHandle) throws ParseException { + if (telegramHandle != null) { + String trimmedHandle = telegramHandle.trim(); + + if (trimmedHandle.startsWith("@")) { + trimmedHandle = trimmedHandle.substring(1); + } + if (trimmedHandle.contains("/")) { + throw new ParseException(String.format(Messages.MESSAGE_UNKNOWN_PREFIX_FORMAT, "")); + } + if (!TelegramHandle.isValidHandle(telegramHandle)) { + throw new ParseException(TelegramHandle.MESSAGE_CONSTRAINTS); + } + return new TelegramHandle(trimmedHandle); + } else { + return new TelegramHandle(null); + } + } + + /** + * Checks how many valid prefixes are present in args. + * + * @param argMultimap tokenized list of arguments. + * @return number of provided prefixes. + */ + public static int numberOfValidPrefixes(ArgumentMultimap argMultimap) { + int i = 0; + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + i++; + } + if (argMultimap.getValue(PREFIX_ID).isPresent()) { + i++; + } + if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { + i++; + } + if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { + i++; + } + if (argMultimap.getValue(PREFIX_GITHUB_USERNAME).isPresent()) { + i++; + } + if (argMultimap.getValue(PREFIX_TELEGRAM_HANDLE).isPresent()) { + i++; + } + return i; + } + + /** + * Returns if any prefixes that are duplicates. + * {@code ArgumentMultimap}. + */ + public static boolean isDuplicatePrefix(String args, Prefix... prefixes) { + for (Prefix prefix : prefixes) { + String prefixAsString = " " + prefix.getPrefix(); + if (StringUtil.countMatch(args, prefixAsString) > 1) { + return true; + } + } + return false; + } + + /** + * Returns any prefixes that are duplicates. + * {@code ArgumentMultimap}. + */ + public static StringBuilder findDuplicatePrefixes(String args, Prefix... prefixes) { + StringBuilder duplicatePrefixesList = new StringBuilder(); + for (Prefix prefix : prefixes) { + String prefixAsString = " " + prefix.getPrefix(); + if (StringUtil.countMatch(args, prefixAsString) > 1) { + duplicatePrefixesList.append(prefix).append(" "); + } + } + return duplicatePrefixesList; + } + + static void checkDuplicate(String args, ArgumentMultimap argMultimap, boolean duplicatePrefix) + throws ParseException { + if (ParserUtil.numberOfValidPrefixes(argMultimap) != StringUtil.countMatch(args, '/')) { + if (duplicatePrefix) { + throw new ParseException("error"); + } + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/Prefix.java b/src/main/java/seedu/modulink/logic/parser/Prefix.java similarity index 95% rename from src/main/java/seedu/address/logic/parser/Prefix.java rename to src/main/java/seedu/modulink/logic/parser/Prefix.java index c859d5fa5db..b286d48d325 100644 --- a/src/main/java/seedu/address/logic/parser/Prefix.java +++ b/src/main/java/seedu/modulink/logic/parser/Prefix.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.modulink.logic.parser; /** * A prefix that marks the beginning of an argument in an arguments string. diff --git a/src/main/java/seedu/modulink/logic/parser/RemFavCommandParser.java b/src/main/java/seedu/modulink/logic/parser/RemFavCommandParser.java new file mode 100644 index 00000000000..1edc4fcc23a --- /dev/null +++ b/src/main/java/seedu/modulink/logic/parser/RemFavCommandParser.java @@ -0,0 +1,39 @@ +package seedu.modulink.logic.parser; + +import static seedu.modulink.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.modulink.commons.core.Messages.MESSAGE_UNEXPECTED_INPUT_FORMAT; + +import seedu.modulink.logic.commands.RemFavCommand; +import seedu.modulink.logic.parser.exceptions.ParseException; +import seedu.modulink.model.person.StudentId; + +/** + * Parses input arguments and creates a new AddFavCommand object + */ +public class RemFavCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddFavCommand + * and returns a AddFavCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public RemFavCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, RemFavCommand.MESSAGE_USAGE)); + } + + String[] nameKeywords = trimmedArgs.split("\\s+"); + if (nameKeywords.length > 1) { + throw new ParseException(RemFavCommand.MULTIPLE_ID_ERROR); + } + if (!nameKeywords[0].matches(StudentId.VALIDATION_REGEX)) { + throw new ParseException(String.format(MESSAGE_UNEXPECTED_INPUT_FORMAT, + nameKeywords[0], RemFavCommand.MESSAGE_USAGE)); + } + + return new RemFavCommand(nameKeywords[0]); + } + +} diff --git a/src/main/java/seedu/modulink/logic/parser/RemoveModCommandParser.java b/src/main/java/seedu/modulink/logic/parser/RemoveModCommandParser.java new file mode 100644 index 00000000000..89a7397798c --- /dev/null +++ b/src/main/java/seedu/modulink/logic/parser/RemoveModCommandParser.java @@ -0,0 +1,66 @@ +package seedu.modulink.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.modulink.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.modulink.logic.parser.CliSyntax.PREFIX_MOD; + +import java.util.Collection; +import java.util.Optional; +import java.util.Set; + +import seedu.modulink.commons.util.StringUtil; +import seedu.modulink.logic.commands.EditCommand.EditPersonDescriptor; +import seedu.modulink.logic.commands.RemoveModCommand; +import seedu.modulink.logic.parser.exceptions.ParseException; +import seedu.modulink.model.tag.Mod; + + +public class RemoveModCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the AddModCommand + * and returns an AddModCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public RemoveModCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_MOD); + + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty() + || StringUtil.countMatch(args, '/') != 1 + || parseModsToRemove(argMultimap.getAllValues(PREFIX_MOD)).isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, RemoveModCommand.MESSAGE_USAGE)); + } + + EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); + + parseModsToRemove(argMultimap.getAllValues(PREFIX_MOD)).ifPresent(editPersonDescriptor::setTags); + + if (!editPersonDescriptor.isAnyFieldEdited()) { + throw new ParseException(RemoveModCommand.MESSAGE_NO_CHANGE); + } + + return new RemoveModCommand(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> parseModsToRemove(Collection tags) throws ParseException { + assert tags != null; + + if (tags.isEmpty()) { + throw new ParseException(RemoveModCommand.MESSAGE_NO_CHANGE); + } + + if (tags.size() == 1 && tags.contains("")) { + throw new ParseException(RemoveModCommand.MESSAGE_NO_CHANGE); + } else { + return Optional.of(ParserUtil.parseTags(tags)); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java b/src/main/java/seedu/modulink/logic/parser/exceptions/ParseException.java similarity index 72% rename from src/main/java/seedu/address/logic/parser/exceptions/ParseException.java rename to src/main/java/seedu/modulink/logic/parser/exceptions/ParseException.java index 158a1a54c1c..00b0cf1e489 100644 --- a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java +++ b/src/main/java/seedu/modulink/logic/parser/exceptions/ParseException.java @@ -1,6 +1,6 @@ -package seedu.address.logic.parser.exceptions; +package seedu.modulink.logic.parser.exceptions; -import seedu.address.commons.exceptions.IllegalValueException; +import seedu.modulink.commons.exceptions.IllegalValueException; /** * Represents a parse error encountered by a parser. diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/modulink/model/AddressBook.java similarity index 72% rename from src/main/java/seedu/address/model/AddressBook.java rename to src/main/java/seedu/modulink/model/AddressBook.java index 1a943a0781a..e49bfa57531 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/modulink/model/AddressBook.java @@ -1,12 +1,12 @@ -package seedu.address.model; +package seedu.modulink.model; import static java.util.Objects.requireNonNull; import java.util.List; import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.UniquePersonList; +import seedu.modulink.model.person.Person; +import seedu.modulink.model.person.UniquePersonList; /** * Wraps all data at the address-book level @@ -66,6 +66,34 @@ public boolean hasPerson(Person person) { return persons.contains(person); } + /** + * Returns true if a person with the same Student ID as {@code person} exists in the address book. + */ + public boolean hasStudentId(Person personToCompare) { + requireNonNull(personToCompare); + for (Person persons : persons) { + if (persons.getStudentId().equals(personToCompare.getStudentId())) { + return true; + } + } + return false; + } + + /** + * Returns true if a person other than the profile + * with the same Student ID as {@code person} exists in the address book. + */ + public boolean hasStudentIdNotProfile(Person personToCompare) { + requireNonNull(personToCompare); + for (Person persons : persons) { + if (persons.getStudentId().equals(personToCompare.getStudentId()) + && !persons.getIsMyProfile()) { + return true; + } + } + return false; + } + /** * Adds a person to the address book. * The person must not already exist in the address book. @@ -74,6 +102,14 @@ public void addPerson(Person p) { persons.add(p); } + /** + * Adds a person to the first index of the address book. + * This person is the user profile. + */ + public void addPersonToTop(Person p) { + persons.add(0, p); + } + /** * Replaces the given person {@code target} in the list with {@code editedPerson}. * {@code target} must exist in the address book. diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/modulink/model/Model.java similarity index 71% rename from src/main/java/seedu/address/model/Model.java rename to src/main/java/seedu/modulink/model/Model.java index d54df471c1f..3169d254559 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/modulink/model/Model.java @@ -1,11 +1,11 @@ -package seedu.address.model; +package seedu.modulink.model; import java.nio.file.Path; import java.util.function.Predicate; import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.Person; +import seedu.modulink.commons.core.GuiSettings; +import seedu.modulink.model.person.Person; /** * The API of the Model component. @@ -57,6 +57,17 @@ public interface Model { */ boolean hasPerson(Person person); + /** + * Returns true if a person with the same Student ID as {@code person} exists in the address book. + */ + boolean hasStudentId(Person person); + + /** + * Returns true if a person other than the profile + * with the same Student ID as {@code person} exists in the address book. + */ + boolean hasStudentIdNotProfile(Person person); + /** * Deletes the given person. * The person must exist in the address book. @@ -64,11 +75,16 @@ public interface Model { void deletePerson(Person target); /** - * Adds the given person. + * Adds a person to the address book. * {@code person} must not already exist in the address book. */ void addPerson(Person person); + /** + * Adds a user profile. + */ + void addProfile(Person person); + /** * Replaces the given person {@code target} with {@code editedPerson}. * {@code target} must exist in the address book. @@ -76,12 +92,23 @@ public interface Model { */ void setPerson(Person target, Person editedPerson); + /** + * Returns the user's profile marked with isMyProfile. + */ + Person getProfile(); + /** Returns an unmodifiable view of the filtered person list */ ObservableList getFilteredPersonList(); + /** Refreshes current filtered person list to show any changes */ + void refreshFilteredPersonList(); + /** * Updates the filter of the filtered person list to filter by the given {@code predicate}. * @throws NullPointerException if {@code predicate} is null. */ void updateFilteredPersonList(Predicate predicate); + + /** Returns an unmodifiable view of the unfiltered person list */ + ObservableList getPersonList(); } diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/modulink/model/ModelManager.java similarity index 72% rename from src/main/java/seedu/address/model/ModelManager.java rename to src/main/java/seedu/modulink/model/ModelManager.java index 0650c954f5c..082bb409ebe 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/modulink/model/ModelManager.java @@ -1,7 +1,7 @@ -package seedu.address.model; +package seedu.modulink.model; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.modulink.commons.util.CollectionUtil.requireAllNonNull; import java.nio.file.Path; import java.util.function.Predicate; @@ -9,9 +9,9 @@ import javafx.collections.ObservableList; import javafx.collections.transformation.FilteredList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; +import seedu.modulink.commons.core.GuiSettings; +import seedu.modulink.commons.core.LogsCenter; +import seedu.modulink.model.person.Person; /** * Represents the in-memory model of the address book data. @@ -94,6 +94,18 @@ public boolean hasPerson(Person person) { return addressBook.hasPerson(person); } + @Override + public boolean hasStudentId(Person person) { + requireNonNull(person); + return addressBook.hasStudentId(person); + } + + @Override + public boolean hasStudentIdNotProfile(Person person) { + requireNonNull(person); + return addressBook.hasStudentIdNotProfile(person); + } + @Override public void deletePerson(Person target) { addressBook.removePerson(target); @@ -105,6 +117,12 @@ public void addPerson(Person person) { updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); } + @Override + public void addProfile(Person person) { + addressBook.addPersonToTop(person); + updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + } + @Override public void setPerson(Person target, Person editedPerson) { requireAllNonNull(target, editedPerson); @@ -112,6 +130,18 @@ public void setPerson(Person target, Person editedPerson) { addressBook.setPerson(target, editedPerson); } + @Override + public Person getProfile() { + Person profile = null; + for (Person p : this.addressBook.getPersonList()) { + if (p.getIsMyProfile()) { + profile = p; + break; + } + } + + return profile; + } //=========== Filtered Person List Accessors ============================================================= /** @@ -129,6 +159,18 @@ public void updateFilteredPersonList(Predicate predicate) { filteredPersons.setPredicate(predicate); } + @Override + public void refreshFilteredPersonList() { + @SuppressWarnings("unchecked") + Predicate predicate = (Predicate) filteredPersons.getPredicate(); + if (predicate == null) { + predicate = PREDICATE_SHOW_ALL_PERSONS; + } + Predicate updatePredicate = unused -> false; + updateFilteredPersonList(updatePredicate); + updateFilteredPersonList(predicate); + } + @Override public boolean equals(Object obj) { // short circuit if same object @@ -148,4 +190,9 @@ public boolean equals(Object obj) { && filteredPersons.equals(other.filteredPersons); } + @Override + public ObservableList getPersonList() { + return this.addressBook.getPersonList(); + } + } diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/modulink/model/ReadOnlyAddressBook.java similarity index 80% rename from src/main/java/seedu/address/model/ReadOnlyAddressBook.java rename to src/main/java/seedu/modulink/model/ReadOnlyAddressBook.java index 6ddc2cd9a29..21f4289f071 100644 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ b/src/main/java/seedu/modulink/model/ReadOnlyAddressBook.java @@ -1,7 +1,7 @@ -package seedu.address.model; +package seedu.modulink.model; import javafx.collections.ObservableList; -import seedu.address.model.person.Person; +import seedu.modulink.model.person.Person; /** * Unmodifiable view of an address book diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/seedu/modulink/model/ReadOnlyUserPrefs.java similarity index 70% rename from src/main/java/seedu/address/model/ReadOnlyUserPrefs.java rename to src/main/java/seedu/modulink/model/ReadOnlyUserPrefs.java index befd58a4c73..0d6f868487e 100644 --- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java +++ b/src/main/java/seedu/modulink/model/ReadOnlyUserPrefs.java @@ -1,8 +1,8 @@ -package seedu.address.model; +package seedu.modulink.model; import java.nio.file.Path; -import seedu.address.commons.core.GuiSettings; +import seedu.modulink.commons.core.GuiSettings; /** * Unmodifiable view of user prefs. diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/modulink/model/UserPrefs.java similarity index 93% rename from src/main/java/seedu/address/model/UserPrefs.java rename to src/main/java/seedu/modulink/model/UserPrefs.java index 25a5fd6eab9..1bb45a71ad1 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/modulink/model/UserPrefs.java @@ -1,4 +1,4 @@ -package seedu.address.model; +package seedu.modulink.model; import static java.util.Objects.requireNonNull; @@ -6,7 +6,7 @@ import java.nio.file.Paths; import java.util.Objects; -import seedu.address.commons.core.GuiSettings; +import seedu.modulink.commons.core.GuiSettings; /** * Represents User's preferences. @@ -14,7 +14,7 @@ public class UserPrefs implements ReadOnlyUserPrefs { private GuiSettings guiSettings = new GuiSettings(); - private Path addressBookFilePath = Paths.get("data" , "addressbook.json"); + private Path addressBookFilePath = Paths.get("data" , "modulink.json"); /** * Creates a {@code UserPrefs} with default values. diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/modulink/model/person/Email.java similarity index 96% rename from src/main/java/seedu/address/model/person/Email.java rename to src/main/java/seedu/modulink/model/person/Email.java index f866e7133de..13b09d0a74e 100644 --- a/src/main/java/seedu/address/model/person/Email.java +++ b/src/main/java/seedu/modulink/model/person/Email.java @@ -1,7 +1,7 @@ -package seedu.address.model.person; +package seedu.modulink.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.modulink.commons.util.AppUtil.checkArgument; /** * Represents a Person's email in the address book. diff --git a/src/main/java/seedu/modulink/model/person/GitHubUsername.java b/src/main/java/seedu/modulink/model/person/GitHubUsername.java new file mode 100644 index 00000000000..1fc849c3622 --- /dev/null +++ b/src/main/java/seedu/modulink/model/person/GitHubUsername.java @@ -0,0 +1,55 @@ +package seedu.modulink.model.person; + +import static seedu.modulink.commons.util.AppUtil.checkArgument; + +public class GitHubUsername { + + public static final String MESSAGE_CONSTRAINTS = + "GitHub username should start with an alphanumeric character, " + + "can only contain alphanumeric characters and hyphens, " + + "be longer than one character, and must not contain any spaces."; + public static final String VALIDATION_REGEX = "^[A-Za-z0-9]+[A-Za-z0-9-]+$"; + public final String value; + + /** + * Constructs an {@code GitHub username}. + * + * @param username A valid GitHub username. + */ + public GitHubUsername(String username) { + checkArgument(isValidUsername(username), MESSAGE_CONSTRAINTS); + value = username; + } + + /** + * Returns true if a given string is a valid GitHub username. + */ + public static boolean isValidUsername(String test) { + if (test == null) { + return true; + } else { + return test.matches(VALIDATION_REGEX); + } + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof GitHubUsername // instanceof handles nulls + && value.equals(((GitHubUsername) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + public boolean isNull() { + return value == null; + } +} diff --git a/src/main/java/seedu/modulink/model/person/IsFavouritePredicate.java b/src/main/java/seedu/modulink/model/person/IsFavouritePredicate.java new file mode 100644 index 00000000000..0df08ecd947 --- /dev/null +++ b/src/main/java/seedu/modulink/model/person/IsFavouritePredicate.java @@ -0,0 +1,14 @@ +package seedu.modulink.model.person; + +import java.util.function.Predicate; + +/** + * Tests that a {@code Person}'s {@code isFavourite} is true. + */ +public class IsFavouritePredicate implements Predicate { + + @Override + public boolean test(Person person) { + return person.getIsFavourite(); + } +} diff --git a/src/main/java/seedu/modulink/model/person/ModuleContainsKeywordsPredicate.java b/src/main/java/seedu/modulink/model/person/ModuleContainsKeywordsPredicate.java new file mode 100644 index 00000000000..70ef7940858 --- /dev/null +++ b/src/main/java/seedu/modulink/model/person/ModuleContainsKeywordsPredicate.java @@ -0,0 +1,58 @@ +package seedu.modulink.model.person; + +import static java.util.Objects.requireNonNull; + +import java.util.Set; +import java.util.function.Predicate; + +import seedu.modulink.model.tag.Mod; +import seedu.modulink.model.tag.Status; + +/** + * Tests that a {@code Person}'s {@code Module} and optionally {@code Status} matches any of the keywords given. + */ +public class ModuleContainsKeywordsPredicate implements Predicate { + + private final Set mods; + + /** + * Constructor for the ModuleContainsKeywordsPredicate class. + * + * @param mods Set of modules. + */ + public ModuleContainsKeywordsPredicate(Set mods) { + requireNonNull(mods); + this.mods = mods; + } + + @Override + public boolean test(Person person) { + boolean doesMatchMod = false; + if (person.getIsMyProfile()) { + return false; + } + for (Mod mod : person.getMods()) { + for (Mod module : mods) { + if (module.modName.equalsIgnoreCase(mod.modName)) { + if (!module.status.equals(Status.NONE)) { + if (mod.status.equals(module.status)) { + doesMatchMod = true; + break; + } + } else { + doesMatchMod = true; + break; + } + } + } + } + return doesMatchMod; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ModuleContainsKeywordsPredicate // instanceof handles nulls + && mods.equals(((ModuleContainsKeywordsPredicate) other).mods)); // state check + } +} diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/modulink/model/person/Name.java similarity index 93% rename from src/main/java/seedu/address/model/person/Name.java rename to src/main/java/seedu/modulink/model/person/Name.java index 79244d71cf7..2cecc9803bb 100644 --- a/src/main/java/seedu/address/model/person/Name.java +++ b/src/main/java/seedu/modulink/model/person/Name.java @@ -1,7 +1,7 @@ -package seedu.address.model.person; +package seedu.modulink.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.modulink.commons.util.AppUtil.checkArgument; /** * Represents a Person's name in the address book. diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/modulink/model/person/NameContainsKeywordsPredicate.java similarity index 91% rename from src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java rename to src/main/java/seedu/modulink/model/person/NameContainsKeywordsPredicate.java index c9b5868427c..495984bb28a 100644 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ b/src/main/java/seedu/modulink/model/person/NameContainsKeywordsPredicate.java @@ -1,9 +1,9 @@ -package seedu.address.model.person; +package seedu.modulink.model.person; import java.util.List; import java.util.function.Predicate; -import seedu.address.commons.util.StringUtil; +import seedu.modulink.commons.util.StringUtil; /** * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. diff --git a/src/main/java/seedu/modulink/model/person/Person.java b/src/main/java/seedu/modulink/model/person/Person.java new file mode 100644 index 00000000000..808560815c8 --- /dev/null +++ b/src/main/java/seedu/modulink/model/person/Person.java @@ -0,0 +1,178 @@ +package seedu.modulink.model.person; + +import static seedu.modulink.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import seedu.modulink.model.tag.Mod; + +/** + * Represents a Person in the address book. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Person { + private static final Person placeholder = new Person(new Name("Your name"), + new StudentId("A0000000A"), new Phone("81234567"), + new Email("youremail@email.com"), new GitHubUsername("your-github-user"), + new TelegramHandle("yourtelehandle"), false, + new HashSet<>(), true); + + // Identity fields + private final Name name; + private final StudentId id; + private final Phone phone; + private final Email email; + private final GitHubUsername gitHubUsername; + private final TelegramHandle telegramHandle; + private boolean isFavourite; + + // Data fields + private final Set mods = new HashSet<>(); + private final boolean isMyProfile; + + /** + * Every field must be present and not null. + */ + public Person(Name name, StudentId id, Phone phone, Email email, GitHubUsername gitHubUsername, + TelegramHandle telegramHandle, boolean isFavourite, Set mods, boolean isMyProfile) { + requireAllNonNull(name, id, phone, email, mods); + this.name = name; + this.id = id; + this.phone = phone; + this.email = email; + this.gitHubUsername = gitHubUsername; + this.telegramHandle = telegramHandle; + this.isFavourite = isFavourite; + this.mods.addAll(mods); + this.isMyProfile = isMyProfile; + } + + public static Person getPlaceholder() { + return placeholder; + } + + public Name getName() { + return name; + } + + public StudentId getStudentId() { + return id; + } + + public Phone getPhone() { + return phone; + } + + public Email getEmail() { + return email; + } + + public GitHubUsername getGithubUsername() { + return gitHubUsername; + } + + public TelegramHandle getTelegramHandle() { + return telegramHandle; + } + + /** + * Returns an immutable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public Set getMods() { + return Collections.unmodifiableSet(mods); + } + + public boolean getIsMyProfile() { + return isMyProfile; + } + + public boolean getIsFavourite() { + return isFavourite; + } + + /** + * Returns true if both persons have the same name. + * This defines a weaker notion of equality between two persons. + */ + public boolean isSamePerson(Person otherPerson) { + if (otherPerson == this) { + return true; + } + + return otherPerson != null + && otherPerson.getName().equals(getName()); + } + + public void setFavouriteTrue() { + this.isFavourite = true; + } + + public void setFavouriteFalse() { + this.isFavourite = false; + } + + /** + * Returns true if both persons have the same identity and data fields. + * This defines a stronger notion of equality between two persons. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Person)) { + return false; + } + + Person otherPerson = (Person) other; + boolean sameMods = true; + for (Mod mod : otherPerson.getMods()) { + sameMods &= getMods().contains(mod); + } + + + return otherPerson.getName().equals(getName()) + && otherPerson.getPhone().equals(getPhone()) + && otherPerson.getEmail().equals(getEmail()) + && otherPerson.getGithubUsername().equals(getGithubUsername()) + && otherPerson.getTelegramHandle().equals(getTelegramHandle()) + && sameMods + && otherPerson.getIsFavourite() == getIsFavourite() + && otherPerson.getStudentId().equals(getStudentId()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(name, phone, email, gitHubUsername, mods); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getName()) + .append("; Student ID: ") + .append(getStudentId()) + .append("; Phone: ") + .append(getPhone()) + .append("; Email: ") + .append(getEmail()) + .append("; GitHub username: ") + .append(getGithubUsername()) + .append("; Telegram handle: ") + .append(getTelegramHandle()); + + Set mods = getMods(); + if (!mods.isEmpty()) { + builder.append("; Modules: "); + mods.forEach(builder::append); + } + return builder.toString(); + } + +} diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/modulink/model/person/Phone.java similarity index 92% rename from src/main/java/seedu/address/model/person/Phone.java rename to src/main/java/seedu/modulink/model/person/Phone.java index 872c76b382f..6b035078c98 100644 --- a/src/main/java/seedu/address/model/person/Phone.java +++ b/src/main/java/seedu/modulink/model/person/Phone.java @@ -1,7 +1,7 @@ -package seedu.address.model.person; +package seedu.modulink.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.modulink.commons.util.AppUtil.checkArgument; /** * Represents a Person's phone number in the address book. diff --git a/src/main/java/seedu/modulink/model/person/StudentId.java b/src/main/java/seedu/modulink/model/person/StudentId.java new file mode 100644 index 00000000000..0faae88bb5d --- /dev/null +++ b/src/main/java/seedu/modulink/model/person/StudentId.java @@ -0,0 +1,49 @@ +package seedu.modulink.model.person; + +import static java.util.Objects.requireNonNull; +import static seedu.modulink.commons.util.AppUtil.checkArgument; + +public class StudentId { + + public static final String MESSAGE_CONSTRAINTS = + "Student ID should start with A, have 7 numbers and end with a letter"; + public static final String VALIDATION_REGEX = "^[aA]\\d{7}[a-zA-z]$"; + public final String value; + + + /** + * Constructs an {@code Student ID}. + * + * @param studentId A valid student ID. + */ + public StudentId(String studentId) { + requireNonNull(studentId); + checkArgument(isValidId(studentId), MESSAGE_CONSTRAINTS); + value = studentId.toUpperCase(); + } + + /** + * Returns true if a given string is a valid student ID. + */ + public static boolean isValidId(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof StudentId // instanceof handles nulls + && value.equals(((StudentId) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/modulink/model/person/StudentIdContainsKeywordsPredicate.java b/src/main/java/seedu/modulink/model/person/StudentIdContainsKeywordsPredicate.java new file mode 100644 index 00000000000..7a315455009 --- /dev/null +++ b/src/main/java/seedu/modulink/model/person/StudentIdContainsKeywordsPredicate.java @@ -0,0 +1,31 @@ +package seedu.modulink.model.person; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.modulink.commons.util.StringUtil; + +/** + * Tests that a {@code Person}'s {@code StudentId} matches any of the keywords given. + */ +public class StudentIdContainsKeywordsPredicate implements Predicate { + private final List keywords; + + public StudentIdContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Person person) { + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getStudentId().value, keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof StudentIdContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((StudentIdContainsKeywordsPredicate) other).keywords)); // state check + } + +} diff --git a/src/main/java/seedu/modulink/model/person/TelegramHandle.java b/src/main/java/seedu/modulink/model/person/TelegramHandle.java new file mode 100644 index 00000000000..0c8ac88e973 --- /dev/null +++ b/src/main/java/seedu/modulink/model/person/TelegramHandle.java @@ -0,0 +1,60 @@ +package seedu.modulink.model.person; + +import static seedu.modulink.commons.util.AppUtil.checkArgument; + +public class TelegramHandle { + + public static final String MESSAGE_CONSTRAINTS = + "Telegram handle can only have alphanumeric and hyphen characters, and should not contain any spaces. " + + "The handle can either start with '@', or just the handle itself."; + public static final String VALIDATION_REGEX = "^[A-Za-z0-9_@][A-Za-z0-9_]+$"; + public final String value; + + /** + * Constructs an {@code TelegramHandle}. + * + * @param handle A valid Telegram handle. + */ + public TelegramHandle(String handle) { + checkArgument(isValidHandle(handle), MESSAGE_CONSTRAINTS); + + // for JSON file input + if (handle != null && handle.startsWith("@")) { + handle = handle.substring(1); + } + + value = handle; + } + + /** + * Returns true if a given string is a valid Telegram handle. + */ + public static boolean isValidHandle(String test) { + if (test == null) { + return true; + } else { + return test.matches(VALIDATION_REGEX); + } + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TelegramHandle // instanceof handles nulls + && value.equals(((TelegramHandle) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + public boolean isNull() { + return value == null; + } +} diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/modulink/model/person/UniquePersonList.java similarity index 87% rename from src/main/java/seedu/address/model/person/UniquePersonList.java rename to src/main/java/seedu/modulink/model/person/UniquePersonList.java index 0fee4fe57e6..00ca2d6ba82 100644 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ b/src/main/java/seedu/modulink/model/person/UniquePersonList.java @@ -1,15 +1,15 @@ -package seedu.address.model.person; +package seedu.modulink.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.modulink.commons.util.CollectionUtil.requireAllNonNull; import java.util.Iterator; import java.util.List; import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; +import seedu.modulink.model.person.exceptions.DuplicatePersonException; +import seedu.modulink.model.person.exceptions.PersonNotFoundException; /** * A list of persons that enforces uniqueness between its elements and does not allow nulls. @@ -48,6 +48,18 @@ public void add(Person toAdd) { internalList.add(toAdd); } + /** + * Adds a person to the list at a specific index. + * The person must not already exist in the list. + */ + public void add(int index, Person toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicatePersonException(); + } + internalList.add(index, toAdd); + } + /** * Replaces the person {@code target} in the list with {@code editedPerson}. * {@code target} must exist in the list. diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/seedu/modulink/model/person/exceptions/DuplicatePersonException.java similarity index 86% rename from src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java rename to src/main/java/seedu/modulink/model/person/exceptions/DuplicatePersonException.java index d7290f59442..bdb1f5f8800 100644 --- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java +++ b/src/main/java/seedu/modulink/model/person/exceptions/DuplicatePersonException.java @@ -1,4 +1,4 @@ -package seedu.address.model.person.exceptions; +package seedu.modulink.model.person.exceptions; /** * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/modulink/model/person/exceptions/PersonNotFoundException.java similarity index 75% rename from src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java rename to src/main/java/seedu/modulink/model/person/exceptions/PersonNotFoundException.java index fa764426ca7..cbe0b2b1566 100644 --- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java +++ b/src/main/java/seedu/modulink/model/person/exceptions/PersonNotFoundException.java @@ -1,4 +1,4 @@ -package seedu.address.model.person.exceptions; +package seedu.modulink.model.person.exceptions; /** * Signals that the operation is unable to find the specified person. diff --git a/src/main/java/seedu/modulink/model/person/exceptions/UserProfileIsFavouriteException.java b/src/main/java/seedu/modulink/model/person/exceptions/UserProfileIsFavouriteException.java new file mode 100644 index 00000000000..c04839de7f0 --- /dev/null +++ b/src/main/java/seedu/modulink/model/person/exceptions/UserProfileIsFavouriteException.java @@ -0,0 +1,11 @@ +package seedu.modulink.model.person.exceptions; + +/** + * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same + * identity). + */ +public class UserProfileIsFavouriteException extends RuntimeException { + public UserProfileIsFavouriteException() { + super("User profile has been set as favourite"); + } +} diff --git a/src/main/java/seedu/modulink/model/tag/Mod.java b/src/main/java/seedu/modulink/model/tag/Mod.java new file mode 100644 index 00000000000..7e89a3621e7 --- /dev/null +++ b/src/main/java/seedu/modulink/model/tag/Mod.java @@ -0,0 +1,108 @@ +package seedu.modulink.model.tag; + +import static java.util.Objects.requireNonNull; +import static seedu.modulink.commons.util.AppUtil.checkArgument; + +import seedu.modulink.logic.parser.exceptions.ParseException; + + +/** + * Represents a Tag in the address book. + * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)} + */ +public class Mod { + + public static final String MESSAGE_CONSTRAINTS = + "Please enter a valid Module Code (e.g. CS2103T). A valid Module Code starts with 2-3 letters, followed by" + + "4 digits, and optionally ends with a letter." + + " You may also include your grouping status.\n" + + "Available grouping statuses are: need member, need group. Please leave it blank to " + + "indicate you do not need a group."; + public static final String VALIDATION_REGEX = + "([A-Z]){2,3}[0-9]{4}([A-Z])?"; + + public final String oriInput; + public final String modName; + public final Status status; + + /** + * Constructs a {@code Tag}. + * + * @param modString A valid tag name. + */ + public Mod(String modString) throws ParseException { + requireNonNull(modString); + + this.oriInput = modString; + int i = modString.indexOf(' '); + String modCode; + + + if (i < 0) { + modCode = modString.toUpperCase(); + } else { + modCode = modString.substring(0, i).toUpperCase(); + } + + checkArgument(isValidTagName(modCode), MESSAGE_CONSTRAINTS); + + + if (i < 0) { + this.modName = modCode.toUpperCase(); + this.status = Status.NONE; + } else { + this.modName = modCode; + this.status = Status.parseStatusFromString(modString.substring(i)); + } + } + + /** + * Returns true if a given string is a valid tag name. + */ + public static boolean isValidTagName(String test) { + test = test.toUpperCase(); + int i = test.indexOf(' '); + String modCode; + + + if (i < 0) { + test = test.toUpperCase(); + } else { + test = test.substring(0, i).toUpperCase(); + } + + return test.matches(VALIDATION_REGEX); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Mod // instanceof handles nulls + && modName.equals(((Mod) other).modName)); // state check + } + + /** + * Checks if the statuses of two {@Code Mod} objects are the same. + * + * @param other Other object to compare + * @return boolean to indicate if statuses are the same or not. + */ + public boolean equalsStatus(Object other) { + return other == this // short circuit if same object + || (other instanceof Mod // instanceof handles nulls + && status.equals(((Mod) other).status)); // state check + } + + @Override + public int hashCode() { + return modName.hashCode(); + } + + /** + * Format state as text for viewing. + */ + public String toString() { + return '[' + modName + ' ' + status.toString() + ']'; + } + +} diff --git a/src/main/java/seedu/modulink/model/tag/Status.java b/src/main/java/seedu/modulink/model/tag/Status.java new file mode 100644 index 00000000000..edfb648c6bc --- /dev/null +++ b/src/main/java/seedu/modulink/model/tag/Status.java @@ -0,0 +1,59 @@ +package seedu.modulink.model.tag; + +import seedu.modulink.logic.parser.exceptions.ParseException; + +public enum Status { + NONE("G"), + NEED_GROUP("SG"), + NEED_MEMBER("SM"); + + public static final String MESSAGE_CONSTRAINTS = + "Available grouping statuses are: \"need group\" and \"need member\".\n" + + "Please leave the module description blank to indicate you do not need a group."; + + public static final String VALIDATION_REGEX = + "((need group)|(need members?))?"; + + private String status; + + Status(String s) { + this.status = s; + } + + /** + * Returns true if a given string is a valid status. + */ + public static boolean isValidStatus(String test) { + test = test.toLowerCase(); + return test.matches(VALIDATION_REGEX); + } + + /** + * Converts a String into a Status by checking for keywords. + * Used for parsing user input. + * + * @param s the given string. + * @return the matching status + */ + public static Status parseStatusFromString(String s) throws ParseException { + s = s.trim(); + s = s.toLowerCase(); + if (!isValidStatus(s)) { + throw new ParseException(MESSAGE_CONSTRAINTS); + } + + if (s.equalsIgnoreCase("need group")) { + return Status.NEED_GROUP; + } else if (s.equalsIgnoreCase("need member") + || s.equalsIgnoreCase("need members")) { + return Status.NEED_MEMBER; + } else { + return Status.NONE; + } + } + + @Override + public String toString() { + return "" + status; + } +} diff --git a/src/main/java/seedu/modulink/model/util/SampleDataUtil.java b/src/main/java/seedu/modulink/model/util/SampleDataUtil.java new file mode 100644 index 00000000000..8a4a45f484f --- /dev/null +++ b/src/main/java/seedu/modulink/model/util/SampleDataUtil.java @@ -0,0 +1,269 @@ +package seedu.modulink.model.util; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import seedu.modulink.logic.parser.exceptions.ParseException; +import seedu.modulink.model.AddressBook; +import seedu.modulink.model.ReadOnlyAddressBook; +import seedu.modulink.model.person.Email; +import seedu.modulink.model.person.GitHubUsername; +import seedu.modulink.model.person.Name; +import seedu.modulink.model.person.Person; +import seedu.modulink.model.person.Phone; +import seedu.modulink.model.person.StudentId; +import seedu.modulink.model.person.TelegramHandle; +import seedu.modulink.model.tag.Mod; + + +/** + * Contains utility methods for populating {@code AddressBook} with sample data. + */ +public class SampleDataUtil { + public static Person[] getSamplePersons() { + return new Person[] { + new Person(new Name("Your name"), new StudentId("A0000000A"), new Phone("81234567"), + new Email("youremail@email.com"), new GitHubUsername("your-github-user"), + new TelegramHandle("yourtelehandle"), false, + new HashSet<>(), true), + new Person(new Name("Alex Yeoh"), new StudentId("A1234567R"), new Phone("87438807"), + new Email("alexyeoh@example.com"), new GitHubUsername("alex-yeoh-y"), + new TelegramHandle("@alexyeoh"), false, + getTagSet("CS2103T", "CS2106", "CS2101 need member"), false), + new Person(new Name("Zachary Lau"), new StudentId("A1234567H"), new Phone("96523487"), + new Email("zachlau@example.com"), new GitHubUsername("zachattach"), + new TelegramHandle("zacharrryyyy"), false, + getTagSet("CS2100", "CS2106", "CS2101 need group"), false), + new Person(new Name("Ng Jia Yuan"), new StudentId("A1234567N"), new Phone("98432567"), + new Email("ngjy@example.com"), new GitHubUsername("yuan"), + new TelegramHandle("yuan"), false, + getTagSet("CS5210 need group", "CS2106", "CS2100 need member"), false), + new Person(new Name("Charlton Tan"), new StudentId("A1234567L"), new Phone("97324615"), + new Email("charles@example.com"), new GitHubUsername("C-dollarsign"), + new TelegramHandle("followmyspotify"), false, + getTagSet("CS2101 need group", "IS1101"), false), + new Person(new Name("Ahmad bin Ali"), new StudentId("A1234567B"), new Phone("95238541"), + new Email("aba@example.com"), new GitHubUsername("alibinahmad"), + new TelegramHandle("diversity"), false, + getTagSet("CS2100", "CS2106", "CS2103T need group"), false), + new Person(new Name("Bernice Yu"), new StudentId("A1234568X"), new Phone("99272758"), + new Email("berniceyu@example.com"), new GitHubUsername(null), + new TelegramHandle("berniceYuuu"), false, + getTagSet("CS2100", "CS3230 need group", "CS4234 need member"), false), + new Person(new Name("Charlotte Oliveiro"), new StudentId("A1234569Y"), new Phone("93210283"), + new Email("charlotte@example.com"), new GitHubUsername("charlotteoliveiro"), + new TelegramHandle("charlotte24"), false, + getTagSet("CS1101S", "CS3230 need member"), false), + new Person(new Name("David Li"), new StudentId("A1234570Z"), new Phone("91031282"), + new Email("lidavid@example.com"), new GitHubUsername("Davidli"), + new TelegramHandle(null), false, + getTagSet("CS2101"), false), + new Person(new Name("Irfan Ibrahim"), new StudentId("A1234571R"), new Phone("92492021"), + new Email("irfan@example.com"), new GitHubUsername("Irfanib"), + new TelegramHandle("irfanibrahim"), false, + getTagSet("CS2101 need member", "ES2660 need group", "CS2103T"), false), + new Person(new Name("Roy Balakrishnan"), new StudentId("A1234572X"), new Phone("92624417"), + new Email("royb@example.com"), new GitHubUsername("Roybalakrishnan"), + new TelegramHandle("heyitsroy"), false, + getTagSet("CS2100", "CS2103T", "CS2101 need group"), false), + new Person(new Name("Nicole Wong"), new StudentId("A1234567Q"), new Phone("96523546"), + new Email("niolewong@example.com"), new GitHubUsername("NicoleW"), + new TelegramHandle("yellowNicole"), false, + getTagSet("CS2101 need member", "MA2001"), false), + new Person(new Name("Jeremy Tan"), new StudentId("A1234567W"), new Phone("96875439"), + new Email("jTan2000@example.com"), new GitHubUsername("jTanremy"), + new TelegramHandle("jeret"), false, + getTagSet("CS2101", "CS1101S need member", "CS2103T"), false), + new Person(new Name("Aniq Nathaniel"), new StudentId("A1234567E"), new Phone("92584685"), + new Email("aniqNathan@example.com"), new GitHubUsername("NathAniq"), + new TelegramHandle("Aniqle4urThoughts"), false, + getTagSet("CS2101 need group", "CS4234", "CS2103T need group"), false), + new Person(new Name("Timmy Turner"), new StudentId("A1234569E"), new Phone("95483265"), + new Email("timmyturns@example.com"), new GitHubUsername("TimTam"), + new TelegramHandle(null), false, + getTagSet("ST2334 need group", "GEA1000 need member"), false), + new Person(new Name("Alice Lin"), new StudentId("A1310684L"), new Phone("88702899"), + new Email("alicelin@outlook.com"), new GitHubUsername(null), + new TelegramHandle(null), false, + getTagSet("CS2030S"), false), + new Person(new Name("Alex Ong"), new StudentId("A1158684X"), new Phone("83149485"), + new Email("alexong@hotmail.com"), new GitHubUsername("alexong4"), + new TelegramHandle(null), false, + getTagSet("CS2101"), false), + new Person(new Name("Aaron Heng"), new StudentId("A1132128A"), new Phone("81762929"), + new Email("aaronheng@gmail.com"), new GitHubUsername("aaronheng21"), + new TelegramHandle(null), false, + getTagSet("CS3243 need member"), false), + new Person(new Name("Ahmad Ong"), new StudentId("A1191832Z"), new Phone("89946306"), + new Email("ahmadong@email.com"), new GitHubUsername("ahmadong4"), + new TelegramHandle("ahmadong42"), false, + getTagSet("CS2030S need group"), false), + new Person(new Name("Bob Johnson"), new StudentId("A1872556X"), new Phone("92296295"), + new Email("bobjohnson@outlook.com"), new GitHubUsername(null), + new TelegramHandle("bobjohnson71"), false, + getTagSet("CS2030S need group"), false), + new Person(new Name("Brandon Jones"), new StudentId("A1096456L"), new Phone("90676077"), + new Email("brandonjones@hotmail.com"), new GitHubUsername("brandonjones17"), + new TelegramHandle("brandonjones64"), false, + getTagSet("CS2101 need group"), false), + new Person(new Name("Bernard William"), new StudentId("A1664306Y"), new Phone("94379764"), + new Email("bernardwilliam@outlook.com"), new GitHubUsername("bernardwilliam39"), + new TelegramHandle("bernardwilliam15"), false, + getTagSet("CS3243 need group"), false), + new Person(new Name("Charlie Davis"), new StudentId("A1359885Y"), new Phone("85889043"), + new Email("charliedavis@hotmail.com"), new GitHubUsername(null), + new TelegramHandle("charliedavis57"), false, + getTagSet("CS2103T need member"), false), + new Person(new Name("Cassandra Ong"), new StudentId("A1984289X"), new Phone("84809611"), + new Email("cassandraong@u.nus.edu"), new GitHubUsername("cassandraong60"), + new TelegramHandle(null), false, + getTagSet("CS3243"), false), + new Person(new Name("Charles William"), new StudentId("A1900975L"), new Phone("98935555"), + new Email("charleswilliam@hotmail.com"), new GitHubUsername("charleswilliam3"), + new TelegramHandle(null), false, + getTagSet("CS2106 need member"), false), + new Person(new Name("Damien Lim"), new StudentId("A1963998Z"), new Phone("98454108"), + new Email("damienlim@gmail.com"), new GitHubUsername(null), + new TelegramHandle(null), false, + getTagSet("CS2102 need group"), false), + new Person(new Name("Dan Heng"), new StudentId("A1481608Y"), new Phone("86924167"), + new Email("danheng@outlook.com"), new GitHubUsername("danheng47"), + new TelegramHandle("danheng45"), false, + getTagSet("CS2101"), false), + new Person(new Name("Denise Gray"), new StudentId("A1617407Z"), new Phone("98499986"), + new Email("denisegray@hotmail.com"), new GitHubUsername(null), + new TelegramHandle("denisegray39"), false, + getTagSet("CS2040S need member"), false), + new Person(new Name("Eliza Davis"), new StudentId("A1473884R"), new Phone("83974364"), + new Email("elizadavis@yahoo.com"), new GitHubUsername("elizadavis50"), + new TelegramHandle(null), false, + getTagSet("CS3243"), false), + new Person(new Name("Felicia Lin"), new StudentId("A1830220Z"), new Phone("93013799"), + new Email("felicialin@yahoo.com"), new GitHubUsername("felicialin40"), + new TelegramHandle(null), false, + getTagSet("CS3230"), false), + new Person(new Name("Fundy Smith"), new StudentId("A1711717X"), new Phone("97454364"), + new Email("fundysmith@hotmail.com"), new GitHubUsername("fundysmith32"), + new TelegramHandle("fundysmith45"), false, + getTagSet("CS2101 need member"), false), + new Person(new Name("Gordon Gray"), new StudentId("A1149650L"), new Phone("87929336"), + new Email("gordongray@email.com"), new GitHubUsername("gordongray82"), + new TelegramHandle("gordongray45"), false, + getTagSet("CS3243"), false), + new Person(new Name("James Ong"), new StudentId("A1899147R"), new Phone("94102049"), + new Email("jamesong@yahoo.com"), new GitHubUsername(null), + new TelegramHandle(null), false, + getTagSet("CS2102"), false), + new Person(new Name("Jane Gray"), new StudentId("A1371778Y"), new Phone("98642544"), + new Email("janegray@u.nus.edu"), new GitHubUsername("janegray14"), + new TelegramHandle("janegray5"), false, + getTagSet("CS1231S"), false), + new Person(new Name("Janice Johnson"), new StudentId("A1779972A"), new Phone("95017499"), + new Email("janicejohnson@outlook.com"), new GitHubUsername(null), + new TelegramHandle("janicejohnson94"), false, + getTagSet("CS1231S need member"), false), + new Person(new Name("Justin Jones"), new StudentId("A1380349Z"), new Phone("96762415"), + new Email("justinjones@hotmail.com"), new GitHubUsername(null), + new TelegramHandle("justinjones31"), false, + getTagSet("CS1101S"), false), + new Person(new Name("Harold Lin"), new StudentId("A1838586Y"), new Phone("82578104"), + new Email("haroldlin@email.com"), new GitHubUsername("haroldlin58"), + new TelegramHandle("haroldlin9"), false, + getTagSet("CS2103T need group"), false), + new Person(new Name("Henderson William"), new StudentId("A1196405R"), new Phone("89277387"), + new Email("hendersonwilliam@yahoo.com"), new GitHubUsername("hendersonwilliam70"), + new TelegramHandle("hendersonwilliam27"), false, + getTagSet("CS3243"), false), + new Person(new Name("Ishan Heng"), new StudentId("A1246783X"), new Phone("89588652"), + new Email("ishanheng@hotmail.com"), new GitHubUsername("ishanheng76"), + new TelegramHandle("ishanheng95"), false, + getTagSet("CS2102"), false), + new Person(new Name("Destin Smith"), new StudentId("A1412064Z"), new Phone("81394689"), + new Email("destinsmith@email.com"), new GitHubUsername("destinsmith44"), + new TelegramHandle("destinsmith62"), false, + getTagSet("CS2040S need group"), false), + new Person(new Name("Logan Jones"), new StudentId("A1112170R"), new Phone("91333890"), + new Email("loganjones@hotmail.com"), new GitHubUsername("loganjones79"), + new TelegramHandle("loganjones55"), false, + getTagSet("CS2106 need group"), false), + new Person(new Name("Paul Gray"), new StudentId("A1601243Y"), new Phone("89977560"), + new Email("paulgray@email.com"), new GitHubUsername("paulgray2"), + new TelegramHandle("paulgray58"), false, + getTagSet("CS2100 need group"), false), + new Person(new Name("Harry Chin"), new StudentId("A1334871X"), new Phone("95501504"), + new Email("harrychin@outlook.com"), new GitHubUsername("harrychin92"), + new TelegramHandle("harrychin16"), false, + getTagSet("CS1101S need group"), false), + new Person(new Name("Reginald Heng"), new StudentId("A1951638Z"), new Phone("94225881"), + new Email("reginaldheng@outlook.com"), new GitHubUsername("reginaldheng92"), + new TelegramHandle("reginaldheng55"), false, + getTagSet("CS2106"), false), + new Person(new Name("Kevin Lin"), new StudentId("A1768364R"), new Phone("90027713"), + new Email("kevinlin@email.com"), new GitHubUsername(null), + new TelegramHandle(null), false, + getTagSet("CS2106 need group"), false), + new Person(new Name("Kirsten Davis"), new StudentId("A1930742L"), new Phone("89859195"), + new Email("kirstendavis@hotmail.com"), new GitHubUsername("kirstendavis78"), + new TelegramHandle("kirstendavis58"), false, + getTagSet("CS3243 need member"), false), + new Person(new Name("Bernice Smith"), new StudentId("A1757485Z"), new Phone("82955392"), + new Email("bernicesmith@yahoo.com"), new GitHubUsername(null), + new TelegramHandle("bernicesmith1"), false, + getTagSet("CS2105"), false), + new Person(new Name("Charmaine Heng"), new StudentId("A1134115A"), new Phone("98190393"), + new Email("charmaineheng@outlook.com"), new GitHubUsername("charmaineheng55"), + new TelegramHandle("charmaineheng78"), false, + getTagSet("CS2102"), false), + new Person(new Name("Jonathan Lau"), new StudentId("A1707247L"), new Phone("95430738"), + new Email("jonathanlau@u.nus.edu"), new GitHubUsername(null), + new TelegramHandle("@jonathanlau41"), false, + getTagSet("CS1101S need group"), false), + new Person(new Name("Melvin Johnson"), new StudentId("A1341762Z"), new Phone("87270892"), + new Email("melvinjohnson@email.com"), new GitHubUsername("melvinjohnson1"), + new TelegramHandle(null), false, + getTagSet("CS1231S need group"), false), + new Person(new Name("Mary William"), new StudentId("A1383490L"), new Phone("87715884"), + new Email("marywilliam@u.nus.edu"), new GitHubUsername("marywilliam92"), + new TelegramHandle("marywilliam92"), false, + getTagSet("CS3244 need group"), false), + new Person(new Name("Nelson Heng"), new StudentId("A1620514A"), new Phone("83104259"), + new Email("nelsonheng@outlook.com"), new GitHubUsername("nelsonheng58"), + new TelegramHandle("nelsonheng95"), false, + getTagSet("CS3230 need member"), false), + new Person(new Name("Nicole Gray"), new StudentId("A1443745Z"), new Phone("95841730"), + new Email("nicolegray@u.nus.edu"), new GitHubUsername("nicolegray0"), + new TelegramHandle("nicolegray97"), false, + getTagSet("CS2102 need group"), false), + new Person(new Name("Nico Ong"), new StudentId("A1428496Y"), new Phone("88133575"), + new Email("nicoong@outlook.com"), new GitHubUsername("nicoong92"), + new TelegramHandle(null), false, + getTagSet("CS2103T"), false) + }; + } + + public static ReadOnlyAddressBook getSampleAddressBook() { + AddressBook sampleAb = new AddressBook(); + for (Person samplePerson : getSamplePersons()) { + sampleAb.addPerson(samplePerson); + } + return sampleAb; + } + + /** + * Returns a tag set containing the list of strings given. + */ + public static Set getTagSet(String... strings) { + return Arrays.stream(strings) + .map(modString -> { + try { + return new Mod(modString); + } catch (ParseException e) { + return null; + } + }) + .collect(Collectors.toSet()); + } + +} diff --git a/src/main/java/seedu/address/storage/AddressBookStorage.java b/src/main/java/seedu/modulink/storage/AddressBookStorage.java similarity index 85% rename from src/main/java/seedu/address/storage/AddressBookStorage.java rename to src/main/java/seedu/modulink/storage/AddressBookStorage.java index 4599182b3f9..10b9e66a87f 100644 --- a/src/main/java/seedu/address/storage/AddressBookStorage.java +++ b/src/main/java/seedu/modulink/storage/AddressBookStorage.java @@ -1,14 +1,14 @@ -package seedu.address.storage; +package seedu.modulink.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; +import seedu.modulink.commons.exceptions.DataConversionException; +import seedu.modulink.model.ReadOnlyAddressBook; /** - * Represents a storage for {@link seedu.address.model.AddressBook}. + * Represents a storage for {@link seedu.modulink.model.AddressBook}. */ public interface AddressBookStorage { diff --git a/src/main/java/seedu/modulink/storage/JsonAdaptedPerson.java b/src/main/java/seedu/modulink/storage/JsonAdaptedPerson.java new file mode 100644 index 00000000000..ab9b563777b --- /dev/null +++ b/src/main/java/seedu/modulink/storage/JsonAdaptedPerson.java @@ -0,0 +1,138 @@ +package seedu.modulink.storage; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.modulink.commons.exceptions.IllegalValueException; +import seedu.modulink.model.person.Email; +import seedu.modulink.model.person.GitHubUsername; +import seedu.modulink.model.person.Name; +import seedu.modulink.model.person.Person; +import seedu.modulink.model.person.Phone; +import seedu.modulink.model.person.StudentId; +import seedu.modulink.model.person.TelegramHandle; +import seedu.modulink.model.tag.Mod; + + +/** + * 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 id; + private final String phone; + private final String email; + private final String gitHubUsername; + private final String telegramHandle; + private final boolean isFavourite; + private final List modules = new ArrayList<>(); + private final boolean isMyProfile; + + /** + * Constructs a {@code JsonAdaptedPerson} with the given person details. + */ + @JsonCreator + public JsonAdaptedPerson(@JsonProperty("name") String name, + @JsonProperty("id") String id, @JsonProperty("phone") String phone, + @JsonProperty("email") String email, @JsonProperty("gitHubUsername") String gitHubUsername, + @JsonProperty("telegramHandle") String telegramHandle, @JsonProperty("isFavourite") boolean isFavourite, + @JsonProperty("modules") List modules, @JsonProperty("isMyProfile") boolean isMyProfile) { + this.name = name; + this.id = id; + this.phone = phone; + this.email = email; + this.gitHubUsername = gitHubUsername; + this.telegramHandle = telegramHandle; + this.isFavourite = isFavourite; + if (modules != null) { + this.modules.addAll(modules); + } + this.isMyProfile = isMyProfile; + } + + /** + * Converts a given {@code Person} into this class for Jackson use. + */ + public JsonAdaptedPerson(Person source) { + name = source.getName().fullName; + id = source.getStudentId().value; + phone = source.getPhone().value; + email = source.getEmail().value; + gitHubUsername = source.getGithubUsername().value; + telegramHandle = source.getTelegramHandle().value; + isFavourite = source.getIsFavourite(); + modules.addAll(source.getMods().stream() + .map(JsonAdaptedTag::new) + .collect(Collectors.toList())); + isMyProfile = source.getIsMyProfile(); + } + + /** + * Converts this Jackson-friendly adapted person object into the model's {@code Person} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted person. + */ + public Person toModelType() throws IllegalValueException { + final List personMods = new ArrayList<>(); + for (JsonAdaptedTag tag : modules) { + personMods.add(tag.toModelType()); + } + + if (name == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); + } + if (!Name.isValidName(name)) { + throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS); + } + final Name modelName = new Name(name); + + if (id == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + StudentId.class.getSimpleName())); + } + if (!StudentId.isValidId(id)) { + throw new IllegalValueException(StudentId.MESSAGE_CONSTRAINTS); + } + final StudentId modelId = new StudentId(id); + + if (phone == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName())); + } + if (!Phone.isValidPhone(phone)) { + throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS); + } + final Phone modelPhone = new Phone(phone); + + if (email == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName())); + } + if (!Email.isValidEmail(email)) { + throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS); + } + final Email modelEmail = new Email(email); + + if (!GitHubUsername.isValidUsername(gitHubUsername)) { + throw new IllegalValueException(GitHubUsername.MESSAGE_CONSTRAINTS); + } + final GitHubUsername modelUsername = new GitHubUsername(gitHubUsername); + + if (!TelegramHandle.isValidHandle(telegramHandle)) { + throw new IllegalValueException(TelegramHandle.MESSAGE_CONSTRAINTS); + } + final TelegramHandle modelHandle = new TelegramHandle(telegramHandle); + + final Set modelMods = new HashSet<>(personMods); + return new Person(modelName, modelId, modelPhone, modelEmail, + modelUsername, modelHandle, this.isFavourite, modelMods, this.isMyProfile); + } + +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/seedu/modulink/storage/JsonAdaptedTag.java similarity index 63% rename from src/main/java/seedu/address/storage/JsonAdaptedTag.java rename to src/main/java/seedu/modulink/storage/JsonAdaptedTag.java index 0df22bdb754..0ebde01ddc5 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java +++ b/src/main/java/seedu/modulink/storage/JsonAdaptedTag.java @@ -1,13 +1,13 @@ -package seedu.address.storage; +package seedu.modulink.storage; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.tag.Tag; +import seedu.modulink.commons.exceptions.IllegalValueException; +import seedu.modulink.model.tag.Mod; /** - * Jackson-friendly version of {@link Tag}. + * Jackson-friendly version of {@link Mod}. */ class JsonAdaptedTag { @@ -24,8 +24,8 @@ public JsonAdaptedTag(String tagName) { /** * Converts a given {@code Tag} into this class for Jackson use. */ - public JsonAdaptedTag(Tag source) { - tagName = source.tagName; + public JsonAdaptedTag(Mod source) { + tagName = source.oriInput; } @JsonValue @@ -38,11 +38,11 @@ public String getTagName() { * * @throws IllegalValueException if there were any data constraints violated in the adapted tag. */ - public Tag toModelType() throws IllegalValueException { - if (!Tag.isValidTagName(tagName)) { - throw new IllegalValueException(Tag.MESSAGE_CONSTRAINTS); + public Mod toModelType() throws IllegalValueException { + if (!Mod.isValidTagName(tagName)) { + throw new IllegalValueException(Mod.MESSAGE_CONSTRAINTS); } - return new Tag(tagName); + return new Mod(tagName); } } diff --git a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java b/src/main/java/seedu/modulink/storage/JsonAddressBookStorage.java similarity index 86% rename from src/main/java/seedu/address/storage/JsonAddressBookStorage.java rename to src/main/java/seedu/modulink/storage/JsonAddressBookStorage.java index dfab9daaa0d..efa0c5cb3a3 100644 --- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java +++ b/src/main/java/seedu/modulink/storage/JsonAddressBookStorage.java @@ -1,4 +1,4 @@ -package seedu.address.storage; +package seedu.modulink.storage; import static java.util.Objects.requireNonNull; @@ -7,12 +7,12 @@ import java.util.Optional; import java.util.logging.Logger; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.commons.util.FileUtil; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.ReadOnlyAddressBook; +import seedu.modulink.commons.core.LogsCenter; +import seedu.modulink.commons.exceptions.DataConversionException; +import seedu.modulink.commons.exceptions.IllegalValueException; +import seedu.modulink.commons.util.FileUtil; +import seedu.modulink.commons.util.JsonUtil; +import seedu.modulink.model.ReadOnlyAddressBook; /** * A class to access AddressBook data stored as a json file on the hard disk. diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/modulink/storage/JsonSerializableAddressBook.java similarity index 89% rename from src/main/java/seedu/address/storage/JsonSerializableAddressBook.java rename to src/main/java/seedu/modulink/storage/JsonSerializableAddressBook.java index 5efd834091d..0a932a5169d 100644 --- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java +++ b/src/main/java/seedu/modulink/storage/JsonSerializableAddressBook.java @@ -1,4 +1,4 @@ -package seedu.address.storage; +package seedu.modulink.storage; import java.util.ArrayList; import java.util.List; @@ -8,10 +8,10 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonRootName; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; +import seedu.modulink.commons.exceptions.IllegalValueException; +import seedu.modulink.model.AddressBook; +import seedu.modulink.model.ReadOnlyAddressBook; +import seedu.modulink.model.person.Person; /** * An Immutable AddressBook that is serializable to JSON format. diff --git a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java b/src/main/java/seedu/modulink/storage/JsonUserPrefsStorage.java similarity index 82% rename from src/main/java/seedu/address/storage/JsonUserPrefsStorage.java rename to src/main/java/seedu/modulink/storage/JsonUserPrefsStorage.java index bc2bbad84aa..f98d9b90c89 100644 --- a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java +++ b/src/main/java/seedu/modulink/storage/JsonUserPrefsStorage.java @@ -1,13 +1,13 @@ -package seedu.address.storage; +package seedu.modulink.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import seedu.modulink.commons.exceptions.DataConversionException; +import seedu.modulink.commons.util.JsonUtil; +import seedu.modulink.model.ReadOnlyUserPrefs; +import seedu.modulink.model.UserPrefs; /** * A class to access UserPrefs stored in the hard disk as a json file diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/seedu/modulink/storage/Storage.java similarity index 73% rename from src/main/java/seedu/address/storage/Storage.java rename to src/main/java/seedu/modulink/storage/Storage.java index beda8bd9f11..6ddde061e0e 100644 --- a/src/main/java/seedu/address/storage/Storage.java +++ b/src/main/java/seedu/modulink/storage/Storage.java @@ -1,13 +1,13 @@ -package seedu.address.storage; +package seedu.modulink.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import seedu.modulink.commons.exceptions.DataConversionException; +import seedu.modulink.model.ReadOnlyAddressBook; +import seedu.modulink.model.ReadOnlyUserPrefs; +import seedu.modulink.model.UserPrefs; /** * API of the Storage component diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/modulink/storage/StorageManager.java similarity index 89% rename from src/main/java/seedu/address/storage/StorageManager.java rename to src/main/java/seedu/modulink/storage/StorageManager.java index 79868290974..cf6db0c8790 100644 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ b/src/main/java/seedu/modulink/storage/StorageManager.java @@ -1,15 +1,15 @@ -package seedu.address.storage; +package seedu.modulink.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; import java.util.logging.Logger; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import seedu.modulink.commons.core.LogsCenter; +import seedu.modulink.commons.exceptions.DataConversionException; +import seedu.modulink.model.ReadOnlyAddressBook; +import seedu.modulink.model.ReadOnlyUserPrefs; +import seedu.modulink.model.UserPrefs; /** * Manages storage of AddressBook data in local storage. diff --git a/src/main/java/seedu/address/storage/UserPrefsStorage.java b/src/main/java/seedu/modulink/storage/UserPrefsStorage.java similarity index 71% rename from src/main/java/seedu/address/storage/UserPrefsStorage.java rename to src/main/java/seedu/modulink/storage/UserPrefsStorage.java index 29eef178dbc..ecb9271d837 100644 --- a/src/main/java/seedu/address/storage/UserPrefsStorage.java +++ b/src/main/java/seedu/modulink/storage/UserPrefsStorage.java @@ -1,15 +1,15 @@ -package seedu.address.storage; +package seedu.modulink.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import seedu.modulink.commons.exceptions.DataConversionException; +import seedu.modulink.model.ReadOnlyUserPrefs; +import seedu.modulink.model.UserPrefs; /** - * Represents a storage for {@link seedu.address.model.UserPrefs}. + * Represents a storage for {@link seedu.modulink.model.UserPrefs}. */ public interface UserPrefsStorage { @@ -27,7 +27,7 @@ public interface UserPrefsStorage { Optional readUserPrefs() throws DataConversionException, IOException; /** - * Saves the given {@link seedu.address.model.ReadOnlyUserPrefs} to the storage. + * Saves the given {@link seedu.modulink.model.ReadOnlyUserPrefs} to the storage. * @param userPrefs cannot be null. * @throws IOException if there was any problem writing to the file. */ diff --git a/src/main/java/seedu/address/ui/CommandBox.java b/src/main/java/seedu/modulink/ui/CommandBox.java similarity index 89% rename from src/main/java/seedu/address/ui/CommandBox.java rename to src/main/java/seedu/modulink/ui/CommandBox.java index 9e75478664b..4354a9fe2bc 100644 --- a/src/main/java/seedu/address/ui/CommandBox.java +++ b/src/main/java/seedu/modulink/ui/CommandBox.java @@ -1,12 +1,12 @@ -package seedu.address.ui; +package seedu.modulink.ui; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.control.TextField; import javafx.scene.layout.Region; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.modulink.logic.commands.CommandResult; +import seedu.modulink.logic.commands.exceptions.CommandException; +import seedu.modulink.logic.parser.exceptions.ParseException; /** * The UI component that is responsible for receiving user command inputs. @@ -77,7 +77,7 @@ public interface CommandExecutor { /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see seedu.modulink.logic.Logic#execute(String) */ CommandResult execute(String commandText) throws CommandException, ParseException; } diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/modulink/ui/HelpWindow.java similarity index 92% rename from src/main/java/seedu/address/ui/HelpWindow.java rename to src/main/java/seedu/modulink/ui/HelpWindow.java index 9a665915949..85d7eb33a11 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/seedu/modulink/ui/HelpWindow.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.modulink.ui; import java.util.logging.Logger; @@ -8,14 +8,14 @@ import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.stage.Stage; -import seedu.address.commons.core.LogsCenter; +import seedu.modulink.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 USERGUIDE_URL = "https://ay2122s1-cs2103t-w12-4.github.io/tp/UserGuide"; public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL; private static final Logger logger = LogsCenter.getLogger(HelpWindow.class); diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/modulink/ui/MainWindow.java similarity index 93% rename from src/main/java/seedu/address/ui/MainWindow.java rename to src/main/java/seedu/modulink/ui/MainWindow.java index 9106c3aa6e5..eba4e35243a 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/modulink/ui/MainWindow.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.modulink.ui; import java.util.logging.Logger; @@ -10,12 +10,12 @@ import javafx.scene.input.KeyEvent; import javafx.scene.layout.StackPane; import javafx.stage.Stage; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.logic.Logic; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.modulink.commons.core.GuiSettings; +import seedu.modulink.commons.core.LogsCenter; +import seedu.modulink.logic.Logic; +import seedu.modulink.logic.commands.CommandResult; +import seedu.modulink.logic.commands.exceptions.CommandException; +import seedu.modulink.logic.parser.exceptions.ParseException; /** * The Main Window. Provides the basic application layout containing @@ -170,7 +170,7 @@ public PersonListPanel getPersonListPanel() { /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see seedu.modulink.logic.Logic#execute(String) */ private CommandResult executeCommand(String commandText) throws CommandException, ParseException { try { diff --git a/src/main/java/seedu/modulink/ui/PersonCard.java b/src/main/java/seedu/modulink/ui/PersonCard.java new file mode 100644 index 00000000000..6336c372ff8 --- /dev/null +++ b/src/main/java/seedu/modulink/ui/PersonCard.java @@ -0,0 +1,121 @@ +package seedu.modulink.ui; + +import java.util.Comparator; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.modulink.model.person.Person; +import seedu.modulink.model.tag.Status; + +/** + * An UI component that displays information of a {@code Person}. + */ +public class PersonCard extends UiPart { + + private static final String FXML = "PersonListCard.fxml"; + + /** + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + + public final Person person; + + @FXML + private HBox cardPane; + @FXML + private Label id; + @FXML + private Label name; + @FXML + private Label studentId; + @FXML + private Label phone; + @FXML + private Label email; + @FXML + private Label gitHubUsername; + @FXML + private Label telegramHandle; + @FXML + private ImageView fav; + @FXML + private ImageView userProfile; + @FXML + private FlowPane mods; + + /** + * Creates a {@code PersonCode} with the given {@code Person} and index to display. + */ + public PersonCard(Person person, int displayedIndex) { + super(FXML); + this.person = person; + id.setText(displayedIndex + "."); + name.setText(person.getName().fullName); + if (person.getIsFavourite()) { + Image favIcon = new Image(getClass().getResourceAsStream("/images/fav_icon.png")); + fav.setImage(favIcon); + } + if (person.getIsMyProfile()) { + Image userIcon = new Image(getClass().getResourceAsStream("/images/user_icon.png")); + userProfile.setImage(userIcon); + } + studentId.setText(person.getStudentId().value); + phone.setText(person.getPhone().value); + email.setText(person.getEmail().value); + if (!person.getGithubUsername().isNull()) { + gitHubUsername.setText("GitHub Account: www.github.com/" + person.getGithubUsername().value); + } else { + gitHubUsername.setText("No GitHub Account!"); + } + if (!person.getTelegramHandle().isNull()) { + telegramHandle.setText("Telegram Chat: https://t.me/" + person.getTelegramHandle().value); + } else { + telegramHandle.setText("No Telegram Account!"); + } + person.getMods().stream() + .sorted(Comparator.comparing(mod -> mod.modName)) + .forEach(mod -> { + if (mod.status.equals(Status.NONE)) { + mods.getChildren().add(new Label(mod.modName)); + } else if (mod.status.equals(Status.NEED_GROUP)) { + Label l = new Label(mod.modName); + l.setStyle("-fx-background-color: #ff0000;"); + mods.getChildren().add(l); + } else if (mod.status.equals(Status.NEED_MEMBER)) { + Label l = new Label(mod.modName); + l.setStyle("-fx-background-color: yellow; -fx-text-fill: black;"); + mods.getChildren().add(l); + } else { + mods.getChildren().add(new Label(mod.modName)); + } + }); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof PersonCard)) { + return false; + } + + // state check + PersonCard card = (PersonCard) other; + return id.getText().equals(card.id.getText()) + && person.equals(card.person); + } + +} diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/modulink/ui/PersonListPanel.java similarity index 76% rename from src/main/java/seedu/address/ui/PersonListPanel.java rename to src/main/java/seedu/modulink/ui/PersonListPanel.java index f4c501a897b..19ac4c73597 100644 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ b/src/main/java/seedu/modulink/ui/PersonListPanel.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.modulink.ui; import java.util.logging.Logger; @@ -7,8 +7,8 @@ import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.scene.layout.Region; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; +import seedu.modulink.commons.core.LogsCenter; +import seedu.modulink.model.person.Person; /** * Panel containing the list of persons. @@ -26,6 +26,7 @@ public class PersonListPanel extends UiPart { public PersonListPanel(ObservableList personList) { super(FXML); personListView.setItems(personList); + personListView.setCellFactory(listView -> new PersonListViewCell()); } @@ -36,12 +37,16 @@ class PersonListViewCell extends ListCell { @Override protected void updateItem(Person person, boolean empty) { super.updateItem(person, empty); - if (empty || person == null) { setGraphic(null); setText(null); + } else if (person.getIsMyProfile()) { + setGraphic(new PersonCard(person, getIndex() + 1).getRoot()); + setStyle("-fx-background-color: #228B22; -fx-background-radius: 20px; -fx-padding: 10px"); } else { + setStyle("-fx-background-radius: 20px; -fx-padding: 10px"); setGraphic(new PersonCard(person, getIndex() + 1).getRoot()); + } } } diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/seedu/modulink/ui/ResultDisplay.java similarity index 95% rename from src/main/java/seedu/address/ui/ResultDisplay.java rename to src/main/java/seedu/modulink/ui/ResultDisplay.java index 7d98e84eedf..e373e7ea941 100644 --- a/src/main/java/seedu/address/ui/ResultDisplay.java +++ b/src/main/java/seedu/modulink/ui/ResultDisplay.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.modulink.ui; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/seedu/modulink/ui/StatusBarFooter.java similarity index 96% rename from src/main/java/seedu/address/ui/StatusBarFooter.java rename to src/main/java/seedu/modulink/ui/StatusBarFooter.java index b577f829423..0a7c9702797 100644 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ b/src/main/java/seedu/modulink/ui/StatusBarFooter.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.modulink.ui; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/main/java/seedu/address/ui/Ui.java b/src/main/java/seedu/modulink/ui/Ui.java similarity index 85% rename from src/main/java/seedu/address/ui/Ui.java rename to src/main/java/seedu/modulink/ui/Ui.java index 17aa0b494fe..2ba098e6160 100644 --- a/src/main/java/seedu/address/ui/Ui.java +++ b/src/main/java/seedu/modulink/ui/Ui.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.modulink.ui; import javafx.stage.Stage; @@ -9,5 +9,4 @@ public interface Ui { /** Starts the UI (and the App). */ void start(Stage primaryStage); - } diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/modulink/ui/UiManager.java similarity index 93% rename from src/main/java/seedu/address/ui/UiManager.java rename to src/main/java/seedu/modulink/ui/UiManager.java index 882027e4537..a80bcbb11a8 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/seedu/modulink/ui/UiManager.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.modulink.ui; import java.util.logging.Logger; @@ -7,10 +7,10 @@ import javafx.scene.control.Alert.AlertType; import javafx.scene.image.Image; import javafx.stage.Stage; -import seedu.address.MainApp; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; +import seedu.modulink.MainApp; +import seedu.modulink.commons.core.LogsCenter; +import seedu.modulink.commons.util.StringUtil; +import seedu.modulink.logic.Logic; /** * The manager of the UI component. diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/seedu/modulink/ui/UiPart.java similarity index 97% rename from src/main/java/seedu/address/ui/UiPart.java rename to src/main/java/seedu/modulink/ui/UiPart.java index fc820e01a9c..98fbf28aee9 100644 --- a/src/main/java/seedu/address/ui/UiPart.java +++ b/src/main/java/seedu/modulink/ui/UiPart.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.modulink.ui; import static java.util.Objects.requireNonNull; @@ -6,7 +6,7 @@ import java.net.URL; import javafx.fxml.FXMLLoader; -import seedu.address.MainApp; +import seedu.modulink.MainApp; /** * Represents a distinct part of the UI. e.g. Windows, dialogs, panels, status bars, etc. diff --git a/src/main/resources/images/fav_icon.png b/src/main/resources/images/fav_icon.png new file mode 100644 index 00000000000..09141ad0444 Binary files /dev/null and b/src/main/resources/images/fav_icon.png differ diff --git a/src/main/resources/images/img.png b/src/main/resources/images/img.png new file mode 100644 index 00000000000..27a56ffb443 Binary files /dev/null and b/src/main/resources/images/img.png differ diff --git a/src/main/resources/images/user_icon.png b/src/main/resources/images/user_icon.png new file mode 100644 index 00000000000..fcee3eaaf15 Binary files /dev/null and b/src/main/resources/images/user_icon.png differ diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 9ce9bcfb569..741676ac0bf 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -337,12 +337,12 @@ -fx-background-radius: 0; } -#tags { +#mods { -fx-hgap: 7; -fx-vgap: 3; } -#tags .label { +#mods .label { -fx-text-fill: white; -fx-background-color: #3e7b91; -fx-padding: 1 3 1 3; diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index 32bcf2c8e70..89f86c3ba50 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -12,7 +12,7 @@ + title="ModuLink" minWidth="450" minHeight="600" onCloseRequest="#handleExit"> @@ -41,7 +41,7 @@ + minHeight="100" prefHeight="200" maxHeight="200"> diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml index f08ea32ad55..17ed02749c7 100644 --- a/src/main/resources/view/PersonListCard.fxml +++ b/src/main/resources/view/PersonListCard.fxml @@ -2,35 +2,45 @@ + + + - + - + - + - - + + + + + diff --git a/src/main/resources/view/ResultDisplay.fxml b/src/main/resources/view/ResultDisplay.fxml index 58d5ad3dc56..2173e64bf0b 100644 --- a/src/main/resources/view/ResultDisplay.fxml +++ b/src/main/resources/view/ResultDisplay.fxml @@ -5,5 +5,5 @@ -