diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 0f98b563e5c..dce95469068 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -61,7 +61,7 @@ The _Sequence Diagram_ below shows how the components interact with each other f Each of the four main components (also shown in the diagram above), - defines its _API_ in an `interface` with the same name as the Component. -- implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API `interface` mentioned in the previous point. +- implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API `interface` mentioned in the previous point). For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below. @@ -104,9 +104,9 @@ The sequence diagram below illustrates the interactions within the `Logic` compo How the `Logic` component works: 1. When `Logic` is called upon to execute a command, it is passed to an `AddressBookParser` object which in turn creates a parser that matches the command (e.g., `DeleteCommandParser`) and uses it to parse the command. -1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `DeleteCommand`) which is executed by the `LogicManager`. -1. The command can communicate with the `Model` when it is executed (e.g. to delete a person). -1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. +2. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `DeleteCommand`) which is executed by the `LogicManager`. +3. The command can communicate with the `Model` when it is executed (e.g. to delete a person). +4. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command: @@ -158,27 +158,62 @@ Classes used by multiple components are in the `seedu.addressbook.commons` packa This section describes some noteworthy details on how certain features are implemented. +### Edit Contacts/Meetings feature + +Both the edit contact command `editc` and edit meeting command `editm` are implemented quite similarly due to the similarities between the `Person` and `Meeting` classes. + +This section shall therefore only go in deep detail for the implementation of the `editm` command. However, the `editc` equivalent of certain commands used by `editm` will be detailed, such that the implementation of `editc` can be fully derived. + +The class diagrams for both edit commands differ from the other commands in that an additional `EditMeetingDescriptor` or `EditPersonDescriptor` class is used to create the commands. The diagram for `editm` is as seen below. + +![EditMeetingClassDiagram](images/edit/EditMeetingClassDiagram.png) + +To start off, both `editc` and `editm` take in an index as their first argument, which refers to a `Person` in the displayed person list, or a `Meeting` in the displayed meeting list respectively. + +Next, both commands take in a variable number of optional arguments based on the arguments used by the `addc` and `addm` commands. This allows the user to input only the fields they wish to edit in the chosen `Person` or `Meeting` object, as opposed to having to type in every field. + +Using `editm 3 m/Friend meetup a/Mall` as an example, when input by the user, an instance of an `EditMeetingCommand` (`EditCommand` in the case of `editc` with its respective `Person` fields as arguments) is created as shown in the following Sequence Diagram. + +![CreateEditMeetingCommandSequenceDiagram](images/edit/CreateEditMeetingCommand-Create_EditMeetingCommand.png) + +As seen above, before the `EditMeetingCommandParser` creates the `EditMeetingCommand` object, it first creates an instance of an `EditMeetingDescriptor` (`EditPersonDescriptor` in the case of `editc`). This descriptor stores the new information of the fields to be edited based on the user input. From our example, it would store the `Title: Friend meetup` and `Location: Mall`. + +Once the instance of `EditMeetingCommand` is created, it is executed by the `LogicManager`. During execution, the current `Meeting` object referenced by the input index (3 in our example) is obtained from the meeting list returned by `getFilteredMeetingList`. + +Next, a new instance of `Meeting` is created using the fields from the `EditMeetingDescriptor`. Any fields not present in the descriptor us obtained from the old `Meeting` object from the previous step, as these fields do not need to be edited. From our example, the `START`, `END`, `TAG` and `ATTENDEE_LIST` will be obtained from the current `Meeting` instance as the Descriptor only contains the `TITLE` and `LOCATION` fields. + +Finally, the old `Meeting` object is replaced with the new instance, and the `ModelManager` is updated. These steps are denoted in the Sequence Diagram below. + +![EditMeetingSequenceDiagram](images/edit/EditMeetingSequenceDiagram-Execute_EditMeetingCommand.png) + +#### Design Considerations and Rationale + +1. `editm` allows the user to edit every field of `Meeting` apart from the attendee list. + - The commands `addmc` and `rmmc` are used to modify the attendee list of a meeting instead. + - This retains the identity of the edit commands as commands that modify field information, as opposed to `addmc` and `rmmc` which can be seen as commands that link multiple objects together. + ### View Contacts/Meetings feature #### Implementation -Both the view contact command `viewc` and the view meeting command `viewm` are implemented in the exact same way due to the similarities between the `Person` and `Meeting` classes. +Just like the Edit commands, the view contact command `viewc` and the view meeting command `viewm` are implemented in the exact same way due to the similarities between the `Person` and `Meeting` classes. -As such, this section shall only detail the implementation of the `viewc` command. However the implementation of `viewm` can be derived by replacing some `Person` related functions/classes/objects with its `Meeting` counterpart. +As such, this section shall only detail the implementation of the `viewc` command. However, the implementation of `viewm` can be derived by replacing some `Person` related functions/classes/objects with its `Meeting` counterpart. -`viewc` and `viewm` both take in an index as their only argument. This refers to the `Person` or `Meeting` index respectively as displayed on either the Contact list or Meeting list. +`viewc` and `viewm` both take in an index as their only argument, which refers to a `Person` in the displayed person list, or a `Meeting` in the displayed meeting list respectively. -When `viewc 2` is used, an instance of a `ViewContactCommand` (`ViewMeetingCommand` in the case of `viewm`) is created as shown in the following Sequence Diagram. This step does not differ from the way other commands have been shown to be created. The argument for our example would just be `2`, which would be stored as the `targetIndex` field of the `ViewContactCommand` object. +Using `viewc 2` as an example, when input by the user, an instance of a `ViewContactCommand` (`ViewMeetingCommand` in the case of `viewm`) is created as shown in the following Sequence Diagram. This step does not differ from the way other commands have been shown to be created. The argument for our example would just be `2`, which would be stored as the `targetIndex` field of the `ViewContactCommand` object. -![ViewContactCommandSequenceDiagram](images/tracing/ViewContactCommandSequenceDiagram-ViewContactCommandSequence.png) +![CreateViewContactCommandSequenceDiagram](images/view/CreateViewContactCommand-Create_ViewContactCommand.png) -Once the instance of `ViewContactCommand` is created, it is executed. During execution, the command stores the contents of its `targetIndex` field in the `ModelManager` using its `setViewedPersonIndex` method as shown in the next Sequence Diagram. For `ViewMeetingCommand` it would use the `setViewedMeetingIndex` method instead. +Once the instance of `ViewContactCommand` is created, it is executed by the `LogicManager`. During execution, the command stores the contents of its `targetIndex` field in the `ModelManager` using its `setViewedPersonIndex` method as shown in the next Sequence Diagram. For `ViewMeetingCommand` it would use the `setViewedMeetingIndex` method instead. -![StoreViewedItemsToModelDiagram](images/tracing/ViewCommandsSequenceDiagram-StoreViewedItemsToModel.png) +![StoreViewedItemsToModelDiagram](images/view/ViewCommandsSequenceDiagram-StoreViewedItemsToModel.png) Once the indexes of the `Person` and `Meeting` objects to view (if any) are stored in `ModelManager`, their corresponding `Person` and `Meeting` objects (in this case the 2nd `Person` as displayed on the list) are obtained by the `MainWindow` as a `Pair` through the `getViewedItems` method of the `LogicManager` class. As such, both objects can then be forwarded to the `InfoDisplayPanel` using `setViewedModel`, which then displays detailed information of both objects. This process is denoted in the final Sequence Diagram below. -![ForwardViewedPersonMeetingtoUiDiagram](images/tracing/UiViewItemsSequenceDiagram-ForwardViewedPerson&MeetingToUi.png) +![ForwardViewedPersonMeetingtoUiDiagram](images/view/UiViewItemsSequenceDiagram-ForwardViewedPerson&MeetingToUi.png) + #### Design Considerations and Rationale 1. Passing viewed `Person` and `Meeting` from Model to Ui through Logic: @@ -195,7 +230,6 @@ Once the indexes of the `Person` and `Meeting` objects to view (if any) are stor - For the case of `editc` and `editm`, this is judged to not be an issue as the view commands still obey their definition of displaying the item at a specified list index. - For the case of `deletec`, `deletem`, `findc` and `findm`, a simple fix is to simply set the stored `Index` to null only for these commands. - ### Find meeting feature #### Behaviour and Implementation @@ -224,7 +258,6 @@ Step 2. The `FindMeetingCommand` will be immediately executed and will call `set The following diagrams show the entire sequence flow for `LogicManager#execute()` for FindMeetingCommand. ![FindMeetingSequence2](images/FindMeetingSequence2.png) - #### Design Considerations and Rationale 1. Given the amount of predicates `FindMeetingCommand` is supposed to use, every predicate needs to be combined in order to maintain good SLAP. @@ -253,8 +286,8 @@ The following sequence diagram shows how the add attendee operation works: A Person object can be obtained from a Meeting's list of attendees by searching through `UniquePersonList` for a `Person` with a name matching `attendeeName`. - ### Remove attendee feature + User can specify an Attendee to remove from a specified Meeting by specifying its index in the list of Attendees. This is the main motivation behind using a LinkedHashSet for the implementation of the Attendee Set. @@ -262,7 +295,6 @@ The following sequence diagram shows how the remove attendee operation works: ![RemoveAttendeeSequenceDiagram](images/RemoveAttendeeSequenceDiagram.png) - ### \[Proposed\] Undo/redo feature #### Proposed Implementation @@ -346,31 +378,31 @@ _{more aspects and alternatives to be added}_ ### Keeping track of last meeting with contact -Keeping track of the user's last meeting with their contact is facilitated by the addition of a `LastContactedTime` object to `Person`. -Thus, each instance of `Person` will contain an immutable `LastContactedTime` object that stores the user's last meeting with that contact. +Keeping track of the user's last meeting with their contact is facilitated by the addition of a `LastContactedTime` object to `Person`. +Thus, each instance of `Person` will contain an immutable `LastContactedTime` object that stores the user's last meeting with that contact. The following steps shows how `LastContactedTime` is implemented and utilized in the application. Step 1. The user inputs the `addc` command into the `CommandBox` input field, with the added field `l/[LAST_CONTACTED_TIME]`. -The following diagram summarizes steps 2 to 6: +The following diagram summarizes steps 2 to 6: -Step 2. Entering a correct command with the `Enter` key then calls `execute` on `LogicManager`. -Step 3. `LogicManager` then calls `AddressBookParser#parseCommand(commandText)` on the `commandText` String, which recognizes that it is an `addc` command. -Step 4. `AddressBookParser` then calls `AddCommandParser#parse()` on the command arguments. -Step 5. `AddCommandParser` then calls `ParserUtil#parseContactTime()` which parses the last contacted time and returns a `LocalDateTime` object called `lastContactedTime`. +Step 2. Entering a correct command with the `Enter` key then calls `execute` on `LogicManager`. +Step 3. `LogicManager` then calls `AddressBookParser#parseCommand(commandText)` on the `commandText` String, which recognizes that it is an `addc` command. +Step 4. `AddressBookParser` then calls `AddCommandParser#parse()` on the command arguments. +Step 5. `AddCommandParser` then calls `ParserUtil#parseContactTime()` which parses the last contacted time and returns a `LocalDateTime` object called `lastContactedTime`. Step 6. The `lastContactedTime` object is then passed to the `Person` constructor, which creates a new `Person` that calls the `LastContactedTime` constructor with it. The following diagram summarizes steps 7 and 8: -Step 7. The completed `Person` is passed to an `AddCommand` constructor which return a new `AddCommand` that can be executed. -Step 8. `LogicManager` then executes the `AddCommand` on the application model. -Step 9. Futher execution is carried out, which like before adds the `Person` object to the list of `Person`s in the `Model`, and updates the `Storage` with this new `Person`. +Step 7. The completed `Person` is passed to an `AddCommand` constructor which return a new `AddCommand` that can be executed. +Step 8. `LogicManager` then executes the `AddCommand` on the application model. +Step 9. Further execution is carried out, which like before adds the `Person` object to the list of `Person`s in the `Model`, and updates the `Storage` with this new `Person`. #### Design Consideration: Updating last meeting with contact -Solution: +Solution: This is facilitated by the addition of the `MarkDoneCommand`. When a meeting is marked as done, the attendees of the meeting will be updated with their LastContactedTime field updated to the end time of the meeting. --- @@ -402,7 +434,7 @@ This is facilitated by the addition of the `MarkDoneCommand`. When a meeting is Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` -| Priority | As a …​ | I want to …​ | So that I can…​ | +| Priority | As a …​ | I want to …​ | So that I can…​ | | -------- | ----------------------------------------- | ------------------------------- | ------------------------------------- | | `[EPIC]` | agent who has meetings | have a meeting schedule | keep track of them | | `* * *` | agent | create new meetings | | @@ -424,6 +456,7 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli | `* * *` | agent | add contacts to meetings | | | `* * *` | agent | remove contacts from meetings | | | `* * *` | agent | view contacts in meetings | | +| `* *` | agent | mark meetings as complete | know which meetings are still pending | | `*` | agent who wants to meet clients regularly | know the last contacted date | when to touch base with a client | _{More to be added}_ @@ -479,10 +512,10 @@ _{More to be added}_ 2. OutBook shows a list of meetings. 3. User requests to view details of a specific meeting. 4. OutBook shows the details of the meeting. -4. User requests to remove a specific contact from the meeting. -5. OutBook removes the contact from the meeting. 5. User requests to remove a specific contact from the meeting. 6. OutBook removes the contact from the meeting. +7. User requests to remove a specific contact from the meeting. +8. OutBook removes the contact from the meeting. Use case ends. @@ -593,16 +626,16 @@ 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.
+ 2. Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained. -1. _{ more test cases …​ }_ +3. _{ more test cases …​ }_ ### Deleting a person @@ -610,16 +643,16 @@ testers are expected to do more *exploratory* testing. 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. - 1. Test case: `delete 1`
+ 2. 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`
+ 3. 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)
+ 4. 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 …​ }_ +2. _{ more test cases …​ }_ ### Saving data @@ -627,4 +660,4 @@ testers are expected to do more *exploratory* testing. 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ -1. _{ more test cases …​ }_ +2. _{ more test cases …​ }_ diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 80957f00dcc..0868f42a758 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -3,22 +3,21 @@ layout: page title: OutBook User Guide --- -OutBook is an app that allows freelance insurance agents to manage their numerous contacts and meeting schedule. It is optimised for Command Line Interface (CLI) aims to significantly reduce the time needed for organizational tasks. +OutBook is an app that allows freelance insurance agents to manage their numerous contacts and meeting schedule. It is optimised for Command Line Interface (CLI) and aims to significantly reduce the time needed for organizational tasks. -This guide is to help you explore and learn about what are its features and how to use them. -Outbook has 2 lists which are used to track contacts and meetings respectively. These list can be filtered to show the specific data you need. +This guide is to help you explore and learn about what are its features and how to use them. +Outbook has 2 lists which are used to track contacts and meetings respectively. These list can be filtered to show the specific data you need. It is able to add, edit and delete any contacts and meetings you want. As well as add custom remarks and tags for your specific needs. - -
# Table of Content -* Table of Contents -{:toc} +- Table of Contents + {:toc} + +--- ---------------------------------------------------------------------------------------------------------------------
# Quick start @@ -36,20 +35,21 @@ It is able to add, edit and delete any contacts and meetings you want. As well a 5. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
Some example commands you can try: - * `listc` : Lists all contacts. + - `listc` : Lists all contacts. - * `addc n/John Doe p/98765432 e/johnd@example.com l/10.10.2023 1000 o/NUS` : Adds a contact named `John Doe` to OutBook. + - `addc n/John Doe p/98765432 e/johnd@example.com lc/10.10.2023 1000 o/NUS` : Adds a contact named `John Doe` to OutBook. - * `deletec 3` : Deletes the 3rd contact shown in the contact list. + - `deletec 3` : Deletes the 3rd contact shown in the contact list. - * `deletem 1` : Deletes the 1st meeting shown in the meeting list. + - `deletem 1` : Deletes the 1st meeting shown in the meeting list. - * `clear` : Deletes all contacts and meetings. + - `clear` : Deletes all contacts and meetings. + + - `exit` : Exits the app. - * `exit` : Exits the app. 6. Refer to the [Features](#features) below for details of each command. --------------------------------------------------------------------------------------------------------------------- +---
@@ -60,21 +60,21 @@ It is able to add, edit and delete any contacts and meetings you want. As well a **:information_source: Notes about the command format:**
* Words in `UPPER_CASE` are the parameters to be supplied by you.
- e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`. + e.g. in `addc n/NAME`, `NAME` is a parameter which can be used as `addc n/John Doe`. -* Items in square brackets are optional.
+- Items in square brackets are optional.
e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`. -* Items with `…`​ after them can be used multiple times including zero times.
+- 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. -* You can place parameters in any order.
+- You can place parameters in any order.
e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable. -* Any extraneous parameters you place for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
+* Any extraneous parameters you place for commands that do not take in parameters (such as `help`, `listc`, `exit` and `clear`) will be ignored.
e.g. if you type `help 123`, it will be interpreted as `help`. -* If you are using a PDF version of this document, be careful when copying and pasting commands that span multiple lines as space characters surrounding line-breaks may be omitted when copied over to the application. +- If you are using a PDF version of this document, be careful when copying and pasting commands that span multiple lines as space characters surrounding line-breaks may be omitted when copied over to the application. ### Viewing help : `help` @@ -93,82 +93,77 @@ Format: `help` Adds a contact to OutBook. -<<<<<<< HEAD -Format: `addc n/NAME p/PHONE_NUMBER e/EMAIL l/LAST_CONTACTED_TIME s/STATUS [r/REMARK][t/TAG]…​` -======= Format: `addc n/NAME p/PHONE_NUMBER e/EMAIL lc/LAST_CONTACTED_TIME [s/STATUS] [r/REMARK] [t/TAG]…​` ->>>>>>> master - -* NAME, PHONE_NUMBER, EMAIL and LAST_CONTACTED_TIME are compulsory fields. STATUS, REMARK and TAG are optional. -* PHONE_NUMBER must contain only numbers, and be at least 3 digits long. -* EMAIL must be of the format local-part@domain and adhere to the following constraints: - 1. The local-part should only contain alphanumeric characters and these special characters, excluding the parentheses, (+_.-). +* `NAME`, `PHONE_NUMBER`, `EMAIL` and `LAST_CONTACTED_TIME` are compulsory fields. `STATUS`, `REMARK` and `TAG` are optional. +* `PHONE_NUMBER` must contain only numbers, and be at least 3 digits long. +* `EMAIL` must be of the format local-part@domain and adhere to the following constraints: + 1. The local-part should only contain alphanumeric characters and the following special characters `+ _ . -`. 2. The local-part may not start or end with any special characters. 3. The domain name is made up of domain labels separated by periods. The domain name must: - - end with a domain label at least 2 characters long - - have each domain label start and end with alphanumeric characters - - have each domain label consist of alphanumeric characters, separated only by hyphen + - end with a domain label at least 2 characters long + - have each domain label start and end with alphanumeric characters + - have each domain label consist of alphanumeric characters, separated only by hyphen -* LAST_CONTACTED_TIME must contain both date and time and adhere to the dd.MM.yyyy HHmm format. - - For instance, 1st October 2023, 10:00am will be written as 01.10.2023 1000. -* STATUS, if included, must be one of { NIL, Prospective, Active, Inactive, Claimant, Renewal } or blank. +* `LAST_CONTACTED_TIME` must contain both date and time and adhere to the `DD.MM.YYYY HHMM` format. + - eg. 1st October 2023, 10:00am will be written as `01.10.2023 1000`. +* STATUS, if included, must be one of `NIL, Prospective, Active, Inactive, Claimant, Renewal` or blank.
:bulb: **Tip:** You can put any number of tags (including 0) on a contact.
-* `addc n/John Doe p/98765432 e/johnd@example.com lc/01.10.2023 1000` -* `addc n/Betsy Crowe t/friend e/betsycrowe@example.com p/1234567 lc/01.01.2023 0100 t/Professor` +- `addc n/John Doe p/98765432 e/johnd@example.com lc/01.10.2023 1000` +- `addc n/Betsy Crowe t/friend e/betsycrowe@example.com p/1234567 lc/01.01.2023 0100 t/Professor`

### Listing all persons : `listc` -<<<<<<< HEAD -Shows you a list of all contacts in OutBook. -======= Shows a list of all contacts in OutBook. Contacts are sorted by LAST_CONTACTED_TIME by default. ->>>>>>> master Format: `listc` - ### Deleting a person : `deletec` Deletes a contact from OutBook. Format: `deletec INDEX` -* Deletes the contact 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, …​ +- Deletes the contact at the specified `INDEX`. +- The index refers to the index number shown in the displayed person list. +- The index **must be a positive integer** 1, 2, 3, …​ Examples: -* `listc` followed by `delete 2` deletes the 2nd person in the results of the `listc` command. -* `findc Betsy` followed by `delete 1` deletes the 1st person in the results of the `findc` command. +- `listc` followed by `delete 2` deletes the 2nd person in the results of the `listc` command. +- `findc Betsy` followed by `delete 1` deletes the 1st person in the results of the `findc` command. ### Editing a contact : `editc` Edits an existing contact in OutBook. -Format: `editc INDEX [n/NAME] [p/PHONE] [e/EMAIL] [lc/LAST_CONTACTED_TIME] [s/STATUS] [s/REMARK] [t/TAG]…​` +Format: `editc INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [lc/LAST_CONTACTED_TIME] [s/STATUS] [r/REMARK] [t/TAG]…​` -* Edits the contact 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, …​ -* All fields are optional, but at least one 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. +- Edits the contact 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, …​ +- All fields are optional, but at least one 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. Examples: * `editc 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. * `editc 2 n/Betsy Crower t/` Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags. +
+ +- `editc 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. +- `editc 2 n/Betsy Crower t/` Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags. + ### Viewing detailed contact information : `viewc` Views detailed information of a contact in OutBook. @@ -180,72 +175,42 @@ Format: `viewc INDEX` * The index **must be a positive integer** 1, 2, 3, …​ * Displays contact Name, Phone, Email, Last Contacted Time, Status, Remarks and Tags. * The displayed contact is reset when the `deletec` and `findc` commands are used. +* The displayed contact may change when `editc` is used in a way that modifies the order of the displayed person list, such as by editing the `LAST_CONTACTED_DATE`. This is intentional as the `editc` command is meant to display information based on the contact list index. Examples: * `viewc 2` Displays detailed information related to the 2nd contact on the list. - -
- -### Editing a contact : `editc` - -Edits an existing contact in OutBook. - -Format: `editc INDEX [n/NAME] [p/PHONE] [e/EMAIL] [l/LAST_CONTACTED_TIME] [s/STATUS] [r/REMARK] [t/TAG]…​` - -* Edits the contact 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, …​ -* All fields are optional, but at least one 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. - -Examples: -* `editc 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. -* `editc 2 n/Betsy Crower t/` Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags. - - -### Remarking a contact : `remark` - -Adds a remark to a contact. - -Format: `remark INDEX r/REMARK` - -* Adds a remark to the contact specified with `INDEX`. The index refers to the index number shown in the displayed person list. The index**must be a positive integer** 1, 2, 3, …​ -* Existing remark will be replaced with `REMARK`. -* You can remove an existing remark by typing `r/`. - -*Examples: -* `remark 1 r/Owes me a favour` Replaces the previous remark for the 1st contact with "Owes me a favour". -



