:bulb: **Tip:**
-A person can have any number of tags (including 0)
+| Before | After |
+ :---:|:---:
+| ![before command execution.png](images/screenshots/beforeCommand.png) | ![result for 'add n/Alisson Becker p/12345678 e/alisson111@example.com a/VVD street, block 123, #01-01'](images/screenshots/addAlissonBecker.png) |
+
+
+
+**:x: Invalid Examples:**
+
+* `add n/Kim Lai n/Fred Tang p/12345678 e/kimlai222@example.com a/KL street, block 190, #01-23`
+
+Not allowed as `n/` prefix is used more than once.
+
+* `add n/Kim Lai p/ e/kimlai222@example.com a/KL street, block 190, #01-23`
+
+Not allowed as `PHONE_NUMBER` is blank.
+
+#### Deleting a person : `delete`
+
+Deletes the specified person from NUSocials.
+
+Format: `delete INDEX`
+
+* Deletes the person at the specified `INDEX`.
+* The index refers to the index number shown in the displayed person list.
+
+Constraints:
+* `INDEX` provided has to be on the currently shown contact list.
+
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`
+* `list` followed by `delete 2` deletes the 2nd person in the currently shown contact list.
+* `find n/Betsy` followed by `delete 1` deletes the 1st person from the resulting list of the `find` command.
-### Listing all persons : `list`
+#### Deleting multiple persons : `delete`
-Shows a list of all persons in the address book.
+Deletes all the specified persons from NUSocials.
-Format: `list`
+Format: `delete INDEX…`
+
+* Deletes multiple persons at the specified `INDEX` numbers.
+* The index refers to the index number shown in the displayed person list.
+* Each index **must be separated by a whitespace** and **must be unique**.
+
+Constraints:
+* All `INDEX` numbers provided has to be on the currently shown contact list.
+
+Example:
+* `list` followed by `delete 2 5 7` deletes the 2nd, 5th and 7th person in the currently shown list.
+
+#### Editing a person : `edit`
+
+Edits an existing person's details in NUSocials.
+
+Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS]`
+
+* Edits the person at the specified `INDEX`.
+* The index refers to the index number shown in the displayed person list.
+* At least one of the optional fields must be provided.
+
+Constraints:
+* Only 1 prefix for each field allowed.
+* `NAME` has to be alphanumeric not blank.
+* `PHONE_NUMBER` has to be between 3 and 10 digits long and not blank.
+* `EMAIL` has to be in a valid email format and not blank.
+* `ADDRESS` has to not be blank.
+* `INDEX` provided has to be on the currently shown contact list.
+
+Example:
+* `edit 1 p/91234567 e/KL123@example.com`
+ Edits the phone number and email address of the 1st person to `91234567` and `KL123@example.com` respectively.
+
+
+
+**:exclamation: Caution:**
+Existing values will be overwritten and updated to the new input values!
+
+
+#### Tagging a person: `tag`
+Tags additional information to an existing contact.
+
+Format: `tag INDEX [i/INTERNSHIP]… [m/MODULE]… [c/CCA]… [edu/EDUCATION]…`
+
+* Tags the relevant information to the person at the specified `INDEX`.
+* The index refers to the index number shown in the displayed person list.
+* Alphabets in the input tag values will be converted to lowercase.
+* Input tag values will be added to the existing tags in their respective fields.
+* If the new input tag values are the same as existing tags, then nothing will be added.
+
+Constraints:
+* At least one of the prefixes must be provided.
+* If a prefix is used, the input after must not be blank and must be alphanumeric.
+* `INDEX` provided has to be on the currently shown contact list.
+
+Example:
+* `tag 1 i/abc company m/CS2100 m/CS2105`(as shown below)
+ Tags the internship company and 2 modules to the 1st person in the currently shown contact list.
+
+| Before | After |
+ :---:|:---:
+| ![before command execution.png](images/screenshots/beforeCommand.png) | ![result for 'tag 1 i/abc company m/CS2100 m/CS2105'](images/screenshots/tagInternshipModuleModule.png) |
+
+
+
+**:x: Invalid Examples:**
+
+* `tag 1`
+
+Not allowed as no prefix provided.
+
+* `tag 1 i/ m/`
+
+Not allowed as there is no input given after a prefix is used.
+
+* `tag 0 i/xyz company m/CS2103T`
+
+Not allowed as there `INDEX` 0 does not exist in the contact list.
+
+
+#### Removing specific tags from person: `removetag`
+Removes the specific tags of an existing contact.
+
+Format: `removetag INDEX [i/INTERNSHIP]… [m/MODULE]… [c/CCA]… [edu/EDUCATION]…`
+
+* Removes the tags from the person at the specified `INDEX`.
+* The index refers to the index number shown in the displayed person list.
+
+Constraints:
+* At least one of the prefixes must be provided.
+* If a prefix is used, the input after must not be blank.
+* All inputs for tags provided must be an exact match to existing tags.
+* `INDEX` provided has to be on the currently shown contact list.
+
+Example:
+* `removetag 1 i/abc company m/CS2100 m/CS2030S`
+Removes the internship company tag and the 2 modules tags from the 1st person in the currently shown contact list.
+
+
+
+**:x: Invalid Examples:**
+
+* `removetag 1 i/ m/`
+
+Not allowed as there is no input given after a prefix is used.
+
+* `removetag 1 edu/computer` while person 1 has an education tag with `computer science`
+
+Not allowed as it is not an exact match.
+
-### Editing a person : `edit`
+#### Finding persons: `find`
-Edits an existing person in the address book.
+Finds persons that match any of the given fields and tags.
-Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…`
+Format: `find [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [i/INTERNSHIP]… [m/MODULE]… [c/CCA]… [edu/EDUCATION]…`
-* 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, …
+* The search is case-insensitive. e.g `hans` will match `Hans`
+* The matching is done by character sequence. e.g. `ha` or `ns` will match `Hans`
+* Persons matching at least one of the fields or tags will be returned.
+
+Constraints:
* 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.
+* Only 1 prefix for each basic particulars field is allowed.
Examples:
-* `edit 1 p/91234567 e/johndoe@example.com` Edits the phone number and email address of the 1st person to be `91234567` and `johndoe@example.com` respectively.
-* `edit 2 n/Betsy Crower t/` Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags.
+* `find m/cs2030s m/cs2040s`
+Returns anyone tagged with either `cs2030s` or `cs2040s` or both
+* `find n/Hans m/cs2100`
+Returns `Hans` and `Bo Yang` (i.e. Bo Yang is tagged with cs2100)
+* `find i/Shopee m/cs2040s m/cs2030s`
+Returns `Alex Yeoh` and `Bernice Yu` (as shown below)
+
+| Before | After |
+:---:|:---:
+| ![before command execution.png](images/screenshots/beforeCommand.png) | ![result for 'find i/Shopee m/cs2040s cs2030s'](images/screenshots/findShopeeCS2040sCS2030sResult.png) |
+
+
+
+**:x: Invalid Examples:**
+
+* `find n/ m/`
+
+Not allowed as there is no input given after a prefix is used.
+
+* `find n/Hans n/Chewbacca`
+
+Not allowed as the `n/` prefix is used more than once.
+
-### Locating persons by name: `find`
+#### Finding specific persons: `find -s`
-Finds persons whose names contain any of the given keywords.
+Finds persons that match all given fields and tags.
-Format: `find KEYWORD [MORE_KEYWORDS]`
+Format: `find -s [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [i/INTERNSHIP]… [m/MODULE]… [c/CCA]… [edu/EDUCATION]…`
* The search is case-insensitive. e.g `hans` will match `Hans`
-* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans`
-* Only the name is searched.
-* Only full words will be matched e.g. `Han` will not match `Hans`
-* Persons matching at least one keyword will be returned (i.e. `OR` search).
- e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang`
+* The matching is done by character sequence. e.g. `ha` or `ns` will match `Hans`
+* Only persons matching all fields and tags will be returned.
+
+Constraints:
+* At least one of the optional fields must be provided.
+* Only 1 prefix for each basic particulars field is allowed.
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 -s n/Bo Yang m/cs2040s`
+ Returns `Bo Yang` (i.e. Bo Yang is tagged with cs2040s)
-### Deleting a person : `delete`
+* `find -s i/Shopee m/cs2040s m/cs2030s`
+ Returns `Alex Yeoh` (as shown below)
-Deletes the specified person from the address book.
+| Before | After |
+:---:|:---:
+| ![before command execution.png](images/screenshots/beforeCommand.png) | ![result for 'find -s i/Shopee m/cs2040s cs2030s'](images/screenshots/find-sShopeeCS2040sCS2030s.png) |
-Format: `delete INDEX`
+
-* Deletes the person at the specified `INDEX`.
+**:x: Invalid Examples:**
+
+* `find -s n/ m/`
+
+Not allowed as there is no input given after a prefix is used.
+
+* `find -s n/Hans n/Solo`
+
+Not allowed as the `n/` prefix is used more than once.
+
+
+### Event Commands:
+#### Showing events: `showevents`
+Shows a list of all events in NUSocials.
+
+Format: `showevents`
+
+* Events shown are automatically sorted in chronological order.
+
+
+
+**:information_source: Tip :**
+Use the `-upcoming` or `-past` flags to filter the event list.
+
+Alternate formats:
+1. `showevents -upcoming`
+* Shows a list of all upcoming events instead
+
+2. `showevents -past`
+* Shows a list of all past events instead
+
+
+#### Adding an event: `event`
+Adds an event into NUSocials.
+
+Format: `event INDEX… name/EVENT NAME info/EVENT DETAILS d/DATE t/TIME`
+
+* Tags the participating persons to the events based on 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, …
+* Each index **must be separated by a whitespace** and **must be unique**.
+
+Constraints:
+* All fields must be provided.
+* Only 1 prefix for each field is allowed.
+* `DATE` has to be in the format of `yyyy-MM-dd`.
+* `TIME` has to be in the format of `HH:mm`.
+* `DATE` and `TIME` has to be valid (i.e Date and Time specified must be after the current date and time)
+* `EVENT NAME` has a limit of 100 characters.
+* `EVENT DETAILS` has a limit of 300 characters.
+
+Example:
+* `event 2 3 name/Movie marathon info/Harry Potter movies d/2022-08-15 t/14:00`
+Creates the Event and adds into NUSocials. (as shown below)
+
+| Before | After |
+:---:|:---:
+| ![before command execution.png](images/screenshots/beforeCommand.png) | ![result for 'event 2 3 name/Movie marathon info/Harry Potter movies d/2022-08-15 t/14:00'](images/screenshots/eventMovieMarathon.png) |
+
+
+
+**:x: Invalid Examples:**
+
+* `event 1 2 name/ info/At Michael's d/2022-08-22 t/19:00`
+
+Not allowed as there is no input after a prefix is used.
+
+* `event 1 2 name/Dinner appointment name/Game night info/At Michael's d/2022-08-22 t/19:00`
+
+Not allowed as the `name/` prefix is more than once.
+
+
+#### Cancelling an event : `cancelevent`
+
+Deletes the specified event from NUSocials.
+
+Format: `cancelevent INDEX`
+
+* Deletes the event at the specified `INDEX`.
+* The index refers to the index number shown in the displayed event list.
+
+Constraints:
+* `INDEX` number provided has to be on the currently shown contact list.
+
+Examples:
+* `cancelevent 2` deletes the 2nd event in the currently shown event list.
+
+#### Cancelling multiple events: `cancelevent`
+
+Deletes all the specified events from NUSocials.
+
+Format: `cancelevent INDEX…`
+
+* Deletes multiple events at the specified `INDEX` numbers.
+* The index refers to the index number shown in the displayed event list.
+* Each index **must be separated by a whitespace** and **must be unique**.
+
+Constraints:
+* All `INDEX` numbers provided has to be on the currently shown contact list.
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.
+* `cancelevent 2 5 7` deletes the 2nd, 5th and 7th events in the currently shown event list.
+
+#### Finding an event: `find -e`
+
+Finds an event that matches any of the given details
+
+Format: `find -e [name/EVENT NAME]… [info/INFORMATION]… [part/PARTICPANT]… [dt/DATE AND TIME]…`
+
+* The search is case-insensitive. e.g `lunch` will match `Lunch`
+* The matching is done by character sequence. e.g. `lun` will match `lunch`
+* Events matching at least one of the fields will be returned.
+
+Constraints:
+* At least one of the optional fields must be provided.
+* Only 1 prefix for each basic particulars field is allowed.
+* `DATE` has to be in the format of `yyyy-MM-dd`.
+* `TIME` has to be in the format of `HH:mm`.
+* `DATE` and `TIME` has to be valid (i.e Date and Time specified must be after the current date and time)
+
+Example:
+* `find -e name/lunch part/Alex Yeoh` returns all events containing `lunch` in its name and all events involving Alex Yeoh
+
+
+
+**:x: Invalid Examples:**
+
+* `find -e name/ info/`
+
+Not allowed as there is no input given after a prefix is used.
+
+* `find -e name/Dinner name/Lunch`
+
+Not allowed as the `name/` prefix is used more than once.
+
### Clearing all entries : `clear`
-Clears all entries from the address book.
+Clears all entries from NUSocials.
Format: `clear`
@@ -156,26 +495,26 @@ Format: `exit`
### 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.
+NUSocials data are 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.
+NUSocials 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.
-
: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.
-
+
-### Archiving data files `[coming in v2.0]`
+**:exclamation: Caution:**
+If your changes to the data file makes its format invalid, NUSocials will discard all data and start with an empty data file at the next run.
+
-_Details coming soon ..._
+
--------------------------------------------------------------------------------------------------------------------
## FAQ
**Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder.
+**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous NUSocials home folder.
--------------------------------------------------------------------------------------------------------------------
@@ -183,10 +522,17 @@ _Details coming soon ..._
Action | Format, Examples
--------|------------------
-**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…`
e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague`
+**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS`
e.g. `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665`
+**Tag** | `tag INDEX [i/INTERNSHIP]… [m/MODULE]… [c/CCA]… [edu/EDUCATION]…`
e.g. `tag 1 m/CS2105 m/CS2106`
+**Remove Tag** | `removetag INDEX [i/INTERNSHIP]… [m/MODULE]… [c/CCA]… [edu/EDUCATION]…`
e.g. `removetag 1 c/Bouldering m/CS2105 m/CS2106`
+**Event** | `event INDEX… name/EVENT NAME info/INFORMATION d/DATE t/TIME`
e.g. `event 1 name/Dinner Date info/Having Dinner at Bread Street Kitchen by Gordon Ramsay d/2022-12-20 t/20:15`
+**Cancel Event** | `cancelevent INDEX…`
e.g. `cancelevent 1 2 3`
**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`
+**Delete** | `delete INDEX`
e.g. `delete 3`
`delete INDEX…`
e.g. `delete 1 3 5`
+**Edit** | `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS]`
e.g. `edit 2 n/Fred e/fred111@example.com`
+**Find** | `find [n/NAME]… [i/INTERNSHIP]… [m/MODULE]… [c/CCA]… [edu/EDUCATION]…`
e.g. `find n/john edu/computer science`
+**Find specific match** | `find -s [n/NAME]… [i/INTERNSHIP]… [m/MODULE]… [c/CCA]… [edu/EDUCATION]…`
e.g. `find -s n/john i/bytedance edu/computer science`
+**Find Event** | `find -e [name/EVENT NAME]… [info/INFORMATION]… [part/PARTICIPANT]… [dt/DATE AND TIME]…`
e.g. `find -e name/Dinner info/Candice's birthday dt/2022-05-12 19:30`
**List** | `list`
+**Show Events** | `showevents` `showevents -upcoming` `showevents -past`
**Help** | `help`
diff --git a/docs/_config.yml b/docs/_config.yml
index 6bd245d8f4e..35d32960627 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -1,4 +1,4 @@
-title: "AB-3"
+title: "NUSocials"
theme: minima
header_pages:
@@ -8,7 +8,7 @@ header_pages:
markdown: kramdown
-repository: "se-edu/addressbook-level3"
+repository: "AY2122S2-CS2103T-W11-1/tp"
github_icon: "images/github-icon.png"
plugins:
diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss
index 0d3f6e80ced..2893c4479ee 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: "NUSocials";
font-size: 32px;
}
}
diff --git a/docs/diagrams/CancelEventSequenceDiagram.puml b/docs/diagrams/CancelEventSequenceDiagram.puml
new file mode 100644
index 00000000000..f36f6d2abaf
--- /dev/null
+++ b/docs/diagrams/CancelEventSequenceDiagram.puml
@@ -0,0 +1,86 @@
+@startuml
+!include style.puml
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":CancelEventCommandParser" as CancelEventCommandParser LOGIC_COLOR
+participant ":ParserUtil" as ParserUtil LOGIC_COLOR
+participant "c:CancelEventCommand" as CancelEventCommand 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("cancelevent 1 2 ")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("cancelevent 1 2")
+activate AddressBookParser
+
+create CancelEventCommandParser
+AddressBookParser -> CancelEventCommandParser
+activate CancelEventCommandParser
+
+CancelEventCommandParser --> AddressBookParser
+deactivate CancelEventCommandParser
+
+AddressBookParser -> CancelEventCommandParser : parse("1 2")
+activate CancelEventCommandParser
+
+CancelEventCommandParser -> ParserUtil : parseIndexes("1 2")
+activate ParserUtil
+create CancelEventCommand
+ParserUtil -> CancelEventCommand
+activate CancelEventCommand
+
+CancelEventCommand --> ParserUtil : c
+deactivate CancelEventCommand
+ParserUtil --> CancelEventCommandParser : c
+deactivate ParserUtil
+
+CancelEventCommandParser --> AddressBookParser : c
+deactivate CancelEventCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+CancelEventCommandParser -[hidden]-> AddressBookParser
+destroy CancelEventCommandParser
+
+AddressBookParser --> LogicManager : c
+deactivate AddressBookParser
+
+LogicManager -> CancelEventCommand : execute()
+activate CancelEventCommand
+CancelEventCommand -> CancelEventCommand : extractDeletedInfo()
+activate CancelEventCommand
+CancelEventCommand --> CancelEventCommand
+deactivate CancelEventCommand
+CancelEventCommand -> CancelEventCommand : deleteFromList()
+activate CancelEventCommand
+CancelEventCommand -> Model : deleteEvent(Event2)
+activate Model
+Model --> CancelEventCommand
+deactivate Model
+
+CancelEventCommand -> Model : deleteEvent(Event1)
+activate Model
+Model --> CancelEventCommand
+deactivate Model
+
+CancelEventCommand --> CancelEventCommand
+deactivate CancelEventCommand
+
+create CommandResult
+CancelEventCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> CancelEventCommand
+deactivate CommandResult
+
+CancelEventCommand --> LogicManager : result
+deactivate CancelEventCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/CancelEventSequenceDiagram0.puml b/docs/diagrams/CancelEventSequenceDiagram0.puml
new file mode 100644
index 00000000000..18251cbb740
--- /dev/null
+++ b/docs/diagrams/CancelEventSequenceDiagram0.puml
@@ -0,0 +1,65 @@
+@startuml
+!include style.puml
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":CancelEventCommandParser" as CancelEventCommandParser LOGIC_COLOR
+participant ":ParserUtil" as ParserUtil LOGIC_COLOR
+participant "c:CancelEventCommand" as CancelEventCommand LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("cancelevent 1 2 ")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("cancelevent 1 2")
+activate AddressBookParser
+
+create CancelEventCommandParser
+AddressBookParser -> CancelEventCommandParser
+activate CancelEventCommandParser
+
+CancelEventCommandParser --> AddressBookParser
+deactivate CancelEventCommandParser
+
+AddressBookParser -> CancelEventCommandParser : parse("1 2")
+activate CancelEventCommandParser
+
+CancelEventCommandParser -> ParserUtil : parseIndexes("1 2")
+activate ParserUtil
+create CancelEventCommand
+ParserUtil -> CancelEventCommand
+activate CancelEventCommand
+
+CancelEventCommand --> ParserUtil : c
+deactivate CancelEventCommand
+ParserUtil --> CancelEventCommandParser : c
+deactivate ParserUtil
+
+CancelEventCommandParser --> AddressBookParser : c
+deactivate CancelEventCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+CancelEventCommandParser -[hidden]-> AddressBookParser
+destroy CancelEventCommandParser
+
+AddressBookParser --> LogicManager : c
+deactivate AddressBookParser
+
+LogicManager -> CancelEventCommand : execute()
+activate CancelEventCommand
+
+ref over CancelEventCommand, Model
+Cancelling the event
+end ref
+
+CancelEventCommand --> LogicManager : result
+deactivate CancelEventCommand
+
+[<--LogicManager
+deactivate LogicManager
+
+@enduml
diff --git a/docs/diagrams/CancelEventSequenceDiagram1.puml b/docs/diagrams/CancelEventSequenceDiagram1.puml
new file mode 100644
index 00000000000..aeccd7d72f6
--- /dev/null
+++ b/docs/diagrams/CancelEventSequenceDiagram1.puml
@@ -0,0 +1,42 @@
+@startuml
+!include style.puml
+
+mainframe **sd** Cancelling the event
+
+box Logic LOGIC_COLOR_T1
+participant "c:CancelEventCommand" as CancelEventCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+activate CancelEventCommand
+CancelEventCommand -> CancelEventCommand : extractDeletedInfo()
+activate CancelEventCommand
+CancelEventCommand --> CancelEventCommand
+deactivate CancelEventCommand
+CancelEventCommand -> CancelEventCommand : deleteFromList()
+activate CancelEventCommand
+CancelEventCommand -> Model : deleteEvent(Event2)
+activate Model
+Model --> CancelEventCommand
+deactivate Model
+
+CancelEventCommand -> Model : deleteEvent(Event1)
+activate Model
+Model --> CancelEventCommand
+deactivate Model
+
+CancelEventCommand --> CancelEventCommand
+deactivate CancelEventCommand
+
+create CommandResult
+CancelEventCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> CancelEventCommand
+deactivate CommandResult
+
+@enduml
diff --git a/docs/diagrams/DeleteMultipleSequenceDiagram.puml b/docs/diagrams/DeleteMultipleSequenceDiagram.puml
new file mode 100644
index 00000000000..eaed6e1abb0
--- /dev/null
+++ b/docs/diagrams/DeleteMultipleSequenceDiagram.puml
@@ -0,0 +1,89 @@
+@startuml
+!include style.puml
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR
+participant ":ParserUtil" as ParserUtil LOGIC_COLOR
+participant "d:DeleteCommand" as DeleteCommand 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("delete 1 2 3")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("delete 1 2 3")
+activate AddressBookParser
+
+create DeleteCommandParser
+AddressBookParser -> DeleteCommandParser
+activate DeleteCommandParser
+
+DeleteCommandParser --> AddressBookParser
+deactivate DeleteCommandParser
+
+AddressBookParser -> DeleteCommandParser : parse("1 2 3")
+activate DeleteCommandParser
+
+DeleteCommandParser -> ParserUtil : parseIndexes("1 2 3")
+activate ParserUtil
+create DeleteCommand
+ParserUtil -> DeleteCommand
+activate DeleteCommand
+
+DeleteCommand --> ParserUtil : d
+deactivate DeleteCommand
+ParserUtil --> DeleteCommandParser : d
+deactivate ParserUtil
+
+DeleteCommandParser --> AddressBookParser : d
+deactivate DeleteCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+DeleteCommandParser -[hidden]-> AddressBookParser
+destroy DeleteCommandParser
+
+AddressBookParser --> LogicManager : d
+deactivate AddressBookParser
+
+LogicManager -> DeleteCommand : execute()
+activate DeleteCommand
+
+DeleteCommand -> DeleteCommand : deleteFromList()
+activate DeleteCommand
+
+DeleteCommand -> Model : deletePerson(Person3)
+activate Model
+Model --> DeleteCommand
+deactivate Model
+
+DeleteCommand -> Model : deletePerson(Person2)
+activate Model
+Model --> DeleteCommand
+deactivate Model
+
+DeleteCommand -> Model : deletePerson(Person1)
+activate Model
+Model --> DeleteCommand
+deactivate Model
+
+DeleteCommand --> DeleteCommand
+deactivate DeleteCommand
+
+create CommandResult
+DeleteCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> DeleteCommand
+deactivate CommandResult
+
+DeleteCommand --> LogicManager : result
+deactivate DeleteCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/DeleteMultipleSequenceDiagram0.puml b/docs/diagrams/DeleteMultipleSequenceDiagram0.puml
new file mode 100644
index 00000000000..392f3ed1291
--- /dev/null
+++ b/docs/diagrams/DeleteMultipleSequenceDiagram0.puml
@@ -0,0 +1,65 @@
+@startuml
+!include style.puml
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR
+participant ":ParserUtil" as ParserUtil LOGIC_COLOR
+participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("delete 1 2 3")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("delete 1 2 3")
+activate AddressBookParser
+
+create DeleteCommandParser
+AddressBookParser -> DeleteCommandParser
+activate DeleteCommandParser
+
+DeleteCommandParser --> AddressBookParser
+deactivate DeleteCommandParser
+
+AddressBookParser -> DeleteCommandParser : parse("1 2 3")
+activate DeleteCommandParser
+
+DeleteCommandParser -> ParserUtil : parseIndexes("1 2 3")
+activate ParserUtil
+create DeleteCommand
+ParserUtil -> DeleteCommand
+activate DeleteCommand
+
+DeleteCommand --> ParserUtil : d
+deactivate DeleteCommand
+ParserUtil --> DeleteCommandParser : d
+deactivate ParserUtil
+
+DeleteCommandParser --> AddressBookParser : d
+deactivate DeleteCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+DeleteCommandParser -[hidden]-> AddressBookParser
+destroy DeleteCommandParser
+
+AddressBookParser --> LogicManager : d
+deactivate AddressBookParser
+
+LogicManager -> DeleteCommand : execute()
+activate DeleteCommand
+
+ref over DeleteCommand, Model
+Deletion of multiple contacts
+end ref
+
+DeleteCommand --> LogicManager : result
+deactivate DeleteCommand
+
+[<--LogicManager
+deactivate LogicManager
+
+@enduml
diff --git a/docs/diagrams/DeleteMultipleSequenceDiagram1.puml b/docs/diagrams/DeleteMultipleSequenceDiagram1.puml
new file mode 100644
index 00000000000..de90686336f
--- /dev/null
+++ b/docs/diagrams/DeleteMultipleSequenceDiagram1.puml
@@ -0,0 +1,45 @@
+@startuml
+!include style.puml
+
+mainframe **sd** Deletion of multiple contacts
+
+box Logic LOGIC_COLOR_T1
+participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+activate DeleteCommand
+
+DeleteCommand -> DeleteCommand : deleteFromList()
+activate DeleteCommand
+
+DeleteCommand -> Model : deletePerson(Person3)
+activate Model
+Model --> DeleteCommand
+deactivate Model
+
+DeleteCommand -> Model : deletePerson(Person2)
+activate Model
+Model --> DeleteCommand
+deactivate Model
+
+DeleteCommand -> Model : deletePerson(Person1)
+activate Model
+Model --> DeleteCommand
+deactivate Model
+
+DeleteCommand --> DeleteCommand
+deactivate DeleteCommand
+
+create CommandResult
+DeleteCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> DeleteCommand
+deactivate CommandResult
+
+@enduml
diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml
index 1dc2311b245..47284d0d25b 100644
--- a/docs/diagrams/DeleteSequenceDiagram.puml
+++ b/docs/diagrams/DeleteSequenceDiagram.puml
@@ -5,6 +5,7 @@ box Logic LOGIC_COLOR_T1
participant ":LogicManager" as LogicManager LOGIC_COLOR
participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR
+participant ":ParserUtil" as ParserUtil LOGIC_COLOR
participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR
participant ":CommandResult" as CommandResult LOGIC_COLOR
end box
@@ -29,12 +30,16 @@ deactivate DeleteCommandParser
AddressBookParser -> DeleteCommandParser : parse("1")
activate DeleteCommandParser
+DeleteCommandParser -> ParserUtil : parseIndexes("1")
+activate ParserUtil
create DeleteCommand
-DeleteCommandParser -> DeleteCommand
+ParserUtil -> DeleteCommand
activate DeleteCommand
-DeleteCommand --> DeleteCommandParser : d
+DeleteCommand --> ParserUtil : d
deactivate DeleteCommand
+ParserUtil --> DeleteCommandParser : d
+deactivate ParserUtil
DeleteCommandParser --> AddressBookParser : d
deactivate DeleteCommandParser
@@ -48,12 +53,17 @@ deactivate AddressBookParser
LogicManager -> DeleteCommand : execute()
activate DeleteCommand
-DeleteCommand -> Model : deletePerson(1)
-activate Model
+DeleteCommand -> DeleteCommand : deleteFromList()
+activate DeleteCommand
+DeleteCommand -> Model : deletePerson(Person1)
+activate Model
Model --> DeleteCommand
deactivate Model
+DeleteCommand --> DeleteCommand
+deactivate DeleteCommand
+
create CommandResult
DeleteCommand -> CommandResult
activate CommandResult
diff --git a/docs/diagrams/EditSequenceDiagram0.puml b/docs/diagrams/EditSequenceDiagram0.puml
new file mode 100644
index 00000000000..20abddaf6df
--- /dev/null
+++ b/docs/diagrams/EditSequenceDiagram0.puml
@@ -0,0 +1,85 @@
+@startuml
+!include style.puml
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":EditCommandParser" as EditCommandParser LOGIC_COLOR
+participant ":ParserUtil" as ParserUtil LOGIC_COLOR
+participant "e:EditCommand" as EditCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant "p2:Person" as Person MODEL_COLOR
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("edit 2 \nn/John Doe ")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("edit 2 \nn/John Doe ")
+activate AddressBookParser
+
+create EditCommandParser
+AddressBookParser -> EditCommandParser
+activate EditCommandParser
+
+EditCommandParser --> AddressBookParser
+deactivate EditCommandParser
+
+AddressBookParser -> EditCommandParser : parse("2")
+activate EditCommandParser
+
+EditCommandParser -> ParserUtil : parseName("John Doe")
+activate ParserUtil
+create EditCommand
+ParserUtil -> EditCommand
+activate EditCommand
+
+EditCommand --> ParserUtil : e
+deactivate EditCommand
+ParserUtil --> EditCommandParser : e
+deactivate ParserUtil
+
+EditCommandParser --> AddressBookParser : t
+deactivate EditCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+EditCommandParser -[hidden]-> AddressBookParser
+destroy EditCommandParser
+
+AddressBookParser --> LogicManager : t
+deactivate AddressBookParser
+
+LogicManager -> EditCommand : execute()
+activate EditCommand
+
+create Person
+EditCommand -> Person
+activate Person
+Person --> EditCommand : p2
+deactivate Person
+
+EditCommand -> EditCommand : edit name field of p2
+activate EditCommand
+EditCommand --> EditCommand : new_p2
+deactivate EditCommand
+
+EditCommand -> Model : setPerson(p2, new_p2)
+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/EventClassDiagram.puml b/docs/diagrams/EventClassDiagram.puml
new file mode 100644
index 00000000000..3a67299a7fa
--- /dev/null
+++ b/docs/diagrams/EventClassDiagram.puml
@@ -0,0 +1,14 @@
+@startuml
+!include style.puml
+skinparam arrowThickness 1.1
+skinparam arrowColor MODEL_COLOR
+skinparam classBackgroundColor MODEL_COLOR
+
+AddressBook *-right-> "1" UniqueEventList
+UniqueEventList -right-> Event
+
+Event *-up-> "1" EventName
+Event *-up-> "1" Information
+Event *-up-> "1" Participants
+Event *-up-> "1" DateTime
+@enduml
diff --git a/docs/diagrams/EventSequenceDiagram.puml b/docs/diagrams/EventSequenceDiagram.puml
new file mode 100644
index 00000000000..ae3fc88a8e2
--- /dev/null
+++ b/docs/diagrams/EventSequenceDiagram.puml
@@ -0,0 +1,83 @@
+@startuml
+!include style.puml
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":EventCommandParser" as EventCommandParser LOGIC_COLOR
+participant ":ParserUtil" as ParserUtil LOGIC_COLOR
+participant "e:EventCommand" as EventCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant "e1:Event" as Event MODEL_COLOR
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("event 1 \nname/Lunch Appt \ninfo/Having lunch at Hai Di Lao \nd/2023-02-20 t/12:15")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("event 1 \nname/Lunch Appt \ninfo/Having lunch at Hai Di Lao \nd/2023-02-20 t/12:15")
+activate AddressBookParser
+
+create EventCommandParser
+AddressBookParser -> EventCommandParser
+activate EventCommandParser
+
+EventCommandParser --> AddressBookParser
+deactivate EventCommandParser
+
+AddressBookParser -> EventCommandParser : parse("1")
+activate EventCommandParser
+
+EventCommandParser -> ParserUtil : parseEventName("Lunch Appt")
+activate ParserUtil
+EventCommandParser -> ParserUtil : parseInfo("Having lunch at Hai Di Lao")
+EventCommandParser -> ParserUtil : parseDateTime("2023-02-20", "12:15")
+create EventCommand
+ParserUtil -> EventCommand
+activate EventCommand
+
+EventCommand --> ParserUtil : e
+deactivate EventCommand
+ParserUtil --> EventCommandParser : e
+deactivate ParserUtil
+
+EventCommandParser --> AddressBookParser : e
+deactivate EventCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+EventCommandParser -[hidden]-> AddressBookParser
+destroy EventCommandParser
+
+AddressBookParser --> LogicManager : e
+deactivate AddressBookParser
+
+LogicManager -> EventCommand : execute()
+activate EventCommand
+
+create Event
+EventCommand -> Event : create event e1
+activate Event
+Event --> EventCommand : e1
+deactivate Event
+
+
+EventCommand -> Model : addEvent(e1)
+activate Model
+Model --> EventCommand
+deactivate Model
+
+create CommandResult
+EventCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> EventCommand
+deactivate CommandResult
+
+EventCommand --> LogicManager : result
+deactivate EventCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/EventSequenceDiagram0.puml b/docs/diagrams/EventSequenceDiagram0.puml
new file mode 100644
index 00000000000..0eafc4244ea
--- /dev/null
+++ b/docs/diagrams/EventSequenceDiagram0.puml
@@ -0,0 +1,67 @@
+@startuml
+!include style.puml
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":EventCommandParser" as EventCommandParser LOGIC_COLOR
+participant ":ParserUtil" as ParserUtil LOGIC_COLOR
+participant "e:EventCommand" as EventCommand LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("event 1 \nname/Lunch Appt \ninfo/Having lunch at Hai Di Lao \nd/2023-02-20 t/12:15")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("event 1 \nname/Lunch Appt \ninfo/Having lunch at Hai Di Lao \nd/2023-02-20 t/12:15")
+activate AddressBookParser
+
+create EventCommandParser
+AddressBookParser -> EventCommandParser
+activate EventCommandParser
+
+EventCommandParser --> AddressBookParser
+deactivate EventCommandParser
+
+AddressBookParser -> EventCommandParser : parse("1")
+activate EventCommandParser
+
+EventCommandParser -> ParserUtil : parseEventName("Lunch Appt")
+activate ParserUtil
+EventCommandParser -> ParserUtil : parseInfo("Having lunch at Hai Di Lao")
+EventCommandParser -> ParserUtil : parseDateTime("2023-02-20", "12:15")
+create EventCommand
+ParserUtil -> EventCommand
+activate EventCommand
+
+EventCommand --> ParserUtil : e
+deactivate EventCommand
+ParserUtil --> EventCommandParser : e
+deactivate ParserUtil
+
+EventCommandParser --> AddressBookParser : e
+deactivate EventCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+EventCommandParser -[hidden]-> AddressBookParser
+destroy EventCommandParser
+
+AddressBookParser --> LogicManager : t
+deactivate AddressBookParser
+
+LogicManager -> EventCommand : execute()
+activate EventCommand
+
+ref over EventCommand, Model
+Adding the event
+end ref
+
+EventCommand --> LogicManager : result
+deactivate EventCommand
+
+[<--LogicManager
+deactivate LogicManager
+
+@enduml
diff --git a/docs/diagrams/EventSequenceDiagram1.puml b/docs/diagrams/EventSequenceDiagram1.puml
new file mode 100644
index 00000000000..87d72cea89d
--- /dev/null
+++ b/docs/diagrams/EventSequenceDiagram1.puml
@@ -0,0 +1,37 @@
+@startuml
+!include style.puml
+
+mainframe **sd** Adding the event
+
+box Logic LOGIC_COLOR_T1
+participant "e:EventCommand" as EventCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant "e1:Event" as Event MODEL_COLOR
+participant ":Model" as Model MODEL_COLOR
+end box
+
+activate EventCommand
+
+create Event
+EventCommand -> Event : create event e1
+activate Event
+Event --> EventCommand : e1
+deactivate Event
+
+
+EventCommand -> Model : addEvent(e1)
+activate Model
+Model --> EventCommand
+deactivate Model
+
+create CommandResult
+EventCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> EventCommand
+deactivate CommandResult
+
+@enduml
diff --git a/docs/diagrams/FindClassDiagram.puml b/docs/diagrams/FindClassDiagram.puml
new file mode 100644
index 00000000000..33b22968c85
--- /dev/null
+++ b/docs/diagrams/FindClassDiagram.puml
@@ -0,0 +1,22 @@
+@startuml
+!include style.puml
+skinparam arrowThickness 1.1
+skinparam arrowColor MODEL_COLOR
+skinparam classBackgroundColor MODEL_COLOR
+
+Class "FindCommand" as FindCommand
+Command <|-- FindCommand
+FindCommand o--right "1" Predicate : contains
+FindPersonDescriptor .up. FindCommand
+
+Name "*" -up-* FindPersonDescriptor
+Phone "*" -up-* FindPersonDescriptor
+Address "*" -up-* FindPersonDescriptor
+Tag "*" -up-* FindPersonDescriptor
+
+FindOrPredicateParser .right. FindCommand
+FindOrPredicateParser .right. FindPersonDescriptor
+
+FindAndPredicateParser .up. FindCommand
+FindAndPredicateParser .up. FindPersonDescriptor
+@enduml
diff --git a/docs/diagrams/FindPredicatesClassDiagram.puml b/docs/diagrams/FindPredicatesClassDiagram.puml
new file mode 100644
index 00000000000..d13761e1dca
--- /dev/null
+++ b/docs/diagrams/FindPredicatesClassDiagram.puml
@@ -0,0 +1,34 @@
+@startuml
+!include style.puml
+skinparam arrowThickness 1.1
+skinparam arrowColor MODEL_COLOR
+skinparam classBackgroundColor MODEL_COLOR
+
+class "<
>\nPredicate" as Predicate
+class "{abstract}\nContainsKeywordPredicate" as ckp
+Predicate <|-- ckp
+class "{abstract}\nFieldContainsKeywordPredicateAnd" as fckpAnd
+class "{abstract}\nFieldContainsKeywordPredicateOr" as fckpOr
+ckp <|-- fckpAnd
+ckp <|--- fckpOr
+class "{abstract}\nTagsContainsKeywordPredicateAnd" as tckpAnd
+class "{abstract}\nTagsContainsKeywordPredicateOr" as tckpOr
+ckp <|---- tckpAnd
+ckp <|----- tckpOr
+class "{abstract}\nNameContainsKeywordPredicateAnd" as nckpAnd
+class "{abstract}\nNameContainsKeywordPredicateOr" as nckpOr
+class "{abstract}\nPhoneContainsKeywordPredicateAnd" as pckpAnd
+class "{abstract}\nPhoneContainsKeywordPredicateOr" as pckpOr
+fckpAnd <|-- nckpAnd
+fckpOr <|-- nckpOr
+fckpAnd <|-- pckpAnd
+fckpOr <|-- pckpOr
+class "{abstract}\nCcaContainsKeywordPredicateAnd" as cckpAnd
+class "{abstract}\nCcaContainsKeywordPredicateOr" as cckpOr
+class "{abstract}\nModuleContainsKeywordPredicateAnd" as mckpAnd
+class "{abstract}\nModuleContainsKeywordPredicateOr" as mckpOr
+tckpAnd <|-- cckpAnd
+tckpOr <|-- cckpOr
+tckpAnd <|-- mckpAnd
+tckpOr <|-- mckpOr
+@enduml
diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml
index 4439108973a..4eb74be1e44 100644
--- a/docs/diagrams/ModelClassDiagram.puml
+++ b/docs/diagrams/ModelClassDiagram.puml
@@ -13,13 +13,9 @@ Class ModelManager
Class UserPrefs
Class UniquePersonList
+Class UniqueEventList
Class Person
-Class Address
-Class Email
-Class Name
-Class Phone
-Class Tag
-
+Class Event
}
Class HiddenOutside #FFFFFF
@@ -35,16 +31,10 @@ ModelManager -right-> "1" UserPrefs
UserPrefs .up.|> ReadOnlyUserPrefs
AddressBook *--> "1" UniquePersonList
+AddressBook *--> "1" UniqueEventList
UniquePersonList --> "~* all" Person
-Person *--> Name
-Person *--> Phone
-Person *--> Email
-Person *--> Address
-Person *--> "*" Tag
-
-Name -[hidden]right-> Phone
-Phone -[hidden]right-> Address
-Address -[hidden]right-> Email
+UniqueEventList --> "~* all" Event
ModelManager -->"~* filtered" Person
+ModelManager -->"~* filtered" Event
@enduml
diff --git a/docs/diagrams/RemoveTagSequenceDiagram.puml b/docs/diagrams/RemoveTagSequenceDiagram.puml
new file mode 100644
index 00000000000..08586360ec1
--- /dev/null
+++ b/docs/diagrams/RemoveTagSequenceDiagram.puml
@@ -0,0 +1,85 @@
+@startuml
+!include style.puml
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":RemoveTagCommandParser" as RemoveTagCommandParser LOGIC_COLOR
+participant ":ParserUtil" as ParserUtil LOGIC_COLOR
+participant "t:RemoveTagCommand" as RemoveTagCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant "p2:Person" as Person MODEL_COLOR
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("removetag 2 \nm/cs2100 m/cs2107")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("removetag 2 \nm/cs2100 m/cs2107")
+activate AddressBookParser
+
+create RemoveTagCommandParser
+AddressBookParser -> RemoveTagCommandParser
+activate RemoveTagCommandParser
+
+RemoveTagCommandParser --> AddressBookParser
+deactivate RemoveTagCommandParser
+
+AddressBookParser -> RemoveTagCommandParser : parse("2")
+activate RemoveTagCommandParser
+
+RemoveTagCommandParser -> ParserUtil : parseTags("cs2100, cs2107")
+activate ParserUtil
+create RemoveTagCommand
+ParserUtil -> RemoveTagCommand
+activate RemoveTagCommand
+
+RemoveTagCommand --> ParserUtil : t
+deactivate RemoveTagCommand
+ParserUtil --> RemoveTagCommandParser : t
+deactivate ParserUtil
+
+RemoveTagCommandParser --> AddressBookParser : t
+deactivate RemoveTagCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+RemoveTagCommandParser -[hidden]-> AddressBookParser
+destroy RemoveTagCommandParser
+
+AddressBookParser --> LogicManager : t
+deactivate AddressBookParser
+
+LogicManager -> RemoveTagCommand : execute()
+activate RemoveTagCommand
+
+create Person
+RemoveTagCommand -> Person
+activate Person
+Person --> RemoveTagCommand : p2
+deactivate Person
+
+RemoveTagCommand -> RemoveTagCommand : remove matching tags from p2
+activate RemoveTagCommand
+RemoveTagCommand --> RemoveTagCommand : new_p2
+deactivate RemoveTagCommand
+
+RemoveTagCommand -> Model : setPerson(p2, new_p2)
+activate Model
+Model --> RemoveTagCommand
+deactivate Model
+
+create CommandResult
+RemoveTagCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> RemoveTagCommand
+deactivate CommandResult
+
+RemoveTagCommand --> LogicManager : result
+deactivate RemoveTagCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/RemoveTagSequenceDiagram0.puml b/docs/diagrams/RemoveTagSequenceDiagram0.puml
new file mode 100644
index 00000000000..a74ff6b7071
--- /dev/null
+++ b/docs/diagrams/RemoveTagSequenceDiagram0.puml
@@ -0,0 +1,65 @@
+@startuml
+!include style.puml
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":RemoveTagCommandParser" as RemoveTagCommandParser LOGIC_COLOR
+participant ":ParserUtil" as ParserUtil LOGIC_COLOR
+participant "t:RemoveTagCommand" as RemoveTagCommand LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("removetag 2 \nm/cs2100 m/cs2107")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("removetag 2 \nm/cs2100 m/cs2107")
+activate AddressBookParser
+
+create RemoveTagCommandParser
+AddressBookParser -> RemoveTagCommandParser
+activate RemoveTagCommandParser
+
+RemoveTagCommandParser --> AddressBookParser
+deactivate RemoveTagCommandParser
+
+AddressBookParser -> RemoveTagCommandParser : parse("2")
+activate RemoveTagCommandParser
+
+RemoveTagCommandParser -> ParserUtil : parseTags("cs2100, cs2107")
+activate ParserUtil
+create RemoveTagCommand
+ParserUtil -> RemoveTagCommand
+activate RemoveTagCommand
+
+RemoveTagCommand --> ParserUtil : t
+deactivate RemoveTagCommand
+ParserUtil --> RemoveTagCommandParser : t
+deactivate ParserUtil
+
+RemoveTagCommandParser --> AddressBookParser : t
+deactivate RemoveTagCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+RemoveTagCommandParser -[hidden]-> AddressBookParser
+destroy RemoveTagCommandParser
+
+AddressBookParser --> LogicManager : t
+deactivate AddressBookParser
+
+LogicManager -> RemoveTagCommand : execute()
+activate RemoveTagCommand
+
+ref over RemoveTagCommand, Model
+Removal of matching tags
+end ref
+
+RemoveTagCommand --> LogicManager : result
+deactivate RemoveTagCommand
+
+[<--LogicManager
+deactivate LogicManager
+
+@enduml
diff --git a/docs/diagrams/RemoveTagSequenceDiagram1.puml b/docs/diagrams/RemoveTagSequenceDiagram1.puml
new file mode 100644
index 00000000000..0c29abbd048
--- /dev/null
+++ b/docs/diagrams/RemoveTagSequenceDiagram1.puml
@@ -0,0 +1,41 @@
+@startuml
+!include style.puml
+
+mainframe **sd** Removal of matching tags
+
+box Logic LOGIC_COLOR_T1
+participant "t:RemoveTagCommand" as RemoveTagCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant "p2:Person" as Person MODEL_COLOR
+participant ":Model" as Model MODEL_COLOR
+end box
+
+activate RemoveTagCommand
+
+create Person
+RemoveTagCommand -> Person
+activate Person
+Person --> RemoveTagCommand : p2
+deactivate Person
+
+RemoveTagCommand -> RemoveTagCommand : remove matching tags from p2
+activate RemoveTagCommand
+RemoveTagCommand --> RemoveTagCommand : new_p2
+deactivate RemoveTagCommand
+
+RemoveTagCommand -> Model : setPerson(p2, new_p2)
+activate Model
+Model --> RemoveTagCommand
+deactivate Model
+
+create CommandResult
+RemoveTagCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> RemoveTagCommand
+deactivate CommandResult
+
+@enduml
diff --git a/docs/diagrams/RemoveTagState0.puml b/docs/diagrams/RemoveTagState0.puml
new file mode 100644
index 00000000000..093559e1e72
--- /dev/null
+++ b/docs/diagrams/RemoveTagState0.puml
@@ -0,0 +1,29 @@
+@startuml
+
+skinparam ClassFontColor #000000
+skinparam ClassBorderColor #000000
+
+title Before removetag()
+
+object "__David:Person__" as Person {
+ name = "David Choo"
+ address = "Example Street"
+ phone = 81234567
+ email = "david@example.com"
+}
+
+object "__nus:Education__" as nusTag
+object "__dso:Internship__" as dsoTag
+object "__cs2100:Module__" as cs2100Tag
+object "__cs2107:Module__" as cs2107Tag
+
+Person -down-> nusTag
+Person -down-> dsoTag
+Person -down-> cs2100Tag
+Person -down-> cs2107Tag
+
+nusTag -[hidden]right- dsoTag
+dsoTag -[hidden]right- cs2100Tag
+cs2100Tag -[hidden]right- cs2107Tag
+
+@enduml
diff --git a/docs/diagrams/RemoveTagState1.puml b/docs/diagrams/RemoveTagState1.puml
new file mode 100644
index 00000000000..111c2985991
--- /dev/null
+++ b/docs/diagrams/RemoveTagState1.puml
@@ -0,0 +1,28 @@
+@startuml
+
+skinparam ClassFontColor #000000
+skinparam ClassBorderColor #000000
+
+title After removetag()
+
+object "__David:Person__" as Person {
+ name = "David Choo"
+ address = "Example Street"
+ phone = 81234567
+ email = "david@example.com"
+}
+
+object "__cs2100:Module__" as cs2100Tag
+object "__nus:Education__" as nusTag
+object "__dso:Internship__" as dsoTag
+object "__cs2107:Module__" as cs2107Tag
+
+Person -down-> nusTag
+Person -down-> dsoTag
+Person -down-> cs2100Tag
+Person -down-> cs2107Tag
+
+hide cs2100Tag
+hide cs2107Tag
+
+@enduml
diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml
index 760305e0e58..c44def2e5f2 100644
--- a/docs/diagrams/StorageClassDiagram.puml
+++ b/docs/diagrams/StorageClassDiagram.puml
@@ -20,6 +20,8 @@ Class JsonAddressBookStorage
Class JsonSerializableAddressBook
Class JsonAdaptedPerson
Class JsonAdaptedTag
+Class JsonAdaptedEvent
+Class JsonAdaptedName
}
}
@@ -38,6 +40,8 @@ JsonUserPrefsStorage .up.|> UserPrefsStorage
JsonAddressBookStorage .up.|> AddressBookStorage
JsonAddressBookStorage ..> JsonSerializableAddressBook
JsonSerializableAddressBook --> "*" JsonAdaptedPerson
+JsonSerializableAddressBook --> "*" JsonAdaptedEvent
+JsonAdaptedEvent --> "*" JsonAdaptedName
JsonAdaptedPerson --> "*" JsonAdaptedTag
@enduml
diff --git a/docs/diagrams/TagClassDiagram.puml b/docs/diagrams/TagClassDiagram.puml
new file mode 100644
index 00000000000..c2b7f513ae6
--- /dev/null
+++ b/docs/diagrams/TagClassDiagram.puml
@@ -0,0 +1,22 @@
+@startuml
+!include style.puml
+skinparam arrowThickness 1.1
+skinparam arrowColor MODEL_COLOR
+skinparam classBackgroundColor MODEL_COLOR
+
+Class "{abstract}\nTag" as Tag
+
+AddressBook *-right-> "1" UniquePersonList
+UniquePersonList -right-> Person
+
+Person -down-> "*" Tag
+Cca -up-|> Tag
+Education -up-|> Tag
+Internship -up-|> Tag
+Module -up-|> Tag
+
+Person *-up-> "1" Name
+Person *-up-> "1" Phone
+Person *-up-> "1" Email
+Person *-up-> "1 "Address
+@enduml
diff --git a/docs/diagrams/TagSequenceDiagram.puml b/docs/diagrams/TagSequenceDiagram.puml
new file mode 100644
index 00000000000..227f61e7e74
--- /dev/null
+++ b/docs/diagrams/TagSequenceDiagram.puml
@@ -0,0 +1,86 @@
+@startuml
+!include style.puml
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":TagCommandParser" as TagCommandParser LOGIC_COLOR
+participant ":ParserUtil" as ParserUtil LOGIC_COLOR
+participant "t:TagCommand" as TagCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant "p1:Person" as Person MODEL_COLOR
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("tag 1 \nedu/computer science \nm/cs2030s m/cs2040s")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("tag 1 \nedu/computer science \nm/cs2030s m/cs2040s")
+activate AddressBookParser
+
+create TagCommandParser
+AddressBookParser -> TagCommandParser
+activate TagCommandParser
+
+TagCommandParser --> AddressBookParser
+deactivate TagCommandParser
+
+AddressBookParser -> TagCommandParser : parse("1")
+activate TagCommandParser
+
+TagCommandParser -> ParserUtil : parseTags("computer science")
+activate ParserUtil
+TagCommandParser -> ParserUtil : parseTags("cs2030s, cs2040s")
+create TagCommand
+ParserUtil -> TagCommand
+activate TagCommand
+
+TagCommand --> ParserUtil : t
+deactivate TagCommand
+ParserUtil --> TagCommandParser : t
+deactivate ParserUtil
+
+TagCommandParser --> AddressBookParser : t
+deactivate TagCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+TagCommandParser -[hidden]-> AddressBookParser
+destroy TagCommandParser
+
+AddressBookParser --> LogicManager : t
+deactivate AddressBookParser
+
+LogicManager -> TagCommand : execute()
+activate TagCommand
+
+create Person
+TagCommand -> Person
+activate Person
+Person --> TagCommand : p1
+deactivate Person
+
+TagCommand -> TagCommand : add tags to p1
+activate TagCommand
+TagCommand --> TagCommand : new_p1
+deactivate TagCommand
+
+TagCommand -> Model : setPerson(p1, new_p1)
+activate Model
+Model --> TagCommand
+deactivate Model
+
+create CommandResult
+TagCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> TagCommand
+deactivate CommandResult
+
+TagCommand --> LogicManager : result
+deactivate TagCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/TagSequenceDiagram0.puml b/docs/diagrams/TagSequenceDiagram0.puml
new file mode 100644
index 00000000000..92b8898fa2f
--- /dev/null
+++ b/docs/diagrams/TagSequenceDiagram0.puml
@@ -0,0 +1,66 @@
+@startuml
+!include style.puml
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":TagCommandParser" as TagCommandParser LOGIC_COLOR
+participant ":ParserUtil" as ParserUtil LOGIC_COLOR
+participant "t:TagCommand" as TagCommand LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("tag 1 \nedu/computer science \nm/cs2030s m/cs2040s")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("tag 1 \nedu/computer science \nm/cs2030s m/cs2040s")
+activate AddressBookParser
+
+create TagCommandParser
+AddressBookParser -> TagCommandParser
+activate TagCommandParser
+
+TagCommandParser --> AddressBookParser
+deactivate TagCommandParser
+
+AddressBookParser -> TagCommandParser : parse("1")
+activate TagCommandParser
+
+TagCommandParser -> ParserUtil : parseTags("computer science")
+activate ParserUtil
+TagCommandParser -> ParserUtil : parseTags("cs2030s, cs2040s")
+create TagCommand
+ParserUtil -> TagCommand
+activate TagCommand
+
+TagCommand --> ParserUtil : t
+deactivate TagCommand
+ParserUtil --> TagCommandParser : t
+deactivate ParserUtil
+
+TagCommandParser --> AddressBookParser : t
+deactivate TagCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+TagCommandParser -[hidden]-> AddressBookParser
+destroy TagCommandParser
+
+AddressBookParser --> LogicManager : t
+deactivate AddressBookParser
+
+LogicManager -> TagCommand : execute()
+activate TagCommand
+
+ref over TagCommand, Model
+Adding tags to the person
+end ref
+
+TagCommand --> LogicManager : result
+deactivate TagCommand
+
+[<--LogicManager
+deactivate LogicManager
+
+@enduml
diff --git a/docs/diagrams/TagSequenceDiagram1.puml b/docs/diagrams/TagSequenceDiagram1.puml
new file mode 100644
index 00000000000..4f2861646fc
--- /dev/null
+++ b/docs/diagrams/TagSequenceDiagram1.puml
@@ -0,0 +1,41 @@
+@startuml
+!include style.puml
+
+mainframe **sd** Adding tags to the person
+
+box Logic LOGIC_COLOR_T1
+participant "t:TagCommand" as TagCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant "p1:Person" as Person MODEL_COLOR
+participant ":Model" as Model MODEL_COLOR
+end box
+
+activate TagCommand
+
+create Person
+TagCommand -> Person
+activate Person
+Person --> TagCommand : p1
+deactivate Person
+
+TagCommand -> TagCommand : add tags to p1
+activate TagCommand
+TagCommand --> TagCommand : new_p1
+deactivate TagCommand
+
+TagCommand -> Model : setPerson(p1, new_p1)
+activate Model
+Model --> TagCommand
+deactivate Model
+
+create CommandResult
+TagCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> TagCommand
+deactivate CommandResult
+
+@enduml
diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml
index 95473d5aa19..b4432ff88f8 100644
--- a/docs/diagrams/UiClassDiagram.puml
+++ b/docs/diagrams/UiClassDiagram.puml
@@ -12,7 +12,9 @@ Class MainWindow
Class HelpWindow
Class ResultDisplay
Class PersonListPanel
+Class EventListPanel
Class PersonCard
+Class EventCard
Class StatusBarFooter
Class CommandBox
}
@@ -33,10 +35,12 @@ UiManager -down-> "1" MainWindow
MainWindow *-down-> "1" CommandBox
MainWindow *-down-> "1" ResultDisplay
MainWindow *-down-> "1" PersonListPanel
+MainWindow *-down-> "1" EventListPanel
MainWindow *-down-> "1" StatusBarFooter
MainWindow --> "0..1" HelpWindow
PersonListPanel -down-> "*" PersonCard
+EventListPanel -down-> "*" EventCard
MainWindow -left-|> UiPart
@@ -44,17 +48,22 @@ ResultDisplay --|> UiPart
CommandBox --|> UiPart
PersonListPanel --|> UiPart
PersonCard --|> UiPart
+EventListPanel --|> UiPart
+EventCard --|> UiPart
StatusBarFooter --|> UiPart
HelpWindow --|> UiPart
PersonCard ..> Model
+EventCard ..> Model
UiManager -right-> Logic
MainWindow -left-> Logic
PersonListPanel -[hidden]left- HelpWindow
+EventListPanel -[hidden]left- PersonListPanel
HelpWindow -[hidden]left- CommandBox
CommandBox -[hidden]left- ResultDisplay
ResultDisplay -[hidden]left- StatusBarFooter
+EventCard -[hidden]down- PersonCard
MainWindow -[hidden]-|> UiPart
@enduml
diff --git a/docs/diagrams/UndoRedoState0.puml b/docs/diagrams/UndoRedoState0.puml
index 96e30744d24..34885420931 100644
--- a/docs/diagrams/UndoRedoState0.puml
+++ b/docs/diagrams/UndoRedoState0.puml
@@ -15,6 +15,6 @@ State2 -[hidden]right-> State3
hide State2
hide State3
-class Pointer as "Current State" #FFFFF
+class Pointer as "Current State" #FFFFFF
Pointer -up-> State1
@end
diff --git a/docs/diagrams/UndoRedoState1.puml b/docs/diagrams/UndoRedoState1.puml
index 01fcb9b2b96..0e2c8c72d33 100644
--- a/docs/diagrams/UndoRedoState1.puml
+++ b/docs/diagrams/UndoRedoState1.puml
@@ -16,7 +16,7 @@ State2 -[hidden]right-> State3
hide State3
-class Pointer as "Current State" #FFFFF
+class Pointer as "Current State" #FFFFFF
Pointer -up-> State2
@end
diff --git a/docs/diagrams/UndoRedoState2.puml b/docs/diagrams/UndoRedoState2.puml
index bccc230a5d1..0ce7073e187 100644
--- a/docs/diagrams/UndoRedoState2.puml
+++ b/docs/diagrams/UndoRedoState2.puml
@@ -14,7 +14,7 @@ package States <> {
State1 -[hidden]right-> State2
State2 -[hidden]right-> State3
-class Pointer as "Current State" #FFFFF
+class Pointer as "Current State" #FFFFFF
Pointer -up-> State3
@end
diff --git a/docs/diagrams/UndoRedoState3.puml b/docs/diagrams/UndoRedoState3.puml
index ea29c9483e4..50bf43b3f34 100644
--- a/docs/diagrams/UndoRedoState3.puml
+++ b/docs/diagrams/UndoRedoState3.puml
@@ -14,7 +14,7 @@ package States <> {
State1 -[hidden]right-> State2
State2 -[hidden]right-> State3
-class Pointer as "Current State" #FFFFF
+class Pointer as "Current State" #FFFFFF
Pointer -up-> State2
@end
diff --git a/docs/diagrams/UndoRedoState4.puml b/docs/diagrams/UndoRedoState4.puml
index 1b784cece80..83cbe4c740c 100644
--- a/docs/diagrams/UndoRedoState4.puml
+++ b/docs/diagrams/UndoRedoState4.puml
@@ -14,7 +14,7 @@ package States <> {
State1 -[hidden]right-> State2
State2 -[hidden]right-> State3
-class Pointer as "Current State" #FFFFF
+class Pointer as "Current State" #FFFFFF
Pointer -up-> State2
@end
diff --git a/docs/diagrams/UndoRedoState5.puml b/docs/diagrams/UndoRedoState5.puml
index 88927be32bc..fc89dd99d2d 100644
--- a/docs/diagrams/UndoRedoState5.puml
+++ b/docs/diagrams/UndoRedoState5.puml
@@ -14,7 +14,7 @@ package States <> {
State1 -[hidden]right-> State2
State2 -[hidden]right-> State3
-class Pointer as "Current State" #FFFFF
+class Pointer as "Current State" #FFFFFF
Pointer -up-> State3
note right on link: State ab2 deleted.
diff --git a/docs/images/CancelEventSequenceDiagram.png b/docs/images/CancelEventSequenceDiagram.png
new file mode 100644
index 00000000000..7e38b361f48
Binary files /dev/null and b/docs/images/CancelEventSequenceDiagram.png differ
diff --git a/docs/images/CancelEventSequenceDiagram0.png b/docs/images/CancelEventSequenceDiagram0.png
new file mode 100644
index 00000000000..3b7ff960d87
Binary files /dev/null and b/docs/images/CancelEventSequenceDiagram0.png differ
diff --git a/docs/images/CancelEventSequenceDiagram1.png b/docs/images/CancelEventSequenceDiagram1.png
new file mode 100644
index 00000000000..f740950dc63
Binary files /dev/null and b/docs/images/CancelEventSequenceDiagram1.png differ
diff --git a/docs/images/DeleteMultipleSequenceDiagram.png b/docs/images/DeleteMultipleSequenceDiagram.png
new file mode 100644
index 00000000000..096fce528f7
Binary files /dev/null and b/docs/images/DeleteMultipleSequenceDiagram.png differ
diff --git a/docs/images/DeleteMultipleSequenceDiagram0.png b/docs/images/DeleteMultipleSequenceDiagram0.png
new file mode 100644
index 00000000000..9ac6c81a9b1
Binary files /dev/null and b/docs/images/DeleteMultipleSequenceDiagram0.png differ
diff --git a/docs/images/DeleteMultipleSequenceDiagram1.png b/docs/images/DeleteMultipleSequenceDiagram1.png
new file mode 100644
index 00000000000..d1589ae4003
Binary files /dev/null and b/docs/images/DeleteMultipleSequenceDiagram1.png differ
diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png
index fa327b39618..0ee62b04f0f 100644
Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ
diff --git a/docs/images/EditSequenceDiagram0.png b/docs/images/EditSequenceDiagram0.png
new file mode 100644
index 00000000000..7558831164f
Binary files /dev/null and b/docs/images/EditSequenceDiagram0.png differ
diff --git a/docs/images/EventClassDiagram.png b/docs/images/EventClassDiagram.png
new file mode 100644
index 00000000000..b68c0bc1d28
Binary files /dev/null and b/docs/images/EventClassDiagram.png differ
diff --git a/docs/images/EventSequenceDiagram.png b/docs/images/EventSequenceDiagram.png
new file mode 100644
index 00000000000..42213fe6e90
Binary files /dev/null and b/docs/images/EventSequenceDiagram.png differ
diff --git a/docs/images/EventSequenceDiagram0.png b/docs/images/EventSequenceDiagram0.png
new file mode 100644
index 00000000000..6fd2cc27e1d
Binary files /dev/null and b/docs/images/EventSequenceDiagram0.png differ
diff --git a/docs/images/EventSequenceDiagram1.png b/docs/images/EventSequenceDiagram1.png
new file mode 100644
index 00000000000..125d064880a
Binary files /dev/null and b/docs/images/EventSequenceDiagram1.png differ
diff --git a/docs/images/FindClassDiagram.png b/docs/images/FindClassDiagram.png
new file mode 100644
index 00000000000..55f19e5e31d
Binary files /dev/null and b/docs/images/FindClassDiagram.png differ
diff --git a/docs/images/FindPredicatesClassDiagram.png b/docs/images/FindPredicatesClassDiagram.png
new file mode 100644
index 00000000000..5e5181be7ad
Binary files /dev/null and b/docs/images/FindPredicatesClassDiagram.png differ
diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png
index 04070af60d8..0bd5fd3449e 100644
Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ
diff --git a/docs/images/RemoveTagSequenceDiagram.png b/docs/images/RemoveTagSequenceDiagram.png
new file mode 100644
index 00000000000..5b4e0a4e092
Binary files /dev/null and b/docs/images/RemoveTagSequenceDiagram.png differ
diff --git a/docs/images/RemoveTagSequenceDiagram0.png b/docs/images/RemoveTagSequenceDiagram0.png
new file mode 100644
index 00000000000..5ee0e12ef5e
Binary files /dev/null and b/docs/images/RemoveTagSequenceDiagram0.png differ
diff --git a/docs/images/RemoveTagSequenceDiagram1.png b/docs/images/RemoveTagSequenceDiagram1.png
new file mode 100644
index 00000000000..f4c310aff08
Binary files /dev/null and b/docs/images/RemoveTagSequenceDiagram1.png differ
diff --git a/docs/images/RemoveTagState0.png b/docs/images/RemoveTagState0.png
new file mode 100644
index 00000000000..be970537dc2
Binary files /dev/null and b/docs/images/RemoveTagState0.png differ
diff --git a/docs/images/RemoveTagState1.png b/docs/images/RemoveTagState1.png
new file mode 100644
index 00000000000..1e368da21e2
Binary files /dev/null and b/docs/images/RemoveTagState1.png differ
diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png
index 2533a5c1af0..0527aa9c569 100644
Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ
diff --git a/docs/images/TagClassDiagram.png b/docs/images/TagClassDiagram.png
new file mode 100644
index 00000000000..93c2991c9b6
Binary files /dev/null and b/docs/images/TagClassDiagram.png differ
diff --git a/docs/images/TagSequenceDiagram.png b/docs/images/TagSequenceDiagram.png
new file mode 100644
index 00000000000..530c045e872
Binary files /dev/null and b/docs/images/TagSequenceDiagram.png differ
diff --git a/docs/images/TagSequenceDiagram0.png b/docs/images/TagSequenceDiagram0.png
new file mode 100644
index 00000000000..6c0a2290aed
Binary files /dev/null and b/docs/images/TagSequenceDiagram0.png differ
diff --git a/docs/images/TagSequenceDiagram1.png b/docs/images/TagSequenceDiagram1.png
new file mode 100644
index 00000000000..a50889ca3e0
Binary files /dev/null and b/docs/images/TagSequenceDiagram1.png differ
diff --git a/docs/images/Ui.png b/docs/images/Ui.png
index 5bd77847aa2..5a57521d350 100644
Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ
diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png
index 785e04dbab4..aace26f1b04 100644
Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ
diff --git a/docs/images/UiSampleAddressBook.png b/docs/images/UiSampleAddressBook.png
new file mode 100644
index 00000000000..e6003998cc7
Binary files /dev/null and b/docs/images/UiSampleAddressBook.png differ
diff --git a/docs/images/fredtwt.png b/docs/images/fredtwt.png
new file mode 100644
index 00000000000..63db18fafef
Binary files /dev/null and b/docs/images/fredtwt.png differ
diff --git a/docs/images/help_message.png b/docs/images/help_message.png
new file mode 100644
index 00000000000..b0312be5029
Binary files /dev/null and b/docs/images/help_message.png differ
diff --git a/docs/images/manu2002g.png b/docs/images/manu2002g.png
new file mode 100644
index 00000000000..f429d6a32d7
Binary files /dev/null and b/docs/images/manu2002g.png differ
diff --git a/docs/images/ongkimlai.png b/docs/images/ongkimlai.png
new file mode 100644
index 00000000000..e96b66ff3a7
Binary files /dev/null and b/docs/images/ongkimlai.png differ
diff --git a/docs/images/screenshots/addAlissonBecker.png b/docs/images/screenshots/addAlissonBecker.png
new file mode 100644
index 00000000000..537d824b04e
Binary files /dev/null and b/docs/images/screenshots/addAlissonBecker.png differ
diff --git a/docs/images/screenshots/beforeCommand.png b/docs/images/screenshots/beforeCommand.png
new file mode 100644
index 00000000000..0a05f83f8cb
Binary files /dev/null and b/docs/images/screenshots/beforeCommand.png differ
diff --git a/docs/images/screenshots/eventMovieMarathon.png b/docs/images/screenshots/eventMovieMarathon.png
new file mode 100644
index 00000000000..0eb40511a64
Binary files /dev/null and b/docs/images/screenshots/eventMovieMarathon.png differ
diff --git a/docs/images/screenshots/find-sShopeeCS2040sCS2030s.png b/docs/images/screenshots/find-sShopeeCS2040sCS2030s.png
new file mode 100644
index 00000000000..cdce02e4344
Binary files /dev/null and b/docs/images/screenshots/find-sShopeeCS2040sCS2030s.png differ
diff --git a/docs/images/findAlexDavidResult.png b/docs/images/screenshots/findAlexDavidResult.png
similarity index 100%
rename from docs/images/findAlexDavidResult.png
rename to docs/images/screenshots/findAlexDavidResult.png
diff --git a/docs/images/screenshots/findShopeeCS2040sCS2030sResult.png b/docs/images/screenshots/findShopeeCS2040sCS2030sResult.png
new file mode 100644
index 00000000000..4ae04166350
Binary files /dev/null and b/docs/images/screenshots/findShopeeCS2040sCS2030sResult.png differ
diff --git a/docs/images/screenshots/samplePersonCard.png b/docs/images/screenshots/samplePersonCard.png
new file mode 100644
index 00000000000..c67599a19fc
Binary files /dev/null and b/docs/images/screenshots/samplePersonCard.png differ
diff --git a/docs/images/screenshots/tagInternshipModuleModule.png b/docs/images/screenshots/tagInternshipModuleModule.png
new file mode 100644
index 00000000000..54a4610c328
Binary files /dev/null and b/docs/images/screenshots/tagInternshipModuleModule.png differ
diff --git a/docs/images/viki0526.png b/docs/images/viki0526.png
new file mode 100644
index 00000000000..1ce7ce16dc8
Binary files /dev/null and b/docs/images/viki0526.png differ
diff --git a/docs/img.png b/docs/img.png
new file mode 100644
index 00000000000..c67599a19fc
Binary files /dev/null and b/docs/img.png differ
diff --git a/docs/index.md b/docs/index.md
index 7601dbaad0d..f7ac387fc87 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,17 +1,17 @@
---
layout: page
-title: AddressBook Level-3
+title: NUSocials
---
-[![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)
+[![CI Status](https://github.com/AY2122S2-CS2103T-W11-1/tp//workflows/Java%20CI/badge.svg)](https://github.com/AY2122S2-CS2103T-W11-1/tp/actions)
+[![codecov](https://codecov.io/gh/AY2122S2-CS2103T-W11-1/tp/branch/master/graph/badge.svg?token=EQL5RQUWFN)](https://codecov.io/gh/AY2122S2-CS2103T-W11-1/tp)
![Ui](images/Ui.png)
-**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface).
+**NUSocials 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).
-* 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 NUSocials, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start).
+* If you are interested about developing NUSocials, the [**Developer Guide**](DeveloperGuide.html) is a good place to start.
**Acknowledgements**
diff --git a/docs/team/fredtwt.md b/docs/team/fredtwt.md
new file mode 100644
index 00000000000..6db536ad3d9
--- /dev/null
+++ b/docs/team/fredtwt.md
@@ -0,0 +1,58 @@
+---
+layout: page
+title: Frederick Tang's Project Portfolio Page
+---
+
+### Project: NUSocials
+
+NUSocials is a desktop address book application for university students who like to maintain a professional contact list. 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 tag additional information to an existing contact entry. [(#35)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/35)
+ * What it does: allows the user to tag additional information to an existing contact.
+ * Justification: This feature improves the product significantly because a user can tag important information to their own contacts for future references.
+ * Highlights: This enhancement affects the existing UI layout. It required an in-depth analysis of how the tagged information should be displayed alongside with their respective contacts.
+
+* **New Feature**: Added the ability to add events to the address book. [(#64, ](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/64) [#73, ](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/73) [#82)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/82)
+ * What it does: allows the user to add events and tag them to contacts that are participating.
+ * Justification: This feature improves the product significantly because a user can keep track of upcoming events they have with their contacts.
+ * Highlights: This enhancement affects the existing UI layout. It required an in-depth analysis of how the event information should be displayed alongside the contact list in the application.
+
+* **New Feature**: Added the ability to cancel events and delete them from the address book. [(#76)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/76)
+ * What it does: allows the user to cancel events and delete them from the address book.
+ * Justification: This feature improves the product significantly because a user can remove unwanted events and reduce clutter in their event list.
+ * Highlights: This enhancement requires an in-depth analysis of how the UI layout of the event list should be altered after an event has been deleted.
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=fredtwt&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2022-02-18&tabOpen=true&tabType=authorship&tabAuthor=fredtwt&tabRepo=AY2122S2-CS2103T-W11-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false)
+
+* **Enhancements to existing features:**
+ * Removed the tag functionality from add command [(#33)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/33)
+ * Created a tag command that can tag specific fields to existing contact entries
+ * Enhanced the cancel event command to handle multiple cancellations at once
+ * Contributed to the logic for `find` command to handle the following cases: [(#75, ](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/75) [#95)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/95)
+ * basic particulars' prefixes can only be used once for each field
+ * search inputs can take in multiple strings now (i.e edu/computer science)
+
+* **Test cases implemented:**
+ * EventCommand [(#157)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/157)
+ * EventCommandParser [(#161)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/161)
+ * CancelEventCommand [(#158)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/158)
+ * CancelEventCommandParser [(#161)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/161)
+ * All the event field types [(#158)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/158)
+ * JsonAdaptedEvent [(#161)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/161)
+
+* **Documentation**:
+ * User Guide:
+ * Added documentation for the features `event`, `tag`, `cancelevent`.
+ * Did cosmetic tweaks on the documentation for existing features `list`, `add`, `delete`.
+ * Did cosmetic tweaks on the command summary to reflect NUSocials command format.
+
+ * Developer Guide:
+ * Added user stories for the features `add`, `tag`, `event`, `cancelevent`. [(#87)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/87)
+ * Added use cases for the features `add`, `tag`, `event`, `cancelevent`. [(#87)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/87)
+ * Modified the `ModelClassDiagram` and `StorageClassDiagram`. [(#87)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/87)
+ * Added `TagSequenceDiagram`, `CancelEventSequenceDiagram`, `EventSequenceDiagram`. [(#167)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/167)
+ * Added the implementation design details for `tag`, `event`, `cancelevent`. [(#67, ](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/67) [#88, ](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/88) [#90)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/90)
+ * Added manual testing instructions for `tag`, `event`, `cancelevent`. [(#171)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/171)
+ * Contributed to the NFRs and glossary.
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/manu2002g.md b/docs/team/manu2002g.md
new file mode 100644
index 00000000000..50a424aef73
--- /dev/null
+++ b/docs/team/manu2002g.md
@@ -0,0 +1,42 @@
+---
+layout: page
+title: Manusha Galappaththi's Project Portfolio Page
+---
+
+### Project: NUSocials
+
+NUSocials is a desktop address book application for university students who like to maintain a professional contact list. 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 edit previously added entries
+ * What it does: Allows the user to change the details of a previously added contact. This feature initially supported editing tags as well but was later removed.
+ * Justification: Users may make mistakes when entering contact details or tags. We should allow them to rectify this using an edit command
+ * 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.
+
+* **New Feature**: Added the ability to filter the events list
+ * What it does: Allows the user to view upcoming, past or all events in the database.
+ * Justification: Being able to filter and view events by upcoming/past greatly improved the usefulness of the events feature.
+
+* **New Feature**: Added the ability to find contact entries
+ * What it does: Allows the user search for a contact entry in the list according to basic particulars. Functionality for tags was added by another teammate.
+ * Justification: Users may have very large lists of contacts and may want to filter them by a certain tag or detail. We should allow them to do this using a find command.
+ * Highlights: This enhancement offers both 'AND' and 'OR' search for the fields searched for. The implementation was challenging as it required good knowledge of java predicates and functional programming.
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2022-02-18&tabOpen=true&tabType=authorship&tabAuthor=manu2002g&tabRepo=AY2122S2-CS2103T-W11-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=&authorshipIsBinaryFileTypeChecked=false)
+
+* **Documentation**:
+ * User Guide:
+ * Added documentation for `edit` and `showevent` features.
+ * Developer Guide:
+ * Added implementation for `edit`including the sequence diagram
+ * Added class diagram describing the predicates used in `find` and `find -s`
+ * Contributed to user stories, use cases, non-functional requirements and glossary
+
+* **Test Case Implementation**:
+ * ShowEventsCommand
+ * ShowEventsCommandParser
+ * FindCommand
+ * EditCommand
+
+
diff --git a/docs/team/ongkimlai.md b/docs/team/ongkimlai.md
new file mode 100644
index 00000000000..259280b7a6c
--- /dev/null
+++ b/docs/team/ongkimlai.md
@@ -0,0 +1,61 @@
+---
+layout: page
+title: Ong Kim Lai's Project Portfolio Page
+---
+
+### Project: NUSocials
+
+NUSocials is a desktop address book application for university students who like to maintain a professional contact list. 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.
+
+* **Feature Enhancement**: Added the ability to delete multiple contacts. [(#34)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/34)
+ * What it does: allows the user to delete multiple contacts in a single command.
+ * Justification: This feature improves the product as a user can efficiently delete many contacts at once instead of inputting a delete command for each contact that he or she wants to delete.
+ * Highlights: This enhancement affects the existing UI layout. It required an in-depth analysis of how the contacts would be displayed during the deletion process.
+
+* **New Feature**: Added the ability to remove tags from existing contacts. [(#52)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/52)
+ * What it does: allows the user to remove tags from an existing contact.
+ * Justification: This feature improves the product significantly because it allows the user to simply remove a tag, otherwise users have to recreate another contact.
+
+* Updated the UI to fit requirements for new features [(#70)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/70)
+ * Added a split-pane to fit 2 panels. (contacts panel and events panel)
+ * Designed the events panel.
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2022-02-18&tabOpen=true&tabType=authorship&tabAuthor=ongkimlai&tabRepo=AY2122S2-CS2103T-W11-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs&authorshipIsBinaryFileTypeChecked=false&zA=ongkimlai&zR=AY2122S2-CS2103T-W11-1%2Ftp%5Bmaster%5D&zACS=81.23391812865498&zS=2022-02-18&zFS=&zU=2022-02-26&zMG=false&zFTF=commit&zFGS=groupByRepos&zFR=false)
+
+* **Documentation**:
+ * User Guide:
+ * Added documentation for `removetag` and `delete` enhancement
+ * Updated documentation for exact tag names for `find` and `tag`
+ * Added all screenshots found in User Guide
+ * Added constraints section for all features
+ * Developer Guide:
+ * Added user stories for `removetag`, `delete` multiple contacts, viewing events
+ * Added use cases for `removetag`, `find`, `delete` multiple contacts, viewing events
+ * Added implementation for `delete` multiple contacts and its sequence diagrams
+ * Added implementation for `removetag` and its sequence and object diagrams
+ * Updated UI component class diagram
+ * Cosmetic tweaks to the formatting of use cases
+ * Contributed to the NFRs
+
+* **Test Case Implementation**:
+ * DeleteCommand [(#34, ](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/34) [#41)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/41)
+ * DeleteCommandParser [(#34)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/34)
+ * TagCommand [(#41)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/41)
+ * TagCommandParser [(#156)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/156)
+ * RemoveTagCommand [(#52)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/52)
+ * RemoveTagCommandParser [(#156)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/156)
+ * JsonAdaptedName [(#159)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/159)
+ * EditCommand (fixed bugs) [(#170)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/170)
+
+* **Review/mentoring contributions**:
+ * Supported a teammate with the steps to implement `showevents`
+ * Suggested fixes for a few test cases in `EditCommand` [(#92)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/92)
+
+* **Tools**:
+ * Integrated Codecov into team repo
+ * Change the header to NUSocials [(#5)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/55)
+ * Update the links to the Java-CI and CodeCov status-banners
+ * Update the link to the project’s GitHub repository
+ * Update site-wide settings [(#18)](https://github.com/AY2122S2-CS2103T-W11-1/tp/pull/18)
diff --git a/docs/team/viki0526.md b/docs/team/viki0526.md
new file mode 100644
index 00000000000..23df9e38195
--- /dev/null
+++ b/docs/team/viki0526.md
@@ -0,0 +1,39 @@
+---
+layout: page
+title: Vikrant Prakash's Project Portfolio Page
+---
+
+### Project: NUSocials
+
+NUSocials is a desktop address book application for university students who like to maintain a professional contact list. 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.
+
+* Updated the UI to display the tags added
+ * Used JavaFX Hboxes to create rudimentary tags for testing purposes before the final tag UI was designed
+
+* **New Feature**: Added the ability to view all contact entries.
+ * What it does: allows the user to view a list of all contacts in the stored in the database.
+ * Justification: This feature improves the product significantly because a user can view all their contacts and choose which ones to tag or delete.
+ * Highlights: This enhancement required an analysis of the UI design layout to find the best possible way to display the result.
+
+* **Feature Enhancement**: Added the ability to find contact entries by tags
+ * What it does: allows the user search for a contact entry in the list according to tags.
+ * Justification: Users may have very large lists of contacts and may want to filter them by a certain tag. We should allow them to do this using by extending the find command for tags
+ * Highlights: This enhancement offers both 'AND' and 'OR' search for the fields searched for. The implementation was challenging as it required good knowledge of java predicates and functional programming.
+
+* **New Feature**: Added the ability to find events
+ * What it does: allows the user search for an event in the list according to the event details
+ * Justification: Users may have very large lists of upcoming events and may want to look for a specific one. We should allow them to do this using a find event command
+
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=viki0526&breakdown=true&sort=groupTitle&sortWithin=title&since=2022-02-18&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other&tabOpen=true&tabType=authorship&tabAuthor=viki0526&tabRepo=AY2122S2-CS2103T-W11-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false)
+
+* **Documentation**:
+ * User Guide:
+ * Added documentation for `find`, `find -s`, `find -e`
+ * Added documentation for the `list` feature
+ * Developer Guide:
+ * Contributed to user stories, usecases, non-functional requirements and glossary
+ * Added implementation for find contacts including its class diagram
+ * Added implementation for find events
diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java
index 1deb3a1e469..0396c6a89af 100644
--- a/src/main/java/seedu/address/commons/core/Messages.java
+++ b/src/main/java/seedu/address/commons/core/Messages.java
@@ -8,6 +8,8 @@ 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!";
+ public static final String MESSAGE_INVALID_EVENT_DISPLAYED_INDEX = "The event index provided is invalid";
+ public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d PERSON(S) LISTED!";
+ public static final String MESSAGE_EVENTS_LISTED_OVERVIEW = "%1$d EVENT(S) LISTED!";
}
diff --git a/src/main/java/seedu/address/commons/core/index/Index.java b/src/main/java/seedu/address/commons/core/index/Index.java
index 19536439c09..813673c51f7 100644
--- a/src/main/java/seedu/address/commons/core/index/Index.java
+++ b/src/main/java/seedu/address/commons/core/index/Index.java
@@ -8,8 +8,9 @@
* base the other component is using for its index. However, after receiving the {@code Index}, that component can
* convert it back to an int if the index will not be passed to a different component again.
*/
-public class Index {
+public class Index implements Comparable {
private int zeroBasedIndex;
+ private final int originalZeroBasedIndex;
/**
* Index can only be created by calling {@link Index#fromZeroBased(int)} or
@@ -21,6 +22,7 @@ private Index(int zeroBasedIndex) {
}
this.zeroBasedIndex = zeroBasedIndex;
+ this.originalZeroBasedIndex = zeroBasedIndex;
}
public int getZeroBased() {
@@ -31,6 +33,25 @@ public int getOneBased() {
return zeroBasedIndex + 1;
}
+ public int getOriginalZeroBased() {
+ return zeroBasedIndex;
+ }
+
+ public Index getOriginalZeroBasedAsIndex() {
+ return new Index(getOriginalZeroBased());
+ }
+
+ public int getOrignalOneBased() {
+ return zeroBasedIndex + 1;
+ }
+
+ /**
+ * Decreases the index by 1
+ */
+ public void decreaseIndex() {
+ zeroBasedIndex--;
+ }
+
/**
* Creates a new {@code Index} using a zero-based index.
*/
@@ -49,6 +70,13 @@ public static Index fromOneBased(int oneBasedIndex) {
public boolean equals(Object other) {
return other == this // short circuit if same object
|| (other instanceof Index // instanceof handles nulls
- && zeroBasedIndex == ((Index) other).zeroBasedIndex); // state check
+ //&& zeroBasedIndex == ((Index) other).zeroBasedIndex); // state check
+ && originalZeroBasedIndex == ((Index) other).originalZeroBasedIndex); // state check
+
+ }
+
+ @Override
+ public int compareTo(Index other) {
+ return other.originalZeroBasedIndex - originalZeroBasedIndex;
}
}
diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/address/commons/util/StringUtil.java
index 61cc8c9a1cb..373c9514293 100644
--- a/src/main/java/seedu/address/commons/util/StringUtil.java
+++ b/src/main/java/seedu/address/commons/util/StringUtil.java
@@ -6,6 +6,7 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
+import java.util.HashSet;
/**
* Helper functions for handling strings.
@@ -38,6 +39,23 @@ public static boolean containsWordIgnoreCase(String sentence, String word) {
.anyMatch(preppedWord::equalsIgnoreCase);
}
+ /**
+ * Returns true if the {@code string} contains the {@code substring}.
+ * Ignores case, a full substring match is required.
+ *
+ * @param string cannot be null
+ * @param substring cannot be null, cannot be empty
+ */
+ public static boolean containsSubstringIgnoreCase(String string, String substring) {
+ requireNonNull(string);
+ requireNonNull(substring);
+
+ String preppedSubstring = substring.trim();
+ checkArgument(!preppedSubstring.isEmpty(), "Substring parameter cannot be empty");
+
+ return string.contains(preppedSubstring);
+ }
+
/**
* Returns a detailed message of the t, including the stack trace.
*/
@@ -65,4 +83,64 @@ public static boolean isNonZeroUnsignedInteger(String s) {
return false;
}
}
+
+ /**
+ * Returns true if {@code s} contains multiple entries, does not check for validness.
+ * Returns false if {@code s} is only a single entry.
+ * e.g. "1", "2" returns false, "1 2 3", "5 10" (multiple whitespaces) returns true
+ *
+ * @param s trimmed string of arguments
+ * @throws NullPointerException if {@code s} is null.
+ */
+ public static boolean containsMultipleIndex(String s) {
+ requireNonNull(s);
+ String[] indexes = s.split(" ");
+
+ return indexes.length != 1;
+ }
+
+ /**
+ * Returns true if {@code s} contains multiple all unique entries (integers), does not check for validness.
+ * Returns false if {@code s} contains duplicate entries (integers).
+ * e.g.:
+ * "1 1", "1 2 1 3" returns false
+ * "1 2 3", "5 10 2 3" (multiple whitespaces) returns true
+ *
+ * @param s trimmed string of arguments
+ * @throws NullPointerException if {@code s} is null.
+ */
+ public static boolean isAllUniqueIntegers(String s) {
+ requireNonNull(s);
+ String[] indexes = s.split(" ");
+ HashSet hashSet = new HashSet<>();
+ for (String t : indexes) {
+ int n = Integer.parseInt(t);
+ if (!hashSet.contains(n)) {
+ hashSet.add(n);
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if every entry in {@code s} represents a non-zero unsigned integer
+ * e.g. "5 6 7 8"
+ * Will return false for any other non-null string input
+ * e.g. empty string, "-1", "0", "+1", and " 2 " (untrimmed), "1 a" (contains letters)
+ * "1, 2 3" (contains comma), "1 2 3" (multiple whitespaces between adjacent integers)
+ *
+ * @throws NullPointerException if {@code s} is null.
+ */
+ public static boolean isAllNonZeroUnsignedInteger(String s) {
+ requireNonNull(s);
+ String[] indexes = s.split(" ");
+ boolean result = true;
+
+ for (String t : indexes) {
+ result = result && isNonZeroUnsignedInteger(t);
+ }
+ return result;
+ }
}
diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java
index 92cd8fa605a..51acb7789f7 100644
--- a/src/main/java/seedu/address/logic/Logic.java
+++ b/src/main/java/seedu/address/logic/Logic.java
@@ -8,6 +8,7 @@
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.event.Event;
import seedu.address.model.person.Person;
/**
@@ -33,6 +34,9 @@ public interface Logic {
/** Returns an unmodifiable view of the filtered list of persons */
ObservableList getFilteredPersonList();
+ /** Returns an unmodifiable view of the filtered list of events */
+ ObservableList getFilteredEventList();
+
/**
* Returns the user prefs' address book file path.
*/
diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java
index 9d9c6d15bdc..5772c169e76 100644
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ b/src/main/java/seedu/address/logic/LogicManager.java
@@ -14,6 +14,7 @@
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.Model;
import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.event.Event;
import seedu.address.model.person.Person;
import seedu.address.storage.Storage;
@@ -40,7 +41,6 @@ public LogicManager(Model model, Storage storage) {
@Override
public CommandResult execute(String commandText) throws CommandException, ParseException {
logger.info("----------------[USER COMMAND][" + commandText + "]");
-
CommandResult commandResult;
Command command = addressBookParser.parseCommand(commandText);
commandResult = command.execute(model);
@@ -64,6 +64,11 @@ public ObservableList getFilteredPersonList() {
return model.getFilteredPersonList();
}
+ @Override
+ public ObservableList getFilteredEventList() {
+ return model.getFilteredEventList();
+ }
+
@Override
public Path getAddressBookFilePath() {
return model.getAddressBookFilePath();
diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java
index 71656d7c5c8..bea8e3069ff 100644
--- a/src/main/java/seedu/address/logic/commands/AddCommand.java
+++ b/src/main/java/seedu/address/logic/commands/AddCommand.java
@@ -5,7 +5,6 @@
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
@@ -18,22 +17,20 @@ public class AddCommand extends Command {
public static final String COMMAND_WORD = "add";
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. "
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book.\n"
+ + "Only 1 prefix for basic particular (n/, p/, e/, a/) can be provided\n"
+ "Parameters: "
+ PREFIX_NAME + "NAME "
+ PREFIX_PHONE + "PHONE "
+ PREFIX_EMAIL + "EMAIL "
- + PREFIX_ADDRESS + "ADDRESS "
- + "[" + PREFIX_TAG + "TAG]...\n"
+ + PREFIX_ADDRESS + "ADDRESS \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";
+ + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 ";
- public static final String MESSAGE_SUCCESS = "New person added: %1$s";
+ public static final String MESSAGE_SUCCESS = "NEW PERSON ADDED: %n%1$s";
public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book";
private final Person toAdd;
diff --git a/src/main/java/seedu/address/logic/commands/CancelEventCommand.java b/src/main/java/seedu/address/logic/commands/CancelEventCommand.java
new file mode 100644
index 00000000000..aa300b66e69
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/CancelEventCommand.java
@@ -0,0 +1,129 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Arrays;
+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.event.Event;
+
+/**
+ * Deletes an event identified using it's displayed index from the address book
+ */
+public class CancelEventCommand extends Command {
+
+ public static final String COMMAND_WORD = "cancelevent";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Deletes the event identified by the index number(s) used in the displayed event\n"
+ + "Parameters: INDEX (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + "1\n"
+ + "OR\n"
+ + "Parameters: INDEX... (all indexes must be unique and positive integers)"
+ + "Example: " + COMMAND_WORD + "1 3 5";
+
+ public static final String MESSAGE_DELETE_EVENT_SUCCESS = "CANCELLED EVENT:%n%1$s";
+ public static final String MESSAGE_DELETE_EVENTS_SUCCESS = "CANCELLED EVENTS:%n%1$s";
+
+ private final Index[] targetIndexArr;
+ private final Index targetIndex;
+
+ /**
+ * Constructor for CancelEventCommand, for singular deletion
+ *
+ * @param targetIndex the event to be deleted
+ */
+ public CancelEventCommand(Index targetIndex) {
+ this.targetIndex = targetIndex;
+ this.targetIndexArr = new Index[]{targetIndex};
+ }
+
+ /**
+ * Constructor for CancelEventCommand, for multiple deletions
+ *
+ * @param targetIndexArr the events to be deleted
+ */
+ public CancelEventCommand(Index[] targetIndexArr) {
+ this.targetIndexArr = targetIndexArr;
+ this.targetIndex = targetIndexArr[0];
+ }
+
+ /**
+ * In the multiple deletion case, ensures all user input indexes are within range of the list.
+ *
+ * @param listSize the size of the last displayed list.
+ * @return true if every index is within the range of the list.
+ */
+ private boolean checkIndexRange(int listSize) {
+ boolean result = true;
+ for (Index target : targetIndexArr) {
+ result = result && (target.getZeroBased() < listSize);
+ }
+ return result;
+ }
+
+ /**
+ * Extracts the information before deletion for the success message.
+ *
+ * @param lastShownList the last displayed list.
+ * @return a string containing all the information of the event(s) to be deleted.
+ */
+ private String extractDeletedInfo(List lastShownList) {
+ final StringBuilder deletedEventOrEvents = new StringBuilder();
+ for (int i = 0; i < targetIndexArr.length; i++) {
+ Index target = targetIndexArr[i];
+ Event eventToDelete = lastShownList.get(target.getZeroBased());
+ if (i > 0) {
+ deletedEventOrEvents.append(System.lineSeparator());
+ }
+ deletedEventOrEvents.append(eventToDelete);
+ }
+ return deletedEventOrEvents.toString();
+ }
+
+ /**
+ * Deletes the events specified in the targetIndexArr.
+ * A copy of targetIndexArr is created for defensive programming.
+ * targetIndexArrClone is sorted in descending order so that the deletion process will not delete the wrong event.
+ * Example: If index 1 is deleted first, the original index 2 becomes index 1.
+ *
+ * @param model the addressbook model
+ * @param lastShownList the last displayed person list
+ */
+ private void deleteFromList(Model model, List lastShownList) {
+ Index[] targetIndexArrClone = targetIndexArr.clone();
+ Arrays.sort(targetIndexArrClone);
+ for (Index target : targetIndexArrClone) {
+ Event eventToDelete = lastShownList.get(target.getZeroBased());
+ model.deleteEvent(eventToDelete);
+ }
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredEventList();
+ int lastShownListSize = lastShownList.size();
+
+ if (!checkIndexRange(lastShownListSize)) {
+ throw new CommandException(Messages.MESSAGE_INVALID_EVENT_DISPLAYED_INDEX);
+ }
+ String deletedEventOrEvents = extractDeletedInfo(lastShownList);
+ deleteFromList(model, lastShownList);
+
+ return targetIndexArr.length == 1
+ ? new CommandResult(String.format(MESSAGE_DELETE_EVENT_SUCCESS, deletedEventOrEvents))
+ : new CommandResult(String.format(MESSAGE_DELETE_EVENTS_SUCCESS, deletedEventOrEvents));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof CancelEventCommand // instanceof handles nulls
+ && targetIndex.equals(((CancelEventCommand) other).targetIndex)); // state check
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java
index 9c86b1fa6e4..1c7992d8198 100644
--- a/src/main/java/seedu/address/logic/commands/ClearCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java
@@ -11,7 +11,7 @@
public class ClearCommand extends Command {
public static final String COMMAND_WORD = "clear";
- public static final String MESSAGE_SUCCESS = "Address book has been cleared!";
+ public static final String MESSAGE_SUCCESS = "NUSocials has been cleared!";
@Override
diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
index 02fd256acba..385d0865709 100644
--- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java
+++ b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
@@ -2,12 +2,15 @@
import static java.util.Objects.requireNonNull;
+import java.util.Arrays;
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.event.Event;
+import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
/**
@@ -18,30 +21,126 @@ public class DeleteCommand extends Command {
public static final String COMMAND_WORD = "delete";
public static final String MESSAGE_USAGE = COMMAND_WORD
- + ": Deletes the person identified by the index number used in the displayed person list.\n"
+ + ": Deletes the person identified by the index number(s) used in the displayed person list.\n"
+ "Parameters: INDEX (must be a positive integer)\n"
- + "Example: " + COMMAND_WORD + " 1";
+ + "Example: " + COMMAND_WORD + " 1\n"
+ + "OR\n"
+ + "Parameters: INDEX... (all indexes must be unique and positive integers)\n"
+ + "Example: " + COMMAND_WORD + " 1 3 5";
- public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s";
+ public static final String MESSAGE_DELETE_PERSON_SUCCESS = "DELETED PERSON: %n%1$s";
+ public static final String MESSAGE_DELETE_MULTIPLE_PERSON_SUCCESS = "DELETED PERSONS: %n%1$s";
+
+ private final Index[] targetIndexArr;
private final Index targetIndex;
+ /**
+ * Constructor for DeleteCommand, for singular deletion
+ *
+ * @param targetIndex the person to be deleted
+ */
public DeleteCommand(Index targetIndex) {
this.targetIndex = targetIndex;
+ this.targetIndexArr = new Index[]{targetIndex};
+ }
+
+ /**
+ * Constructor for DeleteCommand, for multiple deletions
+ *
+ * @param targetIndexArr the persons to be deleted
+ */
+ public DeleteCommand(Index[] targetIndexArr) {
+ this.targetIndexArr = targetIndexArr;
+ this.targetIndex = targetIndexArr[0];
+ }
+
+ /**
+ * In the multiple deletion case, ensures all user input indexes are within range of the list.
+ *
+ * @param listSize the size of the last displayed list.
+ * @return true if every index is within the range of the list.
+ */
+ private boolean checkIndexRange(int listSize) {
+ boolean result = true;
+ for (Index target : targetIndexArr) {
+ result = result && (target.getZeroBased() < listSize);
+ }
+ return result;
+ }
+
+ /**
+ * Extracts the information before deletion for the success message.
+ *
+ * @param lastShownList the last displayed list.
+ * @return a string containing all the information of the person(s) to be deleted.
+ */
+ private String extractDeletedInfo(List lastShownList) {
+ final StringBuilder deletedPersonOrPersons = new StringBuilder();
+ for (int i = 0; i < targetIndexArr.length; i++) {
+ Index target = targetIndexArr[i];
+ Person personToDelete = lastShownList.get(target.getZeroBased());
+ if (i > 0) {
+ deletedPersonOrPersons.append(System.lineSeparator());
+ }
+ deletedPersonOrPersons.append(personToDelete);
+ }
+ return deletedPersonOrPersons.toString();
+ }
+
+ /**
+ * Deletes the persons specified in the targetIndexArr.
+ * A copy of targetIndexArr is created for defensive programming.
+ * targetIndexArrClone is sorted in descending order so that the deletion process will not delete the wrong person.
+ * Example: If index 1 is deleted first, the original index 2 becomes index 1.
+ *
+ * @param model the addressbook model
+ * @param lastShownList the last displayed person list
+ * @param lastEventList the last display event list
+ */
+ private void deleteFromList(Model model, List lastShownList, List lastEventList) {
+ Index[] targetIndexArrClone = targetIndexArr.clone();
+ Arrays.sort(targetIndexArrClone);
+ for (Index target : targetIndexArrClone) {
+ Person personToDelete = lastShownList.get(target.getZeroBased());
+ updateEvents(model, personToDelete, lastEventList);
+ model.deletePerson(personToDelete);
+ }
+ }
+
+ private void updateEvents(Model model, Person person, List lastEventList) {
+ Name name = person.getName();
+
+ for (int i = 0; i < lastEventList.size(); i++) {
+ Event currEvent = lastEventList.get(i);
+ List editedParticipants = currEvent.getParticipants();
+ if (editedParticipants.contains(name) && editedParticipants.size() == 1) {
+ model.deleteEvent(currEvent);
+ } else if (editedParticipants.contains(name) && editedParticipants.size() > 1) {
+ editedParticipants.remove(name);
+ Event editedEvent = new Event(currEvent.getEventName(), currEvent.getEventInfo(), editedParticipants,
+ currEvent.getDateTime());
+ model.setEvent(currEvent, editedEvent);
+ }
+ }
}
@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
List lastShownList = model.getFilteredPersonList();
+ List lastEventList = model.getFilteredEventList();
+ int lastShownListSize = lastShownList.size();
- if (targetIndex.getZeroBased() >= lastShownList.size()) {
+ if (!checkIndexRange(lastShownListSize)) {
throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
}
+ String deletedPersonOrPersons = extractDeletedInfo(lastShownList);
+ deleteFromList(model, lastShownList, lastEventList);
- Person personToDelete = lastShownList.get(targetIndex.getZeroBased());
- model.deletePerson(personToDelete);
- return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete));
+ return targetIndexArr.length == 1
+ ? new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, deletedPersonOrPersons))
+ : new CommandResult(String.format(MESSAGE_DELETE_MULTIPLE_PERSON_SUCCESS, deletedPersonOrPersons));
}
@Override
diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java
index 7e36114902f..7c4eb1af51b 100644
--- a/src/main/java/seedu/address/logic/commands/EditCommand.java
+++ b/src/main/java/seedu/address/logic/commands/EditCommand.java
@@ -5,20 +5,18 @@
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
-import java.util.Collections;
-import java.util.HashSet;
+import java.util.ArrayList;
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.event.Event;
import seedu.address.model.person.Address;
import seedu.address.model.person.Email;
import seedu.address.model.person.Name;
@@ -34,20 +32,20 @@ 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. "
+ + "by the index number used in the displayed person list.\n"
+ "Existing values will be overwritten by the input values.\n"
+ "Parameters: INDEX (must be a positive integer) "
+ "[" + PREFIX_NAME + "NAME] "
+ "[" + PREFIX_PHONE + "PHONE] "
+ "[" + PREFIX_EMAIL + "EMAIL] "
- + "[" + PREFIX_ADDRESS + "ADDRESS] "
- + "[" + PREFIX_TAG + "TAG]...\n"
+ + "[" + PREFIX_ADDRESS + "ADDRESS]\n"
+ "Example: " + COMMAND_WORD + " 1 "
+ PREFIX_PHONE + "91234567 "
+ PREFIX_EMAIL + "johndoe@example.com";
- public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s";
- public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided.";
+ public static final String MESSAGE_EDIT_PERSON_SUCCESS = "EDITED PERSON:%n%1$s";
+ public static final String MESSAGE_NOT_EDITED_OR_INVALID = "At least one field to edit must be provided."
+ + "The edit command does not accept tags.";
public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book.";
private final Index index;
@@ -69,6 +67,7 @@ public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) {
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
List lastShownList = model.getFilteredPersonList();
+ List lastEventList = model.getFilteredEventList();
if (index.getZeroBased() >= lastShownList.size()) {
throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
@@ -82,24 +81,59 @@ public CommandResult execute(Model model) throws CommandException {
}
model.setPerson(personToEdit, editedPerson);
+ updateEvent(model, personToEdit, editedPerson, lastEventList);
model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson));
}
+ /**
+ * Updates the event to reflect the new participants
+ * @param model the current model
+ * @param personToEdit the person that is being edited
+ * @param editedPerson the person that has been edited
+ * @param lastEventList the event list
+ */
+ public void updateEvent(Model model, Person personToEdit, Person editedPerson, List lastEventList) {
+ Name oldName = personToEdit.getName();
+ Name newName = editedPerson.getName();
+
+ for (int i = 0; i < lastEventList.size(); i++) {
+ Event currEvent = lastEventList.get(i);
+ List participants = currEvent.getParticipants();
+ if (participants.remove(oldName)) {
+ participants.add(newName);
+ }
+ model.setEvent(currEvent, new Event(currEvent.getEventName(), currEvent.getEventInfo(), participants,
+ currEvent.getDateTime()));
+ }
+ }
+
/**
* 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;
+ List updatedEducations = editPersonDescriptor.getEducations().isEmpty()
+ ? personToEdit.getEducations()
+ : editPersonDescriptor.getEducations();
+ List updatedCcas = editPersonDescriptor.getCcas().isEmpty()
+ ? personToEdit.getCcas()
+ : editPersonDescriptor.getCcas();
+ List updatedInternships = editPersonDescriptor.getInternships().isEmpty()
+ ? personToEdit.getInternships()
+ : editPersonDescriptor.getInternships();
+ List updatedModules = editPersonDescriptor.getModules().isEmpty()
+ ? personToEdit.getModules()
+ : editPersonDescriptor.getModules();
Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName());
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);
+ return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress,
+ updatedEducations, updatedInternships, updatedModules, updatedCcas);
}
@Override
@@ -129,7 +163,10 @@ public static class EditPersonDescriptor {
private Phone phone;
private Email email;
private Address address;
- private Set tags;
+ private List educations = new ArrayList<>();
+ private List internships = new ArrayList<>();
+ private List modules = new ArrayList<>();
+ private List ccas = new ArrayList<>();
public EditPersonDescriptor() {}
@@ -142,14 +179,21 @@ public EditPersonDescriptor(EditPersonDescriptor toCopy) {
setPhone(toCopy.phone);
setEmail(toCopy.email);
setAddress(toCopy.address);
- setTags(toCopy.tags);
+ setEducations(toCopy.educations);
+ setInternships(toCopy.internships);
+ setModules(toCopy.modules);
+ setCcas(toCopy.ccas);
}
/**
* Returns true if at least one field is edited.
*/
public boolean isAnyFieldEdited() {
- return CollectionUtil.isAnyNonNull(name, phone, email, address, tags);
+ return CollectionUtil.isAnyNonNull(name, phone, email, address)
+ && educations.isEmpty()
+ && internships.isEmpty()
+ && modules.isEmpty()
+ && ccas.isEmpty();
}
public void setName(Name name) {
@@ -184,21 +228,36 @@ public Optional getAddress() {
return Optional.ofNullable(address);
}
- /**
- * Sets {@code tags} to this object's {@code tags}.
- * A defensive copy of {@code tags} is used internally.
- */
- public void setTags(Set tags) {
- this.tags = (tags != null) ? new HashSet<>(tags) : null;
+ public void setEducations(List tags) {
+ this.educations = tags;
}
- /**
- * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException}
- * if modification is attempted.
- * Returns {@code Optional#empty()} if {@code tags} is null.
- */
- public Optional> getTags() {
- return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty();
+ public List getEducations() {
+ return educations;
+ }
+
+ public void setInternships(List tags) {
+ this.internships = tags;
+ }
+
+ public List getInternships() {
+ return internships;
+ }
+
+ public void setModules(List tags) {
+ this.modules = tags;
+ }
+
+ public List getModules() {
+ return modules;
+ }
+
+ public void setCcas(List tags) {
+ this.ccas = tags;
+ }
+
+ public List getCcas() {
+ return ccas;
}
@Override
@@ -220,7 +279,10 @@ public boolean equals(Object other) {
&& getPhone().equals(e.getPhone())
&& getEmail().equals(e.getEmail())
&& getAddress().equals(e.getAddress())
- && getTags().equals(e.getTags());
+ && getEducations().equals(e.getEducations())
+ && getInternships().equals(e.getInternships())
+ && getModules().equals(e.getModules())
+ && getCcas().equals(e.getCcas());
}
}
}
diff --git a/src/main/java/seedu/address/logic/commands/EventCommand.java b/src/main/java/seedu/address/logic/commands/EventCommand.java
new file mode 100644
index 00000000000..8ac2f32d8ab
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/EventCommand.java
@@ -0,0 +1,124 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EVENT_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INFO;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TIME;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+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.event.DateTime;
+import seedu.address.model.event.Event;
+import seedu.address.model.event.EventName;
+import seedu.address.model.event.Information;
+import seedu.address.model.person.Name;
+import seedu.address.model.person.Person;
+
+public class EventCommand extends Command {
+ public static final String COMMAND_WORD = "event";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Tags an event to the person identified by the index "
+ + "number. \nAll prefix must be used and each prefix must be used only once!"
+ + "\nParameters: INDEX (must be a positive integer) " + PREFIX_EVENT_NAME + "EVENT NAME " + PREFIX_INFO
+ + "EVENT DETAILS " + PREFIX_DATE + "yyyy-MM-dd " + PREFIX_TIME + "HH:mm";
+ public static final String MESSAGE_ARGUMENTS = "TAGGED THE FOLLOWING EVENT TO %1$s:" + "\n%2$s";
+ public static final String MESSAGE_DUPLICATE_EVENT = "This event already exists in the address book";
+ public static final String MESSAGE_TOO_MANY_PREFIXES = "At most one prefix for basic particulars can be provided";
+
+
+ private final Index[] indexes;
+ private final EventName name;
+ private final Information information;
+ private final DateTime dateTime;
+ private final Set names;
+
+ /**
+ * Constructor for EventCommand.
+ * @param indexes the indexes of the persons tagged to this event
+ * @param name the name of the event
+ * @param information the details of the event
+ * @param dateTime the date and time of the event
+ */
+ public EventCommand(Index[] indexes, EventName name, Information information, DateTime dateTime) {
+ requireAllNonNull(indexes, name, information, dateTime);
+
+ this.indexes = indexes;
+ this.name = name;
+ this.information = information;
+ this.dateTime = dateTime;
+ this.names = new HashSet<>();
+ }
+
+ /**
+ * In the multiple deletion case, ensures all user input indexes are within range of the list.
+ *
+ * @param listSize the size of the last displayed list.
+ * @return true if every index is within the range of the list.
+ */
+ private boolean checkIndexRange(int listSize) {
+ boolean result = true;
+ for (Index target : indexes) {
+ result = result && (target.getZeroBased() < listSize);
+ }
+ return result;
+ }
+
+ private Event createEvent(List lst, Index[] indexes) {
+ Set tempList = new HashSet<>();
+ for (Index currIndex : indexes) {
+ tempList.add(lst.get(currIndex.getZeroBased()).getName());
+ }
+ return new Event(this.name, this.information, new ArrayList<>(tempList), this.dateTime);
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredPersonList();
+ int listSize = lastShownList.size();
+
+ if (!checkIndexRange(listSize)) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ // Creating the event with the list of participants provided
+ Event currEvent = createEvent(lastShownList, indexes);
+
+ // Tagging the event to each participant
+ for (Index currIndex : indexes) {
+ Person currPerson = lastShownList.get(currIndex.getZeroBased());
+ names.add(currPerson.getName().toString());
+ }
+ if (model.hasEvent(currEvent)) {
+ throw new CommandException(MESSAGE_DUPLICATE_EVENT);
+ }
+
+ model.addEvent(currEvent);
+ model.updateFilteredEventList(Model.PREDICATE_SHOW_UPCOMING_EVENTS);
+ return new CommandResult(String.format(MESSAGE_ARGUMENTS, names, currEvent));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof EventCommand)) {
+ return false;
+ }
+
+ EventCommand e = (EventCommand) other;
+ return name.equals(e.name) && information.equals(e.information)
+ && Arrays.equals(indexes, e.indexes) && dateTime.equals(e.dateTime);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java
index d6b19b0a0de..629e3cdf305 100644
--- a/src/main/java/seedu/address/logic/commands/FindCommand.java
+++ b/src/main/java/seedu/address/logic/commands/FindCommand.java
@@ -1,42 +1,253 @@
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_CCA;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EDUCATION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INTERNSHIP;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MODULE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
import seedu.address.commons.core.Messages;
+import seedu.address.commons.util.CollectionUtil;
import seedu.address.model.Model;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import seedu.address.model.person.Address;
+import seedu.address.model.person.Name;
+import seedu.address.model.person.Person;
+import seedu.address.model.person.Phone;
+import seedu.address.model.tag.Tag;
+
/**
- * Finds and lists all persons in address book whose name contains any of the argument keywords.
- * Keyword matching is case insensitive.
+ * Finds and lists all persons or events in address book whose name contains any of the argument keywords.
+ * Keyword matching is case-insensitive.
*/
public class FindCommand extends Command {
public static final String COMMAND_WORD = "find";
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of "
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose fields contain any of "
+ "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n"
- + "Parameters: KEYWORD [MORE_KEYWORDS]...\n"
- + "Example: " + COMMAND_WORD + " alice bob charlie";
+ + "Use find -s to find all person where fields must contain all the keywords provided.\n"
+ + "Only 1 prefix for basic particular (n/, p/, e/, a/) can be provided\n"
+ + "Parameters: "
+ + "[" + PREFIX_NAME + "NAME] "
+ + "[" + PREFIX_PHONE + "PHONE] "
+ + "[" + PREFIX_EMAIL + "EMAIL] "
+ + "[" + PREFIX_ADDRESS + "ADDRESS] "
+ + "[" + PREFIX_EDUCATION + "EDUCATION]"
+ + "[" + PREFIX_INTERNSHIP + "INTERNSHIP]"
+ + "[" + PREFIX_MODULE + "MODULE]"
+ + "[" + PREFIX_CCA + "CCA]\n"
+ + "Example: " + COMMAND_WORD + " n/alice e/gmail.com i/Facebook";
+
+ public static final String MESSAGE_NO_PARAMETERS = "At least one field must be provided.";
+ public static final String MESSAGE_TOO_MANY_PREFIXES = "At most one prefix for basic particulars can be provided";
- private final NameContainsKeywordsPredicate predicate;
+ private final Predicate predicate;
- public FindCommand(NameContainsKeywordsPredicate predicate) {
+ public FindCommand(Predicate predicate) {
this.predicate = predicate;
}
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
+
model.updateFilteredPersonList(predicate);
+
return new CommandResult(
String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size()));
}
@Override
public boolean equals(Object other) {
- return other == this // short circuit if same object
- || (other instanceof FindCommand // instanceof handles nulls
- && predicate.equals(((FindCommand) other).predicate)); // state check
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof FindCommand)) {
+ return false;
+ }
+
+ FindCommand e = (FindCommand) other;
+ System.out.println(predicate.equals(e.predicate));
+ return predicate.equals(e.predicate);
+ }
+
+ /**
+ * Stores the details to find the person with. Each non-empty field value will
+ * be added to the predicate to filter the contact list
+ */
+ public static class FindPersonDescriptor {
+ private List names;
+ private List phones;
+ private List emails; //This is String to facilitate the use of partial emails for searching.
+ private List addresses;
+ private List educations;
+ private List internships;
+ private List modules;
+ private List ccas;
+
+
+ public FindPersonDescriptor() {
+ }
+
+ /**
+ * Copy constructor.
+ */
+ public FindPersonDescriptor(FindPersonDescriptor toCopy) {
+ setNames(toCopy.names);
+ setPhones(toCopy.phones);
+ setEmails(toCopy.emails);
+ setAddresses(toCopy.addresses);
+ setEducations(toCopy.educations);
+ setInternships(toCopy.internships);
+ setModules(toCopy.modules);
+ setCcas(toCopy.ccas);
+ }
+
+ /**
+ * Returns true if at least one field is searched for.
+ */
+ public boolean isAnyFieldPresent() {
+ return CollectionUtil.isAnyNonNull(names, phones, emails, addresses,
+ educations, internships, modules, ccas);
+ }
+
+ public void setNames(List names) {
+ this.names = names;
+ }
+
+ public Optional> getNames() {
+ return Optional.ofNullable(names);
+ }
+
+ public Optional> getStringNames() {
+ return getNames().map(names ->
+ names.stream().map(name -> name.fullName).collect(Collectors.toList()));
+ }
+
+ public void setPhones(List phones) {
+ this.phones = phones;
+ }
+
+ public Optional> getPhones() {
+ return Optional.ofNullable(phones);
+ }
+
+ public Optional> getStringPhones() {
+ return getPhones().map(list ->
+ list.stream().map(name -> name.value).collect(Collectors.toList()));
+ }
+
+ public void setEmails(List emails) {
+ this.emails = emails;
+ }
+
+ public Optional> getEmails() {
+ return Optional.ofNullable(emails);
+ }
+
+ public Optional> getStringEmails() {
+ return getEmails();
+ }
+
+ public void setAddresses(List addresses) {
+ this.addresses = addresses;
+ }
+
+ public Optional> getAddresses() {
+ return Optional.ofNullable(addresses);
+ }
+
+ public Optional> getStringAddresses() {
+ return getAddresses().map(list ->
+ list.stream().map(name -> name.value).collect(Collectors.toList()));
+ }
+
+ //Methods for getting and setting tag lists
+
+ public void setEducations(List tags) {
+ this.educations = tags;
+ }
+
+ public Optional> getEducations() {
+ return Optional.ofNullable(educations);
+ }
+
+ public Optional> getStringEducations() {
+ return getEducations().map(list ->
+ list.stream().map(Tag::getTagString).collect(Collectors.toList()));
+ }
+
+ public void setInternships(List tags) {
+ this.internships = tags;
+ }
+
+ public Optional> getInternships() {
+ return Optional.ofNullable(internships);
+ }
+
+ public Optional> getStringInternships() {
+ return getInternships().map(list ->
+ list.stream().map(Tag::getTagString).collect(Collectors.toList()));
+ }
+
+ public void setModules(List tags) {
+ this.modules = tags;
+ }
+
+ public Optional> getModules() {
+ return Optional.ofNullable(modules);
+ }
+
+ public Optional> getStringModules() {
+ return getModules().map(list ->
+ list.stream().map(Tag::getTagString).collect(Collectors.toList()));
+ }
+
+ public void setCcas(List tags) {
+ this.ccas = tags;
+ }
+
+ public Optional> getCcas() {
+ return Optional.ofNullable(ccas);
+ }
+
+ public Optional> getStringCcas() {
+ return getCcas().map(list ->
+ list.stream().map(Tag::getTagString).collect(Collectors.toList()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // short circuit if same object
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof FindCommand.FindPersonDescriptor)) {
+ return false;
+ }
+
+ // state check
+ FindPersonDescriptor e = (FindPersonDescriptor) other;
+
+ return getNames().equals(e.getNames())
+ && getPhones().equals(e.getPhones())
+ && getEmails().equals(e.getEmails())
+ && getAddresses().equals(e.getAddresses());
+ }
}
}
+
+
diff --git a/src/main/java/seedu/address/logic/commands/FindEventCommand.java b/src/main/java/seedu/address/logic/commands/FindEventCommand.java
new file mode 100644
index 00000000000..f12fa8351fe
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/FindEventCommand.java
@@ -0,0 +1,147 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.commons.util.CollectionUtil;
+import seedu.address.model.Model;
+import seedu.address.model.event.DateTime;
+import seedu.address.model.event.Event;
+import seedu.address.model.event.EventName;
+import seedu.address.model.event.Information;
+import seedu.address.model.person.Name;
+
+public class FindEventCommand extends Command {
+ public static final String COMMAND_WORD = "find";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all events whose details contain any of "
+ + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n"
+ + "Parameters: KEYWORD [MORE_KEYWORDS]...\n"
+ + "Example: " + COMMAND_WORD + " name/lunch appointment";
+
+ private final Predicate predicate;
+
+ public FindEventCommand(Predicate predicate) {
+ this.predicate = predicate;
+ }
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+
+ model.updateFilteredEventList(predicate);
+
+ return new CommandResult(
+ String.format(Messages.MESSAGE_EVENTS_LISTED_OVERVIEW, model.getFilteredEventList().size()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof FindEventCommand // instanceof handles nulls
+ && predicate.equals(((FindEventCommand) other).predicate)); // state check
+ }
+
+ /**
+ * Stores the details to find an event with. Each non-empty field value will
+ * be added to the predicate to filter the contact list
+ */
+ public static class FindEventDescriptor {
+ private List names;
+ private List informations;
+ private List participants;
+ private List dateTimes;
+
+ public FindEventDescriptor() {
+ }
+
+ /**
+ * Copy constructor.
+ */
+ public FindEventDescriptor(FindEventCommand.FindEventDescriptor toCopy) {
+ setNames(toCopy.names);
+ setInformations(toCopy.informations);
+ setParticipants(toCopy.participants);
+ setDateTimes(toCopy.dateTimes);
+ }
+
+ /**
+ * Returns true if at least one field is searched for.
+ */
+ public boolean isAnyFieldPresent() {
+ return CollectionUtil.isAnyNonNull(names, informations, participants, dateTimes);
+ }
+
+ public void setNames(List names) {
+ this.names = names;
+ }
+
+ public Optional> getNames() {
+ return Optional.ofNullable(names);
+ }
+
+ public Optional> getStringNames() {
+ return getNames().map(names ->
+ names.stream().map(name -> name.value).collect(Collectors.toList()));
+ }
+
+ public void setInformations(List informations) {
+ this.informations = informations;
+ }
+
+ public Optional> getInformations() {
+ return Optional.ofNullable(informations);
+ }
+
+ public Optional> getStringInformations() {
+ return getInformations().map(list ->
+ list.stream().map(info -> info.value).collect(Collectors.toList()));
+ }
+
+ public void setParticipants(List participants) {
+ this.participants = participants;
+ }
+
+ public Optional> getParticipants() {
+ return Optional.ofNullable(participants);
+ }
+
+ public Optional> getStringParticipants() {
+ return getParticipants().map(list ->
+ list.stream().map(participants -> participants.fullName).collect(Collectors.toList()));
+ }
+
+ public void setDateTimes(List dateTimes) {
+ this.dateTimes = dateTimes;
+ }
+
+ public Optional> getDateTimes() {
+ return Optional.ofNullable(dateTimes);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // short circuit if same object
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof FindEventCommand.FindEventDescriptor)) {
+ return false;
+ }
+
+ // state check
+ FindEventCommand.FindEventDescriptor e = (FindEventCommand.FindEventDescriptor) other;
+
+ return getNames().equals(e.getNames())
+ && getNames().equals(e.getInformations())
+ && getInformations().equals(e.getParticipants());
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java
index 84be6ad2596..59b1684a3ca 100644
--- a/src/main/java/seedu/address/logic/commands/ListCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ListCommand.java
@@ -12,13 +12,14 @@ 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 PERSONS";
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(MESSAGE_SUCCESS);
+
+ return new CommandResult(MESSAGE_SUCCESS, false, false);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/RemoveTagCommand.java b/src/main/java/seedu/address/logic/commands/RemoveTagCommand.java
new file mode 100644
index 00000000000..7f4067d9661
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/RemoveTagCommand.java
@@ -0,0 +1,144 @@
+package seedu.address.logic.commands;
+
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_CCA;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EDUCATION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INTERNSHIP;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MODULE;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.person.Person;
+import seedu.address.model.tag.Tag;
+
+public class RemoveTagCommand extends Command {
+
+ public static final String COMMAND_WORD = "removetag";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Removes the specified tags from the person identified "
+ + "by the index number used in the last person listing. "
+ + "If there are non-existing tags, the command will not work.\n"
+ + "Parameters: INDEX (must be a positive integer) "
+ + PREFIX_EDUCATION + "[EDUCATION]... "
+ + PREFIX_CCA + "[CCA]... "
+ + PREFIX_INTERNSHIP + "[INTERNSHIP]... "
+ + PREFIX_MODULE + "[MODULE]..."
+ + "\nExample: " + COMMAND_WORD + " 1 "
+ + PREFIX_EDUCATION + "Computer Science "
+ + PREFIX_CCA + "Tennis "
+ + PREFIX_INTERNSHIP + "Grab "
+ + PREFIX_MODULE + "CS2040S";
+ public static final String MESSAGE_NO_PARAMETERS = "At least 1 field must be used and not blank.";
+ public static final String MESSAGE_NO_TAG_FOUND = "Deleting a non-existent tag cannot be done. "
+ + "All specified tags must be present in %1$s";
+ public static final String MESSAGE_ARGUMENTS = "TAGS REMOVED FROM %1$s \n-Current Tags- \nEducation: %2$s "
+ + "\nInternship: %3$s " + "\nModule: %4$s " + "\nCCA: %5$s";
+
+ private final Index index;
+ private final List educations;
+ private final List internships;
+ private final List modules;
+ private final List ccas;
+
+ /**
+ * @param index of the person in the filtered person list to tag
+ * @param education of the person to be updated to
+ * @param internship of the person to be updated to
+ * @param module of the person to be updated to
+ * @param cca of the person to be updated to
+ */
+ public RemoveTagCommand(Index index, List education, List internship, List module, List cca) {
+ requireAllNonNull(index, education, internship, module, cca);
+
+ this.index = index;
+ this.educations = education;
+ this.internships = internship;
+ this.modules = module;
+ this.ccas = cca;
+ }
+
+ private boolean isAllEmpty() {
+ return educations.isEmpty() && internships.isEmpty() && modules.isEmpty() && ccas.isEmpty();
+ }
+
+ /**
+ * Generates a command execution success message based on whether
+ * the tag is added to or removed from
+ * {@code personToEdit}.
+ */
+ private String generateSuccessMessage(Person personToEdit) {
+ return String.format(MESSAGE_ARGUMENTS,
+ personToEdit.getName(),
+ personToEdit.getEducations(),
+ personToEdit.getInternships(),
+ personToEdit.getModules(),
+ personToEdit.getCcas());
+ }
+
+ private boolean isAllTagsPresent(Set eduSet, Set internSet, Set moduleSet, Set ccaSet) {
+ return eduSet.containsAll(educations) && internSet.containsAll(internships)
+ && moduleSet.containsAll(modules) && moduleSet.containsAll(modules) && ccaSet.containsAll(ccas);
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ List lastShownList = model.getFilteredPersonList();
+
+ if (index.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ if (isAllEmpty()) {
+ throw new CommandException(MESSAGE_NO_PARAMETERS);
+ }
+
+ Person personToEdit = lastShownList.get(index.getZeroBased());
+ Set currEducations = new HashSet<>(personToEdit.getEducations());
+ Set currInternships = new HashSet<>(personToEdit.getInternships());
+ Set currModules = new HashSet<>(personToEdit.getModules());
+ Set currCcas = new HashSet<>(personToEdit.getCcas());
+
+ if (!isAllTagsPresent(currEducations, currInternships, currModules, currCcas)) {
+ throw new CommandException(String.format(MESSAGE_NO_TAG_FOUND, personToEdit.getName()));
+ }
+ currEducations.removeAll(educations);
+ currInternships.removeAll(internships);
+ currModules.removeAll(modules);
+ currCcas.removeAll(ccas);
+
+
+ Person editedPerson = new Person(
+ personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(), personToEdit.getAddress(),
+ new ArrayList<>(currEducations), new ArrayList<>(currInternships),
+ new ArrayList<>(currModules), new ArrayList<>(currCcas));
+
+ model.setPerson(personToEdit, editedPerson);
+ model.updateFilteredPersonList(Model.PREDICATE_SHOW_ALL_PERSONS);
+
+ return new CommandResult(generateSuccessMessage(editedPerson));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // short circuit if same object
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof RemoveTagCommand)) {
+ return false;
+ }
+
+ // state check
+ RemoveTagCommand e = (RemoveTagCommand) other;
+ return index.equals(e.index) && educations.equals(e.educations) && internships.equals(e.internships)
+ && modules.equals(e.modules) && ccas.equals(e.ccas);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ShowEventsCommand.java b/src/main/java/seedu/address/logic/commands/ShowEventsCommand.java
new file mode 100644
index 00000000000..d19dd718d61
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ShowEventsCommand.java
@@ -0,0 +1,52 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.function.Predicate;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.event.Event;
+
+public class ShowEventsCommand extends Command {
+
+ public static final String COMMAND_WORD = "showevents";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Filters the events list to show either upcoming, past or all events. \n"
+ + "By defualt shows all events, unless one of the parameter flags is specified.\n"
+ + "Parameter flags: "
+ + "[-upcoming] [-past]\n"
+ + "Example: " + COMMAND_WORD + "-upcoming";
+
+ private final Predicate predicate;
+
+ public ShowEventsCommand(Predicate predicate) {
+ this.predicate = predicate;
+ }
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+
+ model.updateFilteredEventList(predicate);
+
+ return new CommandResult(
+ String.format(Messages.MESSAGE_EVENTS_LISTED_OVERVIEW, model.getFilteredEventList().size()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof ShowEventsCommand)) {
+ return false;
+ }
+
+ ShowEventsCommand e = (ShowEventsCommand) other;
+ return predicate.equals(e.predicate);
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/commands/TagCommand.java b/src/main/java/seedu/address/logic/commands/TagCommand.java
new file mode 100644
index 00000000000..0b981a9ac7b
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/TagCommand.java
@@ -0,0 +1,136 @@
+package seedu.address.logic.commands;
+
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_CCA;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EDUCATION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INTERNSHIP;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MODULE;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.person.Person;
+import seedu.address.model.tag.Tag;
+
+/**
+ * Tags additional information of an existing person in the address book.
+ */
+public class TagCommand extends Command {
+ public static final String COMMAND_WORD = "tag";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Tags additional information to the person identified "
+ + "by the index number used in the last person listing. "
+ + "If there are existing tags, the new tags will be appended to them.\n"
+ + "Parameters: INDEX (must be a positive integer) "
+ + PREFIX_EDUCATION + "[EDUCATION]... "
+ + PREFIX_CCA + "[CCA]... "
+ + PREFIX_INTERNSHIP + "[INTERNSHIP]... "
+ + PREFIX_MODULE + "[MODULE]..."
+ + "\nExample: " + COMMAND_WORD + " 1 "
+ + PREFIX_EDUCATION + "Computer Science "
+ + PREFIX_CCA + "Bouldering "
+ + PREFIX_INTERNSHIP + "GIC "
+ + PREFIX_MODULE + "CS2040S";
+ public static final String MESSAGE_NO_PARAMETERS = "At least 1 field must be used and not blank.";
+ public static final String MESSAGE_ARGUMENTS = "TAGGED TO %1$s \n-Current Tags- \nEducation: %2$s"
+ + "\nInternship: %3$s " + "\nModule: %4$s " + "\nCCA: %5$s";
+
+ private final Index index;
+ private final List educations;
+ private final List internships;
+ private final List modules;
+ private final List ccas;
+
+ /**
+ * @param index of the person in the filtered person list to tag
+ * @param education of the person to be updated to
+ * @param internship of the person to be updated to
+ * @param module of the person to be updated to
+ * @param cca of the person to be updated to
+ */
+ public TagCommand(Index index, List education, List internship, List module, List cca) {
+ requireAllNonNull(index, education, internship, module, cca);
+
+ this.index = index;
+ this.educations = education;
+ this.internships = internship;
+ this.modules = module;
+ this.ccas = cca;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ List lastShownList = model.getFilteredPersonList();
+
+ if (index.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ if (isAllEmpty()) {
+ throw new CommandException(MESSAGE_NO_PARAMETERS);
+ }
+
+ Person personToEdit = lastShownList.get(index.getZeroBased());
+ Set currEducations = new HashSet<>(personToEdit.getEducations());
+ Set currInternships = new HashSet<>(personToEdit.getInternships());
+ Set currModules = new HashSet<>(personToEdit.getModules());
+ Set currCcas = new HashSet<>(personToEdit.getCcas());
+
+ currEducations.addAll(educations);
+ currInternships.addAll(internships);
+ currModules.addAll(modules);
+ currCcas.addAll(ccas);
+
+ Person editedPerson = new Person(
+ personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(), personToEdit.getAddress(),
+ new ArrayList<>(currEducations), new ArrayList<>(currInternships),
+ new ArrayList<>(currModules), new ArrayList<>(currCcas));
+
+ model.setPerson(personToEdit, editedPerson);
+ model.updateFilteredPersonList(Model.PREDICATE_SHOW_ALL_PERSONS);
+
+
+ return new CommandResult(generateSuccessMessage(editedPerson));
+ }
+
+ private boolean isAllEmpty() {
+ return educations.isEmpty() && internships.isEmpty() && modules.isEmpty() && ccas.isEmpty();
+ }
+
+ /**
+ * Generates a command execution success message based on whether
+ * the tag is added to or removed from
+ * {@code personToEdit}.
+ */
+ private String generateSuccessMessage(Person personToEdit) {
+ return String.format(MESSAGE_ARGUMENTS,
+ personToEdit.getName(),
+ personToEdit.getEducations(),
+ personToEdit.getInternships(),
+ personToEdit.getModules(),
+ personToEdit.getCcas());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // short circuit if same object
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof TagCommand)) {
+ return false;
+ }
+
+ // state check
+ TagCommand e = (TagCommand) other;
+ return index.equals(e.index) && educations.equals(e.educations) && internships.equals(e.internships)
+ && modules.equals(e.modules) && ccas.equals(e.ccas);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
index 3b8bfa035e8..29993d526f6 100644
--- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
@@ -5,9 +5,7 @@
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-import java.util.Set;
import java.util.stream.Stream;
import seedu.address.logic.commands.AddCommand;
@@ -17,7 +15,6 @@
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
@@ -31,7 +28,7 @@ public class AddCommandParser implements Parser {
*/
public AddCommand parse(String args) throws ParseException {
ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL)
|| !argMultimap.getPreamble().isEmpty()) {
@@ -42,9 +39,8 @@ public AddCommand parse(String args) throws ParseException {
Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get());
Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get());
Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get());
- Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));
- Person person = new Person(name, phone, email, address, tagList);
+ Person person = new Person(name, phone, email, address);
return new AddCommand(person);
}
diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
index 1e466792b46..52baba4794e 100644
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
@@ -7,14 +7,19 @@
import java.util.regex.Pattern;
import seedu.address.logic.commands.AddCommand;
+import seedu.address.logic.commands.CancelEventCommand;
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.EventCommand;
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.commands.RemoveTagCommand;
+import seedu.address.logic.commands.ShowEventsCommand;
+import seedu.address.logic.commands.TagCommand;
import seedu.address.logic.parser.exceptions.ParseException;
/**
@@ -53,6 +58,9 @@ public Command parseCommand(String userInput) throws ParseException {
case DeleteCommand.COMMAND_WORD:
return new DeleteCommandParser().parse(arguments);
+ case TagCommand.COMMAND_WORD:
+ return new TagCommandParser().parse(arguments);
+
case ClearCommand.COMMAND_WORD:
return new ClearCommand();
@@ -62,6 +70,18 @@ public Command parseCommand(String userInput) throws ParseException {
case ListCommand.COMMAND_WORD:
return new ListCommand();
+ case ShowEventsCommand.COMMAND_WORD:
+ return new ShowEventsCommandParser().parse(arguments);
+
+ case RemoveTagCommand.COMMAND_WORD:
+ return new RemoveTagCommandParser().parse(arguments);
+
+ case EventCommand.COMMAND_WORD:
+ return new EventCommandParser().parse(arguments);
+
+ case CancelEventCommand.COMMAND_WORD:
+ return new CancelEventCommandParser().parse(arguments);
+
case ExitCommand.COMMAND_WORD:
return new ExitCommand();
diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
index 5c9aebfa488..b14e0745439 100644
--- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
+++ b/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
@@ -1,10 +1,23 @@
package seedu.address.logic.parser;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EVENT_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INFO;
+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_TIME;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
+import seedu.address.logic.commands.EventCommand;
+import seedu.address.logic.commands.FindCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
/**
* Tokenizes arguments string of the form: {@code preamble value value ...}
* e.g. {@code some preamble text t/ 11.00 t/12.00 k/ m/ July} where prefixes are {@code t/ k/ m/}.
@@ -37,14 +50,21 @@ public static ArgumentMultimap tokenize(String argsString, Prefix... prefixes) {
*/
private static List findAllPrefixPositions(String argsString, Prefix... prefixes) {
return Arrays.stream(prefixes)
- .flatMap(prefix -> findPrefixPositions(argsString, prefix).stream())
+ .flatMap(prefix -> {
+ try {
+ return findPrefixPositions(argsString, prefix).stream();
+ } catch (ParseException e) {
+ e.initCause(new ParseException(FindCommand.MESSAGE_TOO_MANY_PREFIXES));
+ }
+ return null;
+ })
.collect(Collectors.toList());
}
/**
* {@see findAllPrefixPositions}
*/
- private static List findPrefixPositions(String argsString, Prefix prefix) {
+ private static List findPrefixPositions(String argsString, Prefix prefix) throws ParseException {
List positions = new ArrayList<>();
int prefixPosition = findPrefixPosition(argsString, prefix.getPrefix(), 0);
@@ -54,6 +74,16 @@ private static List findPrefixPositions(String argsString, Prefi
prefixPosition = findPrefixPosition(argsString, prefix.getPrefix(), prefixPosition);
}
+ if ((prefix.equals(PREFIX_NAME) || prefix.equals(PREFIX_ADDRESS)
+ || prefix.equals(PREFIX_EMAIL) || prefix.equals(PREFIX_PHONE))
+ && positions.size() > 1) {
+ throw new ParseException(FindCommand.MESSAGE_TOO_MANY_PREFIXES);
+ }
+
+ if ((prefix.equals(PREFIX_EVENT_NAME) || prefix.equals(PREFIX_INFO) || prefix.equals(PREFIX_DATE)
+ || prefix.equals(PREFIX_TIME)) && positions.size() > 1) {
+ throw new ParseException(EventCommand.MESSAGE_TOO_MANY_PREFIXES);
+ }
return positions;
}
diff --git a/src/main/java/seedu/address/logic/parser/CancelEventCommandParser.java b/src/main/java/seedu/address/logic/parser/CancelEventCommandParser.java
new file mode 100644
index 00000000000..c66f391d784
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/CancelEventCommandParser.java
@@ -0,0 +1,28 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.CancelEventCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new CancelEventCommand object
+ */
+public class CancelEventCommandParser implements Parser {
+
+ /**
+ * Parses the give {@code String} of argument sin the context of the CancelEventCommand
+ * and returns a CancelEventCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public CancelEventCommand parse(String args) throws ParseException {
+ try {
+ Index[] indexes = ParserUtil.parseIndexes(args);
+ return new CancelEventCommand(indexes);
+ } catch (ParseException e) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, CancelEventCommand.MESSAGE_USAGE), e);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java
index 75b1a9bf119..eb28fc83742 100644
--- a/src/main/java/seedu/address/logic/parser/CliSyntax.java
+++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java
@@ -10,6 +10,16 @@ public class CliSyntax {
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_INTERNSHIP = new Prefix("i/");
+ public static final Prefix PREFIX_MODULE = new Prefix("m/");
+ public static final Prefix PREFIX_CCA = new Prefix("c/");
+ public static final Prefix PREFIX_EDUCATION = new Prefix("edu/");
+ public static final Prefix PREFIX_EVENT_NAME = new Prefix("name/");
+ public static final Prefix PREFIX_INFO = new Prefix("info/");
+ public static final Prefix PREFIX_DATE = new Prefix("d/");
+ public static final Prefix PREFIX_TIME = new Prefix("t/");
+ public static final Prefix PREFIX_PARTICIPANTS = new Prefix("part/");
+ public static final Prefix PREFIX_DATETIME = new Prefix("dt/");
+
}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
index 522b93081cc..c0704ae5256 100644
--- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
@@ -18,8 +18,8 @@ public class DeleteCommandParser implements Parser {
*/
public DeleteCommand parse(String args) throws ParseException {
try {
- Index index = ParserUtil.parseIndex(args);
- return new DeleteCommand(index);
+ Index[] indexes = ParserUtil.parseIndexes(args);
+ return new DeleteCommand(indexes);
} catch (ParseException pe) {
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe);
diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
index 845644b7dea..24944d6f718 100644
--- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
@@ -3,15 +3,15 @@
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_CCA;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EDUCATION;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INTERNSHIP;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MODULE;
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 java.util.List;
import seedu.address.commons.core.index.Index;
import seedu.address.logic.commands.EditCommand;
@@ -32,7 +32,8 @@ public class EditCommandParser implements Parser {
public EditCommand parse(String args) throws ParseException {
requireNonNull(args);
ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS,
+ PREFIX_CCA, PREFIX_EDUCATION, PREFIX_MODULE, PREFIX_INTERNSHIP);
Index index;
@@ -55,20 +56,38 @@ public EditCommand parse(String args) throws ParseException {
if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()));
}
- parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags);
+ if (argMultimap.getValue(PREFIX_CCA).isPresent()) {
+ List cca = ParserUtil.parseTagsForEdit(argMultimap.getAllValues(PREFIX_CCA), Tag.CCA);
+ editPersonDescriptor.setCcas(cca);
+ }
+ if (argMultimap.getValue(PREFIX_EDUCATION).isPresent()) {
+ List education = ParserUtil.parseTagsForEdit(argMultimap.getAllValues(PREFIX_EDUCATION),
+ Tag.EDUCATION);
+ editPersonDescriptor.setEducations(education);
+ }
+ if (argMultimap.getValue(PREFIX_MODULE).isPresent()) {
+ List module = ParserUtil.parseTagsForEdit(argMultimap.getAllValues(PREFIX_MODULE), Tag.MODULE);
+ editPersonDescriptor.setModules(module);
+ }
+ if (argMultimap.getValue(PREFIX_INTERNSHIP).isPresent()) {
+ List internship = ParserUtil.parseTagsForEdit(argMultimap.getAllValues(PREFIX_INTERNSHIP),
+ Tag.INTERNSHIP);
+ editPersonDescriptor.setInternships(internship);
+ }
if (!editPersonDescriptor.isAnyFieldEdited()) {
- throw new ParseException(EditCommand.MESSAGE_NOT_EDITED);
+ throw new ParseException(EditCommand.MESSAGE_NOT_EDITED_OR_INVALID);
}
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;
@@ -78,5 +97,5 @@ private Optional> parseTagsForEdit(Collection tags) throws Pars
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/EventCommandParser.java b/src/main/java/seedu/address/logic/parser/EventCommandParser.java
new file mode 100644
index 00000000000..5b52a631064
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/EventCommandParser.java
@@ -0,0 +1,62 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EVENT_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INFO;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TIME;
+
+import java.util.stream.Stream;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.EventCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.event.DateTime;
+import seedu.address.model.event.EventName;
+import seedu.address.model.event.Information;
+
+/**
+ * Parses input arguments and creates a new {@Code EventCommand} object
+ */
+public class EventCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the EventCommand
+ * and returns an EventCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public EventCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_EVENT_NAME, PREFIX_INFO,
+ PREFIX_TIME, PREFIX_DATE);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_EVENT_NAME, PREFIX_INFO, PREFIX_TIME, PREFIX_DATE)) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EventCommand.MESSAGE_USAGE));
+ }
+
+ Index[] indexes;
+ try {
+ indexes = ParserUtil.parseIndexes(argMultimap.getPreamble());
+ } catch (ParseException e) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EventCommand.MESSAGE_USAGE), e);
+ }
+
+ EventName eventName = ParserUtil.parseEventName(argMultimap.getValue(PREFIX_EVENT_NAME).get());
+ Information info = ParserUtil.parseInfo(argMultimap.getValue(PREFIX_INFO).get());
+ DateTime dateTime = ParserUtil.parseDateTime(argMultimap.getValue(PREFIX_DATE).get(),
+ argMultimap.getValue(PREFIX_TIME).get());
+
+ return new EventCommand(indexes, eventName, info, dateTime);
+ }
+
+ /**
+ * 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/FindAndPredicateParser.java b/src/main/java/seedu/address/logic/parser/FindAndPredicateParser.java
new file mode 100644
index 00000000000..dc33857f628
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/FindAndPredicateParser.java
@@ -0,0 +1,53 @@
+package seedu.address.logic.parser;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+
+import seedu.address.logic.commands.FindCommand;
+import seedu.address.model.person.AddressContainsKeywordsPredicateAnd;
+import seedu.address.model.person.CcaContainsKeywordsPredicateAnd;
+import seedu.address.model.person.EducationContainsKeywordsPredicateAnd;
+import seedu.address.model.person.EmailContainsKeywordsPredicateAnd;
+import seedu.address.model.person.InternshipContainsKeywordsPredicateAnd;
+import seedu.address.model.person.ModuleContainsKeywordsPredicateAnd;
+import seedu.address.model.person.NameContainsKeywordsPredicateAnd;
+import seedu.address.model.person.Person;
+import seedu.address.model.person.PhoneContainsKeywordsPredicateAnd;
+
+public class FindAndPredicateParser {
+
+ /**
+ * Parses a FindPersonDescriptor into a single predicate that is true if any of the predicates in each field
+ * of the descriptor are satisfied. A field is satisfied if any item from the list appears. The combined predicate
+ * is satisfied if all the fields are satisfied.
+ *
+ * @param personDescriptor an object describing the predicate list for each field.
+ * @return a FindCommand to be executed.
+ */
+ public FindCommand parse(FindCommand.FindPersonDescriptor personDescriptor) {
+ List> predicateList = new ArrayList<>();
+
+ personDescriptor.getStringNames().ifPresent(names ->
+ predicateList.add(new NameContainsKeywordsPredicateAnd(names)));
+ personDescriptor.getStringPhones().ifPresent(phones ->
+ predicateList.add(new PhoneContainsKeywordsPredicateAnd(phones)));
+ personDescriptor.getStringEmails().ifPresent(emails ->
+ predicateList.add(new EmailContainsKeywordsPredicateAnd(emails)));
+ personDescriptor.getStringAddresses().ifPresent(list ->
+ predicateList.add(new AddressContainsKeywordsPredicateAnd(list)));
+
+ personDescriptor.getStringEducations().ifPresent(list ->
+ predicateList.add(new EducationContainsKeywordsPredicateAnd(list)));
+ personDescriptor.getStringInternships().ifPresent(list ->
+ predicateList.add(new InternshipContainsKeywordsPredicateAnd(list)));
+ personDescriptor.getStringModules().ifPresent(list ->
+ predicateList.add(new ModuleContainsKeywordsPredicateAnd(list)));
+ personDescriptor.getStringCcas().ifPresent(list ->
+ predicateList.add(new CcaContainsKeywordsPredicateAnd(list)));
+
+ Predicate predicate = predicateList.stream().reduce(x->true, Predicate::and);
+
+ return new FindCommand(predicate);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java
index 4fb71f23103..65cd22c3654 100644
--- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/FindCommandParser.java
@@ -1,33 +1,133 @@
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_CCA;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATETIME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EDUCATION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EVENT_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INFO;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INTERNSHIP;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MODULE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PARTICIPANTS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import java.util.Arrays;
+import java.util.List;
+import seedu.address.logic.commands.Command;
import seedu.address.logic.commands.FindCommand;
+import seedu.address.logic.commands.FindCommand.FindPersonDescriptor;
+import seedu.address.logic.commands.FindEventCommand;
+import seedu.address.logic.commands.FindEventCommand.FindEventDescriptor;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import seedu.address.model.tag.Tag;
+
+
/**
* Parses input arguments and creates a new FindCommand object
*/
-public class FindCommandParser implements Parser {
+public class FindCommandParser implements Parser {
+
+ public static final String DATE_TIME_FORMAT = "Please provide the date followed by time\n"
+ + "Eg: find -e dt/2022-08-08 03:00";
+
+ public static final String EMPTY_EMAIL = "Please provide a valid string for email.";
/**
* Parses the given {@code String} of arguments in the context of the FindCommand
* and returns a FindCommand object for execution.
- * @throws ParseException if the user input does not conform the expected format
+ *
+ * @return a FindCommand object that will execute the search.
+ * @throws ParseException if the user input does not conform the expected format.
*/
- public FindCommand parse(String args) throws ParseException {
- String trimmedArgs = args.trim();
- if (trimmedArgs.isEmpty()) {
- throw new ParseException(
- String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
+ public Command parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS,
+ PREFIX_CCA, PREFIX_EDUCATION, PREFIX_MODULE, PREFIX_INTERNSHIP, PREFIX_EVENT_NAME,
+ PREFIX_INFO, PREFIX_PARTICIPANTS, PREFIX_DATETIME);
+
+ boolean isAndSearch = false;
+ boolean isEventSearch = false;
+
+ if (argMultimap.getPreamble().equals("-s")) {
+ isAndSearch = true;
+ } else if (argMultimap.getPreamble().equals("-e")) {
+ isEventSearch = true;
+ } else if (!argMultimap.getPreamble().equals("")) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
}
- String[] nameKeywords = trimmedArgs.split("\\s+");
+ if (!isEventSearch) {
+ FindPersonDescriptor personDescriptor = new FindCommand.FindPersonDescriptor();
+ if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
+ personDescriptor.setNames(ParserUtil.parseNames(argMultimap.getAllValues(PREFIX_NAME)));
+ }
+ if (argMultimap.getValue(PREFIX_PHONE).isPresent()) {
+ personDescriptor.setPhones(ParserUtil.parsePhones(argMultimap.getAllValues(PREFIX_PHONE)));
+ }
+ if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
+ List emails = argMultimap.getAllValues(PREFIX_EMAIL);
+ if (emails.contains("")) {
+ throw new ParseException(EMPTY_EMAIL);
+ }
+ personDescriptor.setEmails(emails);
+ }
+ if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
+ personDescriptor.setAddresses(ParserUtil.parseAddresses(argMultimap.getAllValues(PREFIX_ADDRESS)));
+ }
+ if (argMultimap.getValue(PREFIX_CCA).isPresent()) {
+ List cca = ParserUtil.parseTagsForFind(argMultimap.getAllValues(PREFIX_CCA), Tag.CCA);
+ personDescriptor.setCcas(cca);
+ }
+ if (argMultimap.getValue(PREFIX_EDUCATION).isPresent()) {
+ List education = ParserUtil.parseTagsForFind(argMultimap.getAllValues(PREFIX_EDUCATION),
+ Tag.EDUCATION);
+ personDescriptor.setEducations(education);
+ }
+ if (argMultimap.getValue(PREFIX_MODULE).isPresent()) {
+ List module = ParserUtil.parseTagsForFind(argMultimap.getAllValues(PREFIX_MODULE), Tag.MODULE);
+ personDescriptor.setModules(module);
+ }
+ if (argMultimap.getValue(PREFIX_INTERNSHIP).isPresent()) {
+ List internship = ParserUtil.parseTagsForFind(argMultimap.getAllValues(PREFIX_INTERNSHIP),
+ Tag.INTERNSHIP);
+ personDescriptor.setInternships(internship);
+ }
- return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
- }
+ if (!personDescriptor.isAnyFieldPresent()) {
+ throw new ParseException(FindCommand.MESSAGE_NO_PARAMETERS);
+ }
+
+ if (isAndSearch) {
+ return new FindAndPredicateParser().parse(personDescriptor);
+ } else {
+ return new FindOrPredicateParser().parse(personDescriptor);
+ }
+ } else {
+ FindEventDescriptor eventDescriptor = new FindEventCommand.FindEventDescriptor();
+ if (argMultimap.getValue(PREFIX_EVENT_NAME).isPresent()) {
+ eventDescriptor.setNames(ParserUtil.parseEventNames(argMultimap.getAllValues(PREFIX_EVENT_NAME)));
+ }
+ if (argMultimap.getValue(PREFIX_INFO).isPresent()) {
+ eventDescriptor.setInformations(ParserUtil.parseInfos(argMultimap.getAllValues(PREFIX_INFO)));
+ }
+ if (argMultimap.getValue(PREFIX_PARTICIPANTS).isPresent()) {
+ eventDescriptor.setParticipants(ParserUtil.parseNames(argMultimap.getAllValues(PREFIX_PARTICIPANTS)));
+ }
+ if (argMultimap.getValue(PREFIX_DATETIME).isPresent()) {
+ eventDescriptor.setDateTimes(ParserUtil.parseDateTimes(argMultimap.getAllValues(PREFIX_DATETIME)));
+ }
+
+ if (!eventDescriptor.isAnyFieldPresent()) {
+ throw new ParseException(FindCommand.MESSAGE_NO_PARAMETERS);
+ }
+ return new FindEventPredicateParser().parse(eventDescriptor);
+ }
+ }
}
diff --git a/src/main/java/seedu/address/logic/parser/FindEventPredicateParser.java b/src/main/java/seedu/address/logic/parser/FindEventPredicateParser.java
new file mode 100644
index 00000000000..b760d814c7b
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/FindEventPredicateParser.java
@@ -0,0 +1,38 @@
+package seedu.address.logic.parser;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+
+import seedu.address.logic.commands.FindEventCommand;
+import seedu.address.model.event.DateTimePredicate;
+import seedu.address.model.event.Event;
+import seedu.address.model.event.EventInfoContainsKeywordsPredicate;
+import seedu.address.model.event.EventNameContainsKeywordsPredicate;
+import seedu.address.model.event.EventParticipantsContainsKeywordsPredicate;
+
+public class FindEventPredicateParser {
+ /**
+ * Parses a FindEventDescriptor into a single predicate that is true if any of the predicates in each field
+ * of the descriptor are satisfied.
+ *
+ * @param eventDescriptor an object describing the predicate list for each field.
+ * @return a FindCommand to be executed.
+ */
+ public FindEventCommand parse(FindEventCommand.FindEventDescriptor eventDescriptor) {
+ List> predicateList = new ArrayList<>();
+
+ eventDescriptor.getStringNames().ifPresent(names ->
+ predicateList.add(new EventNameContainsKeywordsPredicate(names)));
+ eventDescriptor.getStringInformations().ifPresent(infos ->
+ predicateList.add(new EventInfoContainsKeywordsPredicate(infos)));
+ eventDescriptor.getStringParticipants().ifPresent(parts ->
+ predicateList.add(new EventParticipantsContainsKeywordsPredicate(parts)));
+ eventDescriptor.getDateTimes().ifPresent(datetime ->
+ predicateList.add(new DateTimePredicate(datetime)));
+
+ Predicate predicate = predicateList.stream().reduce(x->false, Predicate::or);
+
+ return new FindEventCommand(predicate);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/FindOrPredicateParser.java b/src/main/java/seedu/address/logic/parser/FindOrPredicateParser.java
new file mode 100644
index 00000000000..fa28c7ed633
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/FindOrPredicateParser.java
@@ -0,0 +1,55 @@
+package seedu.address.logic.parser;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+
+import seedu.address.logic.commands.FindCommand;
+import seedu.address.logic.commands.FindCommand.FindPersonDescriptor;
+import seedu.address.model.person.AddressContainsKeywordsPredicateOr;
+import seedu.address.model.person.CcaContainsKeywordsPredicateOr;
+import seedu.address.model.person.EducationContainsKeywordsPredicateOr;
+import seedu.address.model.person.EmailContainsKeywordsPredicateOr;
+import seedu.address.model.person.InternshipContainsKeywordsPredicateOr;
+import seedu.address.model.person.ModuleContainsKeywordsPredicateOr;
+import seedu.address.model.person.NameContainsKeywordsPredicateOr;
+import seedu.address.model.person.Person;
+import seedu.address.model.person.PhoneContainsKeywordsPredicateOr;
+
+public class FindOrPredicateParser {
+
+
+ /**
+ * Parses a FindPersonDescriptor into a single predicate that is true if any of the predicates in each field
+ * of the descriptor are satisfied. A field is satisfied if any item from the list appears. The combined predicate
+ * is satisfied if any of the fields are satisfied.
+ *
+ * @param personDescriptor an object describing the predicte list for each field.
+ * @return a FindCommand to be executed.
+ */
+ public FindCommand parse(FindPersonDescriptor personDescriptor) {
+ List> predicateList = new ArrayList<>();
+
+ personDescriptor.getStringNames().ifPresent(names ->
+ predicateList.add(new NameContainsKeywordsPredicateOr(names)));
+ personDescriptor.getStringPhones().ifPresent(phones ->
+ predicateList.add(new PhoneContainsKeywordsPredicateOr(phones)));
+ personDescriptor.getStringEmails().ifPresent(emails ->
+ predicateList.add(new EmailContainsKeywordsPredicateOr(emails)));
+ personDescriptor.getStringAddresses().ifPresent(list ->
+ predicateList.add(new AddressContainsKeywordsPredicateOr(list)));
+
+ personDescriptor.getStringEducations().ifPresent(list ->
+ predicateList.add(new EducationContainsKeywordsPredicateOr(list)));
+ personDescriptor.getStringInternships().ifPresent(list ->
+ predicateList.add(new InternshipContainsKeywordsPredicateOr(list)));
+ personDescriptor.getStringModules().ifPresent(list ->
+ predicateList.add(new ModuleContainsKeywordsPredicateOr(list)));
+ personDescriptor.getStringCcas().ifPresent(list ->
+ predicateList.add(new CcaContainsKeywordsPredicateOr(list)));
+
+ Predicate predicate = predicateList.stream().reduce(x->false, Predicate::or);
+
+ return new FindCommand(predicate);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java
index b117acb9c55..aa955cf4965 100644
--- a/src/main/java/seedu/address/logic/parser/ParserUtil.java
+++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java
@@ -2,15 +2,24 @@
import static java.util.Objects.requireNonNull;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
+import java.util.List;
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.event.DateTime;
+import seedu.address.model.event.EventName;
+import seedu.address.model.event.Information;
import seedu.address.model.person.Address;
+import seedu.address.model.person.Cca;
+import seedu.address.model.person.Education;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Internship;
+import seedu.address.model.person.Module;
import seedu.address.model.person.Name;
import seedu.address.model.person.Phone;
import seedu.address.model.tag.Tag;
@@ -21,6 +30,9 @@
public class ParserUtil {
public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer.";
+ public static final String MESSAGE_INVALID_INDEX_MULTIPLE = "All indexes must be unique"
+ + " and a non-zero unsigned integer.";
+ public static final String INVALID_TAGTYPE = "The tag type is invalid!";
/**
* Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be
@@ -35,6 +47,38 @@ public static Index parseIndex(String oneBasedIndex) throws ParseException {
return Index.fromOneBased(Integer.parseInt(trimmedIndex));
}
+ /**
+ * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be
+ * trimmed.
+ * @throws ParseException if the specified any of the indexes are invalid (not non-zero unsigned integer).
+ */
+ public static Index[] parseIndexes(String oneBasedIndex) throws ParseException {
+ String trimmedIndex = oneBasedIndex.trim();
+ boolean isMultipleIndex = StringUtil.containsMultipleIndex(trimmedIndex);
+ boolean isAllValidIntegers = isMultipleIndex && StringUtil.isAllNonZeroUnsignedInteger(trimmedIndex);
+ boolean isValidMultipleIndex = isAllValidIntegers && StringUtil.isAllUniqueIntegers(trimmedIndex);
+
+ if (!isMultipleIndex && !StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) {
+ throw new ParseException(MESSAGE_INVALID_INDEX);
+ } else if (isMultipleIndex && !isValidMultipleIndex) {
+ throw new ParseException(MESSAGE_INVALID_INDEX_MULTIPLE);
+ }
+
+ return getIndexes(trimmedIndex);
+ }
+
+ /**
+ * Transforms a string of valid one-based indexes into an array of {@code Index}.
+ */
+ private static Index[] getIndexes(String trimmedIndex) {
+ String[] oneBasedArr = trimmedIndex.split(" ");
+ Index[] indexArr = new Index[oneBasedArr.length];
+ for (int i = 0; i < oneBasedArr.length; i++) {
+ indexArr[i] = Index.fromOneBased(Integer.parseInt(oneBasedArr[i]));
+ }
+ return indexArr;
+ }
+
/**
* Parses a {@code String name} into a {@code Name}.
* Leading and trailing whitespaces will be trimmed.
@@ -50,6 +94,21 @@ public static Name parseName(String name) throws ParseException {
return new Name(trimmedName);
}
+ /**
+ * Parses a {@code List } into a {@code List}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException through parseName.
+ */
+ public static List parseNames(List list) throws ParseException {
+ requireNonNull(list);
+ final Set set = new HashSet<>();
+ for (String value : list) {
+ set.add(parseName(value.trim()));
+ }
+ return new ArrayList<>(set);
+ }
+
/**
* Parses a {@code String phone} into a {@code Phone}.
* Leading and trailing whitespaces will be trimmed.
@@ -65,6 +124,21 @@ public static Phone parsePhone(String phone) throws ParseException {
return new Phone(trimmedPhone);
}
+ /**
+ * Parses a {@code List } into a {@code List}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException through parsePhone.
+ */
+ public static List parsePhones(List list) throws ParseException {
+ requireNonNull(list);
+ final Set set = new HashSet<>();
+ for (String value : list) {
+ set.add(parsePhone(value.trim()));
+ }
+ return new ArrayList<>(set);
+ }
+
/**
* Parses a {@code String address} into an {@code Address}.
* Leading and trailing whitespaces will be trimmed.
@@ -80,6 +154,21 @@ public static Address parseAddress(String address) throws ParseException {
return new Address(trimmedAddress);
}
+ /**
+ * Parses a {@code List } into a {@code List}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException through parseAddress.
+ */
+ public static List parseAddresses(List list) throws ParseException {
+ requireNonNull(list);
+ final Set set = new HashSet<>();
+ for (String value : list) {
+ set.add(parseAddress(value.trim()));
+ }
+ return new ArrayList<>(set);
+ }
+
/**
* Parses a {@code String email} into an {@code Email}.
* Leading and trailing whitespaces will be trimmed.
@@ -95,30 +184,215 @@ public static Email parseEmail(String email) throws ParseException {
return new Email(trimmedEmail);
}
+
/**
- * Parses a {@code String tag} into a {@code Tag}.
+ * Parses a {@code List } into a {@code List}.
* Leading and trailing whitespaces will be trimmed.
*
- * @throws ParseException if the given {@code tag} is invalid.
*/
- public static Tag parseTag(String tag) throws ParseException {
+ public static List parseEmails(List list) throws ParseException {
+ requireNonNull(list);
+ final Set set = new HashSet<>();
+ for (String value : list) {
+ for (String s: value.split(" ")) {
+ set.add(parseEmail(s.trim()));
+ }
+ }
+ return new ArrayList<>(set);
+ }
+
+
+ /**
+ * 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, String type) throws ParseException {
requireNonNull(tag);
- String trimmedTag = tag.trim();
- if (!Tag.isValidTagName(trimmedTag)) {
- throw new ParseException(Tag.MESSAGE_CONSTRAINTS);
+ String trimmedTag = tag.trim().toLowerCase();
+
+ switch (type) {
+ case Tag.EDUCATION:
+ if (!Education.isValidTagName(trimmedTag)) {
+ throw new ParseException(Education.MESSAGE_CONSTRAINTS);
+ }
+ return new Education(trimmedTag);
+ case Tag.INTERNSHIP:
+ if (!Internship.isValidTagName(trimmedTag)) {
+ throw new ParseException(Internship.MESSAGE_CONSTRAINTS);
+ }
+ return new Internship(trimmedTag);
+ case Tag.MODULE:
+ if (!Module.isValidTagName(trimmedTag)) {
+ throw new ParseException(Module.MESSAGE_CONSTRAINTS);
+ }
+ return new Module(trimmedTag);
+ case Tag.CCA:
+ if (!Cca.isValidTagName(trimmedTag)) {
+ throw new ParseException(Cca.MESSAGE_CONSTRAINTS);
+ }
+ return new Cca(trimmedTag);
+ default:
+ throw new ParseException(INVALID_TAGTYPE);
+ }
+ }
+
+ /**
+ * Parses {@code Collection tags} into a {@code List}.
+ */
+ public static List parseTags(Collection tags, String type) throws ParseException {
+ requireNonNull(tags);
+ final Set tagSet = new HashSet<>();
+ for (String tagName : tags) {
+ tagSet.add(parseTag(tagName, type));
}
- return new Tag(trimmedTag);
+ return new ArrayList<>(tagSet);
}
/**
* Parses {@code Collection tags} into a {@code Set}.
+ * Returns an empty ArrayList if the tags list is [""], this is the case that the tag list is to be cleared.
*/
- public static Set parseTags(Collection tags) throws ParseException {
+ public static List parseTagsForEdit(Collection tags, String type) throws ParseException {
requireNonNull(tags);
final Set tagSet = new HashSet<>();
+ // This is the case that the tag list is meant to be cleared
+ if (tags.size() == 1 && tags.contains("")) {
+ return new ArrayList<>();
+ }
for (String tagName : tags) {
- tagSet.add(parseTag(tagName));
+ tagSet.add(parseTag(tagName, type));
+ }
+ return new ArrayList<>(tagSet);
+ }
+
+ /**
+ * Parses {@code Collection tags} into a {@code Set}.
+ * Returns a list with all tagNames split using whitespace and trimmed.
+ */
+ public static List parseTagsForFind(Collection tags, String type) throws ParseException {
+ requireNonNull(tags);
+ final Set tagSet = new HashSet<>();
+ for (String tagName : tags) {
+ tagSet.add(parseTag(tagName.trim(), type));
+ }
+ return new ArrayList<>(tagSet);
+ }
+
+ /**
+ * Parses {@code String name} into an {@code EventName}.
+ * Leading and trailing whitespaces will be trimmed.
+ */
+ public static EventName parseEventName(String name) throws ParseException {
+ requireNonNull(name);
+ String trimmedName = name.trim();
+ if (!EventName.isValidEventName(trimmedName)) {
+ throw new ParseException(EventName.MESSAGE_CONSTRAINTS);
+ }
+ return new EventName(trimmedName);
+ }
+
+ /**
+ * Parses a {@code List } into a {@code List}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if any single list item has more than one word.
+ */
+ public static List parseEventNames(List list) throws ParseException {
+ requireNonNull(list);
+ final Set set = new HashSet<>();
+ for (String value : list) {
+ set.add(parseEventName(value.trim()));
+ }
+ return new ArrayList<>(set);
+ }
+
+ /**
+ * Parses {@code String info} into an {@code Information}.
+ * Leading and trailing whitespaces will be trimmed.
+ */
+ public static Information parseInfo(String info) throws ParseException {
+ requireNonNull(info);
+ String trimmedInfo = info.trim();
+ if (!Information.isValidInformation(trimmedInfo)) {
+ throw new ParseException(Information.MESSAGE_CONSTRAINTS);
+ }
+ return new Information(trimmedInfo);
+ }
+
+ /**
+ * Parses a {@code List } into a {@code List}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if any single list item has more than one word.
+ */
+ public static List parseInfos(List list) throws ParseException {
+ requireNonNull(list);
+ final Set set = new HashSet<>();
+ for (String value : list) {
+ set.add(parseInfo(value.trim()));
+ }
+ return new ArrayList<>(set);
+ }
+
+ /**
+ * Parses {@code String date} and {@code String time} into a {@code DateTime}.
+ * Returns a DateTime object that contains formatted date and time of the event.
+ */
+ public static DateTime parseDateTime(String date, String time) throws ParseException {
+ requireNonNull(date);
+ requireNonNull(time);
+ String trimmedDate = date.trim();
+ String trimmedTime = time.trim();
+
+ if (!DateTime.isValidTime(trimmedTime)) {
+ throw new ParseException(DateTime.TIME_MESSAGE_CONSTRAINTS);
+ } else if (!DateTime.isValidDate(trimmedDate)) {
+ throw new ParseException(DateTime.DATE_MESSAGE_CONSTRAINTS);
+ }
+
+ String[] tempDate = trimmedDate.split("-");
+ String[] tempTime = trimmedTime.split(":");
+ int year = Integer.parseInt(tempDate[0]);
+ int month = Integer.parseInt(tempDate[1]);
+ int day = Integer.parseInt(tempDate[2]);
+ int hour = Integer.parseInt(tempTime[0]);
+ int min = Integer.parseInt(tempTime[1]);
+
+
+ if (!DateTime.isValidDateTime(year, month, day, hour, min)) {
+ throw new ParseException(DateTime.DATETIME_MESSAGE_CONSTRAINTS);
+ }
+
+ return new DateTime(year, month, day, hour, min);
+ }
+
+ /**
+ * Parses string find prefix for date and time into a DateTime object
+ * @param dateTime
+ * @throws ParseException
+ */
+ public static DateTime parseDateTimeForFind(String dateTime) throws ParseException {
+ String trimmedDateTime = dateTime.trim();
+ if (trimmedDateTime.split(" ").length != 2) {
+ throw new ParseException(FindCommandParser.DATE_TIME_FORMAT);
+ }
+ return parseDateTime(trimmedDateTime.split(" ")[0], trimmedDateTime.split(" ")[1]);
+ }
+
+ /**
+ * Parses a list of string DateTimes into a list of DateTime objects
+ *
+ * @param stringDateTimes
+ * @throws ParseException
+ */
+ public static List parseDateTimes(List stringDateTimes) throws ParseException {
+ requireNonNull(stringDateTimes);
+ final Set set = new HashSet<>();
+ for (String value : stringDateTimes) {
+ set.add(parseDateTimeForFind(value.trim()));
}
- return tagSet;
+ return new ArrayList<>(set);
}
}
diff --git a/src/main/java/seedu/address/logic/parser/RemoveTagCommandParser.java b/src/main/java/seedu/address/logic/parser/RemoveTagCommandParser.java
new file mode 100644
index 00000000000..cec21f1a4d5
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/RemoveTagCommandParser.java
@@ -0,0 +1,48 @@
+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_CCA;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EDUCATION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INTERNSHIP;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MODULE;
+
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.logic.commands.RemoveTagCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.tag.Tag;
+
+/**
+ * Parses input arguments and creates a new RemoveTagCommand object
+ */
+public class RemoveTagCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the RemoveTagCommand
+ * and returns an RemoveTagCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public RemoveTagCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_CCA, PREFIX_EDUCATION,
+ PREFIX_MODULE, PREFIX_INTERNSHIP);
+
+ Index index;
+ try {
+ index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ } catch (IllegalValueException ive) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, RemoveTagCommand.MESSAGE_USAGE), ive);
+ }
+
+ List education = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_EDUCATION), Tag.EDUCATION);
+ List internship = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_INTERNSHIP), Tag.INTERNSHIP);
+ List module = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_MODULE), Tag.MODULE);
+ List cca = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_CCA), Tag.CCA);
+
+ return new RemoveTagCommand(index, education, internship, module, cca);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/ShowEventsCommandParser.java b/src/main/java/seedu/address/logic/parser/ShowEventsCommandParser.java
new file mode 100644
index 00000000000..41a6fe68ed1
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/ShowEventsCommandParser.java
@@ -0,0 +1,43 @@
+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.model.Model.PREDICATE_SHOW_ALL_EVENTS;
+import static seedu.address.model.Model.PREDICATE_SHOW_PAST_EVENTS;
+import static seedu.address.model.Model.PREDICATE_SHOW_UPCOMING_EVENTS;
+
+import java.util.function.Predicate;
+
+import seedu.address.logic.commands.ShowEventsCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.event.Event;
+
+public class ShowEventsCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the ShowEventCommand
+ * and returns a ShowEventCommand object for execution.
+ *
+ * @return a ShowEventCommand object that will filter the events list.
+ * @throws ParseException if the user input does not conform the expected format, such as having an invalid flag.
+ */
+ public ShowEventsCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args);
+
+ Predicate predicate;
+
+ if (argMultimap.getPreamble().equals("-upcoming")) {
+ predicate = PREDICATE_SHOW_UPCOMING_EVENTS;
+ } else if (argMultimap.getPreamble().equals("-past")) {
+ predicate = PREDICATE_SHOW_PAST_EVENTS;
+ } else if (argMultimap.getPreamble().equals("")) {
+ predicate = PREDICATE_SHOW_ALL_EVENTS;
+ } else {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ShowEventsCommand.MESSAGE_USAGE));
+ }
+
+ return new ShowEventsCommand(predicate);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/TagCommandParser.java b/src/main/java/seedu/address/logic/parser/TagCommandParser.java
new file mode 100644
index 00000000000..f2747d7169b
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/TagCommandParser.java
@@ -0,0 +1,48 @@
+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_CCA;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EDUCATION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INTERNSHIP;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MODULE;
+
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.logic.commands.TagCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.tag.Tag;
+
+
+/**
+ * Parses input arguments and creates a new TagCommand object
+ */
+public class TagCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the TagCommand
+ * and returns an TagCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public TagCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_CCA, PREFIX_EDUCATION,
+ PREFIX_MODULE, PREFIX_INTERNSHIP);
+
+ Index index;
+ try {
+ index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ } catch (IllegalValueException ive) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, TagCommand.MESSAGE_USAGE), ive);
+ }
+
+ List education = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_EDUCATION), Tag.EDUCATION);
+ List internship = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_INTERNSHIP), Tag.INTERNSHIP);
+ List module = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_MODULE), Tag.MODULE);
+ List cca = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_CCA), Tag.CCA);
+
+ return new TagCommand(index, education, internship, module, cca);
+ }
+}
diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java
index 1a943a0781a..6c9209a129b 100644
--- a/src/main/java/seedu/address/model/AddressBook.java
+++ b/src/main/java/seedu/address/model/AddressBook.java
@@ -5,6 +5,8 @@
import java.util.List;
import javafx.collections.ObservableList;
+import seedu.address.model.event.Event;
+import seedu.address.model.event.UniqueEventList;
import seedu.address.model.person.Person;
import seedu.address.model.person.UniquePersonList;
@@ -15,6 +17,7 @@
public class AddressBook implements ReadOnlyAddressBook {
private final UniquePersonList persons;
+ private final UniqueEventList events;
/*
* The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication
@@ -25,6 +28,7 @@ public class AddressBook implements ReadOnlyAddressBook {
*/
{
persons = new UniquePersonList();
+ events = new UniqueEventList();
}
public AddressBook() {}
@@ -54,6 +58,7 @@ public void resetData(ReadOnlyAddressBook newData) {
requireNonNull(newData);
setPersons(newData.getPersonList());
+ setEvents(newData.getEventList());
}
//// person-level operations
@@ -74,6 +79,7 @@ public void addPerson(Person p) {
persons.add(p);
}
+
/**
* Replaces the given person {@code target} in the list with {@code editedPerson}.
* {@code target} must exist in the address book.
@@ -106,15 +112,68 @@ public ObservableList getPersonList() {
return persons.asUnmodifiableObservableList();
}
+ /**
+ * Replaces the contents of the event list with {@code events}.
+ * {@code events} must not contain duplicate events.
+ */
+ public void setEvents(List events) {
+ this.events.setEvents(events);
+ }
+
+ //// person-level operations
+
+ /**
+ * Returns true if an event with the same identity as {@code event} exists in the address book.
+ */
+ public boolean hasEvent(Event event) {
+ requireNonNull(event);
+ return events.contains(event);
+ }
+
+ /**
+ * Adds an event to the address book.
+ * The event must not already exist in the address book.
+ */
+ public void addEvent(Event e) {
+ events.add(e);
+ }
+
+ /**
+ * Replaces the given event {@code target} in the list with {@code editedEvent}.
+ * {@code target} must exist in the address book.
+ * The event identity of {@code editedEvent} must not be the same as another existing event in the address book.
+ */
+ public void setEvent(Event target, Event editedEvent) {
+ requireNonNull(editedEvent);
+
+ events.setEvent(target, editedEvent);
+ }
+
+ /**
+ * Removes {@code key} from this {@code AddressBook}.
+ * {@code key} must exist in the address book.
+ */
+ public void removeEvent(Event key) {
+ events.remove(key);
+ }
+
+ //// util methods
+
+ @Override
+ public ObservableList getEventList() {
+ return events.asUnmodifiableObservableList();
+ }
+
@Override
public boolean equals(Object other) {
return other == this // short circuit if same object
|| (other instanceof AddressBook // instanceof handles nulls
- && persons.equals(((AddressBook) other).persons));
+ && persons.equals(((AddressBook) other).persons)
+ && events.equals(((AddressBook) other).events));
}
@Override
public int hashCode() {
- return persons.hashCode();
+ return persons.hashCode() + events.hashCode();
}
}
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
index d54df471c1f..f207057e84c 100644
--- a/src/main/java/seedu/address/model/Model.java
+++ b/src/main/java/seedu/address/model/Model.java
@@ -1,10 +1,12 @@
package seedu.address.model;
import java.nio.file.Path;
+import java.time.LocalDateTime;
import java.util.function.Predicate;
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
+import seedu.address.model.event.Event;
import seedu.address.model.person.Person;
/**
@@ -14,6 +16,19 @@ public interface Model {
/** {@code Predicate} that always evaluate to true */
Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true;
+ /** {@code Predicate} that always evaluate to true */
+ Predicate PREDICATE_SHOW_ALL_EVENTS = unused -> true;
+
+ Predicate PREDICATE_SHOW_UPCOMING_EVENTS = event -> {
+ LocalDateTime now = LocalDateTime.now();
+ return event.getDateTime().value.isAfter(now);
+ };
+
+ Predicate PREDICATE_SHOW_PAST_EVENTS = event -> {
+ LocalDateTime now = LocalDateTime.now();
+ return event.getDateTime().value.isBefore(now);
+ };
+
/**
* Replaces user prefs data with the data in {@code userPrefs}.
*/
@@ -76,12 +91,45 @@ public interface Model {
*/
void setPerson(Person target, Person editedPerson);
+ /**
+ * Returns true if an event with the same identity as {@code event} exists in the address book.
+ */
+ boolean hasEvent(Event event);
+
+ /**
+ * Deletes the given event.
+ * The event must exist in the address book.
+ */
+ void deleteEvent(Event target);
+
+ /**
+ * Adds the given event.
+ * {@code event} must not already exist in the address book.
+ */
+ void addEvent(Event event);
+
+ /**
+ * Replaces the given event {@code target} with {@code editedEvent}.
+ * {@code target} must exist in the address book.
+ * The event identity of {@code editedEvent} must not be the same as another existing event in the address book.
+ */
+ void setEvent(Event target, Event editedEvent);
+
/** Returns an unmodifiable view of the filtered person list */
ObservableList getFilteredPersonList();
+ /** Returns an unmodifiable view of the filtered event list */
+ ObservableList getFilteredEventList();
+
/**
* 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);
+
+ /**
+ * Updates the filter of the filtered event list to filter by the given {@code predicate}.
+ * @throws NullPointerException if {@code predicate} is null.
+ */
+ void updateFilteredEventList(Predicate predicate);
}
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
index 86c1df298d7..6d7771bb361 100644
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ b/src/main/java/seedu/address/model/ModelManager.java
@@ -11,6 +11,7 @@
import javafx.collections.transformation.FilteredList;
import seedu.address.commons.core.GuiSettings;
import seedu.address.commons.core.LogsCenter;
+import seedu.address.model.event.Event;
import seedu.address.model.person.Person;
/**
@@ -22,6 +23,7 @@ public class ModelManager implements Model {
private final AddressBook addressBook;
private final UserPrefs userPrefs;
private final FilteredList filteredPersons;
+ private final FilteredList filteredEvents;
/**
* Initializes a ModelManager with the given addressBook and userPrefs.
@@ -34,6 +36,7 @@ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs
this.addressBook = new AddressBook(addressBook);
this.userPrefs = new UserPrefs(userPrefs);
filteredPersons = new FilteredList<>(this.addressBook.getPersonList());
+ filteredEvents = new FilteredList<>(this.addressBook.getEventList());
}
public ModelManager() {
@@ -147,4 +150,40 @@ public boolean equals(Object obj) {
&& filteredPersons.equals(other.filteredPersons);
}
+ //=========== EventBook =================================================================================
+
+ @Override
+ public boolean hasEvent(Event event) {
+ requireNonNull(event);
+ return addressBook.hasEvent(event);
+ }
+
+ @Override
+ public void deleteEvent(Event target) {
+ addressBook.removeEvent(target);
+ }
+
+ @Override
+ public void addEvent(Event event) {
+ addressBook.addEvent(event);
+ }
+
+ @Override
+ public void setEvent(Event target, Event editedEvent) {
+ requireAllNonNull(target, editedEvent);
+
+ addressBook.setEvent(target, editedEvent);
+ }
+
+ @Override
+ public ObservableList getFilteredEventList() {
+ return filteredEvents;
+ }
+
+ @Override
+ public void updateFilteredEventList(Predicate predicate) {
+ requireNonNull(predicate);
+ filteredEvents.setPredicate(predicate);
+ }
+
}
diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
index 6ddc2cd9a29..05d681dd157 100644
--- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
+++ b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
@@ -1,6 +1,7 @@
package seedu.address.model;
import javafx.collections.ObservableList;
+import seedu.address.model.event.Event;
import seedu.address.model.person.Person;
/**
@@ -14,4 +15,10 @@ public interface ReadOnlyAddressBook {
*/
ObservableList getPersonList();
+ /**
+ * Returns an unmodifiable view of the events list.
+ * This list will not contain any duplicate events.
+ */
+ ObservableList getEventList();
+
}
diff --git a/src/main/java/seedu/address/model/event/DateTime.java b/src/main/java/seedu/address/model/event/DateTime.java
new file mode 100644
index 00000000000..9a0062e392a
--- /dev/null
+++ b/src/main/java/seedu/address/model/event/DateTime.java
@@ -0,0 +1,76 @@
+package seedu.address.model.event;
+
+import java.time.DateTimeException;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+public class DateTime implements Comparable {
+ public static final String DATETIME_MESSAGE_CONSTRAINTS = "Date and Time has to be valid!";
+ public static final String DATE_MESSAGE_CONSTRAINTS = "Date has to be in the format of yyyy-MM-DD!";
+ public static final String TIME_MESSAGE_CONSTRAINTS = "Time has to be in the format of HH:mm!";
+ public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
+ public static final DateTimeFormatter DISPLAY_FORMATTER = DateTimeFormatter.ofPattern("dd-MMM-yyyy HH:mm");
+
+ public static final String DATE_VALIDATION_REGEX = "^(\\d{4})-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])$";
+ public static final String TIME_VALIDATION_REGEX = "([01]?[0-9]|2[0-3]):[0-5][0-9]";
+
+ public final LocalDateTime value;
+
+ /**
+ * Constructs a {@Code DateTime}
+ * @param year the event year
+ * @param month the event month
+ * @param day the event day
+ * @param hour the event hour
+ * @param min the event min
+ */
+ public DateTime(int year, int month, int day, int hour, int min) {
+ value = LocalDateTime.of(year, month, day, hour, min);
+ }
+
+ /**
+ * Returns true if a given date and time is valid.
+ */
+ public static boolean isValidDateTime(int year, int month, int day, int hour, int min) {
+ LocalDateTime now = LocalDateTime.now();
+ try {
+ return LocalDateTime.of(year, month, day, hour, min).isAfter(now);
+ } catch (DateTimeException e) {
+ return false;
+ }
+ }
+
+ public static boolean isValidDate(String test) {
+ return test.matches(DATE_VALIDATION_REGEX);
+ }
+
+ public static boolean isValidTime(String test) {
+ return test.matches(TIME_VALIDATION_REGEX);
+ }
+
+ public String displayDateTime() {
+ return value.format(DISPLAY_FORMATTER);
+ }
+
+ @Override
+ public String toString() {
+ return value.format(DATE_TIME_FORMATTER);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this
+ || (other instanceof DateTime
+ && value.isEqual(((DateTime) other).value));
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override
+ public int compareTo(DateTime other) {
+ return value.compareTo(other.value);
+ }
+}
diff --git a/src/main/java/seedu/address/model/event/DateTimePredicate.java b/src/main/java/seedu/address/model/event/DateTimePredicate.java
new file mode 100644
index 00000000000..2186b726736
--- /dev/null
+++ b/src/main/java/seedu/address/model/event/DateTimePredicate.java
@@ -0,0 +1,32 @@
+package seedu.address.model.event;
+
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public class DateTimePredicate implements Predicate {
+ private final List keywords;
+ private final Function