+ ### Search for persons using contact fields: `findc` -Find persons whose contact details match the keywords specified for at least 1 of these fields: name, phone, email, status, tag +Find persons whose contact details match the keywords specified for at least 1 of these fields: `NAME`, `PHONE_NUMBER`, `EMAIL`, `STATUS`, `TAG`. Format: `findc [n/KEYWORDS] [p/KEYWORDS] [e/KEYWORDS] [lc/DATETIME] [s/KEYWORDS] [t/KEYWORDS]` -* The search is case-insensitive. e.g `shop` will match `SHOP` -* The order of the keywords does not matter. e.g. `Shop Meet` will match `Meet Shop` -* For name, status and tags, only full words will be matched e.g. `Meet` will not match `Meeting` -* For email, any characters (alphanumeric, special characters) will be matched e.g. `_` will match `m_e@gmail.com` -* For phone, the entire length of the input digits will be matched e.g. `913` will match `90091300` but not `90103000` -* For last contacted time, the input must adhere to the dd.MM.yyyy HHmm format e.g. 9th October 2023 10.30am will be `09.10.2023 1030` -* For a single field, a Person must match at least one keyword to be returned as a result (i.e. `OR` search). - e.g. `John Doe` will return `John Lee`, `James Doe` -* If there are multiple fields specified, the Person must match at least one keyword in each field to be returned as a result (i.e. `AND` search). - e.g. `n/John Doe s/Active` will return `Name: John Lee, Status: Active` +* The search is case-insensitive. e.g `shop` will match `SHOP`. +* The order of the keywords does not matter. e.g. `Shop Meet` will match `Meet Shop`. +* For `NAME`, `STATUS` and `TAG`, only full words will be matched e.g. `Meet` will not match `Meeting`. +* For `EMAIL`, any characters (alphanumeric, special characters) will be matched e.g. `_` will match `m_e@gmail.com`. +* For `PHONE_NUMBER`, the entire length of the input digits will be matched e.g. `913` will match `90091300` but not `90103000`. +* For `LAST_CONTACTED_TIME`, the input must adhere to the `DD.MM.YYYY HHMM` format. + - e.g. 9th October 2023, 10.30am will be written as `09.10.2023 1030`. +* If only one field is provided, all Persons matching at least one keyword will be returned (i.e. `OR` search). + - e.g. `John Doe` will return `John Lee`, `James Doe`. +* If multiple fields are provided, only Persons matching at least one keyword in each field will be returned (i.e. `AND` search). + - e.g. `n/John Doe s/active` will return `Name: John Lee, Status: Active` but not `Name: James Doe, Status: Claimant`. Examples: -* `findc n/alice` returns `Alice` and `alice tan` -* `findc p/51` returns `95163890` and `40351` -* `findc e/_@GMAIL` returns `alice_@gmail.com` -* `findc p/9 s/inactive claimant t/friend` returns persons with a `9` in their phone number, whose status is either `inactive` or `claimant`, and has a `friend` tag - ![result for 'findContact'](images/findContactResult.png) +- `findc n/alice` returns `Alice` and `alice tan` +- `findc p/51` returns `95163890` and `40351` +- `findc e/_@GMAIL` returns `alice_@gmail.com` +- `findc p/9 s/inactive claimant t/friend` returns persons with a `9` in their phone number, whose status is either `inactive` or `claimant`, and has a `friend` tag + ![result for 'findContact'](images/findContactResult.png)
+ ## Meeting commands ### Adding a meeting: `addm` @@ -255,12 +220,13 @@ Meetings are sorted by start time given. Format: `addm m/TITLE a/LOCATION s/START e/END [t/TAG]…​` -* START and END must contain both date and time and adhere to the dd.MM.yyyy HHmm format. - - For instance, 1st October 2023, 10:00am will be written as 01.10.2023 1000. +* `START` and `END` must contain both date and time and adhere to the `DD.MM.YYYY HHMM` format. + - eg. 1st October 2023, 10:00am will be written as `01.10.2023 1000`. Examples: -* `addm m/Lunch a/Cafeteria s/20.09.2023 1200 e/20.09.2023 1300` -* `addm m/CS2103T meeting a/Zoom call url s/20.09.2023 1000 e/20.09.2023 1200` + +- `addm m/Lunch a/Cafeteria s/20.09.2023 1200 e/20.09.2023 1300` +- `addm m/CS2103T meeting a/Zoom call url s/20.09.2023 1000 e/20.09.2023 1200` ### Listing all meetings : `listm` @@ -268,21 +234,20 @@ Shows a list of all meetings in OutBook. Meetings are sorted by START by default Format: `listm` - ### Deleting a meeting : `deletem` Deletes a meeting from OutBook. Format: `deletem INDEX` -* Deletes the meeting at the specified `INDEX`. -* The index refers to the index number shown in the displayed meeting list. -* The index **must be a positive integer** 1, 2, 3, …​ +- Deletes the meeting at the specified `INDEX`. +- The index refers to the index number shown in the displayed meeting list. +- The index **must be a positive integer** 1, 2, 3, …​ Examples: -* `listm` followed by `deletem 2` deletes the 2nd meeting in the results of the `listm` command. -* `findm m/Project` followed by `deletem 1` deletes the 1st meeting in the results of the `findm` command. +- `listm` followed by `deletem 2` deletes the 2nd meeting in the results of the `listm` command. +- `findm m/Project` followed by `deletem 1` deletes the 1st meeting in the results of the `findm` command. ### Editing a meeting : `editm` @@ -290,17 +255,17 @@ Edits an existing meeting in OutBook. Format: `editm INDEX [m/TITLE] [a/LOCATION] [s/START] [e/END] [t/TAG]…​` -* Edits the meeting at the specified `INDEX`. The index refers to the index number shown in the displayed meeting 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 meeting will be removed i.e adding of tags is not cumulative. -* You can remove all the meeting’s tags by typing `t/` without +- Edits the meeting at the specified `INDEX`. The index refers to the index number shown in the displayed meeting 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 meeting will be removed i.e adding of tags is not cumulative. +- You can remove all the meeting’s tags by typing `t/` without specifying any tags after it. Examples: -* `editm 1 a/Hawker Centre s/15.09.2023 1500` Edits the location and start of the 1st meeting to be `Hawker Centre` and `15.09.2023 1500` respectively. -* `editm 2 m/Zoom meeting t/` Edits the title of the 2nd meeting to be `Zoom meeting` and clears all existing tags. +- `editm 1 a/Hawker Centre s/15.09.2023 1500` Edits the location and start of the 1st meeting to be `Hawker Centre` and `15.09.2023 1500` respectively. +- `editm 2 m/Zoom meeting t/` Edits the title of the 2nd meeting to be `Zoom meeting` and clears all existing tags. ### Viewing detailed meeting information : `viewm` @@ -313,33 +278,36 @@ Format: `viewm INDEX` * The index **must be a positive integer** 1, 2, 3, …​ * Displays meeting Title, Location, Start/End, Attendees and Tags. * The displayed meeting is reset when the `deletem` and `findm` commands are used. +* The displayed meeting may change when `editm` is used in a way that modifies the order of the displayed meeting list, such as by editing the `START`. This is intentional as the `editm` command is meant to display information based on the meeting list index. Examples: * `viewm 2` Displays detailed information related to the 2nd meeting on the list, including current attendees. +
+ ### Search for Meetings using meeting fields: `findm` -Find meetings which details match the keywords you specified for at least 1 of these fields: title, location, attendee, tag; and fall within the start and end time you give. +Find meetings with details matching the keywords you specified for at least 1 of these fields: `TITLE`, `LOCATION`, `ATTENDEE_NAME`, `TAG`; and falls within the `START` and `END` time you give. -Format: `findm [m/KEYWORDS] [a/KEYWORDS] [t/KEYWORDS] [n/ATTENDEENAME] [s/START e/END]` +Format: `findm [m/KEYWORDS] [a/KEYWORDS] [t/KEYWORDS] [n/ATTENDEE_NAME] [s/START e/END]` -* The search is case-insensitive. e.g `shop` will match `SHOP` -* The order of the keywords does not matter. e.g. `Shop Meet` will match `Meet Shop` -* Title, location, tags and attendees are searched, within the time frame given by START and END -* Only full words will be matched e.g. `Meet` will not match `Meeting` -* For a single field, a Meeting must match at least one keyword will be returned (i.e. `OR` search). - e.g. `Shop Meet` will return `Meeting: Shop at mall`, `Meeting: Meet client` -* If there are multiple fields the meeting must match at least one keyword for each field to be returned (i.e. `AND` search). - e.g. `m/Shop Meet a/Mall` will return `Meeting: Shop at mall, Location:Mall` +* The search is case-insensitive. e.g `shop` will match `SHOP`. +* The order of the keywords does not matter. e.g. `Shop Meet` will match `Meet Shop`. +* `TITLE`, `LOCATION`, `TAG` and `ATTENDEE_NAME` are searched, within the time frame given by `START` and `END`. +* Only full words will be matched e.g. `Meet` will not match `Meeting`. +* If only one field is provided, all Meetings matching at least one keyword will be returned (i.e. `OR` search). + e.g. `m/Shop Meet` will return `Meeting: Shop at mall`, `Meeting: Meet client`. +* If multiple fields are provided, only Meetings matching at least one keyword in each field will be returned (i.e. `AND` search). + e.g. `m/Shop Meet a/Mall` will return `Meeting: Shop at mall, Location: Mall` but not `Meeting: Meet client, Location: Park`. Examples: * `findm m/project` returns `project` and `Project work` * `findm m/zoom meeting` returns `Zoom session`, `Zoom meeting`, `Meeting advisor` * `findm s/09.09.2023 0000 e/09.10.2023 0000` returns all meetings between 09.09.2023 0000 and 09.10.2023 0000
* `findm m/Meeting s/09.09.2023 0000 e/09.10.2023 0000` returns `Zoom meeting`, `Meeting advisor`, if these meetings start after 09.09.2023 0000 and end before 09.10.2023 0000 - +
![result for 'findAlexMeeting'](images/findAlexMeetingResult.png) @@ -352,7 +320,7 @@ Adds a contact to a meeting as an attendee. Format: `addmc MEETING_INDEX CONTACT_INDEX` -* Adds the contact you specified with `CONTACT_INDEX` to a meeting specified with `MEETING_INDEX`. +* Adds the contact you specified with `CONTACT_INDEX` to a meeting specified with `MEETING_INDEX`. * `MEETING_INDEX` refers to the index number shown in the displayed meeting list. * `CONTACT_INDEX` refers to the index number shown in the displayed contact list. * The indexes **must be positive integers** 1, 2, 3, …​ @@ -360,7 +328,8 @@ Format: `addmc MEETING_INDEX CONTACT_INDEX` * Contact name will be listed in the detailed description of meetings when `viewm` is used. Examples: -* `addmc 3 1` adds the 1st contact as an attendee to the 3rd meeting in OutBook. + +- `addmc 3 1` adds the 1st contact as an attendee to the 3rd meeting in OutBook. ### Remove contact from meeting: `rmmc` @@ -368,16 +337,29 @@ Removes a contact from a meeting. Format: `rmmc MEETING_INDEX ATTENDEE_INDEX` -* Removes a contact at the specified `ATTENDEE_INDEX` to the meeting at the specified `MEETING_INDEX`. -* `MEETING_INDEX` refers to the index number shown in the displayed meeting list. -* `ATTENDEE_INDEX` refers to the index number of the attendee as shown in `viewm`. -* The indexes **must be positive integers** 1, 2, 3, …​ -* Both `MEETING_INDEX` & `ATTENDEE_INDEX` must refer to the index of an existing meeting or attendee. +- Removes a contact at the specified `ATTENDEE_INDEX` to the meeting at the specified `MEETING_INDEX`. +- `MEETING_INDEX` refers to the index number shown in the displayed meeting list. +- `ATTENDEE_INDEX` refers to the index number of the attendee as shown in `viewm`. +- The indexes **must be positive integers** 1, 2, 3, …​ +- Both `MEETING_INDEX` & `ATTENDEE_INDEX` must refer to the index of an existing meeting or attendee. Examples: -* `rmmc 3 2` removes the 2nd attendee from the 3rd meeting in OutBook. -



+- `rmmc 3 2` removes the 2nd attendee from the 3rd meeting in OutBook. + +### Marking a meeting as complete : `mark` + +Marks a meeting in OutBook as complete. All attendees of the meeting will have their LC (last contacted) field updated to the end time of the meeting. + +Format: `mark INDEX` + +- Marks the meeting at the specified `INDEX` as complete. +- The index refers to the index number shown in the displayed meeting list. +- The index **must be a positive integer** 1, 2, 3, …​ + +Examples: + +- `listm` followed by `mark 2` marks the 2nd meeting in the results of the `listm` command. ### Clearing all entries : `clear` @@ -403,8 +385,8 @@ OutBook data are saved automatically as a JSON file `[JAR file location]/data/ou If your changes to the data file makes its format invalid, OutBook will discard all data and start with an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it. +--- ---------------------------------------------------------------------------------------------------------------------
## FAQ @@ -412,33 +394,34 @@ If your changes to the data file makes its format invalid, OutBook will discard **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 OutBook home folder. --------------------------------------------------------------------------------------------------------------------- +--- ## Known issues 1. **When using multiple screens**, if you move the application to a secondary screen, and later switch to using only the primary screen, the GUI will open off-screen. The remedy is to delete the `preferences.json` file created by the application before running the application again. --------------------------------------------------------------------------------------------------------------------- +--- +
## Command summary -| Action | Format, Examples | -|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **Add contact** | `addc n/NAME p/PHONE_NUMBER e/EMAIL l/LAST_CONTACTED_TIME o/ORGANISATION [r/REMARK] [t/TAG]…​`
e.g., `add n/James Ho p/22224444 e/jamesho@example.com l/09.09.2023 0000 o/NUS t/friend t/colleague` | -| **Add contact to meeting** | `addmc MEETING_INDEX CONTACT_INDEX`
e.g., `addmc 2 1` | -| **Add meeting** | `addm m/TITLE a/LOCATION s/START e/END [t/TAG]…​`
e.g., `addm m/Lunch a/Cafeteria s/20.09.2023 1200 e/20.09.2023 1300` | -| **Clear** | `clear` | -| **Delete contact** | `deletec INDEX`
e.g., `deletec 3` | -| **Delete meeting** | `deletem INDEX`
e.g., `deletem 3` | -| **Edit contact** | `editc INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [l/LAST_CONTACTED_TIME] [o/ORGANISATION] [r/REMARK] [t/TAG]…​`
e.g.,`editc 2 n/James Lee e/jameslee@example.com` | -| **Edit meeting** | `editm INDEX [m/TITLE] [a/LOCATION] [s/START] [e/END] [t/TAG]…​`
e.g.,`editm 1 a/Hawker Centre s/15.09.2023 1500` | -| **Find contact** | `findc KEYWORD [MORE_KEYWORDS]`
e.g., `findc James Jake` | -| **Find meeting** | `findm [m/KEYWORDS] [a/KEYWORDS] [t/KEYWORDS] [n/ATTENDEENAME] [s/START e/END]`
e.g., `findm m/Zoom Meet s/09.09.2023 0000 e/09.10.2023 0000` | -| **Help** | `help` | -| **List contacts** | `listc` | -| **List meetings** | `listm` | -| **Remove contact from meeting** | `rmmc MEETING_INDEX ATTENDEE_INDEX`
e.g., `rmmc 2 2` | -| **View contact details** | `viewc INDEX`
e.g., `viewc 4` | -| **View meeting details** | `viewm INDEX`
e.g., `viewm 4` | -| **Exit** | `exit` | +| Action | Format, Examples | +|---------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Add contact** | `addc n/NAME p/PHONE_NUMBER e/EMAIL lc/LAST_CONTACTED_TIME [r/REMARK] [t/TAG]…​`
e.g., `add n/James Ho p/22224444 e/jamesho@example.com lc/09.09.2023 0000 o/NUS t/friend t/colleague` | +| **Add contact to meeting** | `addmc MEETING_INDEX CONTACT_INDEX`
e.g., `addmc 2 1` | +| **Add meeting** | `addm m/TITLE a/LOCATION s/START e/END [t/TAG]…​`
e.g., `addm m/Lunch a/Cafeteria s/20.09.2023 1200 e/20.09.2023 1300` | +| **Clear** | `clear` | +| **Delete contact** | `deletec INDEX`
e.g., `deletec 3` | +| **Delete meeting** | `deletem INDEX`
e.g., `deletem 3` | +| **Edit contact** | `editc INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [lc/LAST_CONTACTED_TIME] [r/REMARK] [t/TAG]…​`
e.g.,`editc 2 n/James Lee e/jameslee@example.com` | +| **Edit meeting** | `editm INDEX [m/TITLE] [a/LOCATION] [s/START] [e/END] [t/TAG]…​`
e.g.,`editm 1 a/Hawker Centre s/15.09.2023 1500` | +| **Find contact** | `findc [n/KEYWORDS] [p/KEYWORDS] [e/KEYWORDS] [lc/LAST_CONTACTED_TIME] [s/KEYWORDS] [t/KEYWORDS]`
e.g., `findc n/James Jake s/active claimant` | +| **Find meeting** | `findm [m/KEYWORDS] [a/KEYWORDS] [t/KEYWORDS] [n/ATTENDEE_NAME] [s/START e/END]`
e.g., `findm m/Zoom Meet s/09.09.2023 0000 e/09.10.2023 0000` | +| **Help** | `help` | +| **List contacts** | `listc` | +| **List meetings** | `listm` | +| **Remove contact from meeting** | `rmmc MEETING_INDEX ATTENDEE_INDEX`
e.g., `rmmc 2 2` | +| **View contact details** | `viewc INDEX`
e.g., `viewc 4` | +| **View meeting details** | `viewm INDEX`
e.g., `viewm 4` | +| **Exit** | `exit` | diff --git a/docs/diagrams/edit/CreateEditMeetingCommand.puml b/docs/diagrams/edit/CreateEditMeetingCommand.puml new file mode 100644 index 00000000000..b54f366f2b4 --- /dev/null +++ b/docs/diagrams/edit/CreateEditMeetingCommand.puml @@ -0,0 +1,26 @@ +@startuml +!include ../style.puml +skinparam ArrowFontStyle plain + +title Create EditMeetingCommand + +Participant ":LogicManager" as logic LOGIC_COLOR +Participant ":AddressBookParser" as abp LOGIC_COLOR +Participant ":EditMeetingCommandParser" as emcp LOGIC_COLOR +Participant "editMeetingDescriptor:EditMeetingDescriptor" as emd LOGIC_COLOR +Participant "command:EditMeetingCommand" as emc LOGIC_COLOR + +activate logic +logic -> abp ++: parseCommand(commandText) +create emcp +abp -> emcp ++: parse(arguments) +create emd +emcp -> emd ++: EditMeetingDescriptor() +emd --> emcp --: +create emc +emcp -> emc ++: EditMeetingCommand(targetIndex, editMeetingDescriptor) +emc --> emcp --: command +emcp --> abp --: command +abp --> logic --: command + +@enduml diff --git a/docs/diagrams/edit/EditMeetingClassDiagram.puml b/docs/diagrams/edit/EditMeetingClassDiagram.puml new file mode 100644 index 00000000000..8eef408cedd --- /dev/null +++ b/docs/diagrams/edit/EditMeetingClassDiagram.puml @@ -0,0 +1,33 @@ +@startuml +!include ../style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor LOGIC_COLOR_T4 +skinparam classBackgroundColor LOGIC_COLOR + +package Parser as ParserPackage { +Class "<>\nParser" as Parser +Class EditMeetingCommandParser +} + +package Command as CommandPackage { +Class EditMeetingCommand +Class EditMeetingDescriptor +Class "{abstract}\nCommand" as Command +} + +Class Index +Class AddressBookParser +Class LogicManager + +AddressBookParser .down.> EditMeetingCommandParser : creates > +EditMeetingCommandParser ..> EditMeetingCommand : creates > +EditMeetingCommand -down->"1" EditMeetingDescriptor +EditMeetingCommand -down->"1" Index +EditMeetingCommand -left-|> Command +EditMeetingCommandParser .down.|> Parser +EditMeetingCommandParser .left.> EditMeetingDescriptor : creates > +EditMeetingCommandParser .down.> Index : creates > +LogicManager -right->"1" AddressBookParser +LogicManager .down.> EditMeetingCommand : executes > + +@enduml diff --git a/docs/diagrams/edit/EditMeetingSequenceDiagram.puml b/docs/diagrams/edit/EditMeetingSequenceDiagram.puml new file mode 100644 index 00000000000..b5b28f9e3ef --- /dev/null +++ b/docs/diagrams/edit/EditMeetingSequenceDiagram.puml @@ -0,0 +1,36 @@ +@startuml +!include ../style.puml +skinparam ArrowFontStyle plain + +title Execute EditMeetingCommand + +box Logic LOGIC_COLOR_T1 +Participant ":LogicManager" as logic LOGIC_COLOR +Participant "command:EditMeetingCommand" as emc LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +Participant ":ModelManager" as model MODEL_COLOR +Participant "editedMeeting:Meeting" as meet MODEL_COLOR +end box + +[->logic : execute +activate logic +ref over logic, emc: Create EditMeetingCommand +logic -> emc ++: execute(model) +emc -> model ++: getFilteredMeetingList +model --> emc --: +emc -> emc ++: createEditedMeeting +create meet +emc -> meet ++: Meeting() +meet --> emc --: +emc --> emc --: +emc -> model ++: setMeeting +model --> emc --: +emc -> model ++:updateFilteredMeetingList +model --> emc --: +emc --> logic --: CommandResult +[<--logic +deactivate logic + +@enduml diff --git a/docs/diagrams/tracing/ViewContactCommandSequenceDiagram.puml b/docs/diagrams/view/CreateViewContactCommand.puml similarity index 81% rename from docs/diagrams/tracing/ViewContactCommandSequenceDiagram.puml rename to docs/diagrams/view/CreateViewContactCommand.puml index 503f59e25a8..5965e7773f1 100644 --- a/docs/diagrams/tracing/ViewContactCommandSequenceDiagram.puml +++ b/docs/diagrams/view/CreateViewContactCommand.puml @@ -2,7 +2,7 @@ !include ../style.puml skinparam ArrowFontStyle plain -title View Contact Command Sequence +title Create ViewContactCommand Participant ":LogicManager" as logic LOGIC_COLOR Participant ":AddressBookParser" as abp LOGIC_COLOR @@ -13,11 +13,10 @@ Participant "command:ViewContactCommand" as ec LOGIC_COLOR activate logic logic -> abp ++: parseCommand(commandText) create vccp -abp -> vccp abp -> vccp ++: parse(arguments) create ec -vccp -> ec ++: index -ec --> vccp -- +vccp -> ec ++: ViewContactCommand(targetIndex) +ec --> vccp --: command vccp --> abp --: command abp --> logic --: command diff --git a/docs/diagrams/tracing/UiViewItemsSequenceDiagram.puml b/docs/diagrams/view/UiViewItemsSequenceDiagram.puml similarity index 100% rename from docs/diagrams/tracing/UiViewItemsSequenceDiagram.puml rename to docs/diagrams/view/UiViewItemsSequenceDiagram.puml diff --git a/docs/diagrams/tracing/ViewCommandsSequenceDiagram.puml b/docs/diagrams/view/ViewCommandsSequenceDiagram.puml similarity index 77% rename from docs/diagrams/tracing/ViewCommandsSequenceDiagram.puml rename to docs/diagrams/view/ViewCommandsSequenceDiagram.puml index 0af839bb727..b3e1a94b255 100644 --- a/docs/diagrams/tracing/ViewCommandsSequenceDiagram.puml +++ b/docs/diagrams/view/ViewCommandsSequenceDiagram.puml @@ -2,11 +2,10 @@ !include ../style.puml skinparam ArrowFontStyle plain -title Store viewed Items to Model +title Store Viewed Items to Model box Logic LOGIC_COLOR_T1 Participant ":LogicManager" as logic LOGIC_COLOR -Participant ":AddressBookParser" as abp LOGIC_COLOR Participant "command:ViewContactCommand" as vcc LOGIC_COLOR end box @@ -16,7 +15,7 @@ end box [->logic : execute activate logic -ref over logic, abp, vcc: View Contact Command Sequence +ref over logic, vcc: Create ViewContactCommand logic -> vcc ++: execute(model) vcc -> model ++: setViewedPersonIndex(targetIndex) model --> vcc --: diff --git a/docs/images/edit/CreateEditMeetingCommand-Create_EditMeetingCommand.png b/docs/images/edit/CreateEditMeetingCommand-Create_EditMeetingCommand.png new file mode 100644 index 00000000000..d316e441481 Binary files /dev/null and b/docs/images/edit/CreateEditMeetingCommand-Create_EditMeetingCommand.png differ diff --git a/docs/images/edit/EditMeetingClassDiagram.png b/docs/images/edit/EditMeetingClassDiagram.png new file mode 100644 index 00000000000..4cd6bd71403 Binary files /dev/null and b/docs/images/edit/EditMeetingClassDiagram.png differ diff --git a/docs/images/edit/EditMeetingSequenceDiagram-Execute_EditMeetingCommand.png b/docs/images/edit/EditMeetingSequenceDiagram-Execute_EditMeetingCommand.png new file mode 100644 index 00000000000..e398300911b Binary files /dev/null and b/docs/images/edit/EditMeetingSequenceDiagram-Execute_EditMeetingCommand.png differ diff --git a/docs/images/tracing/ViewCommandsSequenceDiagram-StoreViewedItemsToModel.png b/docs/images/tracing/ViewCommandsSequenceDiagram-StoreViewedItemsToModel.png deleted file mode 100644 index 4f8906bca86..00000000000 Binary files a/docs/images/tracing/ViewCommandsSequenceDiagram-StoreViewedItemsToModel.png and /dev/null differ diff --git a/docs/images/tracing/ViewContactCommandSequenceDiagram-ViewContactCommandSequence.png b/docs/images/tracing/ViewContactCommandSequenceDiagram-ViewContactCommandSequence.png deleted file mode 100644 index fcb1ee82692..00000000000 Binary files a/docs/images/tracing/ViewContactCommandSequenceDiagram-ViewContactCommandSequence.png and /dev/null differ diff --git a/docs/images/view/CreateViewContactCommand-Create_ViewContactCommand.png b/docs/images/view/CreateViewContactCommand-Create_ViewContactCommand.png new file mode 100644 index 00000000000..07674e73baf Binary files /dev/null and b/docs/images/view/CreateViewContactCommand-Create_ViewContactCommand.png differ diff --git a/docs/images/tracing/UiViewItemsSequenceDiagram-ForwardViewedPerson&MeetingToUi.png b/docs/images/view/UiViewItemsSequenceDiagram-ForwardViewedPerson&MeetingToUi.png similarity index 100% rename from docs/images/tracing/UiViewItemsSequenceDiagram-ForwardViewedPerson&MeetingToUi.png rename to docs/images/view/UiViewItemsSequenceDiagram-ForwardViewedPerson&MeetingToUi.png diff --git a/docs/images/view/ViewCommandsSequenceDiagram-StoreViewedItemsToModel.png b/docs/images/view/ViewCommandsSequenceDiagram-StoreViewedItemsToModel.png new file mode 100644 index 00000000000..a74ffd87ba4 Binary files /dev/null and b/docs/images/view/ViewCommandsSequenceDiagram-StoreViewedItemsToModel.png differ diff --git a/docs/team/jason-raiin.md b/docs/team/jason-raiin.md index 6040360fbf8..d1df38938a7 100644 --- a/docs/team/jason-raiin.md +++ b/docs/team/jason-raiin.md @@ -12,36 +12,40 @@ My contributions to the project are listed below. - **New Feature**: Remove contact from meeting command - Added command and parser - - Thorough testing +- **New Feature**: Mark meeting command + - Added command and parser + - Updates last contacted time for contacts - **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=jason-raiin&breakdown=true) - **Project management**: - - - to be added soon + - Contributed issues + - Reviewed PRs - **Enhancements to existing features**: - - - Add Tag to meetings: logic and UI + - Add Tags field to meetings - Convert Tag to factory class with no duplicates + - Added Status field to meetings - **Documentation**: - User Guide - - Remove meeting contact command + - `rmmc` command + - `mark` command - Minor edits - Developer Guide + - `rmmc` command + - `mark` command - User profile - Value proposition - User stories - Use cases - **Community**: - - to be added soon - **Tools**: - - Added util method `parseIndexes` + - Improved methods for `typicalMeetings` and `typicalAddressBook` diff --git a/docs/team/lomaply.md b/docs/team/lomaply.md index 4b2c3aeba4f..75a5068bdd2 100644 --- a/docs/team/lomaply.md +++ b/docs/team/lomaply.md @@ -26,7 +26,7 @@ My contributions to the project are listed below. - Review and merge pull requests - **Enhancements to existing features**: - + - Sort contacts by last contacted time - New Command - "editm" @@ -39,7 +39,7 @@ My contributions to the project are listed below. - First draft of User Guide with initial plans for v1.2 - Update parts of User Guide on `viewm`, `viewc`, `editm` - Developer Guide - - Add Implementation notes on `viewm` & `viewc` + - Add Implementation notes on `viewm`, `viewc`, `editc` and `editm` - **Community**: diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java index 8a181503c7a..fa867f6e1d4 100644 --- a/src/main/java/seedu/address/logic/Messages.java +++ b/src/main/java/seedu/address/logic/Messages.java @@ -71,6 +71,8 @@ public static String format(Meeting meeting) { .append(meeting.getEnd()) .append("; Attendees: "); meeting.getAttendees().forEach(builder::append); + builder.append("; Completed: ") + .append(meeting.getStatus()); return builder.toString(); } diff --git a/src/main/java/seedu/address/logic/commands/AddMeetingContactCommand.java b/src/main/java/seedu/address/logic/commands/AddMeetingContactCommand.java index b348a8fa54e..90956474d43 100644 --- a/src/main/java/seedu/address/logic/commands/AddMeetingContactCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddMeetingContactCommand.java @@ -79,7 +79,7 @@ static Meeting addAttendee(Meeting meeting, Attendee attendeeToAdd) { Set updatedAttendees = new LinkedHashSet<>(meeting.getAttendees()); updatedAttendees.add(attendeeToAdd); Meeting updatedMeeting = new Meeting(meeting.getTitle(), meeting.getLocation(), meeting.getStart(), - meeting.getEnd(), updatedAttendees, meeting.getTags()); + meeting.getEnd(), updatedAttendees, meeting.getTags(), meeting.getStatus()); return updatedMeeting; } diff --git a/src/main/java/seedu/address/logic/commands/EditMeetingCommand.java b/src/main/java/seedu/address/logic/commands/EditMeetingCommand.java index 0dc569cad03..3cc3e9aa1f4 100644 --- a/src/main/java/seedu/address/logic/commands/EditMeetingCommand.java +++ b/src/main/java/seedu/address/logic/commands/EditMeetingCommand.java @@ -25,6 +25,7 @@ import seedu.address.model.meeting.Attendee; import seedu.address.model.meeting.Location; import seedu.address.model.meeting.Meeting; +import seedu.address.model.meeting.MeetingStatus; import seedu.address.model.meeting.MeetingTime; import seedu.address.model.meeting.Title; import seedu.address.model.tag.Tag; @@ -102,12 +103,13 @@ static Meeting createEditedMeeting(Meeting meetingToEdit, LocalDateTime updatedEnd = editMeetingDescriptor.getEnd().orElse(meetingToEdit.getEnd()); Set attendees = meetingToEdit.getAttendees(); Set updatedTags = editMeetingDescriptor.getTags().orElse(meetingToEdit.getTags()); + MeetingStatus status = meetingToEdit.getStatus(); if (!MeetingTime.isValidMeetingTime(updatedStart, updatedEnd)) { throw new CommandException(MeetingTime.MESSAGE_CONSTRAINTS); } - return new Meeting(updatedTitle, updatedLocation, updatedStart, updatedEnd, attendees, updatedTags); + return new Meeting(updatedTitle, updatedLocation, updatedStart, updatedEnd, attendees, updatedTags, status); } @Override diff --git a/src/main/java/seedu/address/logic/commands/FindMeetingCommand.java b/src/main/java/seedu/address/logic/commands/FindMeetingCommand.java index a7f06bef8d1..ae87b33ca79 100644 --- a/src/main/java/seedu/address/logic/commands/FindMeetingCommand.java +++ b/src/main/java/seedu/address/logic/commands/FindMeetingCommand.java @@ -34,7 +34,7 @@ public class FindMeetingCommand extends Command { * @param predicate The predicate that will be used by the FindMeetingCommand object. */ public FindMeetingCommand(GeneralMeetingPredicate predicate) { - assert predicate != null; + requireNonNull(predicate); this.predicate = predicate; } diff --git a/src/main/java/seedu/address/logic/commands/MarkMeetingCommand.java b/src/main/java/seedu/address/logic/commands/MarkMeetingCommand.java new file mode 100644 index 00000000000..139af978911 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/MarkMeetingCommand.java @@ -0,0 +1,105 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.time.LocalDateTime; +import java.util.Iterator; +import java.util.List; + +import seedu.address.commons.core.index.Index; +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.meeting.Attendee; +import seedu.address.model.meeting.Meeting; +import seedu.address.model.meeting.MeetingStatus; +import seedu.address.model.person.Person; + +/** + * Marks a meeting as complete. + */ +public class MarkMeetingCommand extends Command { + + public static final String COMMAND_WORD = "mark"; + + public static final String MESSAGE_MARK_MEETING_SUCCESS = "Meeting marked as complete: %1$s"; + + public static final String MESSAGE_MEETING_ALREADY_COMPLETE = "Meeting has already been marked as complete."; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Marks the meeting identified by the index number used in the displayed meetings list as complete.\n" + + "Parameters: INDEX (must be a positive integer)\n" + "Example: " + COMMAND_WORD + "1"; + + private final Index targetIndex; + + public MarkMeetingCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredMeetingList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_MEETING_DISPLAYED_INDEX); + } + + Meeting meetingToMark = lastShownList.get(targetIndex.getZeroBased()); + Meeting updatedMeeting = markMeeting(meetingToMark); + model.setMeeting(meetingToMark, updatedMeeting); + + Iterator attendeeIterator = meetingToMark.getAttendees().iterator(); + while (attendeeIterator.hasNext()) { + Attendee attendee = attendeeIterator.next(); + Person person = model.getPerson(attendee.getAttendeeName()); + Person updatedPerson = updateLastContactedTime(person, meetingToMark.getEnd()); + model.setPerson(person, updatedPerson); + } + + return new CommandResult(String.format(MESSAGE_MARK_MEETING_SUCCESS, Messages.format(updatedMeeting))); + } + + static Meeting markMeeting(Meeting meeting) throws CommandException { + if (meeting.getStatus().isComplete) { + throw new CommandException(MESSAGE_MEETING_ALREADY_COMPLETE); + } + + Meeting markedMeeting = new Meeting(meeting.getTitle(), meeting.getLocation(), meeting.getStart(), + meeting.getEnd(), meeting.getAttendees(), meeting.getTags(), new MeetingStatus(true)); + + return markedMeeting; + } + + static Person updateLastContactedTime(Person person, LocalDateTime lastContactedTime) { + if (lastContactedTime.isBefore(person.getLastContactedTime())) { + return person; + } + + Person updatedPerson = new Person(person.getName(), person.getPhone(), person.getEmail(), lastContactedTime, + person.getStatus(), person.getRemark(), person.getTags()); + + return updatedPerson; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof MarkMeetingCommand)) { + return false; + } + + MarkMeetingCommand otherMarkMeetingCommand = (MarkMeetingCommand) other; + return targetIndex.equals(otherMarkMeetingCommand.targetIndex); + } + + @Override + public String toString() { + return new ToStringBuilder(this).add("targetIndex", targetIndex).toString(); + } +} diff --git a/src/main/java/seedu/address/logic/commands/RemoveMeetingContactCommand.java b/src/main/java/seedu/address/logic/commands/RemoveMeetingContactCommand.java index 2b12de4252e..e0f0c163370 100644 --- a/src/main/java/seedu/address/logic/commands/RemoveMeetingContactCommand.java +++ b/src/main/java/seedu/address/logic/commands/RemoveMeetingContactCommand.java @@ -68,7 +68,7 @@ static Meeting removeAttendee(Meeting meeting, Attendee attendeeToRemove) { Set updatedAttendees = new LinkedHashSet<>(meeting.getAttendees()); updatedAttendees.remove(attendeeToRemove); Meeting updatedMeeting = new Meeting(meeting.getTitle(), meeting.getLocation(), meeting.getStart(), - meeting.getEnd(), updatedAttendees, meeting.getTags()); + meeting.getEnd(), updatedAttendees, meeting.getTags(), meeting.getStatus()); return updatedMeeting; } diff --git a/src/main/java/seedu/address/logic/parser/AddMeetingCommandParser.java b/src/main/java/seedu/address/logic/parser/AddMeetingCommandParser.java index c8b4d780313..8c01d89c298 100644 --- a/src/main/java/seedu/address/logic/parser/AddMeetingCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/AddMeetingCommandParser.java @@ -20,6 +20,7 @@ import seedu.address.model.meeting.Attendee; import seedu.address.model.meeting.Location; import seedu.address.model.meeting.Meeting; +import seedu.address.model.meeting.MeetingStatus; import seedu.address.model.meeting.MeetingTime; import seedu.address.model.meeting.Title; import seedu.address.model.tag.Tag; @@ -56,8 +57,9 @@ public AddMeetingCommand parse(String args) throws ParseException { } Set attendeeList = ParserUtil.parseAttendees(argMultimap.getAllValues(PREFIX_NAME)); Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); + MeetingStatus status = new MeetingStatus(false); - Meeting meeting = new Meeting(title, location, start, end, attendeeList, tagList); + Meeting meeting = new Meeting(title, location, start, end, attendeeList, tagList, status); return new AddMeetingCommand(meeting); } diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java index e437598624d..918ae644c32 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java @@ -23,6 +23,7 @@ import seedu.address.logic.commands.HelpCommand; import seedu.address.logic.commands.ListCommand; import seedu.address.logic.commands.ListMeetingCommand; +import seedu.address.logic.commands.MarkMeetingCommand; import seedu.address.logic.commands.RemoveMeetingContactCommand; import seedu.address.logic.commands.ViewContactCommand; import seedu.address.logic.commands.ViewMeetingCommand; @@ -113,6 +114,9 @@ public Command parseCommand(String userInput) throws ParseException { case FindMeetingCommand.COMMAND_WORD: return new FindMeetingCommandParser().parse(arguments); + case MarkMeetingCommand.COMMAND_WORD: + return new MarkMeetingCommandParser().parse(arguments); + default: logger.finer("This user input caused a ParseException: " + userInput); throw new ParseException(MESSAGE_UNKNOWN_COMMAND); diff --git a/src/main/java/seedu/address/logic/parser/MarkMeetingCommandParser.java b/src/main/java/seedu/address/logic/parser/MarkMeetingCommandParser.java new file mode 100644 index 00000000000..07deae8d5be --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/MarkMeetingCommandParser.java @@ -0,0 +1,29 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.MarkMeetingCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new MarkMeetingCommand object + */ +public class MarkMeetingCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the MarkMeetingCommand + * and returns a MarkMeetingCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public MarkMeetingCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new MarkMeetingCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, MarkMeetingCommand.MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java index 90be75d873a..d4eb0bc493d 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/address/model/AddressBook.java @@ -109,6 +109,14 @@ public void addMeeting(Meeting m) { meetings.add(m); } + /** + * Returns the person with the given name + * or throws {@code IndexOutOfBoundsException} if it does not exist. + */ + public Person getPerson(String name) { + return persons.getPerson(name); + } + /** * 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/address/model/Model.java index 8d7ddd20905..53de634126b 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -90,6 +90,12 @@ public interface Model { */ void addMeeting(Meeting meeting); + /** + * Returns the person with the given name. + * A person with the given name must exist in the address book. + */ + Person getPerson(String name); + /** * Replaces the given person {@code target} with {@code editedPerson}. * {@code target} must exist in the address book. diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 09a34df0081..a86ed002641 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -136,6 +136,11 @@ public void addMeeting(Meeting meeting) { updateFilteredMeetingList(PREDICATE_SHOW_ALL_MEETINGS); } + @Override + public Person getPerson(String name) { + return addressBook.getPerson(name); + } + @Override public void setPerson(Person target, Person editedPerson) { requireAllNonNull(target, editedPerson); diff --git a/src/main/java/seedu/address/model/meeting/Meeting.java b/src/main/java/seedu/address/model/meeting/Meeting.java index c6bbcbd007c..b57d98baafc 100644 --- a/src/main/java/seedu/address/model/meeting/Meeting.java +++ b/src/main/java/seedu/address/model/meeting/Meeting.java @@ -24,17 +24,19 @@ public class Meeting { private final MeetingTime meetingTime; private final Set attendees; private final Set tags; + private final MeetingStatus status; /** * Every field must be present and not null. */ public Meeting(Title title, Location location, LocalDateTime start, LocalDateTime end, Set attendees, - Set tags) { + Set tags, MeetingStatus status) { this.title = title; this.location = location; this.meetingTime = new MeetingTime(start, end); this.attendees = new LinkedHashSet<>(attendees); this.tags = new HashSet<>(tags); + this.status = status; } public Title getTitle() { @@ -57,6 +59,10 @@ public MeetingTime getMeetingTime() { return meetingTime; } + public MeetingStatus getStatus() { + return status; + } + public boolean withinSpecifiedTime(LocalDateTime start, LocalDateTime end) { return !meetingTime.getStart().isBefore(start) && !meetingTime.getEnd().isAfter(end); } @@ -135,7 +141,7 @@ public boolean equals(Object other) { Meeting otherMeeting = (Meeting) other; return title.equals(otherMeeting.title) && location.equals(otherMeeting.location) && meetingTime.equals(otherMeeting.meetingTime) && attendees.equals(otherMeeting.attendees) - && tags.equals(otherMeeting.tags); + && tags.equals(otherMeeting.tags) && status.equals(otherMeeting.status); } @Override @@ -148,7 +154,7 @@ public int hashCode() { public String toString() { return new ToStringBuilder(this).add("title", title).add("location", location) .add("start", meetingTime.getStart()).add("end", meetingTime.getEnd()).add("attendees", attendees) - .add("tags", tags).toString(); + .add("tags", tags).add("status", status).toString(); } /** diff --git a/src/main/java/seedu/address/model/meeting/MeetingStatus.java b/src/main/java/seedu/address/model/meeting/MeetingStatus.java new file mode 100644 index 00000000000..011ecaad3e6 --- /dev/null +++ b/src/main/java/seedu/address/model/meeting/MeetingStatus.java @@ -0,0 +1,44 @@ +package seedu.address.model.meeting; + +/** + * Contains a Boolean representing whether a meeting is completed. + */ +public class MeetingStatus { + + public static final String MESSAGE_CONSTRAINTS = "Status must be exactly 'true' or 'false'"; + public final Boolean isComplete; + + /** + * Constructs a {@code Status} field representing whether a meeting is complete. + */ + public MeetingStatus(Boolean isComplete) { + this.isComplete = isComplete; + } + + @Override + public int hashCode() { + return isComplete.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof MeetingStatus)) { + return false; + } + + MeetingStatus completed = (MeetingStatus) other; + return isComplete.equals(completed.isComplete); + } + + /** + * Format state as text for viewing. + */ + @Override + public String toString() { + return isComplete.toString(); + } +} diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java index 1bf241c0c32..22536919254 100644 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ b/src/main/java/seedu/address/model/person/UniquePersonList.java @@ -66,6 +66,21 @@ public void add(Person toAdd) { FXCollections.sort(internalList, sortByLastContactComparator); } + /** + * Returns the person with the given name + * or throws {@code IndexOutOfBoundsException} if it does not exist. + */ + public Person getPerson(String fullName) { + Name name = new Name(fullName); + List filteredList = internalList.filtered(person -> person.getName().equals(name)); + + if (filteredList.size() == 0) { + throw new PersonNotFoundException(); + } + + return filteredList.get(0); + } + /** * 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/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java index 9096dcd8915..5d4bcb4937b 100644 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java @@ -13,6 +13,7 @@ import seedu.address.model.meeting.Attendee; import seedu.address.model.meeting.Location; import seedu.address.model.meeting.Meeting; +import seedu.address.model.meeting.MeetingStatus; import seedu.address.model.meeting.Title; import seedu.address.model.person.Email; import seedu.address.model.person.Name; @@ -54,11 +55,11 @@ public static Meeting[] getSampleMeetings() { new Meeting(new Title("Test Meeting 1"), new Location("Room 1"), LocalDateTime.parse("02.10.2023 1000", FORMAT), LocalDateTime.parse("03.10.2023 1000", FORMAT), - getAttendeeSet("Alex Yeoh"), getTagSet("work")), + getAttendeeSet("Alex Yeoh"), getTagSet("work"), new MeetingStatus(false)), new Meeting(new Title("Test Meeting 2"), new Location("Room 2"), LocalDateTime.parse("02.10.2023 1000", FORMAT), LocalDateTime.parse("02.10.2023 1000", FORMAT), - getAttendeeSet(), getTagSet()), + getAttendeeSet(), getTagSet(), new MeetingStatus(false)), }; } diff --git a/src/main/java/seedu/address/storage/JsonAdaptedMeeting.java b/src/main/java/seedu/address/storage/JsonAdaptedMeeting.java index 702ee11d3cb..a9b8f4bfb24 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedMeeting.java +++ b/src/main/java/seedu/address/storage/JsonAdaptedMeeting.java @@ -15,6 +15,7 @@ import seedu.address.model.meeting.Attendee; import seedu.address.model.meeting.Location; import seedu.address.model.meeting.Meeting; +import seedu.address.model.meeting.MeetingStatus; import seedu.address.model.meeting.MeetingTime; import seedu.address.model.meeting.Title; import seedu.address.model.tag.Tag; @@ -32,6 +33,7 @@ class JsonAdaptedMeeting { private final String end; private final List attendees = new ArrayList<>(); private final List tags = new ArrayList<>(); + private final String status; /** * Constructs a {@code JsonAdaptedPerson} with the given person details. @@ -40,7 +42,7 @@ class JsonAdaptedMeeting { public JsonAdaptedMeeting(@JsonProperty("title") String title, @JsonProperty("location") String location, @JsonProperty("start") String start, @JsonProperty("end") String end, @JsonProperty("attendees") List attendees, - @JsonProperty("tags") List tags) { + @JsonProperty("tags") List tags, @JsonProperty("status") String status) { this.title = title; this.location = location; @@ -52,6 +54,7 @@ public JsonAdaptedMeeting(@JsonProperty("title") String title, @JsonProperty("lo if (tags != null) { this.tags.addAll(tags); } + this.status = status; } /** @@ -68,6 +71,7 @@ public JsonAdaptedMeeting(Meeting source) { tags.addAll(source.getTags().stream() .map(JsonAdaptedTag::new) .collect(Collectors.toList())); + status = source.getStatus().toString(); } /** @@ -80,11 +84,13 @@ public Meeting toModelType() throws IllegalValueException { for (JsonAdaptedAttendee person : attendees) { meetingAttendees.add(person.toModelType()); } + final Set modelAttendees = new HashSet<>(meetingAttendees); final List meetingTags = new ArrayList<>(); for (JsonAdaptedTag tag : tags) { meetingTags.add(tag.toModelType()); } + final Set modelTags = new HashSet<>(meetingTags); if (title == null) { throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, @@ -118,10 +124,16 @@ public Meeting toModelType() throws IllegalValueException { final LocalDateTime modelStart = LocalDateTime.parse(start); final LocalDateTime modelEnd = LocalDateTime.parse(end); - final Set modelAttendees = new HashSet<>(meetingAttendees); + if (status == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + MeetingStatus.class.getSimpleName())); + } + if (!status.equals(Boolean.FALSE.toString()) && !status.equals(Boolean.TRUE.toString())) { + throw new IllegalValueException(MeetingStatus.MESSAGE_CONSTRAINTS); + } + final MeetingStatus modelStatus = new MeetingStatus(Boolean.parseBoolean(status)); - final Set modelTags = new HashSet<>(meetingTags); - return new Meeting(modelTitle, modelLocation, modelStart, modelEnd, modelAttendees, modelTags); + return new Meeting(modelTitle, modelLocation, modelStart, modelEnd, modelAttendees, modelTags, modelStatus); } } diff --git a/src/main/java/seedu/address/ui/MeetingCard.java b/src/main/java/seedu/address/ui/MeetingCard.java index 50b1eb487fa..242de1f7d44 100644 --- a/src/main/java/seedu/address/ui/MeetingCard.java +++ b/src/main/java/seedu/address/ui/MeetingCard.java @@ -50,6 +50,8 @@ public class MeetingCard extends UiPart { private Label end; @FXML private FlowPane tags; + @FXML + private Label status; /** * Creates a {@code MeetingCode} with the given {@code Meeting} and index to display. @@ -71,5 +73,10 @@ public MeetingCard(Meeting meeting, int displayedIndex) { meeting.getTags().stream() .sorted(Comparator.comparing(tag -> tag.tagName)) .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + if (meeting.getStatus().isComplete) { + status.setText("[COMPLETE]"); + } else { + status.setText(""); + } } } diff --git a/src/main/resources/view/MeetingScheduleCard.fxml b/src/main/resources/view/MeetingScheduleCard.fxml index 9b20050fec1..330e62b9bf7 100644 --- a/src/main/resources/view/MeetingScheduleCard.fxml +++ b/src/main/resources/view/MeetingScheduleCard.fxml @@ -28,6 +28,9 @@